blob: 3777b84497899e813776a058e997024e9f27316b [file] [log] [blame]
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001/*
Sujithcee075a2009-03-13 09:07:23 +05302 * Copyright (c) 2008-2009 Atheros Communications Inc.
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Luis R. Rodriguezf078f202008-08-04 00:16:41 -070017#include <linux/nl80211.h>
Sujith394cf0a2009-02-09 13:26:54 +053018#include "ath9k.h"
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -070019#include "btcoex.h"
Luis R. Rodriguezf078f202008-08-04 00:16:41 -070020
Luis R. Rodriguezf078f202008-08-04 00:16:41 -070021static char *dev_info = "ath9k";
22
23MODULE_AUTHOR("Atheros Communications");
24MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
25MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
26MODULE_LICENSE("Dual BSD/GPL");
27
Jouni Malinenb3bd89c2009-02-24 13:42:01 +020028static int modparam_nohwcrypt;
29module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
30MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
31
Luis R. Rodriguezfaa27fa2009-10-06 21:19:06 -040032static unsigned int ath9k_debug = ATH_DBG_DEFAULT;
33module_param_named(debug, ath9k_debug, uint, 0);
Luis R. Rodriguezaf1fc672009-10-08 01:00:18 -040034MODULE_PARM_DESC(debug, "Debugging mask");
Luis R. Rodriguezfaa27fa2009-10-06 21:19:06 -040035
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -080036/* We use the hw_value as an index into our private channel structure */
37
38#define CHAN2G(_freq, _idx) { \
39 .center_freq = (_freq), \
40 .hw_value = (_idx), \
Luis R. Rodriguezeeddfd92009-05-19 17:49:46 -040041 .max_power = 20, \
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -080042}
43
44#define CHAN5G(_freq, _idx) { \
45 .band = IEEE80211_BAND_5GHZ, \
46 .center_freq = (_freq), \
47 .hw_value = (_idx), \
Luis R. Rodriguezeeddfd92009-05-19 17:49:46 -040048 .max_power = 20, \
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -080049}
50
51/* Some 2 GHz radios are actually tunable on 2312-2732
52 * on 5 MHz steps, we support the channels which we know
53 * we have calibration data for all cards though to make
54 * this static */
55static struct ieee80211_channel ath9k_2ghz_chantable[] = {
56 CHAN2G(2412, 0), /* Channel 1 */
57 CHAN2G(2417, 1), /* Channel 2 */
58 CHAN2G(2422, 2), /* Channel 3 */
59 CHAN2G(2427, 3), /* Channel 4 */
60 CHAN2G(2432, 4), /* Channel 5 */
61 CHAN2G(2437, 5), /* Channel 6 */
62 CHAN2G(2442, 6), /* Channel 7 */
63 CHAN2G(2447, 7), /* Channel 8 */
64 CHAN2G(2452, 8), /* Channel 9 */
65 CHAN2G(2457, 9), /* Channel 10 */
66 CHAN2G(2462, 10), /* Channel 11 */
67 CHAN2G(2467, 11), /* Channel 12 */
68 CHAN2G(2472, 12), /* Channel 13 */
69 CHAN2G(2484, 13), /* Channel 14 */
70};
71
72/* Some 5 GHz radios are actually tunable on XXXX-YYYY
73 * on 5 MHz steps, we support the channels which we know
74 * we have calibration data for all cards though to make
75 * this static */
76static struct ieee80211_channel ath9k_5ghz_chantable[] = {
77 /* _We_ call this UNII 1 */
78 CHAN5G(5180, 14), /* Channel 36 */
79 CHAN5G(5200, 15), /* Channel 40 */
80 CHAN5G(5220, 16), /* Channel 44 */
81 CHAN5G(5240, 17), /* Channel 48 */
82 /* _We_ call this UNII 2 */
83 CHAN5G(5260, 18), /* Channel 52 */
84 CHAN5G(5280, 19), /* Channel 56 */
85 CHAN5G(5300, 20), /* Channel 60 */
86 CHAN5G(5320, 21), /* Channel 64 */
87 /* _We_ call this "Middle band" */
88 CHAN5G(5500, 22), /* Channel 100 */
89 CHAN5G(5520, 23), /* Channel 104 */
90 CHAN5G(5540, 24), /* Channel 108 */
91 CHAN5G(5560, 25), /* Channel 112 */
92 CHAN5G(5580, 26), /* Channel 116 */
93 CHAN5G(5600, 27), /* Channel 120 */
94 CHAN5G(5620, 28), /* Channel 124 */
95 CHAN5G(5640, 29), /* Channel 128 */
96 CHAN5G(5660, 30), /* Channel 132 */
97 CHAN5G(5680, 31), /* Channel 136 */
98 CHAN5G(5700, 32), /* Channel 140 */
99 /* _We_ call this UNII 3 */
100 CHAN5G(5745, 33), /* Channel 149 */
101 CHAN5G(5765, 34), /* Channel 153 */
102 CHAN5G(5785, 35), /* Channel 157 */
103 CHAN5G(5805, 36), /* Channel 161 */
104 CHAN5G(5825, 37), /* Channel 165 */
105};
106
Felix Fietkau545750d2009-11-23 22:21:01 +0100107/* Atheros hardware rate code addition for short premble */
108#define SHPCHECK(__hw_rate, __flags) \
109 ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0)
110
111#define RATE(_bitrate, _hw_rate, _flags) { \
112 .bitrate = (_bitrate), \
113 .flags = (_flags), \
114 .hw_value = (_hw_rate), \
115 .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \
116}
117
118static struct ieee80211_rate ath9k_legacy_rates[] = {
119 RATE(10, 0x1b, 0),
120 RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
121 RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
122 RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
123 RATE(60, 0x0b, 0),
124 RATE(90, 0x0f, 0),
125 RATE(120, 0x0a, 0),
126 RATE(180, 0x0e, 0),
127 RATE(240, 0x09, 0),
128 RATE(360, 0x0d, 0),
129 RATE(480, 0x08, 0),
130 RATE(540, 0x0c, 0),
131};
132
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -0800133static void ath_cache_conf_rate(struct ath_softc *sc,
134 struct ieee80211_conf *conf)
Sujithff37e332008-11-24 12:07:55 +0530135{
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800136 switch (conf->channel->band) {
137 case IEEE80211_BAND_2GHZ:
138 if (conf_is_ht20(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100139 sc->cur_rate_mode = ATH9K_MODE_11NG_HT20;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800140 else if (conf_is_ht40_minus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100141 sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800142 else if (conf_is_ht40_plus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100143 sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS;
Luis R. Rodriguez96742252008-12-23 15:58:38 -0800144 else
Felix Fietkau545750d2009-11-23 22:21:01 +0100145 sc->cur_rate_mode = ATH9K_MODE_11G;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800146 break;
147 case IEEE80211_BAND_5GHZ:
148 if (conf_is_ht20(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100149 sc->cur_rate_mode = ATH9K_MODE_11NA_HT20;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800150 else if (conf_is_ht40_minus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100151 sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800152 else if (conf_is_ht40_plus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100153 sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800154 else
Felix Fietkau545750d2009-11-23 22:21:01 +0100155 sc->cur_rate_mode = ATH9K_MODE_11A;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800156 break;
157 default:
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -0800158 BUG_ON(1);
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800159 break;
160 }
Sujithff37e332008-11-24 12:07:55 +0530161}
162
163static void ath_update_txpow(struct ath_softc *sc)
164{
Sujithcbe61d82009-02-09 13:27:12 +0530165 struct ath_hw *ah = sc->sc_ah;
Sujithff37e332008-11-24 12:07:55 +0530166 u32 txpow;
167
Sujith17d79042009-02-09 13:27:03 +0530168 if (sc->curtxpow != sc->config.txpowlimit) {
169 ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit);
Sujithff37e332008-11-24 12:07:55 +0530170 /* read back in case value is clamped */
171 ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
Sujith17d79042009-02-09 13:27:03 +0530172 sc->curtxpow = txpow;
Sujithff37e332008-11-24 12:07:55 +0530173 }
174}
175
176static u8 parse_mpdudensity(u8 mpdudensity)
177{
178 /*
179 * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
180 * 0 for no restriction
181 * 1 for 1/4 us
182 * 2 for 1/2 us
183 * 3 for 1 us
184 * 4 for 2 us
185 * 5 for 4 us
186 * 6 for 8 us
187 * 7 for 16 us
188 */
189 switch (mpdudensity) {
190 case 0:
191 return 0;
192 case 1:
193 case 2:
194 case 3:
195 /* Our lower layer calculations limit our precision to
196 1 microsecond */
197 return 1;
198 case 4:
199 return 2;
200 case 5:
201 return 4;
202 case 6:
203 return 8;
204 case 7:
205 return 16;
206 default:
207 return 0;
208 }
209}
210
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +0530211static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
212 struct ieee80211_hw *hw)
213{
214 struct ieee80211_channel *curchan = hw->conf.channel;
215 struct ath9k_channel *channel;
216 u8 chan_idx;
217
218 chan_idx = curchan->hw_value;
219 channel = &sc->sc_ah->channels[chan_idx];
220 ath9k_update_ichannel(sc, hw, channel);
221 return channel;
222}
223
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700224static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -0700225{
226 unsigned long flags;
227 bool ret;
228
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700229 spin_lock_irqsave(&sc->sc_pm_lock, flags);
230 ret = ath9k_hw_setpower(sc->sc_ah, mode);
231 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -0700232
233 return ret;
234}
235
Luis R. Rodrigueza91d75a2009-09-09 20:29:18 -0700236void ath9k_ps_wakeup(struct ath_softc *sc)
237{
238 unsigned long flags;
239
240 spin_lock_irqsave(&sc->sc_pm_lock, flags);
241 if (++sc->ps_usecount != 1)
242 goto unlock;
243
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700244 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
Luis R. Rodrigueza91d75a2009-09-09 20:29:18 -0700245
246 unlock:
247 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
248}
249
250void ath9k_ps_restore(struct ath_softc *sc)
251{
252 unsigned long flags;
253
254 spin_lock_irqsave(&sc->sc_pm_lock, flags);
255 if (--sc->ps_usecount != 0)
256 goto unlock;
257
258 if (sc->ps_enabled &&
259 !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
260 SC_OP_WAIT_FOR_CAB |
261 SC_OP_WAIT_FOR_PSPOLL_DATA |
262 SC_OP_WAIT_FOR_TX_ACK)))
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700263 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
Luis R. Rodrigueza91d75a2009-09-09 20:29:18 -0700264
265 unlock:
266 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
267}
268
Sujithff37e332008-11-24 12:07:55 +0530269/*
270 * Set/change channels. If the channel is really being changed, it's done
271 * by reseting the chip. To accomplish this we must first cleanup any pending
272 * DMA, then restart stuff.
273*/
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200274int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
275 struct ath9k_channel *hchan)
Sujithff37e332008-11-24 12:07:55 +0530276{
Sujithcbe61d82009-02-09 13:27:12 +0530277 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700278 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700279 struct ieee80211_conf *conf = &common->hw->conf;
Sujithff37e332008-11-24 12:07:55 +0530280 bool fastcc = true, stopped;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800281 struct ieee80211_channel *channel = hw->conf.channel;
282 int r;
Sujithff37e332008-11-24 12:07:55 +0530283
284 if (sc->sc_flags & SC_OP_INVALID)
285 return -EIO;
286
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530287 ath9k_ps_wakeup(sc);
288
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800289 /*
290 * This is only performed if the channel settings have
291 * actually changed.
292 *
293 * To switch channels clear any pending DMA operations;
294 * wait long enough for the RX fifo to drain, reset the
295 * hardware at the new frequency, and then re-enable
296 * the relevant bits of the h/w.
297 */
298 ath9k_hw_set_interrupts(ah, 0);
Sujith043a0402009-01-16 21:38:47 +0530299 ath_drain_all_txq(sc, false);
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800300 stopped = ath_stoprecv(sc);
Sujithff37e332008-11-24 12:07:55 +0530301
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800302 /* XXX: do not flush receive queue here. We don't want
303 * to flush data frames already in queue because of
304 * changing channel. */
Sujithff37e332008-11-24 12:07:55 +0530305
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800306 if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
307 fastcc = false;
Sujithff37e332008-11-24 12:07:55 +0530308
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700309 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700310 "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n",
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700311 sc->sc_ah->curchan->channel,
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700312 channel->center_freq, conf_is_ht40(conf));
Sujith99405f92008-11-24 12:08:35 +0530313
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800314 spin_lock_bh(&sc->sc_resetlock);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800315
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800316 r = ath9k_hw_reset(ah, hchan, fastcc);
317 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700318 ath_print(common, ATH_DBG_FATAL,
319 "Unable to reset channel (%u Mhz) "
320 "reset status %d\n",
321 channel->center_freq, r);
Sujithff37e332008-11-24 12:07:55 +0530322 spin_unlock_bh(&sc->sc_resetlock);
Gabor Juhos39892792009-06-15 17:49:09 +0200323 goto ps_restore;
Sujithff37e332008-11-24 12:07:55 +0530324 }
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800325 spin_unlock_bh(&sc->sc_resetlock);
326
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800327 sc->sc_flags &= ~SC_OP_FULL_RESET;
328
329 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700330 ath_print(common, ATH_DBG_FATAL,
331 "Unable to restart recv logic\n");
Gabor Juhos39892792009-06-15 17:49:09 +0200332 r = -EIO;
333 goto ps_restore;
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800334 }
335
336 ath_cache_conf_rate(sc, &hw->conf);
337 ath_update_txpow(sc);
Sujith17d79042009-02-09 13:27:03 +0530338 ath9k_hw_set_interrupts(ah, sc->imask);
Gabor Juhos39892792009-06-15 17:49:09 +0200339
340 ps_restore:
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530341 ath9k_ps_restore(sc);
Gabor Juhos39892792009-06-15 17:49:09 +0200342 return r;
Sujithff37e332008-11-24 12:07:55 +0530343}
344
345/*
346 * This routine performs the periodic noise floor calibration function
347 * that is used to adjust and optimize the chip performance. This
348 * takes environmental changes (location, temperature) into account.
349 * When the task is complete, it reschedules itself depending on the
350 * appropriate interval that was calculated.
351 */
352static void ath_ani_calibrate(unsigned long data)
353{
Sujith20977d32009-02-20 15:13:28 +0530354 struct ath_softc *sc = (struct ath_softc *)data;
355 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700356 struct ath_common *common = ath9k_hw_common(ah);
Sujithff37e332008-11-24 12:07:55 +0530357 bool longcal = false;
358 bool shortcal = false;
359 bool aniflag = false;
360 unsigned int timestamp = jiffies_to_msecs(jiffies);
Sujith20977d32009-02-20 15:13:28 +0530361 u32 cal_interval, short_cal_interval;
Sujithff37e332008-11-24 12:07:55 +0530362
Sujith20977d32009-02-20 15:13:28 +0530363 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
364 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithff37e332008-11-24 12:07:55 +0530365
Jouni Malinen1ffc1c62009-05-19 17:01:39 +0300366 /* Only calibrate if awake */
367 if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
368 goto set_timer;
369
370 ath9k_ps_wakeup(sc);
371
Sujithff37e332008-11-24 12:07:55 +0530372 /* Long calibration runs independently of short calibration. */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800373 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
Sujithff37e332008-11-24 12:07:55 +0530374 longcal = true;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700375 ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800376 common->ani.longcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530377 }
378
Sujith17d79042009-02-09 13:27:03 +0530379 /* Short calibration applies only while caldone is false */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800380 if (!common->ani.caldone) {
381 if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
Sujithff37e332008-11-24 12:07:55 +0530382 shortcal = true;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700383 ath_print(common, ATH_DBG_ANI,
384 "shortcal @%lu\n", jiffies);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800385 common->ani.shortcal_timer = timestamp;
386 common->ani.resetcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530387 }
388 } else {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800389 if ((timestamp - common->ani.resetcal_timer) >=
Sujithff37e332008-11-24 12:07:55 +0530390 ATH_RESTART_CALINTERVAL) {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800391 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
392 if (common->ani.caldone)
393 common->ani.resetcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530394 }
395 }
396
397 /* Verify whether we must check ANI */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800398 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
Sujithff37e332008-11-24 12:07:55 +0530399 aniflag = true;
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800400 common->ani.checkani_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530401 }
402
403 /* Skip all processing if there's nothing to do. */
404 if (longcal || shortcal || aniflag) {
405 /* Call ANI routine if necessary */
406 if (aniflag)
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530407 ath9k_hw_ani_monitor(ah, ah->curchan);
Sujithff37e332008-11-24 12:07:55 +0530408
409 /* Perform calibration if necessary */
410 if (longcal || shortcal) {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800411 common->ani.caldone =
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700412 ath9k_hw_calibrate(ah,
413 ah->curchan,
414 common->rx_chainmask,
415 longcal);
Sujithff37e332008-11-24 12:07:55 +0530416
Sujith379f0442009-04-13 21:56:48 +0530417 if (longcal)
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800418 common->ani.noise_floor = ath9k_hw_getchan_noise(ah,
Sujith379f0442009-04-13 21:56:48 +0530419 ah->curchan);
Sujithff37e332008-11-24 12:07:55 +0530420
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700421 ath_print(common, ATH_DBG_ANI,
422 " calibrate chan %u/%x nf: %d\n",
423 ah->curchan->channel,
424 ah->curchan->channelFlags,
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800425 common->ani.noise_floor);
Sujithff37e332008-11-24 12:07:55 +0530426 }
427 }
428
Jouni Malinen1ffc1c62009-05-19 17:01:39 +0300429 ath9k_ps_restore(sc);
430
Sujith20977d32009-02-20 15:13:28 +0530431set_timer:
Sujithff37e332008-11-24 12:07:55 +0530432 /*
433 * Set timer interval based on previous results.
434 * The interval must be the shortest necessary to satisfy ANI,
435 * short calibration and long calibration.
436 */
Sujithaac92072008-12-02 18:37:54 +0530437 cal_interval = ATH_LONG_CALINTERVAL;
Sujith2660b812009-02-09 13:27:26 +0530438 if (sc->sc_ah->config.enable_ani)
Sujithaac92072008-12-02 18:37:54 +0530439 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800440 if (!common->ani.caldone)
Sujith20977d32009-02-20 15:13:28 +0530441 cal_interval = min(cal_interval, (u32)short_cal_interval);
Sujithff37e332008-11-24 12:07:55 +0530442
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800443 mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
Sujithff37e332008-11-24 12:07:55 +0530444}
445
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800446static void ath_start_ani(struct ath_common *common)
Sujith415f7382009-04-13 21:56:46 +0530447{
448 unsigned long timestamp = jiffies_to_msecs(jiffies);
449
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800450 common->ani.longcal_timer = timestamp;
451 common->ani.shortcal_timer = timestamp;
452 common->ani.checkani_timer = timestamp;
Sujith415f7382009-04-13 21:56:46 +0530453
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800454 mod_timer(&common->ani.timer,
Sujith415f7382009-04-13 21:56:46 +0530455 jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
456}
457
Sujithff37e332008-11-24 12:07:55 +0530458/*
459 * Update tx/rx chainmask. For legacy association,
460 * hard code chainmask to 1x1, for 11n association, use
Vasanthakumar Thiagarajanc97c92d2009-01-02 15:35:46 +0530461 * the chainmask configuration, for bt coexistence, use
462 * the chainmask configuration even in legacy mode.
Sujithff37e332008-11-24 12:07:55 +0530463 */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200464void ath_update_chainmask(struct ath_softc *sc, int is_ht)
Sujithff37e332008-11-24 12:07:55 +0530465{
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700466 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700467 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700468
Sujith3d832612009-08-21 12:00:28 +0530469 if ((sc->sc_flags & SC_OP_SCANNING) || is_ht ||
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -0700470 (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE)) {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700471 common->tx_chainmask = ah->caps.tx_chainmask;
472 common->rx_chainmask = ah->caps.rx_chainmask;
Sujithff37e332008-11-24 12:07:55 +0530473 } else {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700474 common->tx_chainmask = 1;
475 common->rx_chainmask = 1;
Sujithff37e332008-11-24 12:07:55 +0530476 }
477
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700478 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700479 "tx chmask: %d, rx chmask: %d\n",
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700480 common->tx_chainmask,
481 common->rx_chainmask);
Sujithff37e332008-11-24 12:07:55 +0530482}
483
484static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
485{
486 struct ath_node *an;
487
488 an = (struct ath_node *)sta->drv_priv;
489
Sujith87792ef2009-03-30 15:28:48 +0530490 if (sc->sc_flags & SC_OP_TXAGGR) {
Sujithff37e332008-11-24 12:07:55 +0530491 ath_tx_node_init(sc, an);
Sujith9e98ac62009-07-23 15:32:34 +0530492 an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
Sujith87792ef2009-03-30 15:28:48 +0530493 sta->ht_cap.ampdu_factor);
494 an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
Senthil Balasubramaniana59b5a52009-07-14 20:17:07 -0400495 an->last_rssi = ATH_RSSI_DUMMY_MARKER;
Sujith87792ef2009-03-30 15:28:48 +0530496 }
Sujithff37e332008-11-24 12:07:55 +0530497}
498
499static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
500{
501 struct ath_node *an = (struct ath_node *)sta->drv_priv;
502
503 if (sc->sc_flags & SC_OP_TXAGGR)
504 ath_tx_node_cleanup(sc, an);
505}
506
507static void ath9k_tasklet(unsigned long data)
508{
509 struct ath_softc *sc = (struct ath_softc *)data;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700510 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700511 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700512
Sujith17d79042009-02-09 13:27:03 +0530513 u32 status = sc->intrstatus;
Sujithff37e332008-11-24 12:07:55 +0530514
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400515 ath9k_ps_wakeup(sc);
516
Sujithff37e332008-11-24 12:07:55 +0530517 if (status & ATH9K_INT_FATAL) {
Sujithff37e332008-11-24 12:07:55 +0530518 ath_reset(sc, false);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400519 ath9k_ps_restore(sc);
Sujithff37e332008-11-24 12:07:55 +0530520 return;
Sujithff37e332008-11-24 12:07:55 +0530521 }
522
Sujith063d8be2009-03-30 15:28:49 +0530523 if (status & (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) {
524 spin_lock_bh(&sc->rx.rxflushlock);
525 ath_rx_tasklet(sc, 0);
526 spin_unlock_bh(&sc->rx.rxflushlock);
527 }
528
529 if (status & ATH9K_INT_TX)
530 ath_tx_tasklet(sc);
531
Gabor Juhos96148322009-07-24 17:27:21 +0200532 if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
Jouni Malinen54ce8462009-05-19 17:01:40 +0300533 /*
534 * TSF sync does not look correct; remain awake to sync with
535 * the next Beacon.
536 */
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700537 ath_print(common, ATH_DBG_PS,
538 "TSFOOR - Sync with next Beacon\n");
Jouni Malinenccdfeab2009-05-20 21:59:08 +0300539 sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC;
Jouni Malinen54ce8462009-05-19 17:01:40 +0300540 }
541
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -0700542 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Vasanthakumar Thiagarajanebb8e1d2009-09-01 17:46:32 +0530543 if (status & ATH9K_INT_GENTIMER)
544 ath_gen_timer_isr(sc->sc_ah);
545
Sujithff37e332008-11-24 12:07:55 +0530546 /* re-enable hardware interrupt */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700547 ath9k_hw_set_interrupts(ah, sc->imask);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400548 ath9k_ps_restore(sc);
Sujithff37e332008-11-24 12:07:55 +0530549}
550
Gabor Juhos6baff7f2009-01-14 20:17:06 +0100551irqreturn_t ath_isr(int irq, void *dev)
Sujithff37e332008-11-24 12:07:55 +0530552{
Sujith063d8be2009-03-30 15:28:49 +0530553#define SCHED_INTR ( \
554 ATH9K_INT_FATAL | \
555 ATH9K_INT_RXORN | \
556 ATH9K_INT_RXEOL | \
557 ATH9K_INT_RX | \
558 ATH9K_INT_TX | \
559 ATH9K_INT_BMISS | \
560 ATH9K_INT_CST | \
Vasanthakumar Thiagarajanebb8e1d2009-09-01 17:46:32 +0530561 ATH9K_INT_TSFOOR | \
562 ATH9K_INT_GENTIMER)
Sujith063d8be2009-03-30 15:28:49 +0530563
Sujithff37e332008-11-24 12:07:55 +0530564 struct ath_softc *sc = dev;
Sujithcbe61d82009-02-09 13:27:12 +0530565 struct ath_hw *ah = sc->sc_ah;
Sujithff37e332008-11-24 12:07:55 +0530566 enum ath9k_int status;
567 bool sched = false;
568
Sujith063d8be2009-03-30 15:28:49 +0530569 /*
570 * The hardware is not ready/present, don't
571 * touch anything. Note this can happen early
572 * on if the IRQ is shared.
573 */
574 if (sc->sc_flags & SC_OP_INVALID)
575 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530576
Sujithff37e332008-11-24 12:07:55 +0530577
Sujith063d8be2009-03-30 15:28:49 +0530578 /* shared irq, not for us */
Sujithff37e332008-11-24 12:07:55 +0530579
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400580 if (!ath9k_hw_intrpend(ah))
Sujith063d8be2009-03-30 15:28:49 +0530581 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530582
Sujith063d8be2009-03-30 15:28:49 +0530583 /*
584 * Figure out the reason(s) for the interrupt. Note
585 * that the hal returns a pseudo-ISR that may include
586 * bits we haven't explicitly enabled so we mask the
587 * value to insure we only process bits we requested.
588 */
589 ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
590 status &= sc->imask; /* discard unasked-for bits */
591
592 /*
593 * If there are no status bits set, then this interrupt was not
594 * for me (should have been caught above).
595 */
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400596 if (!status)
Sujith063d8be2009-03-30 15:28:49 +0530597 return IRQ_NONE;
Sujith063d8be2009-03-30 15:28:49 +0530598
599 /* Cache the status */
600 sc->intrstatus = status;
601
602 if (status & SCHED_INTR)
603 sched = true;
604
605 /*
606 * If a FATAL or RXORN interrupt is received, we have to reset the
607 * chip immediately.
608 */
609 if (status & (ATH9K_INT_FATAL | ATH9K_INT_RXORN))
610 goto chip_reset;
611
612 if (status & ATH9K_INT_SWBA)
613 tasklet_schedule(&sc->bcon_tasklet);
614
615 if (status & ATH9K_INT_TXURN)
616 ath9k_hw_updatetxtriglevel(ah, true);
617
618 if (status & ATH9K_INT_MIB) {
619 /*
620 * Disable interrupts until we service the MIB
621 * interrupt; otherwise it will continue to
622 * fire.
623 */
624 ath9k_hw_set_interrupts(ah, 0);
625 /*
626 * Let the hal handle the event. We assume
627 * it will clear whatever condition caused
628 * the interrupt.
629 */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530630 ath9k_hw_procmibevent(ah);
Sujith063d8be2009-03-30 15:28:49 +0530631 ath9k_hw_set_interrupts(ah, sc->imask);
632 }
633
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400634 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
635 if (status & ATH9K_INT_TIM_TIMER) {
Sujith063d8be2009-03-30 15:28:49 +0530636 /* Clear RxAbort bit so that we can
637 * receive frames */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700638 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400639 ath9k_hw_setrxabort(sc->sc_ah, 0);
Sujith063d8be2009-03-30 15:28:49 +0530640 sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
641 }
Sujith063d8be2009-03-30 15:28:49 +0530642
643chip_reset:
644
Sujith817e11d2008-12-07 21:42:44 +0530645 ath_debug_stat_interrupt(sc, status);
646
Sujithff37e332008-11-24 12:07:55 +0530647 if (sched) {
648 /* turn off every interrupt except SWBA */
Sujith17d79042009-02-09 13:27:03 +0530649 ath9k_hw_set_interrupts(ah, (sc->imask & ATH9K_INT_SWBA));
Sujithff37e332008-11-24 12:07:55 +0530650 tasklet_schedule(&sc->intr_tq);
651 }
652
653 return IRQ_HANDLED;
Sujith063d8be2009-03-30 15:28:49 +0530654
655#undef SCHED_INTR
Sujithff37e332008-11-24 12:07:55 +0530656}
657
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700658static u32 ath_get_extchanmode(struct ath_softc *sc,
Sujith99405f92008-11-24 12:08:35 +0530659 struct ieee80211_channel *chan,
Sujith094d05d2008-12-12 11:57:43 +0530660 enum nl80211_channel_type channel_type)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700661{
662 u32 chanmode = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700663
664 switch (chan->band) {
665 case IEEE80211_BAND_2GHZ:
Sujith094d05d2008-12-12 11:57:43 +0530666 switch(channel_type) {
667 case NL80211_CHAN_NO_HT:
668 case NL80211_CHAN_HT20:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700669 chanmode = CHANNEL_G_HT20;
Sujith094d05d2008-12-12 11:57:43 +0530670 break;
671 case NL80211_CHAN_HT40PLUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700672 chanmode = CHANNEL_G_HT40PLUS;
Sujith094d05d2008-12-12 11:57:43 +0530673 break;
674 case NL80211_CHAN_HT40MINUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700675 chanmode = CHANNEL_G_HT40MINUS;
Sujith094d05d2008-12-12 11:57:43 +0530676 break;
677 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700678 break;
679 case IEEE80211_BAND_5GHZ:
Sujith094d05d2008-12-12 11:57:43 +0530680 switch(channel_type) {
681 case NL80211_CHAN_NO_HT:
682 case NL80211_CHAN_HT20:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700683 chanmode = CHANNEL_A_HT20;
Sujith094d05d2008-12-12 11:57:43 +0530684 break;
685 case NL80211_CHAN_HT40PLUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700686 chanmode = CHANNEL_A_HT40PLUS;
Sujith094d05d2008-12-12 11:57:43 +0530687 break;
688 case NL80211_CHAN_HT40MINUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700689 chanmode = CHANNEL_A_HT40MINUS;
Sujith094d05d2008-12-12 11:57:43 +0530690 break;
691 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700692 break;
693 default:
694 break;
695 }
696
697 return chanmode;
698}
699
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800700static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200701 struct ath9k_keyval *hk, const u8 *addr,
702 bool authenticator)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700703{
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800704 struct ath_hw *ah = common->ah;
Jouni Malinen6ace2892008-12-17 13:32:17 +0200705 const u8 *key_rxmic;
706 const u8 *key_txmic;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700707
Jouni Malinen6ace2892008-12-17 13:32:17 +0200708 key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
709 key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700710
711 if (addr == NULL) {
Jouni Malinend216aaa2009-03-03 13:11:53 +0200712 /*
713 * Group key installation - only two key cache entries are used
714 * regardless of splitmic capability since group key is only
715 * used either for TX or RX.
716 */
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200717 if (authenticator) {
718 memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
719 memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
720 } else {
721 memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
722 memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
723 }
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800724 return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700725 }
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800726 if (!common->splitmic) {
Jouni Malinend216aaa2009-03-03 13:11:53 +0200727 /* TX and RX keys share the same key cache entry. */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700728 memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
729 memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800730 return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700731 }
Jouni Malinend216aaa2009-03-03 13:11:53 +0200732
733 /* Separate key cache entries for TX and RX */
734
735 /* TX key goes at first index, RX key at +32. */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700736 memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800737 if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) {
Jouni Malinend216aaa2009-03-03 13:11:53 +0200738 /* TX MIC entry failed. No need to proceed further */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800739 ath_print(common, ATH_DBG_FATAL,
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700740 "Setting TX MIC Key Failed\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700741 return 0;
742 }
743
744 memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
745 /* XXX delete tx key on failure? */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800746 return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200747}
748
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800749static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
Jouni Malinen6ace2892008-12-17 13:32:17 +0200750{
751 int i;
752
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800753 for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
754 if (test_bit(i, common->keymap) ||
755 test_bit(i + 64, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200756 continue; /* At least one part of TKIP key allocated */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800757 if (common->splitmic &&
758 (test_bit(i + 32, common->keymap) ||
759 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200760 continue; /* At least one part of TKIP key allocated */
761
762 /* Found a free slot for a TKIP key */
763 return i;
764 }
765 return -1;
766}
767
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800768static int ath_reserve_key_cache_slot(struct ath_common *common)
Jouni Malinen6ace2892008-12-17 13:32:17 +0200769{
770 int i;
771
772 /* First, try to find slots that would not be available for TKIP. */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800773 if (common->splitmic) {
774 for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
775 if (!test_bit(i, common->keymap) &&
776 (test_bit(i + 32, common->keymap) ||
777 test_bit(i + 64, common->keymap) ||
778 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200779 return i;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800780 if (!test_bit(i + 32, common->keymap) &&
781 (test_bit(i, common->keymap) ||
782 test_bit(i + 64, common->keymap) ||
783 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200784 return i + 32;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800785 if (!test_bit(i + 64, common->keymap) &&
786 (test_bit(i , common->keymap) ||
787 test_bit(i + 32, common->keymap) ||
788 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinenea612132008-12-18 14:31:10 +0200789 return i + 64;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800790 if (!test_bit(i + 64 + 32, common->keymap) &&
791 (test_bit(i, common->keymap) ||
792 test_bit(i + 32, common->keymap) ||
793 test_bit(i + 64, common->keymap)))
Jouni Malinenea612132008-12-18 14:31:10 +0200794 return i + 64 + 32;
Jouni Malinen6ace2892008-12-17 13:32:17 +0200795 }
796 } else {
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800797 for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
798 if (!test_bit(i, common->keymap) &&
799 test_bit(i + 64, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200800 return i;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800801 if (test_bit(i, common->keymap) &&
802 !test_bit(i + 64, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200803 return i + 64;
804 }
805 }
806
807 /* No partially used TKIP slots, pick any available slot */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800808 for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
Jouni Malinenbe2864c2008-12-18 14:33:00 +0200809 /* Do not allow slots that could be needed for TKIP group keys
810 * to be used. This limitation could be removed if we know that
811 * TKIP will not be used. */
812 if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
813 continue;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800814 if (common->splitmic) {
Jouni Malinenbe2864c2008-12-18 14:33:00 +0200815 if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
816 continue;
817 if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
818 continue;
819 }
820
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800821 if (!test_bit(i, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200822 return i; /* Found a free slot for a key */
823 }
824
825 /* No free slot found */
826 return -1;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700827}
828
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800829static int ath_key_config(struct ath_common *common,
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200830 struct ieee80211_vif *vif,
Johannes Bergdc822b52008-12-29 12:55:09 +0100831 struct ieee80211_sta *sta,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700832 struct ieee80211_key_conf *key)
833{
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800834 struct ath_hw *ah = common->ah;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700835 struct ath9k_keyval hk;
836 const u8 *mac = NULL;
837 int ret = 0;
Jouni Malinen6ace2892008-12-17 13:32:17 +0200838 int idx;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700839
840 memset(&hk, 0, sizeof(hk));
841
842 switch (key->alg) {
843 case ALG_WEP:
844 hk.kv_type = ATH9K_CIPHER_WEP;
845 break;
846 case ALG_TKIP:
847 hk.kv_type = ATH9K_CIPHER_TKIP;
848 break;
849 case ALG_CCMP:
850 hk.kv_type = ATH9K_CIPHER_AES_CCM;
851 break;
852 default:
Jouni Malinenca470b22009-01-08 13:32:12 +0200853 return -EOPNOTSUPP;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700854 }
855
Jouni Malinen6ace2892008-12-17 13:32:17 +0200856 hk.kv_len = key->keylen;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700857 memcpy(hk.kv_val, key->key, key->keylen);
858
Jouni Malinen6ace2892008-12-17 13:32:17 +0200859 if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
860 /* For now, use the default keys for broadcast keys. This may
861 * need to change with virtual interfaces. */
862 idx = key->keyidx;
863 } else if (key->keyidx) {
Johannes Bergdc822b52008-12-29 12:55:09 +0100864 if (WARN_ON(!sta))
865 return -EOPNOTSUPP;
866 mac = sta->addr;
867
Jouni Malinen6ace2892008-12-17 13:32:17 +0200868 if (vif->type != NL80211_IFTYPE_AP) {
869 /* Only keyidx 0 should be used with unicast key, but
870 * allow this for client mode for now. */
871 idx = key->keyidx;
872 } else
873 return -EIO;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700874 } else {
Johannes Bergdc822b52008-12-29 12:55:09 +0100875 if (WARN_ON(!sta))
876 return -EOPNOTSUPP;
877 mac = sta->addr;
878
Jouni Malinen6ace2892008-12-17 13:32:17 +0200879 if (key->alg == ALG_TKIP)
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800880 idx = ath_reserve_key_cache_slot_tkip(common);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200881 else
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800882 idx = ath_reserve_key_cache_slot(common);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200883 if (idx < 0)
Jouni Malinenca470b22009-01-08 13:32:12 +0200884 return -ENOSPC; /* no free key cache entries */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700885 }
886
887 if (key->alg == ALG_TKIP)
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800888 ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200889 vif->type == NL80211_IFTYPE_AP);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700890 else
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800891 ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700892
893 if (!ret)
894 return -EIO;
895
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800896 set_bit(idx, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200897 if (key->alg == ALG_TKIP) {
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800898 set_bit(idx + 64, common->keymap);
899 if (common->splitmic) {
900 set_bit(idx + 32, common->keymap);
901 set_bit(idx + 64 + 32, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200902 }
903 }
904
905 return idx;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700906}
907
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800908static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700909{
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800910 struct ath_hw *ah = common->ah;
911
912 ath9k_hw_keyreset(ah, key->hw_key_idx);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200913 if (key->hw_key_idx < IEEE80211_WEP_NKID)
914 return;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700915
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800916 clear_bit(key->hw_key_idx, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200917 if (key->alg != ALG_TKIP)
918 return;
919
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800920 clear_bit(key->hw_key_idx + 64, common->keymap);
921 if (common->splitmic) {
922 clear_bit(key->hw_key_idx + 32, common->keymap);
923 clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200924 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700925}
926
Sujitheb2599c2009-01-23 11:20:44 +0530927static void setup_ht_cap(struct ath_softc *sc,
928 struct ieee80211_sta_ht_cap *ht_info)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700929{
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700930 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530931 u8 tx_streams, rx_streams;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700932
Johannes Bergd9fe60d2008-10-09 12:13:49 +0200933 ht_info->ht_supported = true;
934 ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
935 IEEE80211_HT_CAP_SM_PS |
936 IEEE80211_HT_CAP_SGI_40 |
937 IEEE80211_HT_CAP_DSSSCCK40;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700938
Sujith9e98ac62009-07-23 15:32:34 +0530939 ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
940 ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
Sujitheb2599c2009-01-23 11:20:44 +0530941
Johannes Bergd9fe60d2008-10-09 12:13:49 +0200942 /* set up supported mcs set */
943 memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700944 tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ?
945 1 : 2;
946 rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ?
947 1 : 2;
Sujitheb2599c2009-01-23 11:20:44 +0530948
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530949 if (tx_streams != rx_streams) {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700950 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700951 "TX streams %d, RX streams: %d\n",
952 tx_streams, rx_streams);
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530953 ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
954 ht_info->mcs.tx_params |= ((tx_streams - 1) <<
955 IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
Sujitheb2599c2009-01-23 11:20:44 +0530956 }
957
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530958 ht_info->mcs.rx_mask[0] = 0xff;
959 if (rx_streams >= 2)
960 ht_info->mcs.rx_mask[1] = 0xff;
961
962 ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700963}
964
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530965static void ath9k_bss_assoc_info(struct ath_softc *sc,
Sujith5640b082008-10-29 10:16:06 +0530966 struct ieee80211_vif *vif,
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530967 struct ieee80211_bss_conf *bss_conf)
968{
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -0700969 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700970 struct ath_common *common = ath9k_hw_common(ah);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530971
972 if (bss_conf->assoc) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700973 ath_print(common, ATH_DBG_CONFIG,
974 "Bss Info ASSOC %d, bssid: %pM\n",
975 bss_conf->aid, common->curbssid);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530976
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530977 /* New association, store aid */
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700978 common->curaid = bss_conf->aid;
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -0700979 ath9k_hw_write_associd(ah);
Jouni Malinenccdfeab2009-05-20 21:59:08 +0300980
Senthil Balasubramanian2664f202009-06-24 18:56:39 +0530981 /*
982 * Request a re-configuration of Beacon related timers
983 * on the receipt of the first Beacon frame (i.e.,
984 * after time sync with the AP).
985 */
986 sc->sc_flags |= SC_OP_BEACON_SYNC;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530987
988 /* Configure the beacon */
Jouni Malinen2c3db3d2009-03-03 19:23:26 +0200989 ath_beacon_config(sc, vif);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530990
991 /* Reset rssi stats */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530992 sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530993
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800994 ath_start_ani(common);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530995 } else {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700996 ath_print(common, ATH_DBG_CONFIG, "Bss Info DISASSOC\n");
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700997 common->curaid = 0;
Senthil Balasubramanianf38faa32009-06-24 18:56:40 +0530998 /* Stop ANI */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800999 del_timer_sync(&common->ani.timer);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301000 }
1001}
1002
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301003/********************************/
1004/* LED functions */
1005/********************************/
1006
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301007static void ath_led_blink_work(struct work_struct *work)
1008{
1009 struct ath_softc *sc = container_of(work, struct ath_softc,
1010 ath_led_blink_work.work);
1011
1012 if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED))
1013 return;
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301014
1015 if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
1016 (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301017 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301018 else
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301019 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301020 (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301021
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04001022 ieee80211_queue_delayed_work(sc->hw,
1023 &sc->ath_led_blink_work,
1024 (sc->sc_flags & SC_OP_LED_ON) ?
1025 msecs_to_jiffies(sc->led_off_duration) :
1026 msecs_to_jiffies(sc->led_on_duration));
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301027
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301028 sc->led_on_duration = sc->led_on_cnt ?
1029 max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) :
1030 ATH_LED_ON_DURATION_IDLE;
1031 sc->led_off_duration = sc->led_off_cnt ?
1032 max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) :
1033 ATH_LED_OFF_DURATION_IDLE;
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301034 sc->led_on_cnt = sc->led_off_cnt = 0;
1035 if (sc->sc_flags & SC_OP_LED_ON)
1036 sc->sc_flags &= ~SC_OP_LED_ON;
1037 else
1038 sc->sc_flags |= SC_OP_LED_ON;
1039}
1040
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301041static void ath_led_brightness(struct led_classdev *led_cdev,
1042 enum led_brightness brightness)
1043{
1044 struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
1045 struct ath_softc *sc = led->sc;
1046
1047 switch (brightness) {
1048 case LED_OFF:
1049 if (led->led_type == ATH_LED_ASSOC ||
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301050 led->led_type == ATH_LED_RADIO) {
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301051 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301052 (led->led_type == ATH_LED_RADIO));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301053 sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301054 if (led->led_type == ATH_LED_RADIO)
1055 sc->sc_flags &= ~SC_OP_LED_ON;
1056 } else {
1057 sc->led_off_cnt++;
1058 }
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301059 break;
1060 case LED_FULL:
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301061 if (led->led_type == ATH_LED_ASSOC) {
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301062 sc->sc_flags |= SC_OP_LED_ASSOCIATED;
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04001063 ieee80211_queue_delayed_work(sc->hw,
1064 &sc->ath_led_blink_work, 0);
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301065 } else if (led->led_type == ATH_LED_RADIO) {
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301066 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301067 sc->sc_flags |= SC_OP_LED_ON;
1068 } else {
1069 sc->led_on_cnt++;
1070 }
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301071 break;
1072 default:
1073 break;
1074 }
1075}
1076
1077static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
1078 char *trigger)
1079{
1080 int ret;
1081
1082 led->sc = sc;
1083 led->led_cdev.name = led->name;
1084 led->led_cdev.default_trigger = trigger;
1085 led->led_cdev.brightness_set = ath_led_brightness;
1086
1087 ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev);
1088 if (ret)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001089 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
1090 "Failed to register led:%s", led->name);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301091 else
1092 led->registered = 1;
1093 return ret;
1094}
1095
1096static void ath_unregister_led(struct ath_led *led)
1097{
1098 if (led->registered) {
1099 led_classdev_unregister(&led->led_cdev);
1100 led->registered = 0;
1101 }
1102}
1103
1104static void ath_deinit_leds(struct ath_softc *sc)
1105{
1106 ath_unregister_led(&sc->assoc_led);
1107 sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
1108 ath_unregister_led(&sc->tx_led);
1109 ath_unregister_led(&sc->rx_led);
1110 ath_unregister_led(&sc->radio_led);
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301111 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301112}
1113
1114static void ath_init_leds(struct ath_softc *sc)
1115{
1116 char *trigger;
1117 int ret;
1118
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301119 if (AR_SREV_9287(sc->sc_ah))
1120 sc->sc_ah->led_pin = ATH_LED_PIN_9287;
1121 else
1122 sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
1123
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301124 /* Configure gpio 1 for output */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301125 ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301126 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
1127 /* LED off, active low */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301128 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301129
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301130 INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work);
1131
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301132 trigger = ieee80211_get_radio_led_name(sc->hw);
1133 snprintf(sc->radio_led.name, sizeof(sc->radio_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001134 "ath9k-%s::radio", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301135 ret = ath_register_led(sc, &sc->radio_led, trigger);
1136 sc->radio_led.led_type = ATH_LED_RADIO;
1137 if (ret)
1138 goto fail;
1139
1140 trigger = ieee80211_get_assoc_led_name(sc->hw);
1141 snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001142 "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301143 ret = ath_register_led(sc, &sc->assoc_led, trigger);
1144 sc->assoc_led.led_type = ATH_LED_ASSOC;
1145 if (ret)
1146 goto fail;
1147
1148 trigger = ieee80211_get_tx_led_name(sc->hw);
1149 snprintf(sc->tx_led.name, sizeof(sc->tx_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001150 "ath9k-%s::tx", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301151 ret = ath_register_led(sc, &sc->tx_led, trigger);
1152 sc->tx_led.led_type = ATH_LED_TX;
1153 if (ret)
1154 goto fail;
1155
1156 trigger = ieee80211_get_rx_led_name(sc->hw);
1157 snprintf(sc->rx_led.name, sizeof(sc->rx_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001158 "ath9k-%s::rx", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301159 ret = ath_register_led(sc, &sc->rx_led, trigger);
1160 sc->rx_led.led_type = ATH_LED_RX;
1161 if (ret)
1162 goto fail;
1163
1164 return;
1165
1166fail:
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001167 cancel_delayed_work_sync(&sc->ath_led_blink_work);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301168 ath_deinit_leds(sc);
1169}
1170
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001171void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301172{
Sujithcbe61d82009-02-09 13:27:12 +05301173 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001174 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001175 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001176 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301177
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301178 ath9k_ps_wakeup(sc);
Vivek Natarajan93b1b372009-09-17 09:24:58 +05301179 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithd2f5b3a2009-04-13 21:56:25 +05301180
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301181 if (!ah->curchan)
1182 ah->curchan = ath_get_curchannel(sc, sc->hw);
1183
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301184 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301185 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001186 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001187 ath_print(common, ATH_DBG_FATAL,
1188 "Unable to reset channel %u (%uMhz) ",
1189 "reset status %d\n",
1190 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301191 }
1192 spin_unlock_bh(&sc->sc_resetlock);
1193
1194 ath_update_txpow(sc);
1195 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001196 ath_print(common, ATH_DBG_FATAL,
1197 "Unable to restart recv logic\n");
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301198 return;
1199 }
1200
1201 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001202 ath_beacon_config(sc, NULL); /* restart beacons */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301203
1204 /* Re-Enable interrupts */
Sujith17d79042009-02-09 13:27:03 +05301205 ath9k_hw_set_interrupts(ah, sc->imask);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301206
1207 /* Enable LED */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301208 ath9k_hw_cfg_output(ah, ah->led_pin,
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301209 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301210 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301211
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001212 ieee80211_wake_queues(hw);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301213 ath9k_ps_restore(sc);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301214}
1215
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001216void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301217{
Sujithcbe61d82009-02-09 13:27:12 +05301218 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001219 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001220 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301221
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301222 ath9k_ps_wakeup(sc);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001223 ieee80211_stop_queues(hw);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301224
1225 /* Disable LED */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301226 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
1227 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301228
1229 /* Disable interrupts */
1230 ath9k_hw_set_interrupts(ah, 0);
1231
Sujith043a0402009-01-16 21:38:47 +05301232 ath_drain_all_txq(sc, false); /* clear pending tx frames */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301233 ath_stoprecv(sc); /* turn off frame recv */
1234 ath_flushrecv(sc); /* flush recv queue */
1235
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301236 if (!ah->curchan)
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001237 ah->curchan = ath_get_curchannel(sc, hw);
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301238
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301239 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301240 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001241 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001242 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
1243 "Unable to reset channel %u (%uMhz) "
1244 "reset status %d\n",
1245 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301246 }
1247 spin_unlock_bh(&sc->sc_resetlock);
1248
1249 ath9k_hw_phy_disable(ah);
Vivek Natarajan93b1b372009-09-17 09:24:58 +05301250 ath9k_hw_configpcipowersave(ah, 1, 1);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301251 ath9k_ps_restore(sc);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001252 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301253}
1254
Gabor Juhos5077fd32009-03-06 11:17:55 +01001255/*******************/
1256/* Rfkill */
1257/*******************/
1258
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301259static bool ath_is_rfkill_set(struct ath_softc *sc)
1260{
Sujithcbe61d82009-02-09 13:27:12 +05301261 struct ath_hw *ah = sc->sc_ah;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301262
Sujith2660b812009-02-09 13:27:26 +05301263 return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
1264 ah->rfkill_polarity;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301265}
1266
Johannes Berg3b319aa2009-06-13 14:50:26 +05301267static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301268{
Johannes Berg3b319aa2009-06-13 14:50:26 +05301269 struct ath_wiphy *aphy = hw->priv;
1270 struct ath_softc *sc = aphy->sc;
1271 bool blocked = !!ath_is_rfkill_set(sc);
1272
1273 wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
Johannes Berg19d337d2009-06-02 13:01:37 +02001274}
1275
Johannes Berg3b319aa2009-06-13 14:50:26 +05301276static void ath_start_rfkill_poll(struct ath_softc *sc)
Johannes Berg19d337d2009-06-02 13:01:37 +02001277{
Johannes Berg3b319aa2009-06-13 14:50:26 +05301278 struct ath_hw *ah = sc->sc_ah;
Johannes Berg19d337d2009-06-02 13:01:37 +02001279
Johannes Berg3b319aa2009-06-13 14:50:26 +05301280 if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
1281 wiphy_rfkill_start_polling(sc->hw->wiphy);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301282}
1283
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001284static void ath9k_uninit_hw(struct ath_softc *sc)
1285{
1286 struct ath_hw *ah = sc->sc_ah;
1287
1288 BUG_ON(!ah);
1289
1290 ath9k_exit_debug(ah);
1291 ath9k_hw_detach(ah);
1292 sc->sc_ah = NULL;
1293}
1294
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001295static void ath_clean_core(struct ath_softc *sc)
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301296{
1297 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001298 struct ath_hw *ah = sc->sc_ah;
Sujith9c84b792008-10-29 10:17:13 +05301299 int i = 0;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301300
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301301 ath9k_ps_wakeup(sc);
1302
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001303 dev_dbg(sc->dev, "Detach ATH hw\n");
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301304
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001305 ath_deinit_leds(sc);
Sujithe31f7b92009-09-23 13:49:12 +05301306 wiphy_rfkill_stop_polling(sc->hw->wiphy);
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001307
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001308 for (i = 0; i < sc->num_sec_wiphy; i++) {
1309 struct ath_wiphy *aphy = sc->sec_wiphy[i];
1310 if (aphy == NULL)
1311 continue;
1312 sc->sec_wiphy[i] = NULL;
1313 ieee80211_unregister_hw(aphy->hw);
1314 ieee80211_free_hw(aphy->hw);
1315 }
Vasanthakumar Thiagarajan3fcdfb42008-11-18 01:19:56 +05301316 ieee80211_unregister_hw(hw);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301317 ath_rx_cleanup(sc);
1318 ath_tx_cleanup(sc);
1319
Sujith9c84b792008-10-29 10:17:13 +05301320 tasklet_kill(&sc->intr_tq);
1321 tasklet_kill(&sc->bcon_tasklet);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301322
Sujith9c84b792008-10-29 10:17:13 +05301323 if (!(sc->sc_flags & SC_OP_INVALID))
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001324 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301325
Sujith9c84b792008-10-29 10:17:13 +05301326 /* cleanup tx queues */
1327 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1328 if (ATH_TXQ_SETUP(sc, i))
Sujithb77f4832008-12-07 21:44:03 +05301329 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
Sujith9c84b792008-10-29 10:17:13 +05301330
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001331 if ((sc->btcoex.no_stomp_timer) &&
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001332 ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001333 ath_gen_timer_free(ah, sc->btcoex.no_stomp_timer);
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001334}
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301335
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001336void ath_detach(struct ath_softc *sc)
1337{
1338 ath_clean_core(sc);
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001339 ath9k_uninit_hw(sc);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301340}
1341
Luis R. Rodriguezbd96d392009-10-06 21:19:10 -04001342void ath_cleanup(struct ath_softc *sc)
1343{
1344 struct ath_hw *ah = sc->sc_ah;
1345 struct ath_common *common = ath9k_hw_common(ah);
1346
1347 ath_clean_core(sc);
1348 free_irq(sc->irq, sc);
1349 ath_bus_cleanup(common);
1350 kfree(sc->sec_wiphy);
1351 ieee80211_free_hw(sc->hw);
1352
1353 ath9k_uninit_hw(sc);
1354}
1355
Bob Copelande3bb2492009-03-30 22:30:30 -04001356static int ath9k_reg_notifier(struct wiphy *wiphy,
1357 struct regulatory_request *request)
1358{
1359 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
1360 struct ath_wiphy *aphy = hw->priv;
1361 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001362 struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah);
Bob Copelande3bb2492009-03-30 22:30:30 -04001363
1364 return ath_reg_notifier_apply(wiphy, request, reg);
1365}
1366
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001367/*
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001368 * Detects if there is any priority bt traffic
1369 */
1370static void ath_detect_bt_priority(struct ath_softc *sc)
1371{
1372 struct ath_btcoex *btcoex = &sc->btcoex;
1373 struct ath_hw *ah = sc->sc_ah;
1374
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001375 if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001376 btcoex->bt_priority_cnt++;
1377
1378 if (time_after(jiffies, btcoex->bt_priority_time +
1379 msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
1380 if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001381 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
1382 "BT priority traffic detected");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001383 sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
1384 } else {
1385 sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
1386 }
1387
1388 btcoex->bt_priority_cnt = 0;
1389 btcoex->bt_priority_time = jiffies;
1390 }
1391}
1392
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001393/*
1394 * Configures appropriate weight based on stomp type.
1395 */
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001396static void ath9k_btcoex_bt_stomp(struct ath_softc *sc,
1397 enum ath_stomp_type stomp_type)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001398{
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001399 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001400
1401 switch (stomp_type) {
1402 case ATH_BTCOEX_STOMP_ALL:
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001403 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1404 AR_STOMP_ALL_WLAN_WGHT);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001405 break;
1406 case ATH_BTCOEX_STOMP_LOW:
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001407 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1408 AR_STOMP_LOW_WLAN_WGHT);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001409 break;
1410 case ATH_BTCOEX_STOMP_NONE:
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001411 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1412 AR_STOMP_NONE_WLAN_WGHT);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001413 break;
1414 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001415 ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
1416 "Invalid Stomptype\n");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001417 break;
1418 }
1419
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001420 ath9k_hw_btcoex_enable(ah);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001421}
1422
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001423static void ath9k_gen_timer_start(struct ath_hw *ah,
1424 struct ath_gen_timer *timer,
1425 u32 timer_next,
1426 u32 timer_period)
1427{
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001428 struct ath_common *common = ath9k_hw_common(ah);
1429 struct ath_softc *sc = (struct ath_softc *) common->priv;
1430
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001431 ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);
1432
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001433 if ((sc->imask & ATH9K_INT_GENTIMER) == 0) {
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001434 ath9k_hw_set_interrupts(ah, 0);
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001435 sc->imask |= ATH9K_INT_GENTIMER;
1436 ath9k_hw_set_interrupts(ah, sc->imask);
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001437 }
1438}
1439
1440static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
1441{
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001442 struct ath_common *common = ath9k_hw_common(ah);
1443 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001444 struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
1445
1446 ath9k_hw_gen_timer_stop(ah, timer);
1447
1448 /* if no timer is enabled, turn off interrupt mask */
1449 if (timer_table->timer_mask.val == 0) {
1450 ath9k_hw_set_interrupts(ah, 0);
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001451 sc->imask &= ~ATH9K_INT_GENTIMER;
1452 ath9k_hw_set_interrupts(ah, sc->imask);
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001453 }
1454}
1455
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001456/*
1457 * This is the master bt coex timer which runs for every
1458 * 45ms, bt traffic will be given priority during 55% of this
1459 * period while wlan gets remaining 45%
1460 */
1461static void ath_btcoex_period_timer(unsigned long data)
1462{
1463 struct ath_softc *sc = (struct ath_softc *) data;
1464 struct ath_hw *ah = sc->sc_ah;
1465 struct ath_btcoex *btcoex = &sc->btcoex;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001466
1467 ath_detect_bt_priority(sc);
1468
1469 spin_lock_bh(&btcoex->btcoex_lock);
1470
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001471 ath9k_btcoex_bt_stomp(sc, btcoex->bt_stomp_type);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001472
1473 spin_unlock_bh(&btcoex->btcoex_lock);
1474
1475 if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
1476 if (btcoex->hw_timer_enabled)
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001477 ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001478
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001479 ath9k_gen_timer_start(ah,
1480 btcoex->no_stomp_timer,
1481 (ath9k_hw_gettsf32(ah) +
1482 btcoex->btcoex_no_stomp),
1483 btcoex->btcoex_no_stomp * 10);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001484 btcoex->hw_timer_enabled = true;
1485 }
1486
1487 mod_timer(&btcoex->period_timer, jiffies +
1488 msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
1489}
1490
1491/*
1492 * Generic tsf based hw timer which configures weight
1493 * registers to time slice between wlan and bt traffic
1494 */
1495static void ath_btcoex_no_stomp_timer(void *arg)
1496{
1497 struct ath_softc *sc = (struct ath_softc *)arg;
1498 struct ath_hw *ah = sc->sc_ah;
1499 struct ath_btcoex *btcoex = &sc->btcoex;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001500
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001501 ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
1502 "no stomp timer running \n");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001503
1504 spin_lock_bh(&btcoex->btcoex_lock);
1505
Luis R. Rodrigueze08a6ac2009-09-09 14:26:15 -07001506 if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW)
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001507 ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE);
Luis R. Rodrigueze08a6ac2009-09-09 14:26:15 -07001508 else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001509 ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001510
1511 spin_unlock_bh(&btcoex->btcoex_lock);
1512}
1513
1514static int ath_init_btcoex_timer(struct ath_softc *sc)
1515{
1516 struct ath_btcoex *btcoex = &sc->btcoex;
1517
1518 btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
1519 btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
1520 btcoex->btcoex_period / 100;
1521
1522 setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
1523 (unsigned long) sc);
1524
1525 spin_lock_init(&btcoex->btcoex_lock);
1526
1527 btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
1528 ath_btcoex_no_stomp_timer,
1529 ath_btcoex_no_stomp_timer,
1530 (void *) sc, AR_FIRST_NDP_TIMER);
1531
1532 if (!btcoex->no_stomp_timer)
1533 return -ENOMEM;
1534
1535 return 0;
1536}
1537
1538/*
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001539 * Read and write, they both share the same lock. We do this to serialize
1540 * reads and writes on Atheros 802.11n PCI devices only. This is required
1541 * as the FIFO on these devices can only accept sanely 2 requests. After
1542 * that the device goes bananas. Serializing the reads/writes prevents this
1543 * from happening.
1544 */
1545
1546static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
1547{
1548 struct ath_hw *ah = (struct ath_hw *) hw_priv;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001549 struct ath_common *common = ath9k_hw_common(ah);
1550 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001551
1552 if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
1553 unsigned long flags;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001554 spin_lock_irqsave(&sc->sc_serial_rw, flags);
1555 iowrite32(val, sc->mem + reg_offset);
1556 spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001557 } else
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001558 iowrite32(val, sc->mem + reg_offset);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001559}
1560
1561static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
1562{
1563 struct ath_hw *ah = (struct ath_hw *) hw_priv;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001564 struct ath_common *common = ath9k_hw_common(ah);
1565 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001566 u32 val;
1567
1568 if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
1569 unsigned long flags;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001570 spin_lock_irqsave(&sc->sc_serial_rw, flags);
1571 val = ioread32(sc->mem + reg_offset);
1572 spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001573 } else
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001574 val = ioread32(sc->mem + reg_offset);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001575 return val;
1576}
1577
Luis R. Rodriguez2ddb5c82009-09-14 02:09:38 -07001578static const struct ath_ops ath9k_common_ops = {
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001579 .read = ath9k_ioread32,
1580 .write = ath9k_iowrite32,
1581};
1582
1583/*
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001584 * Initialize and fill ath_softc, ath_sofct is the
1585 * "Software Carrier" struct. Historically it has existed
1586 * to allow the separation between hardware specific
1587 * variables (now in ath_hw) and driver specific variables.
1588 */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001589static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
1590 const struct ath_bus_ops *bus_ops)
Sujithff37e332008-11-24 12:07:55 +05301591{
Sujithcbe61d82009-02-09 13:27:12 +05301592 struct ath_hw *ah = NULL;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001593 struct ath_common *common;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001594 int r = 0, i;
Sujithff37e332008-11-24 12:07:55 +05301595 int csz = 0;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001596 int qnum;
Sujithff37e332008-11-24 12:07:55 +05301597
1598 /* XXX: hardware will not be ready until ath_open() being called */
1599 sc->sc_flags |= SC_OP_INVALID;
Sujith88b126a2008-11-28 22:19:02 +05301600
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001601 spin_lock_init(&sc->wiphy_lock);
Sujithff37e332008-11-24 12:07:55 +05301602 spin_lock_init(&sc->sc_resetlock);
Luis R. Rodriguez61584252009-03-12 18:18:49 -04001603 spin_lock_init(&sc->sc_serial_rw);
Gabor Juhos04717cc2009-07-14 20:17:13 -04001604 spin_lock_init(&sc->sc_pm_lock);
Sujithaa33de02008-12-18 11:40:16 +05301605 mutex_init(&sc->mutex);
Sujithff37e332008-11-24 12:07:55 +05301606 tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
Sujith9fc9ab02009-03-03 10:16:51 +05301607 tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
Sujithff37e332008-11-24 12:07:55 +05301608 (unsigned long)sc);
1609
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001610 ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001611 if (!ah)
1612 return -ENOMEM;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001613
Luis R. Rodriguez8df5d1b2009-08-03 12:24:37 -07001614 ah->hw_version.devid = devid;
Vasanthakumar Thiagarajanaeac3552009-09-09 15:25:49 +05301615 ah->hw_version.subsysid = subsysid;
Luis R. Rodrigueze1e2f932009-08-03 12:24:38 -07001616 sc->sc_ah = ah;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001617
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001618 common = ath9k_hw_common(ah);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001619 common->ops = &ath9k_common_ops;
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001620 common->bus_ops = bus_ops;
Luis R. Rodriguez13b81552009-09-10 17:52:45 -07001621 common->ah = ah;
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -07001622 common->hw = sc->hw;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001623 common->priv = sc;
Luis R. Rodriguezfaa27fa2009-10-06 21:19:06 -04001624 common->debug_mask = ath9k_debug;
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001625
1626 /*
1627 * Cache line size is used to size and align various
1628 * structures used to communicate with the hardware.
1629 */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001630 ath_read_cachesize(common, &csz);
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001631 /* XXX assert csz is non-zero */
1632 common->cachelsz = csz << 2; /* convert to bytes */
1633
Luis R. Rodriguezf637cfd2009-08-03 12:24:46 -07001634 r = ath9k_hw_init(ah);
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001635 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001636 ath_print(common, ATH_DBG_FATAL,
1637 "Unable to initialize hardware; "
1638 "initialization status: %d\n", r);
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001639 goto bad_free_hw;
1640 }
1641
1642 if (ath9k_init_debug(ah) < 0) {
1643 ath_print(common, ATH_DBG_FATAL,
1644 "Unable to create debugfs files\n");
1645 goto bad_free_hw;
Sujithff37e332008-11-24 12:07:55 +05301646 }
Sujithff37e332008-11-24 12:07:55 +05301647
1648 /* Get the hardware key cache size. */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001649 common->keymax = ah->caps.keycache_size;
1650 if (common->keymax > ATH_KEYMAX) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001651 ath_print(common, ATH_DBG_ANY,
1652 "Warning, using only %u entries in %u key cache\n",
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001653 ATH_KEYMAX, common->keymax);
1654 common->keymax = ATH_KEYMAX;
Sujithff37e332008-11-24 12:07:55 +05301655 }
1656
1657 /*
1658 * Reset the key cache since some parts do not
1659 * reset the contents on initial power up.
1660 */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001661 for (i = 0; i < common->keymax; i++)
Sujithff37e332008-11-24 12:07:55 +05301662 ath9k_hw_keyreset(ah, (u16) i);
Sujithff37e332008-11-24 12:07:55 +05301663
Sujithff37e332008-11-24 12:07:55 +05301664 /* default to MONITOR mode */
Sujith2660b812009-02-09 13:27:26 +05301665 sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR;
Colin McCabed97809d2008-12-01 13:38:55 -08001666
Sujithff37e332008-11-24 12:07:55 +05301667 /*
1668 * Allocate hardware transmit queues: one queue for
1669 * beacon frames and one data queue for each QoS
1670 * priority. Note that the hal handles reseting
1671 * these queues at the needed time.
1672 */
Luis R. Rodriguez536b3a72009-10-06 21:19:11 -04001673 sc->beacon.beaconq = ath9k_hw_beaconq_setup(ah);
Sujithb77f4832008-12-07 21:44:03 +05301674 if (sc->beacon.beaconq == -1) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001675 ath_print(common, ATH_DBG_FATAL,
1676 "Unable to setup a beacon xmit queue\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001677 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301678 goto bad2;
1679 }
Sujithb77f4832008-12-07 21:44:03 +05301680 sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
1681 if (sc->beacon.cabq == NULL) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001682 ath_print(common, ATH_DBG_FATAL,
1683 "Unable to setup CAB xmit queue\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001684 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301685 goto bad2;
1686 }
1687
Sujith17d79042009-02-09 13:27:03 +05301688 sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
Sujithff37e332008-11-24 12:07:55 +05301689 ath_cabq_update(sc);
1690
Sujithb77f4832008-12-07 21:44:03 +05301691 for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++)
1692 sc->tx.hwq_map[i] = -1;
Sujithff37e332008-11-24 12:07:55 +05301693
1694 /* Setup data queues */
1695 /* NB: ensure BK queue is the lowest priority h/w queue */
1696 if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001697 ath_print(common, ATH_DBG_FATAL,
1698 "Unable to setup xmit queue for BK traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001699 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301700 goto bad2;
1701 }
1702
1703 if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001704 ath_print(common, ATH_DBG_FATAL,
1705 "Unable to setup xmit queue for BE traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001706 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301707 goto bad2;
1708 }
1709 if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001710 ath_print(common, ATH_DBG_FATAL,
1711 "Unable to setup xmit queue for VI traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001712 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301713 goto bad2;
1714 }
1715 if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001716 ath_print(common, ATH_DBG_FATAL,
1717 "Unable to setup xmit queue for VO traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001718 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301719 goto bad2;
1720 }
1721
1722 /* Initializes the noise floor to a reasonable default value.
1723 * Later on this will be updated during ANI processing. */
1724
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08001725 common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR;
1726 setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
Sujithff37e332008-11-24 12:07:55 +05301727
1728 if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1729 ATH9K_CIPHER_TKIP, NULL)) {
1730 /*
1731 * Whether we should enable h/w TKIP MIC.
1732 * XXX: if we don't support WME TKIP MIC, then we wouldn't
1733 * report WMM capable, so it's always safe to turn on
1734 * TKIP MIC in this case.
1735 */
1736 ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
1737 0, 1, NULL);
1738 }
1739
1740 /*
1741 * Check whether the separate key cache entries
1742 * are required to handle both tx+rx MIC keys.
1743 * With split mic keys the number of stations is limited
1744 * to 27 otherwise 59.
1745 */
1746 if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1747 ATH9K_CIPHER_TKIP, NULL)
1748 && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1749 ATH9K_CIPHER_MIC, NULL)
1750 && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
1751 0, NULL))
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001752 common->splitmic = 1;
Sujithff37e332008-11-24 12:07:55 +05301753
1754 /* turn on mcast key search if possible */
1755 if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
1756 (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
1757 1, NULL);
1758
Sujith17d79042009-02-09 13:27:03 +05301759 sc->config.txpowlimit = ATH_TXPOWER_MAX;
Sujithff37e332008-11-24 12:07:55 +05301760
1761 /* 11n Capabilities */
Sujith2660b812009-02-09 13:27:26 +05301762 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
Sujithff37e332008-11-24 12:07:55 +05301763 sc->sc_flags |= SC_OP_TXAGGR;
1764 sc->sc_flags |= SC_OP_RXAGGR;
1765 }
1766
Luis R. Rodriguez43c27612009-09-13 21:07:07 -07001767 common->tx_chainmask = ah->caps.tx_chainmask;
1768 common->rx_chainmask = ah->caps.rx_chainmask;
Sujithff37e332008-11-24 12:07:55 +05301769
1770 ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
Sujithb77f4832008-12-07 21:44:03 +05301771 sc->rx.defant = ath9k_hw_getdefantenna(ah);
Sujithff37e332008-11-24 12:07:55 +05301772
Jouni Malinen8ca21f02009-03-03 19:23:27 +02001773 if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001774 memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
Sujithff37e332008-11-24 12:07:55 +05301775
Sujithb77f4832008-12-07 21:44:03 +05301776 sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */
Sujithff37e332008-11-24 12:07:55 +05301777
1778 /* initialize beacon slots */
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001779 for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001780 sc->beacon.bslot[i] = NULL;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001781 sc->beacon.bslot_aphy[i] = NULL;
1782 }
Sujithff37e332008-11-24 12:07:55 +05301783
Sujithff37e332008-11-24 12:07:55 +05301784 /* setup channels and rates */
1785
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001786 if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) {
1787 sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable;
1788 sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
1789 sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
1790 ARRAY_SIZE(ath9k_2ghz_chantable);
1791 sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
1792 sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
1793 ARRAY_SIZE(ath9k_legacy_rates);
1794 }
Sujithff37e332008-11-24 12:07:55 +05301795
Sujith2660b812009-02-09 13:27:26 +05301796 if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) {
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001797 sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable;
Sujithff37e332008-11-24 12:07:55 +05301798 sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001799 sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
1800 ARRAY_SIZE(ath9k_5ghz_chantable);
Felix Fietkau545750d2009-11-23 22:21:01 +01001801 sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
1802 ath9k_legacy_rates + 4;
1803 sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
1804 ARRAY_SIZE(ath9k_legacy_rates) - 4;
Sujithff37e332008-11-24 12:07:55 +05301805 }
1806
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001807 switch (ah->btcoex_hw.scheme) {
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001808 case ATH_BTCOEX_CFG_NONE:
1809 break;
1810 case ATH_BTCOEX_CFG_2WIRE:
1811 ath9k_hw_btcoex_init_2wire(ah);
1812 break;
1813 case ATH_BTCOEX_CFG_3WIRE:
1814 ath9k_hw_btcoex_init_3wire(ah);
1815 r = ath_init_btcoex_timer(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301816 if (r)
1817 goto bad2;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001818 qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001819 ath9k_hw_init_btcoex_hw(ah, qnum);
Luis R. Rodrigueze08a6ac2009-09-09 14:26:15 -07001820 sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001821 break;
1822 default:
1823 WARN_ON(1);
1824 break;
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301825 }
Vasanthakumar Thiagarajanc97c92d2009-01-02 15:35:46 +05301826
Sujithff37e332008-11-24 12:07:55 +05301827 return 0;
1828bad2:
1829 /* cleanup tx queues */
1830 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1831 if (ATH_TXQ_SETUP(sc, i))
Sujithb77f4832008-12-07 21:44:03 +05301832 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
Sujithff37e332008-11-24 12:07:55 +05301833
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001834bad_free_hw:
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001835 ath9k_uninit_hw(sc);
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001836 return r;
Sujithff37e332008-11-24 12:07:55 +05301837}
1838
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001839void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301840{
Luis R. Rodriguez14acdde2009-12-18 11:26:04 -05001841 struct ath_hw *ah = sc->sc_ah;
1842
Sujith9c84b792008-10-29 10:17:13 +05301843 hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
1844 IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
1845 IEEE80211_HW_SIGNAL_DBM |
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301846 IEEE80211_HW_AMPDU_AGGREGATION |
1847 IEEE80211_HW_SUPPORTS_PS |
Sujitheeee1322009-03-10 10:39:53 +05301848 IEEE80211_HW_PS_NULLFUNC_STACK |
1849 IEEE80211_HW_SPECTRUM_MGMT;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301850
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02001851 if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt)
Jouni Malinen0ced0e12009-01-08 13:32:13 +02001852 hw->flags |= IEEE80211_HW_MFP_CAPABLE;
1853
Sujith9c84b792008-10-29 10:17:13 +05301854 hw->wiphy->interface_modes =
1855 BIT(NL80211_IFTYPE_AP) |
1856 BIT(NL80211_IFTYPE_STATION) |
Pat Erley9cb54122009-03-20 22:59:59 -04001857 BIT(NL80211_IFTYPE_ADHOC) |
1858 BIT(NL80211_IFTYPE_MESH_POINT);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301859
Luis R. Rodriguez14acdde2009-12-18 11:26:04 -05001860 if (AR_SREV_5416(ah))
1861 hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
John W. Linville18b6c9a2009-11-23 16:15:19 -05001862
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301863 hw->queues = 4;
Sujithe63835b2008-11-18 09:07:53 +05301864 hw->max_rates = 4;
Sujith171387e2009-02-17 15:36:25 +05301865 hw->channel_change_time = 5000;
Jouni Malinen465ca842009-03-03 19:23:34 +02001866 hw->max_listen_interval = 10;
Luis R. Rodriguezdd190182009-07-14 20:13:56 -04001867 /* Hardware supports 10 but we use 4 */
1868 hw->max_rate_tries = 4;
Sujith528f0c62008-10-29 10:14:26 +05301869 hw->sta_data_size = sizeof(struct ath_node);
Sujith17d79042009-02-09 13:27:03 +05301870 hw->vif_data_size = sizeof(struct ath_vif);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301871
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301872 hw->rate_control_algorithm = "ath9k_rate_control";
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301873
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001874 if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
1875 hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
1876 &sc->sbands[IEEE80211_BAND_2GHZ];
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001877 if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
1878 hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
1879 &sc->sbands[IEEE80211_BAND_5GHZ];
1880}
1881
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001882/* Device driver core initialization */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001883int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
1884 const struct ath_bus_ops *bus_ops)
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001885{
1886 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001887 struct ath_common *common;
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001888 struct ath_hw *ah;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001889 int error = 0, i;
Bob Copeland3a702e42009-03-30 22:30:29 -04001890 struct ath_regulatory *reg;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001891
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001892 dev_dbg(sc->dev, "Attach ATH hw\n");
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001893
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001894 error = ath_init_softc(devid, sc, subsysid, bus_ops);
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001895 if (error != 0)
1896 return error;
1897
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001898 ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001899 common = ath9k_hw_common(ah);
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001900
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001901 /* get mac address from hardware and set in mac80211 */
1902
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001903 SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001904
1905 ath_set_hw_capab(sc, hw);
1906
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001907 error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
Luis R. Rodriguezc26c2e52009-05-19 18:27:11 -04001908 ath9k_reg_notifier);
1909 if (error)
1910 return error;
1911
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001912 reg = &common->regulatory;
Luis R. Rodriguezc26c2e52009-05-19 18:27:11 -04001913
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001914 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001915 if (test_bit(ATH9K_MODE_11G, ah->caps.wireless_modes))
1916 setup_ht_cap(sc,
1917 &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001918 if (test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes))
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001919 setup_ht_cap(sc,
1920 &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
Sujith9c84b792008-10-29 10:17:13 +05301921 }
1922
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301923 /* initialize tx/rx engine */
1924 error = ath_tx_init(sc, ATH_TXBUF);
1925 if (error != 0)
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301926 goto error_attach;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301927
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301928 error = ath_rx_init(sc, ATH_RXBUF);
1929 if (error != 0)
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301930 goto error_attach;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301931
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001932 INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
Jouni Malinenf98c3bd2009-03-03 19:23:39 +02001933 INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);
1934 sc->wiphy_scheduler_int = msecs_to_jiffies(500);
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001935
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301936 error = ieee80211_register_hw(hw);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301937
Bob Copeland3a702e42009-03-30 22:30:29 -04001938 if (!ath_is_world_regd(reg)) {
Bob Copelandc02cf372009-03-30 22:30:28 -04001939 error = regulatory_hint(hw->wiphy, reg->alpha2);
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05001940 if (error)
1941 goto error_attach;
1942 }
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001943
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301944 /* Initialize LED control */
1945 ath_init_leds(sc);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301946
Johannes Berg3b319aa2009-06-13 14:50:26 +05301947 ath_start_rfkill_poll(sc);
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001948
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301949 return 0;
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301950
1951error_attach:
1952 /* cleanup tx queues */
1953 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1954 if (ATH_TXQ_SETUP(sc, i))
1955 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
1956
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001957 ath9k_uninit_hw(sc);
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301958
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301959 return error;
1960}
1961
Sujithff37e332008-11-24 12:07:55 +05301962int ath_reset(struct ath_softc *sc, bool retry_tx)
1963{
Sujithcbe61d82009-02-09 13:27:12 +05301964 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001965 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez030bb492008-12-23 15:58:37 -08001966 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001967 int r;
Sujithff37e332008-11-24 12:07:55 +05301968
Sujith2ab81d42009-12-14 16:34:56 +05301969 /* Stop ANI */
1970 del_timer_sync(&common->ani.timer);
1971
Sujithff37e332008-11-24 12:07:55 +05301972 ath9k_hw_set_interrupts(ah, 0);
Sujith043a0402009-01-16 21:38:47 +05301973 ath_drain_all_txq(sc, retry_tx);
Sujithff37e332008-11-24 12:07:55 +05301974 ath_stoprecv(sc);
1975 ath_flushrecv(sc);
1976
1977 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301978 r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001979 if (r)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001980 ath_print(common, ATH_DBG_FATAL,
1981 "Unable to reset hardware; reset status %d\n", r);
Sujithff37e332008-11-24 12:07:55 +05301982 spin_unlock_bh(&sc->sc_resetlock);
1983
1984 if (ath_startrecv(sc) != 0)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001985 ath_print(common, ATH_DBG_FATAL,
1986 "Unable to start recv logic\n");
Sujithff37e332008-11-24 12:07:55 +05301987
1988 /*
1989 * We may be doing a reset in response to a request
1990 * that changes the channel so update any state that
1991 * might change as a result.
1992 */
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -08001993 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +05301994
1995 ath_update_txpow(sc);
1996
1997 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001998 ath_beacon_config(sc, NULL); /* restart beacons */
Sujithff37e332008-11-24 12:07:55 +05301999
Sujith17d79042009-02-09 13:27:03 +05302000 ath9k_hw_set_interrupts(ah, sc->imask);
Sujithff37e332008-11-24 12:07:55 +05302001
2002 if (retry_tx) {
2003 int i;
2004 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
2005 if (ATH_TXQ_SETUP(sc, i)) {
Sujithb77f4832008-12-07 21:44:03 +05302006 spin_lock_bh(&sc->tx.txq[i].axq_lock);
2007 ath_txq_schedule(sc, &sc->tx.txq[i]);
2008 spin_unlock_bh(&sc->tx.txq[i].axq_lock);
Sujithff37e332008-11-24 12:07:55 +05302009 }
2010 }
2011 }
2012
Sujith2ab81d42009-12-14 16:34:56 +05302013 /* Start ANI */
2014 ath_start_ani(common);
2015
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08002016 return r;
Sujithff37e332008-11-24 12:07:55 +05302017}
2018
2019/*
2020 * This function will allocate both the DMA descriptor structure, and the
2021 * buffers it contains. These are used to contain the descriptors used
2022 * by the system.
2023*/
2024int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
2025 struct list_head *head, const char *name,
2026 int nbuf, int ndesc)
2027{
2028#define DS2PHYS(_dd, _ds) \
2029 ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
2030#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
2031#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002032 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujithff37e332008-11-24 12:07:55 +05302033 struct ath_desc *ds;
2034 struct ath_buf *bf;
2035 int i, bsize, error;
2036
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002037 ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n",
2038 name, nbuf, ndesc);
Sujithff37e332008-11-24 12:07:55 +05302039
Senthil Balasubramanianb03a9db2009-03-06 11:24:09 +05302040 INIT_LIST_HEAD(head);
Sujithff37e332008-11-24 12:07:55 +05302041 /* ath_desc must be a multiple of DWORDs */
2042 if ((sizeof(struct ath_desc) % 4) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002043 ath_print(common, ATH_DBG_FATAL,
2044 "ath_desc not DWORD aligned\n");
Luis R. Rodriguez9680e8a2009-09-13 23:28:00 -07002045 BUG_ON((sizeof(struct ath_desc) % 4) != 0);
Sujithff37e332008-11-24 12:07:55 +05302046 error = -ENOMEM;
2047 goto fail;
2048 }
2049
Sujithff37e332008-11-24 12:07:55 +05302050 dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
2051
2052 /*
2053 * Need additional DMA memory because we can't use
2054 * descriptors that cross the 4K page boundary. Assume
2055 * one skipped descriptor per 4K page.
2056 */
Sujith2660b812009-02-09 13:27:26 +05302057 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) {
Sujithff37e332008-11-24 12:07:55 +05302058 u32 ndesc_skipped =
2059 ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len);
2060 u32 dma_len;
2061
2062 while (ndesc_skipped) {
2063 dma_len = ndesc_skipped * sizeof(struct ath_desc);
2064 dd->dd_desc_len += dma_len;
2065
2066 ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len);
2067 };
2068 }
2069
2070 /* allocate descriptors */
Gabor Juhos7da3c552009-01-14 20:17:03 +01002071 dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len,
Senthil Balasubramanianf0e6ce12009-03-06 11:24:08 +05302072 &dd->dd_desc_paddr, GFP_KERNEL);
Sujithff37e332008-11-24 12:07:55 +05302073 if (dd->dd_desc == NULL) {
2074 error = -ENOMEM;
2075 goto fail;
2076 }
2077 ds = dd->dd_desc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002078 ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
2079 name, ds, (u32) dd->dd_desc_len,
2080 ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
Sujithff37e332008-11-24 12:07:55 +05302081
2082 /* allocate buffers */
2083 bsize = sizeof(struct ath_buf) * nbuf;
Senthil Balasubramanianf0e6ce12009-03-06 11:24:08 +05302084 bf = kzalloc(bsize, GFP_KERNEL);
Sujithff37e332008-11-24 12:07:55 +05302085 if (bf == NULL) {
2086 error = -ENOMEM;
2087 goto fail2;
2088 }
Sujithff37e332008-11-24 12:07:55 +05302089 dd->dd_bufptr = bf;
2090
Sujithff37e332008-11-24 12:07:55 +05302091 for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
2092 bf->bf_desc = ds;
2093 bf->bf_daddr = DS2PHYS(dd, ds);
2094
Sujith2660b812009-02-09 13:27:26 +05302095 if (!(sc->sc_ah->caps.hw_caps &
Sujithff37e332008-11-24 12:07:55 +05302096 ATH9K_HW_CAP_4KB_SPLITTRANS)) {
2097 /*
2098 * Skip descriptor addresses which can cause 4KB
2099 * boundary crossing (addr + length) with a 32 dword
2100 * descriptor fetch.
2101 */
2102 while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
Luis R. Rodriguez9680e8a2009-09-13 23:28:00 -07002103 BUG_ON((caddr_t) bf->bf_desc >=
Sujithff37e332008-11-24 12:07:55 +05302104 ((caddr_t) dd->dd_desc +
2105 dd->dd_desc_len));
2106
2107 ds += ndesc;
2108 bf->bf_desc = ds;
2109 bf->bf_daddr = DS2PHYS(dd, ds);
2110 }
2111 }
2112 list_add_tail(&bf->list, head);
2113 }
2114 return 0;
2115fail2:
Gabor Juhos7da3c552009-01-14 20:17:03 +01002116 dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
2117 dd->dd_desc_paddr);
Sujithff37e332008-11-24 12:07:55 +05302118fail:
2119 memset(dd, 0, sizeof(*dd));
2120 return error;
2121#undef ATH_DESC_4KB_BOUND_CHECK
2122#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
2123#undef DS2PHYS
2124}
2125
2126void ath_descdma_cleanup(struct ath_softc *sc,
2127 struct ath_descdma *dd,
2128 struct list_head *head)
2129{
Gabor Juhos7da3c552009-01-14 20:17:03 +01002130 dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
2131 dd->dd_desc_paddr);
Sujithff37e332008-11-24 12:07:55 +05302132
2133 INIT_LIST_HEAD(head);
2134 kfree(dd->dd_bufptr);
2135 memset(dd, 0, sizeof(*dd));
2136}
2137
2138int ath_get_hal_qnum(u16 queue, struct ath_softc *sc)
2139{
2140 int qnum;
2141
2142 switch (queue) {
2143 case 0:
Sujithb77f4832008-12-07 21:44:03 +05302144 qnum = sc->tx.hwq_map[ATH9K_WME_AC_VO];
Sujithff37e332008-11-24 12:07:55 +05302145 break;
2146 case 1:
Sujithb77f4832008-12-07 21:44:03 +05302147 qnum = sc->tx.hwq_map[ATH9K_WME_AC_VI];
Sujithff37e332008-11-24 12:07:55 +05302148 break;
2149 case 2:
Sujithb77f4832008-12-07 21:44:03 +05302150 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +05302151 break;
2152 case 3:
Sujithb77f4832008-12-07 21:44:03 +05302153 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BK];
Sujithff37e332008-11-24 12:07:55 +05302154 break;
2155 default:
Sujithb77f4832008-12-07 21:44:03 +05302156 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +05302157 break;
2158 }
2159
2160 return qnum;
2161}
2162
2163int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
2164{
2165 int qnum;
2166
2167 switch (queue) {
2168 case ATH9K_WME_AC_VO:
2169 qnum = 0;
2170 break;
2171 case ATH9K_WME_AC_VI:
2172 qnum = 1;
2173 break;
2174 case ATH9K_WME_AC_BE:
2175 qnum = 2;
2176 break;
2177 case ATH9K_WME_AC_BK:
2178 qnum = 3;
2179 break;
2180 default:
2181 qnum = -1;
2182 break;
2183 }
2184
2185 return qnum;
2186}
2187
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002188/* XXX: Remove me once we don't depend on ath9k_channel for all
2189 * this redundant data */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002190void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
2191 struct ath9k_channel *ichan)
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002192{
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002193 struct ieee80211_channel *chan = hw->conf.channel;
2194 struct ieee80211_conf *conf = &hw->conf;
2195
2196 ichan->channel = chan->center_freq;
2197 ichan->chan = chan;
2198
2199 if (chan->band == IEEE80211_BAND_2GHZ) {
2200 ichan->chanmode = CHANNEL_G;
Sujith88132622009-09-03 12:08:53 +05302201 ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002202 } else {
2203 ichan->chanmode = CHANNEL_A;
2204 ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
2205 }
2206
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -07002207 if (conf_is_ht(conf))
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002208 ichan->chanmode = ath_get_extchanmode(sc, chan,
2209 conf->channel_type);
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002210}
2211
Sujithff37e332008-11-24 12:07:55 +05302212/**********************/
2213/* mac80211 callbacks */
2214/**********************/
2215
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002216/*
2217 * (Re)start btcoex timers
2218 */
2219static void ath9k_btcoex_timer_resume(struct ath_softc *sc)
2220{
2221 struct ath_btcoex *btcoex = &sc->btcoex;
2222 struct ath_hw *ah = sc->sc_ah;
2223
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002224 ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
2225 "Starting btcoex timers");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002226
2227 /* make sure duty cycle timer is also stopped when resuming */
2228 if (btcoex->hw_timer_enabled)
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07002229 ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002230
2231 btcoex->bt_priority_cnt = 0;
2232 btcoex->bt_priority_time = jiffies;
2233 sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
2234
2235 mod_timer(&btcoex->period_timer, jiffies);
2236}
2237
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002238static int ath9k_start(struct ieee80211_hw *hw)
2239{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002240 struct ath_wiphy *aphy = hw->priv;
2241 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002242 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002243 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002244 struct ieee80211_channel *curchan = hw->conf.channel;
Sujithff37e332008-11-24 12:07:55 +05302245 struct ath9k_channel *init_channel;
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05302246 int r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002247
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002248 ath_print(common, ATH_DBG_CONFIG,
2249 "Starting driver with initial channel: %d MHz\n",
2250 curchan->center_freq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002251
Sujith141b38b2009-02-04 08:10:07 +05302252 mutex_lock(&sc->mutex);
2253
Jouni Malinen9580a222009-03-03 19:23:33 +02002254 if (ath9k_wiphy_started(sc)) {
2255 if (sc->chan_idx == curchan->hw_value) {
2256 /*
2257 * Already on the operational channel, the new wiphy
2258 * can be marked active.
2259 */
2260 aphy->state = ATH_WIPHY_ACTIVE;
2261 ieee80211_wake_queues(hw);
2262 } else {
2263 /*
2264 * Another wiphy is on another channel, start the new
2265 * wiphy in paused state.
2266 */
2267 aphy->state = ATH_WIPHY_PAUSED;
2268 ieee80211_stop_queues(hw);
2269 }
2270 mutex_unlock(&sc->mutex);
2271 return 0;
2272 }
2273 aphy->state = ATH_WIPHY_ACTIVE;
2274
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002275 /* setup initial channel */
2276
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05302277 sc->chan_idx = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002278
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05302279 init_channel = ath_get_curchannel(sc, hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002280
Sujithff37e332008-11-24 12:07:55 +05302281 /* Reset SERDES registers */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002282 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithff37e332008-11-24 12:07:55 +05302283
2284 /*
2285 * The basic interface to setting the hardware in a good
2286 * state is ``reset''. On return the hardware is known to
2287 * be powered up and with interrupts disabled. This must
2288 * be followed by initialization of the appropriate bits
2289 * and then setup of the interrupt mask.
2290 */
2291 spin_lock_bh(&sc->sc_resetlock);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002292 r = ath9k_hw_reset(ah, init_channel, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08002293 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002294 ath_print(common, ATH_DBG_FATAL,
2295 "Unable to reset hardware; reset status %d "
2296 "(freq %u MHz)\n", r,
2297 curchan->center_freq);
Sujithff37e332008-11-24 12:07:55 +05302298 spin_unlock_bh(&sc->sc_resetlock);
Sujith141b38b2009-02-04 08:10:07 +05302299 goto mutex_unlock;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002300 }
Sujithff37e332008-11-24 12:07:55 +05302301 spin_unlock_bh(&sc->sc_resetlock);
2302
2303 /*
2304 * This is needed only to setup initial state
2305 * but it's best done after a reset.
2306 */
2307 ath_update_txpow(sc);
2308
2309 /*
2310 * Setup the hardware after reset:
2311 * The receive engine is set going.
2312 * Frame transmit is handled entirely
2313 * in the frame output path; there's nothing to do
2314 * here except setup the interrupt mask.
2315 */
2316 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002317 ath_print(common, ATH_DBG_FATAL,
2318 "Unable to start recv logic\n");
Sujith141b38b2009-02-04 08:10:07 +05302319 r = -EIO;
2320 goto mutex_unlock;
Sujithff37e332008-11-24 12:07:55 +05302321 }
2322
2323 /* Setup our intr mask. */
Sujith17d79042009-02-09 13:27:03 +05302324 sc->imask = ATH9K_INT_RX | ATH9K_INT_TX
Sujithff37e332008-11-24 12:07:55 +05302325 | ATH9K_INT_RXEOL | ATH9K_INT_RXORN
2326 | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
2327
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002328 if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
Sujith17d79042009-02-09 13:27:03 +05302329 sc->imask |= ATH9K_INT_GTT;
Sujithff37e332008-11-24 12:07:55 +05302330
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002331 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
Sujith17d79042009-02-09 13:27:03 +05302332 sc->imask |= ATH9K_INT_CST;
Sujithff37e332008-11-24 12:07:55 +05302333
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -08002334 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +05302335
2336 sc->sc_flags &= ~SC_OP_INVALID;
2337
2338 /* Disable BMISS interrupt when we're not associated */
Sujith17d79042009-02-09 13:27:03 +05302339 sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002340 ath9k_hw_set_interrupts(ah, sc->imask);
Sujithff37e332008-11-24 12:07:55 +05302341
Jouni Malinenbce048d2009-03-03 19:23:28 +02002342 ieee80211_wake_queues(hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002343
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04002344 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
Senthil Balasubramanian164ace32009-07-14 20:17:09 -04002345
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002346 if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
2347 !ah->btcoex_hw.enabled) {
Luis R. Rodriguez5e197292009-09-09 15:15:55 -07002348 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
2349 AR_STOMP_LOW_WLAN_WGHT);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002350 ath9k_hw_btcoex_enable(ah);
Vasanthakumar Thiagarajanf985ad12009-08-26 21:08:43 +05302351
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07002352 if (common->bus_ops->bt_coex_prep)
2353 common->bus_ops->bt_coex_prep(common);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002354 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002355 ath9k_btcoex_timer_resume(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05302356 }
2357
Sujith141b38b2009-02-04 08:10:07 +05302358mutex_unlock:
2359 mutex_unlock(&sc->mutex);
2360
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08002361 return r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002362}
2363
2364static int ath9k_tx(struct ieee80211_hw *hw,
2365 struct sk_buff *skb)
2366{
Jouni Malinen147583c2008-08-11 14:01:50 +03002367 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
Jouni Malinenbce048d2009-03-03 19:23:28 +02002368 struct ath_wiphy *aphy = hw->priv;
2369 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002370 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith528f0c62008-10-29 10:14:26 +05302371 struct ath_tx_control txctl;
Benoit Papillault1bc14882009-11-24 15:49:18 +01002372 int padpos, padsize;
2373 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
Sujith528f0c62008-10-29 10:14:26 +05302374
Jouni Malinen8089cc42009-03-03 19:23:38 +02002375 if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002376 ath_print(common, ATH_DBG_XMIT,
2377 "ath9k: %s: TX in unexpected wiphy state "
2378 "%d\n", wiphy_name(hw->wiphy), aphy->state);
Jouni Malinenee166a02009-03-03 19:23:36 +02002379 goto exit;
2380 }
2381
Gabor Juhos96148322009-07-24 17:27:21 +02002382 if (sc->ps_enabled) {
Jouni Malinendc8c4582009-05-19 17:01:42 +03002383 /*
2384 * mac80211 does not set PM field for normal data frames, so we
2385 * need to update that based on the current PS mode.
2386 */
2387 if (ieee80211_is_data(hdr->frame_control) &&
2388 !ieee80211_is_nullfunc(hdr->frame_control) &&
2389 !ieee80211_has_pm(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002390 ath_print(common, ATH_DBG_PS, "Add PM=1 for a TX frame "
2391 "while in PS mode\n");
Jouni Malinendc8c4582009-05-19 17:01:42 +03002392 hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
2393 }
2394 }
2395
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002396 if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) {
2397 /*
2398 * We are using PS-Poll and mac80211 can request TX while in
2399 * power save mode. Need to wake up hardware for the TX to be
2400 * completed and if needed, also for RX of buffered frames.
2401 */
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002402 ath9k_ps_wakeup(sc);
2403 ath9k_hw_setrxabort(sc->sc_ah, 0);
2404 if (ieee80211_is_pspoll(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002405 ath_print(common, ATH_DBG_PS,
2406 "Sending PS-Poll to pick a buffered frame\n");
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002407 sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA;
2408 } else {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002409 ath_print(common, ATH_DBG_PS,
2410 "Wake up to complete TX\n");
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002411 sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK;
2412 }
2413 /*
2414 * The actual restore operation will happen only after
2415 * the sc_flags bit is cleared. We are just dropping
2416 * the ps_usecount here.
2417 */
2418 ath9k_ps_restore(sc);
2419 }
2420
Sujith528f0c62008-10-29 10:14:26 +05302421 memset(&txctl, 0, sizeof(struct ath_tx_control));
Jouni Malinen147583c2008-08-11 14:01:50 +03002422
2423 /*
2424 * As a temporary workaround, assign seq# here; this will likely need
2425 * to be cleaned up to work better with Beacon transmission and virtual
2426 * BSSes.
2427 */
2428 if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
Jouni Malinen147583c2008-08-11 14:01:50 +03002429 if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
Sujithb77f4832008-12-07 21:44:03 +05302430 sc->tx.seq_no += 0x10;
Jouni Malinen147583c2008-08-11 14:01:50 +03002431 hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
Sujithb77f4832008-12-07 21:44:03 +05302432 hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
Jouni Malinen147583c2008-08-11 14:01:50 +03002433 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002434
2435 /* Add the padding after the header if this is not already done */
Benoit Papillault1bc14882009-11-24 15:49:18 +01002436 padpos = ath9k_cmn_padpos(hdr->frame_control);
2437 padsize = padpos & 3;
2438 if (padsize && skb->len>padpos) {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002439 if (skb_headroom(skb) < padsize)
2440 return -1;
2441 skb_push(skb, padsize);
Benoit Papillault1bc14882009-11-24 15:49:18 +01002442 memmove(skb->data, skb->data + padsize, padpos);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002443 }
2444
Sujith528f0c62008-10-29 10:14:26 +05302445 /* Check if a tx queue is available */
2446
2447 txctl.txq = ath_test_get_txq(sc, skb);
2448 if (!txctl.txq)
2449 goto exit;
2450
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002451 ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002452
Jouni Malinenc52f33d2009-03-03 19:23:29 +02002453 if (ath_tx_start(hw, skb, &txctl) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002454 ath_print(common, ATH_DBG_XMIT, "TX failed\n");
Sujith528f0c62008-10-29 10:14:26 +05302455 goto exit;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002456 }
2457
2458 return 0;
Sujith528f0c62008-10-29 10:14:26 +05302459exit:
2460 dev_kfree_skb_any(skb);
2461 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002462}
2463
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002464/*
2465 * Pause btcoex timer and bt duty cycle timer
2466 */
2467static void ath9k_btcoex_timer_pause(struct ath_softc *sc)
2468{
2469 struct ath_btcoex *btcoex = &sc->btcoex;
2470 struct ath_hw *ah = sc->sc_ah;
2471
2472 del_timer_sync(&btcoex->period_timer);
2473
2474 if (btcoex->hw_timer_enabled)
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07002475 ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002476
2477 btcoex->hw_timer_enabled = false;
2478}
2479
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002480static void ath9k_stop(struct ieee80211_hw *hw)
2481{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002482 struct ath_wiphy *aphy = hw->priv;
2483 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002484 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002485 struct ath_common *common = ath9k_hw_common(ah);
Sujith9c84b792008-10-29 10:17:13 +05302486
Sujith4c483812009-08-18 10:51:52 +05302487 mutex_lock(&sc->mutex);
2488
Jouni Malinen9580a222009-03-03 19:23:33 +02002489 aphy->state = ATH_WIPHY_INACTIVE;
2490
Luis R. Rodriguezc94dbff2009-07-27 11:53:04 -07002491 cancel_delayed_work_sync(&sc->ath_led_blink_work);
2492 cancel_delayed_work_sync(&sc->tx_complete_work);
2493
2494 if (!sc->num_sec_wiphy) {
2495 cancel_delayed_work_sync(&sc->wiphy_work);
2496 cancel_work_sync(&sc->chan_work);
2497 }
2498
Sujith9c84b792008-10-29 10:17:13 +05302499 if (sc->sc_flags & SC_OP_INVALID) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002500 ath_print(common, ATH_DBG_ANY, "Device not present\n");
Sujith4c483812009-08-18 10:51:52 +05302501 mutex_unlock(&sc->mutex);
Sujith9c84b792008-10-29 10:17:13 +05302502 return;
2503 }
2504
Jouni Malinen9580a222009-03-03 19:23:33 +02002505 if (ath9k_wiphy_started(sc)) {
2506 mutex_unlock(&sc->mutex);
2507 return; /* another wiphy still in use */
2508 }
2509
Sujith3867cf62009-12-23 20:03:27 -05002510 /* Ensure HW is awake when we try to shut it down. */
2511 ath9k_ps_wakeup(sc);
2512
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002513 if (ah->btcoex_hw.enabled) {
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002514 ath9k_hw_btcoex_disable(ah);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002515 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002516 ath9k_btcoex_timer_pause(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05302517 }
2518
Sujithff37e332008-11-24 12:07:55 +05302519 /* make sure h/w will not generate any interrupt
2520 * before setting the invalid flag. */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002521 ath9k_hw_set_interrupts(ah, 0);
Sujithff37e332008-11-24 12:07:55 +05302522
2523 if (!(sc->sc_flags & SC_OP_INVALID)) {
Sujith043a0402009-01-16 21:38:47 +05302524 ath_drain_all_txq(sc, false);
Sujithff37e332008-11-24 12:07:55 +05302525 ath_stoprecv(sc);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002526 ath9k_hw_phy_disable(ah);
Sujithff37e332008-11-24 12:07:55 +05302527 } else
Sujithb77f4832008-12-07 21:44:03 +05302528 sc->rx.rxlink = NULL;
Sujithff37e332008-11-24 12:07:55 +05302529
Sujithff37e332008-11-24 12:07:55 +05302530 /* disable HAL and put h/w to sleep */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002531 ath9k_hw_disable(ah);
2532 ath9k_hw_configpcipowersave(ah, 1, 1);
Sujith3867cf62009-12-23 20:03:27 -05002533 ath9k_ps_restore(sc);
2534
2535 /* Finally, put the chip in FULL SLEEP mode */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07002536 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Sujithff37e332008-11-24 12:07:55 +05302537
2538 sc->sc_flags |= SC_OP_INVALID;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002539
Sujith141b38b2009-02-04 08:10:07 +05302540 mutex_unlock(&sc->mutex);
2541
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002542 ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002543}
2544
2545static int ath9k_add_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01002546 struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002547{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002548 struct ath_wiphy *aphy = hw->priv;
2549 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002550 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berg1ed32e42009-12-23 13:15:45 +01002551 struct ath_vif *avp = (void *)vif->drv_priv;
Colin McCabed97809d2008-12-01 13:38:55 -08002552 enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002553 int ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002554
Sujith141b38b2009-02-04 08:10:07 +05302555 mutex_lock(&sc->mutex);
2556
Jouni Malinen8ca21f02009-03-03 19:23:27 +02002557 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
2558 sc->nvifs > 0) {
2559 ret = -ENOBUFS;
2560 goto out;
2561 }
2562
Johannes Berg1ed32e42009-12-23 13:15:45 +01002563 switch (vif->type) {
Johannes Berg05c914f2008-09-11 00:01:58 +02002564 case NL80211_IFTYPE_STATION:
Colin McCabed97809d2008-12-01 13:38:55 -08002565 ic_opmode = NL80211_IFTYPE_STATION;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002566 break;
Johannes Berg05c914f2008-09-11 00:01:58 +02002567 case NL80211_IFTYPE_ADHOC:
Johannes Berg05c914f2008-09-11 00:01:58 +02002568 case NL80211_IFTYPE_AP:
Pat Erley9cb54122009-03-20 22:59:59 -04002569 case NL80211_IFTYPE_MESH_POINT:
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002570 if (sc->nbcnvifs >= ATH_BCBUF) {
2571 ret = -ENOBUFS;
2572 goto out;
2573 }
Johannes Berg1ed32e42009-12-23 13:15:45 +01002574 ic_opmode = vif->type;
Jouni Malinen2ad67de2008-08-11 14:01:47 +03002575 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002576 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002577 ath_print(common, ATH_DBG_FATAL,
Johannes Berg1ed32e42009-12-23 13:15:45 +01002578 "Interface type %d not yet supported\n", vif->type);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002579 ret = -EOPNOTSUPP;
2580 goto out;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002581 }
2582
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002583 ath_print(common, ATH_DBG_CONFIG,
2584 "Attach a VIF of type: %d\n", ic_opmode);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002585
Sujith17d79042009-02-09 13:27:03 +05302586 /* Set the VIF opmode */
Sujith5640b082008-10-29 10:16:06 +05302587 avp->av_opmode = ic_opmode;
2588 avp->av_bslot = -1;
2589
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002590 sc->nvifs++;
Jouni Malinen8ca21f02009-03-03 19:23:27 +02002591
2592 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
2593 ath9k_set_bssid_mask(hw);
2594
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002595 if (sc->nvifs > 1)
2596 goto out; /* skip global settings for secondary vif */
2597
Sujithb238e902009-03-03 10:16:56 +05302598 if (ic_opmode == NL80211_IFTYPE_AP) {
Sujith5640b082008-10-29 10:16:06 +05302599 ath9k_hw_set_tsfadjust(sc->sc_ah, 1);
Sujithb238e902009-03-03 10:16:56 +05302600 sc->sc_flags |= SC_OP_TSF_RESET;
2601 }
Sujith5640b082008-10-29 10:16:06 +05302602
Sujith5640b082008-10-29 10:16:06 +05302603 /* Set the device opmode */
Sujith2660b812009-02-09 13:27:26 +05302604 sc->sc_ah->opmode = ic_opmode;
Sujith5640b082008-10-29 10:16:06 +05302605
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05302606 /*
2607 * Enable MIB interrupts when there are hardware phy counters.
2608 * Note we only do this (at the moment) for station mode.
2609 */
Johannes Berg1ed32e42009-12-23 13:15:45 +01002610 if ((vif->type == NL80211_IFTYPE_STATION) ||
2611 (vif->type == NL80211_IFTYPE_ADHOC) ||
2612 (vif->type == NL80211_IFTYPE_MESH_POINT)) {
Sujith1aa8e842009-08-13 09:34:25 +05302613 sc->imask |= ATH9K_INT_MIB;
Sujith4af9cf42009-02-12 10:06:47 +05302614 sc->imask |= ATH9K_INT_TSFOOR;
2615 }
2616
Sujith17d79042009-02-09 13:27:03 +05302617 ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05302618
Johannes Berg1ed32e42009-12-23 13:15:45 +01002619 if (vif->type == NL80211_IFTYPE_AP ||
2620 vif->type == NL80211_IFTYPE_ADHOC ||
2621 vif->type == NL80211_IFTYPE_MONITOR)
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08002622 ath_start_ani(common);
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07002623
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002624out:
Sujith141b38b2009-02-04 08:10:07 +05302625 mutex_unlock(&sc->mutex);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002626 return ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002627}
2628
2629static void ath9k_remove_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01002630 struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002631{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002632 struct ath_wiphy *aphy = hw->priv;
2633 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002634 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berg1ed32e42009-12-23 13:15:45 +01002635 struct ath_vif *avp = (void *)vif->drv_priv;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002636 int i;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002637
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002638 ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002639
Sujith141b38b2009-02-04 08:10:07 +05302640 mutex_lock(&sc->mutex);
2641
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07002642 /* Stop ANI */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08002643 del_timer_sync(&common->ani.timer);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002644
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002645 /* Reclaim beacon resources */
Pat Erley9cb54122009-03-20 22:59:59 -04002646 if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
2647 (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
2648 (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
Luis R. Rodriguez5f70a882009-12-23 20:03:28 -05002649 ath9k_ps_wakeup(sc);
Sujithb77f4832008-12-07 21:44:03 +05302650 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002651 ath_beacon_return(sc, avp);
Luis R. Rodriguez5f70a882009-12-23 20:03:28 -05002652 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002653 }
2654
Sujith672840a2008-08-11 14:05:08 +05302655 sc->sc_flags &= ~SC_OP_BEACONS;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002656
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002657 for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
Johannes Berg1ed32e42009-12-23 13:15:45 +01002658 if (sc->beacon.bslot[i] == vif) {
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002659 printk(KERN_DEBUG "%s: vif had allocated beacon "
2660 "slot\n", __func__);
2661 sc->beacon.bslot[i] = NULL;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02002662 sc->beacon.bslot_aphy[i] = NULL;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002663 }
2664 }
2665
Sujith17d79042009-02-09 13:27:03 +05302666 sc->nvifs--;
Sujith141b38b2009-02-04 08:10:07 +05302667
2668 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002669}
2670
Johannes Berge8975582008-10-09 12:18:51 +02002671static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002672{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002673 struct ath_wiphy *aphy = hw->priv;
2674 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002675 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berge8975582008-10-09 12:18:51 +02002676 struct ieee80211_conf *conf = &hw->conf;
Vivek Natarajan8782b412009-03-30 14:17:00 +05302677 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002678 bool disable_radio;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002679
Sujithaa33de02008-12-18 11:40:16 +05302680 mutex_lock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05302681
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002682 /*
2683 * Leave this as the first check because we need to turn on the
2684 * radio if it was disabled before prior to processing the rest
2685 * of the changes. Likewise we must only disable the radio towards
2686 * the end.
2687 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002688 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002689 bool enable_radio;
2690 bool all_wiphys_idle;
2691 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002692
2693 spin_lock_bh(&sc->wiphy_lock);
2694 all_wiphys_idle = ath9k_all_wiphys_idle(sc);
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002695 ath9k_set_wiphy_idle(aphy, idle);
2696
2697 if (!idle && all_wiphys_idle)
2698 enable_radio = true;
2699
2700 /*
2701 * After we unlock here its possible another wiphy
2702 * can be re-renabled so to account for that we will
2703 * only disable the radio toward the end of this routine
2704 * if by then all wiphys are still idle.
2705 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002706 spin_unlock_bh(&sc->wiphy_lock);
2707
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002708 if (enable_radio) {
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08002709 ath_radio_enable(sc, hw);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002710 ath_print(common, ATH_DBG_CONFIG,
2711 "not-idle: enabling radio\n");
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002712 }
2713 }
2714
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002715 /*
2716 * We just prepare to enable PS. We have to wait until our AP has
2717 * ACK'd our null data frame to disable RX otherwise we'll ignore
2718 * those ACKs and end up retransmitting the same null data frames.
2719 * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
2720 */
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302721 if (changed & IEEE80211_CONF_CHANGE_PS) {
2722 if (conf->flags & IEEE80211_CONF_PS) {
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002723 sc->sc_flags |= SC_OP_PS_ENABLED;
Vivek Natarajan8782b412009-03-30 14:17:00 +05302724 if (!(ah->caps.hw_caps &
2725 ATH9K_HW_CAP_AUTOSLEEP)) {
2726 if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
2727 sc->imask |= ATH9K_INT_TIM_TIMER;
2728 ath9k_hw_set_interrupts(sc->sc_ah,
2729 sc->imask);
2730 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302731 }
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002732 /*
2733 * At this point we know hardware has received an ACK
2734 * of a previously sent null data frame.
2735 */
2736 if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) {
2737 sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
2738 sc->ps_enabled = true;
2739 ath9k_hw_setrxabort(sc->sc_ah, 1);
2740 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302741 } else {
Gabor Juhos96148322009-07-24 17:27:21 +02002742 sc->ps_enabled = false;
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002743 sc->sc_flags &= ~(SC_OP_PS_ENABLED |
2744 SC_OP_NULLFUNC_COMPLETED);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07002745 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vivek Natarajan8782b412009-03-30 14:17:00 +05302746 if (!(ah->caps.hw_caps &
2747 ATH9K_HW_CAP_AUTOSLEEP)) {
2748 ath9k_hw_setrxabort(sc->sc_ah, 0);
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002749 sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON |
2750 SC_OP_WAIT_FOR_CAB |
2751 SC_OP_WAIT_FOR_PSPOLL_DATA |
2752 SC_OP_WAIT_FOR_TX_ACK);
Vivek Natarajan8782b412009-03-30 14:17:00 +05302753 if (sc->imask & ATH9K_INT_TIM_TIMER) {
2754 sc->imask &= ~ATH9K_INT_TIM_TIMER;
2755 ath9k_hw_set_interrupts(sc->sc_ah,
2756 sc->imask);
2757 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302758 }
2759 }
2760 }
2761
Johannes Berg47979382009-01-07 10:13:27 +01002762 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
Sujith99405f92008-11-24 12:08:35 +05302763 struct ieee80211_channel *curchan = hw->conf.channel;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002764 int pos = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002765
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002766 aphy->chan_idx = pos;
2767 aphy->chan_is_ht = conf_is_ht(conf);
2768
Jouni Malinen8089cc42009-03-03 19:23:38 +02002769 if (aphy->state == ATH_WIPHY_SCAN ||
2770 aphy->state == ATH_WIPHY_ACTIVE)
2771 ath9k_wiphy_pause_all_forced(sc, aphy);
2772 else {
2773 /*
2774 * Do not change operational channel based on a paused
2775 * wiphy changes.
2776 */
2777 goto skip_chan_change;
2778 }
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002779
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002780 ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
2781 curchan->center_freq);
Johannes Bergae5eb022008-10-14 16:58:37 +02002782
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002783 /* XXX: remove me eventualy */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002784 ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
Sujithe11602b2008-11-27 09:46:27 +05302785
Luis R. Rodriguezecf70442008-12-23 15:58:43 -08002786 ath_update_chainmask(sc, conf_is_ht(conf));
Sujith86060f02009-01-07 14:25:29 +05302787
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002788 if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002789 ath_print(common, ATH_DBG_FATAL,
2790 "Unable to set channel\n");
Sujithaa33de02008-12-18 11:40:16 +05302791 mutex_unlock(&sc->mutex);
Sujithe11602b2008-11-27 09:46:27 +05302792 return -EINVAL;
2793 }
Sujith094d05d2008-12-12 11:57:43 +05302794 }
Sujith86b89ee2008-08-07 10:54:57 +05302795
Jouni Malinen8089cc42009-03-03 19:23:38 +02002796skip_chan_change:
Luis R. Rodriguez5c020dc2008-10-22 13:28:45 -07002797 if (changed & IEEE80211_CONF_CHANGE_POWER)
Sujith17d79042009-02-09 13:27:03 +05302798 sc->config.txpowlimit = 2 * conf->power_level;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002799
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002800 spin_lock_bh(&sc->wiphy_lock);
2801 disable_radio = ath9k_all_wiphys_idle(sc);
2802 spin_unlock_bh(&sc->wiphy_lock);
2803
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002804 if (disable_radio) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002805 ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08002806 ath_radio_disable(sc, hw);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002807 }
2808
Sujithaa33de02008-12-18 11:40:16 +05302809 mutex_unlock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05302810
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002811 return 0;
2812}
2813
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002814#define SUPPORTED_FILTERS \
2815 (FIF_PROMISC_IN_BSS | \
2816 FIF_ALLMULTI | \
2817 FIF_CONTROL | \
Luis R. Rodriguezaf6a3fc2009-08-08 21:55:16 -04002818 FIF_PSPOLL | \
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002819 FIF_OTHER_BSS | \
2820 FIF_BCN_PRBRESP_PROMISC | \
2821 FIF_FCSFAIL)
2822
Sujith7dcfdcd2008-08-11 14:03:13 +05302823/* FIXME: sc->sc_full_reset ? */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002824static void ath9k_configure_filter(struct ieee80211_hw *hw,
2825 unsigned int changed_flags,
2826 unsigned int *total_flags,
Johannes Berg3ac64be2009-08-17 16:16:53 +02002827 u64 multicast)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002828{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002829 struct ath_wiphy *aphy = hw->priv;
2830 struct ath_softc *sc = aphy->sc;
Sujith7dcfdcd2008-08-11 14:03:13 +05302831 u32 rfilt;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002832
2833 changed_flags &= SUPPORTED_FILTERS;
2834 *total_flags &= SUPPORTED_FILTERS;
2835
Sujithb77f4832008-12-07 21:44:03 +05302836 sc->rx.rxfilter = *total_flags;
Jouni Malinenaa68aea2009-05-19 17:01:41 +03002837 ath9k_ps_wakeup(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05302838 rfilt = ath_calcrxfilter(sc);
2839 ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
Jouni Malinenaa68aea2009-05-19 17:01:41 +03002840 ath9k_ps_restore(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05302841
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002842 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
2843 "Set HW RX filter: 0x%x\n", rfilt);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002844}
2845
2846static void ath9k_sta_notify(struct ieee80211_hw *hw,
2847 struct ieee80211_vif *vif,
2848 enum sta_notify_cmd cmd,
Johannes Berg17741cd2008-09-11 00:02:02 +02002849 struct ieee80211_sta *sta)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002850{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002851 struct ath_wiphy *aphy = hw->priv;
2852 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002853
2854 switch (cmd) {
2855 case STA_NOTIFY_ADD:
Sujith5640b082008-10-29 10:16:06 +05302856 ath_node_attach(sc, sta);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002857 break;
2858 case STA_NOTIFY_REMOVE:
Sujithb5aa9bf2008-10-29 10:13:31 +05302859 ath_node_detach(sc, sta);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002860 break;
2861 default:
2862 break;
2863 }
2864}
2865
Sujith141b38b2009-02-04 08:10:07 +05302866static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002867 const struct ieee80211_tx_queue_params *params)
2868{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002869 struct ath_wiphy *aphy = hw->priv;
2870 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002871 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujithea9880f2008-08-07 10:53:10 +05302872 struct ath9k_tx_queue_info qi;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002873 int ret = 0, qnum;
2874
2875 if (queue >= WME_NUM_AC)
2876 return 0;
2877
Sujith141b38b2009-02-04 08:10:07 +05302878 mutex_lock(&sc->mutex);
2879
Sujith1ffb0612009-03-30 15:28:46 +05302880 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
2881
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002882 qi.tqi_aifs = params->aifs;
2883 qi.tqi_cwmin = params->cw_min;
2884 qi.tqi_cwmax = params->cw_max;
2885 qi.tqi_burstTime = params->txop;
2886 qnum = ath_get_hal_qnum(queue, sc);
2887
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002888 ath_print(common, ATH_DBG_CONFIG,
2889 "Configure tx [queue/halq] [%d/%d], "
2890 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
2891 queue, qnum, params->aifs, params->cw_min,
2892 params->cw_max, params->txop);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002893
2894 ret = ath_txq_update(sc, qnum, &qi);
2895 if (ret)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002896 ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002897
Vivek Natarajan94db2932009-11-25 12:01:54 +05302898 if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)
2899 if ((qnum == sc->tx.hwq_map[ATH9K_WME_AC_BE]) && !ret)
2900 ath_beaconq_config(sc);
2901
Sujith141b38b2009-02-04 08:10:07 +05302902 mutex_unlock(&sc->mutex);
2903
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002904 return ret;
2905}
2906
2907static int ath9k_set_key(struct ieee80211_hw *hw,
2908 enum set_key_cmd cmd,
Johannes Bergdc822b52008-12-29 12:55:09 +01002909 struct ieee80211_vif *vif,
2910 struct ieee80211_sta *sta,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002911 struct ieee80211_key_conf *key)
2912{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002913 struct ath_wiphy *aphy = hw->priv;
2914 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002915 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002916 int ret = 0;
2917
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02002918 if (modparam_nohwcrypt)
2919 return -ENOSPC;
2920
Sujith141b38b2009-02-04 08:10:07 +05302921 mutex_lock(&sc->mutex);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302922 ath9k_ps_wakeup(sc);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002923 ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002924
2925 switch (cmd) {
2926 case SET_KEY:
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08002927 ret = ath_key_config(common, vif, sta, key);
Jouni Malinen6ace2892008-12-17 13:32:17 +02002928 if (ret >= 0) {
2929 key->hw_key_idx = ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002930 /* push IV and Michael MIC generation to stack */
2931 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Senthil Balasubramanian1b961752008-09-01 19:45:21 +05302932 if (key->alg == ALG_TKIP)
2933 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Jouni Malinen0ced0e12009-01-08 13:32:13 +02002934 if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
2935 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
Jouni Malinen6ace2892008-12-17 13:32:17 +02002936 ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002937 }
2938 break;
2939 case DISABLE_KEY:
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08002940 ath_key_delete(common, key);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002941 break;
2942 default:
2943 ret = -EINVAL;
2944 }
2945
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302946 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05302947 mutex_unlock(&sc->mutex);
2948
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002949 return ret;
2950}
2951
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002952static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
2953 struct ieee80211_vif *vif,
2954 struct ieee80211_bss_conf *bss_conf,
2955 u32 changed)
2956{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002957 struct ath_wiphy *aphy = hw->priv;
2958 struct ath_softc *sc = aphy->sc;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002959 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07002960 struct ath_common *common = ath9k_hw_common(ah);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002961 struct ath_vif *avp = (void *)vif->drv_priv;
Sujithc6089cc2009-11-16 11:40:48 +05302962 int error;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002963
Sujith141b38b2009-02-04 08:10:07 +05302964 mutex_lock(&sc->mutex);
2965
Sujithc6089cc2009-11-16 11:40:48 +05302966 if (changed & BSS_CHANGED_BSSID) {
2967 /* Set BSSID */
2968 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
2969 memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
Luis R. Rodriguez15107182009-09-10 09:22:37 -07002970 common->curaid = 0;
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -07002971 ath9k_hw_write_associd(ah);
Sujithc6089cc2009-11-16 11:40:48 +05302972
2973 /* Set aggregation protection mode parameters */
2974 sc->config.ath_aggr_prot = 0;
2975
2976 /* Only legacy IBSS for now */
2977 if (vif->type == NL80211_IFTYPE_ADHOC)
2978 ath_update_chainmask(sc, 0);
2979
2980 ath_print(common, ATH_DBG_CONFIG,
2981 "BSSID: %pM aid: 0x%x\n",
2982 common->curbssid, common->curaid);
2983
2984 /* need to reconfigure the beacon */
2985 sc->sc_flags &= ~SC_OP_BEACONS ;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002986 }
2987
Sujithc6089cc2009-11-16 11:40:48 +05302988 /* Enable transmission of beacons (AP, IBSS, MESH) */
2989 if ((changed & BSS_CHANGED_BEACON) ||
2990 ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) {
2991 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
2992 error = ath_beacon_alloc(aphy, vif);
2993 if (!error)
2994 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002995 }
2996
Sujithc6089cc2009-11-16 11:40:48 +05302997 /* Disable transmission of beacons */
2998 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon)
2999 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
3000
3001 if (changed & BSS_CHANGED_BEACON_INT) {
3002 sc->beacon_interval = bss_conf->beacon_int;
3003 /*
3004 * In case of AP mode, the HW TSF has to be reset
3005 * when the beacon interval changes.
3006 */
3007 if (vif->type == NL80211_IFTYPE_AP) {
3008 sc->sc_flags |= SC_OP_TSF_RESET;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02003009 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02003010 error = ath_beacon_alloc(aphy, vif);
3011 if (!error)
3012 ath_beacon_config(sc, vif);
Sujithc6089cc2009-11-16 11:40:48 +05303013 } else {
3014 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02003015 }
3016 }
3017
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003018 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003019 ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
3020 bss_conf->use_short_preamble);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003021 if (bss_conf->use_short_preamble)
Sujith672840a2008-08-11 14:05:08 +05303022 sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003023 else
Sujith672840a2008-08-11 14:05:08 +05303024 sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003025 }
3026
3027 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003028 ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
3029 bss_conf->use_cts_prot);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003030 if (bss_conf->use_cts_prot &&
3031 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
Sujith672840a2008-08-11 14:05:08 +05303032 sc->sc_flags |= SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003033 else
Sujith672840a2008-08-11 14:05:08 +05303034 sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003035 }
3036
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003037 if (changed & BSS_CHANGED_ASSOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003038 ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003039 bss_conf->assoc);
Sujith5640b082008-10-29 10:16:06 +05303040 ath9k_bss_assoc_info(sc, vif, bss_conf);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003041 }
Sujith141b38b2009-02-04 08:10:07 +05303042
3043 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003044}
3045
3046static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
3047{
3048 u64 tsf;
Jouni Malinenbce048d2009-03-03 19:23:28 +02003049 struct ath_wiphy *aphy = hw->priv;
3050 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003051
Sujith141b38b2009-02-04 08:10:07 +05303052 mutex_lock(&sc->mutex);
3053 tsf = ath9k_hw_gettsf64(sc->sc_ah);
3054 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003055
3056 return tsf;
3057}
3058
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003059static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
3060{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003061 struct ath_wiphy *aphy = hw->priv;
3062 struct ath_softc *sc = aphy->sc;
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003063
Sujith141b38b2009-02-04 08:10:07 +05303064 mutex_lock(&sc->mutex);
3065 ath9k_hw_settsf64(sc->sc_ah, tsf);
3066 mutex_unlock(&sc->mutex);
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003067}
3068
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003069static void ath9k_reset_tsf(struct ieee80211_hw *hw)
3070{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003071 struct ath_wiphy *aphy = hw->priv;
3072 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003073
Sujith141b38b2009-02-04 08:10:07 +05303074 mutex_lock(&sc->mutex);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07003075
3076 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05303077 ath9k_hw_reset_tsf(sc->sc_ah);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07003078 ath9k_ps_restore(sc);
3079
Sujith141b38b2009-02-04 08:10:07 +05303080 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003081}
3082
3083static int ath9k_ampdu_action(struct ieee80211_hw *hw,
Johannes Bergc951ad32009-11-16 12:00:38 +01003084 struct ieee80211_vif *vif,
Sujith141b38b2009-02-04 08:10:07 +05303085 enum ieee80211_ampdu_mlme_action action,
3086 struct ieee80211_sta *sta,
3087 u16 tid, u16 *ssn)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003088{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003089 struct ath_wiphy *aphy = hw->priv;
3090 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003091 int ret = 0;
3092
3093 switch (action) {
3094 case IEEE80211_AMPDU_RX_START:
Sujithdca3edb2008-10-29 10:19:01 +05303095 if (!(sc->sc_flags & SC_OP_RXAGGR))
3096 ret = -ENOTSUPP;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003097 break;
3098 case IEEE80211_AMPDU_RX_STOP:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003099 break;
3100 case IEEE80211_AMPDU_TX_START:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05003101 ath9k_ps_wakeup(sc);
Sujithf83da962009-07-23 15:32:37 +05303102 ath_tx_aggr_start(sc, sta, tid, ssn);
Johannes Bergc951ad32009-11-16 12:00:38 +01003103 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05003104 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003105 break;
3106 case IEEE80211_AMPDU_TX_STOP:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05003107 ath9k_ps_wakeup(sc);
Sujithf83da962009-07-23 15:32:37 +05303108 ath_tx_aggr_stop(sc, sta, tid);
Johannes Bergc951ad32009-11-16 12:00:38 +01003109 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05003110 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003111 break;
Johannes Bergb1720232009-03-23 17:28:39 +01003112 case IEEE80211_AMPDU_TX_OPERATIONAL:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05003113 ath9k_ps_wakeup(sc);
Sujith8469cde2008-10-29 10:19:28 +05303114 ath_tx_aggr_resume(sc, sta, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05003115 ath9k_ps_restore(sc);
Sujith8469cde2008-10-29 10:19:28 +05303116 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003117 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003118 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
3119 "Unknown AMPDU action\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003120 }
3121
3122 return ret;
3123}
3124
Sujith0c98de62009-03-03 10:16:45 +05303125static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
3126{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003127 struct ath_wiphy *aphy = hw->priv;
3128 struct ath_softc *sc = aphy->sc;
Sujith05c78d62009-12-14 14:57:04 +05303129 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith0c98de62009-03-03 10:16:45 +05303130
Sujith3d832612009-08-21 12:00:28 +05303131 mutex_lock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02003132 if (ath9k_wiphy_scanning(sc)) {
3133 printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the "
3134 "same time\n");
3135 /*
3136 * Do not allow the concurrent scanning state for now. This
3137 * could be improved with scanning control moved into ath9k.
3138 */
Sujith3d832612009-08-21 12:00:28 +05303139 mutex_unlock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02003140 return;
3141 }
3142
3143 aphy->state = ATH_WIPHY_SCAN;
3144 ath9k_wiphy_pause_all_forced(sc, aphy);
Sujith0c98de62009-03-03 10:16:45 +05303145 sc->sc_flags |= SC_OP_SCANNING;
Sujith05c78d62009-12-14 14:57:04 +05303146 del_timer_sync(&common->ani.timer);
Sujithb6ce5c32009-12-14 14:57:06 +05303147 cancel_delayed_work_sync(&sc->tx_complete_work);
Sujith3d832612009-08-21 12:00:28 +05303148 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05303149}
3150
3151static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
3152{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003153 struct ath_wiphy *aphy = hw->priv;
3154 struct ath_softc *sc = aphy->sc;
Sujith05c78d62009-12-14 14:57:04 +05303155 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith0c98de62009-03-03 10:16:45 +05303156
Sujith3d832612009-08-21 12:00:28 +05303157 mutex_lock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02003158 aphy->state = ATH_WIPHY_ACTIVE;
Sujith0c98de62009-03-03 10:16:45 +05303159 sc->sc_flags &= ~SC_OP_SCANNING;
Sujith9c07a772009-04-13 21:56:36 +05303160 sc->sc_flags |= SC_OP_FULL_RESET;
Sujith05c78d62009-12-14 14:57:04 +05303161 ath_start_ani(common);
Sujithb6ce5c32009-12-14 14:57:06 +05303162 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
Vivek Natarajand0bec342009-09-02 15:50:55 +05303163 ath_beacon_config(sc, NULL);
Sujith3d832612009-08-21 12:00:28 +05303164 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05303165}
3166
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003167struct ieee80211_ops ath9k_ops = {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003168 .tx = ath9k_tx,
3169 .start = ath9k_start,
3170 .stop = ath9k_stop,
3171 .add_interface = ath9k_add_interface,
3172 .remove_interface = ath9k_remove_interface,
3173 .config = ath9k_config,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003174 .configure_filter = ath9k_configure_filter,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003175 .sta_notify = ath9k_sta_notify,
3176 .conf_tx = ath9k_conf_tx,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003177 .bss_info_changed = ath9k_bss_info_changed,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003178 .set_key = ath9k_set_key,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003179 .get_tsf = ath9k_get_tsf,
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003180 .set_tsf = ath9k_set_tsf,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003181 .reset_tsf = ath9k_reset_tsf,
Johannes Berg4233df62008-10-13 13:35:05 +02003182 .ampdu_action = ath9k_ampdu_action,
Sujith0c98de62009-03-03 10:16:45 +05303183 .sw_scan_start = ath9k_sw_scan_start,
3184 .sw_scan_complete = ath9k_sw_scan_complete,
Johannes Berg3b319aa2009-06-13 14:50:26 +05303185 .rfkill_poll = ath9k_rfkill_poll_state,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003186};
3187
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003188static int __init ath9k_init(void)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003189{
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303190 int error;
3191
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303192 /* Register rate control algorithm */
3193 error = ath_rate_control_register();
3194 if (error != 0) {
3195 printk(KERN_ERR
Luis R. Rodriguezb51bb3c2009-01-26 07:30:03 -08003196 "ath9k: Unable to register rate control "
3197 "algorithm: %d\n",
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303198 error);
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003199 goto err_out;
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303200 }
3201
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003202 error = ath9k_debug_create_root();
3203 if (error) {
3204 printk(KERN_ERR
3205 "ath9k: Unable to create debugfs root: %d\n",
3206 error);
3207 goto err_rate_unregister;
3208 }
3209
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003210 error = ath_pci_init();
3211 if (error < 0) {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003212 printk(KERN_ERR
Luis R. Rodriguezb51bb3c2009-01-26 07:30:03 -08003213 "ath9k: No PCI devices found, driver not installed.\n");
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003214 error = -ENODEV;
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003215 goto err_remove_root;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003216 }
3217
Gabor Juhos09329d32009-01-14 20:17:07 +01003218 error = ath_ahb_init();
3219 if (error < 0) {
3220 error = -ENODEV;
3221 goto err_pci_exit;
3222 }
3223
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003224 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003225
Gabor Juhos09329d32009-01-14 20:17:07 +01003226 err_pci_exit:
3227 ath_pci_exit();
3228
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003229 err_remove_root:
3230 ath9k_debug_remove_root();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003231 err_rate_unregister:
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303232 ath_rate_control_unregister();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003233 err_out:
3234 return error;
3235}
3236module_init(ath9k_init);
3237
3238static void __exit ath9k_exit(void)
3239{
Gabor Juhos09329d32009-01-14 20:17:07 +01003240 ath_ahb_exit();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003241 ath_pci_exit();
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003242 ath9k_debug_remove_root();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003243 ath_rate_control_unregister();
Sujith04bd4632008-11-28 22:18:05 +05303244 printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003245}
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003246module_exit(ath9k_exit);