blob: 31a33cf762dae9c7d89ee1aa0d8ebf0766c7e703 [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{
Sujithcbe61d8a2009-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{
Sujithcbe61d8a2009-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;
Sujithcbe61d8a2009-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
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001003void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301004{
Sujithcbe61d8a2009-02-09 13:27:12 +05301005 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001006 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001007 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001008 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301009
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301010 ath9k_ps_wakeup(sc);
Vivek Natarajan93b1b372009-09-17 09:24:58 +05301011 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithd2f5b3a2009-04-13 21:56:25 +05301012
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301013 if (!ah->curchan)
1014 ah->curchan = ath_get_curchannel(sc, sc->hw);
1015
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301016 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301017 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001018 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001019 ath_print(common, ATH_DBG_FATAL,
1020 "Unable to reset channel %u (%uMhz) ",
1021 "reset status %d\n",
1022 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301023 }
1024 spin_unlock_bh(&sc->sc_resetlock);
1025
1026 ath_update_txpow(sc);
1027 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001028 ath_print(common, ATH_DBG_FATAL,
1029 "Unable to restart recv logic\n");
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301030 return;
1031 }
1032
1033 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001034 ath_beacon_config(sc, NULL); /* restart beacons */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301035
1036 /* Re-Enable interrupts */
Sujith17d79042009-02-09 13:27:03 +05301037 ath9k_hw_set_interrupts(ah, sc->imask);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301038
1039 /* Enable LED */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301040 ath9k_hw_cfg_output(ah, ah->led_pin,
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301041 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301042 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301043
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001044 ieee80211_wake_queues(hw);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301045 ath9k_ps_restore(sc);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301046}
1047
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001048void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301049{
Sujithcbe61d8a2009-02-09 13:27:12 +05301050 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001051 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001052 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301053
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301054 ath9k_ps_wakeup(sc);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001055 ieee80211_stop_queues(hw);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301056
1057 /* Disable LED */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301058 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
1059 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301060
1061 /* Disable interrupts */
1062 ath9k_hw_set_interrupts(ah, 0);
1063
Sujith043a0402009-01-16 21:38:47 +05301064 ath_drain_all_txq(sc, false); /* clear pending tx frames */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301065 ath_stoprecv(sc); /* turn off frame recv */
1066 ath_flushrecv(sc); /* flush recv queue */
1067
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301068 if (!ah->curchan)
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001069 ah->curchan = ath_get_curchannel(sc, hw);
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301070
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301071 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301072 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001073 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001074 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
1075 "Unable to reset channel %u (%uMhz) "
1076 "reset status %d\n",
1077 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301078 }
1079 spin_unlock_bh(&sc->sc_resetlock);
1080
1081 ath9k_hw_phy_disable(ah);
Vivek Natarajan93b1b372009-09-17 09:24:58 +05301082 ath9k_hw_configpcipowersave(ah, 1, 1);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301083 ath9k_ps_restore(sc);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001084 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301085}
1086
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001087static void ath9k_uninit_hw(struct ath_softc *sc)
1088{
1089 struct ath_hw *ah = sc->sc_ah;
1090
1091 BUG_ON(!ah);
1092
1093 ath9k_exit_debug(ah);
1094 ath9k_hw_detach(ah);
1095 sc->sc_ah = NULL;
1096}
1097
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001098static void ath_clean_core(struct ath_softc *sc)
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301099{
1100 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001101 struct ath_hw *ah = sc->sc_ah;
Sujith9c84b792008-10-29 10:17:13 +05301102 int i = 0;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301103
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301104 ath9k_ps_wakeup(sc);
1105
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001106 dev_dbg(sc->dev, "Detach ATH hw\n");
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301107
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001108 ath_deinit_leds(sc);
Sujithe31f7b92009-09-23 13:49:12 +05301109 wiphy_rfkill_stop_polling(sc->hw->wiphy);
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001110
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001111 for (i = 0; i < sc->num_sec_wiphy; i++) {
1112 struct ath_wiphy *aphy = sc->sec_wiphy[i];
1113 if (aphy == NULL)
1114 continue;
1115 sc->sec_wiphy[i] = NULL;
1116 ieee80211_unregister_hw(aphy->hw);
1117 ieee80211_free_hw(aphy->hw);
1118 }
Vasanthakumar Thiagarajan3fcdfb42008-11-18 01:19:56 +05301119 ieee80211_unregister_hw(hw);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301120 ath_rx_cleanup(sc);
1121 ath_tx_cleanup(sc);
1122
Sujith9c84b792008-10-29 10:17:13 +05301123 tasklet_kill(&sc->intr_tq);
1124 tasklet_kill(&sc->bcon_tasklet);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301125
Sujith9c84b792008-10-29 10:17:13 +05301126 if (!(sc->sc_flags & SC_OP_INVALID))
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001127 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301128
Sujith9c84b792008-10-29 10:17:13 +05301129 /* cleanup tx queues */
1130 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1131 if (ATH_TXQ_SETUP(sc, i))
Sujithb77f4832008-12-07 21:44:03 +05301132 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
Sujith9c84b792008-10-29 10:17:13 +05301133
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001134 if ((sc->btcoex.no_stomp_timer) &&
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001135 ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001136 ath_gen_timer_free(ah, sc->btcoex.no_stomp_timer);
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001137}
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301138
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001139void ath_detach(struct ath_softc *sc)
1140{
1141 ath_clean_core(sc);
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001142 ath9k_uninit_hw(sc);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301143}
1144
Luis R. Rodriguezbd96d392009-10-06 21:19:10 -04001145void ath_cleanup(struct ath_softc *sc)
1146{
1147 struct ath_hw *ah = sc->sc_ah;
1148 struct ath_common *common = ath9k_hw_common(ah);
1149
1150 ath_clean_core(sc);
1151 free_irq(sc->irq, sc);
1152 ath_bus_cleanup(common);
1153 kfree(sc->sec_wiphy);
1154 ieee80211_free_hw(sc->hw);
1155
1156 ath9k_uninit_hw(sc);
1157}
1158
Bob Copelande3bb2492009-03-30 22:30:30 -04001159static int ath9k_reg_notifier(struct wiphy *wiphy,
1160 struct regulatory_request *request)
1161{
1162 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
1163 struct ath_wiphy *aphy = hw->priv;
1164 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001165 struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah);
Bob Copelande3bb2492009-03-30 22:30:30 -04001166
1167 return ath_reg_notifier_apply(wiphy, request, reg);
1168}
1169
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001170/*
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001171 * Read and write, they both share the same lock. We do this to serialize
1172 * reads and writes on Atheros 802.11n PCI devices only. This is required
1173 * as the FIFO on these devices can only accept sanely 2 requests. After
1174 * that the device goes bananas. Serializing the reads/writes prevents this
1175 * from happening.
1176 */
1177
1178static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
1179{
1180 struct ath_hw *ah = (struct ath_hw *) hw_priv;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001181 struct ath_common *common = ath9k_hw_common(ah);
1182 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001183
1184 if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
1185 unsigned long flags;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001186 spin_lock_irqsave(&sc->sc_serial_rw, flags);
1187 iowrite32(val, sc->mem + reg_offset);
1188 spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001189 } else
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001190 iowrite32(val, sc->mem + reg_offset);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001191}
1192
1193static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
1194{
1195 struct ath_hw *ah = (struct ath_hw *) hw_priv;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001196 struct ath_common *common = ath9k_hw_common(ah);
1197 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001198 u32 val;
1199
1200 if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
1201 unsigned long flags;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001202 spin_lock_irqsave(&sc->sc_serial_rw, flags);
1203 val = ioread32(sc->mem + reg_offset);
1204 spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001205 } else
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001206 val = ioread32(sc->mem + reg_offset);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001207 return val;
1208}
1209
Luis R. Rodriguez2ddb5c82009-09-14 02:09:38 -07001210static const struct ath_ops ath9k_common_ops = {
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001211 .read = ath9k_ioread32,
1212 .write = ath9k_iowrite32,
1213};
1214
1215/*
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001216 * Initialize and fill ath_softc, ath_sofct is the
1217 * "Software Carrier" struct. Historically it has existed
1218 * to allow the separation between hardware specific
1219 * variables (now in ath_hw) and driver specific variables.
1220 */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001221static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
1222 const struct ath_bus_ops *bus_ops)
Sujithff37e332008-11-24 12:07:55 +05301223{
Sujithcbe61d8a2009-02-09 13:27:12 +05301224 struct ath_hw *ah = NULL;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001225 struct ath_common *common;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001226 int r = 0, i;
Sujithff37e332008-11-24 12:07:55 +05301227 int csz = 0;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001228 int qnum;
Sujithff37e332008-11-24 12:07:55 +05301229
1230 /* XXX: hardware will not be ready until ath_open() being called */
1231 sc->sc_flags |= SC_OP_INVALID;
Sujith88b126a2008-11-28 22:19:02 +05301232
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001233 spin_lock_init(&sc->wiphy_lock);
Sujithff37e332008-11-24 12:07:55 +05301234 spin_lock_init(&sc->sc_resetlock);
Luis R. Rodriguez61584252009-03-12 18:18:49 -04001235 spin_lock_init(&sc->sc_serial_rw);
Gabor Juhos04717cc2009-07-14 20:17:13 -04001236 spin_lock_init(&sc->sc_pm_lock);
Sujithaa33de02008-12-18 11:40:16 +05301237 mutex_init(&sc->mutex);
Sujithff37e332008-11-24 12:07:55 +05301238 tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
Sujith9fc9ab02009-03-03 10:16:51 +05301239 tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
Sujithff37e332008-11-24 12:07:55 +05301240 (unsigned long)sc);
1241
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001242 ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001243 if (!ah)
1244 return -ENOMEM;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001245
Luis R. Rodriguez8df5d1b2009-08-03 12:24:37 -07001246 ah->hw_version.devid = devid;
Vasanthakumar Thiagarajanaeac3552009-09-09 15:25:49 +05301247 ah->hw_version.subsysid = subsysid;
Luis R. Rodrigueze1e2f932009-08-03 12:24:38 -07001248 sc->sc_ah = ah;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001249
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001250 common = ath9k_hw_common(ah);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001251 common->ops = &ath9k_common_ops;
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001252 common->bus_ops = bus_ops;
Luis R. Rodriguez13b81552009-09-10 17:52:45 -07001253 common->ah = ah;
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -07001254 common->hw = sc->hw;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001255 common->priv = sc;
Luis R. Rodriguezfaa27fa2009-10-06 21:19:06 -04001256 common->debug_mask = ath9k_debug;
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001257
1258 /*
1259 * Cache line size is used to size and align various
1260 * structures used to communicate with the hardware.
1261 */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001262 ath_read_cachesize(common, &csz);
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001263 /* XXX assert csz is non-zero */
1264 common->cachelsz = csz << 2; /* convert to bytes */
1265
Luis R. Rodriguezf637cfd2009-08-03 12:24:46 -07001266 r = ath9k_hw_init(ah);
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001267 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001268 ath_print(common, ATH_DBG_FATAL,
1269 "Unable to initialize hardware; "
1270 "initialization status: %d\n", r);
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001271 goto bad_free_hw;
1272 }
1273
1274 if (ath9k_init_debug(ah) < 0) {
1275 ath_print(common, ATH_DBG_FATAL,
1276 "Unable to create debugfs files\n");
1277 goto bad_free_hw;
Sujithff37e332008-11-24 12:07:55 +05301278 }
Sujithff37e332008-11-24 12:07:55 +05301279
1280 /* Get the hardware key cache size. */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001281 common->keymax = ah->caps.keycache_size;
1282 if (common->keymax > ATH_KEYMAX) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001283 ath_print(common, ATH_DBG_ANY,
1284 "Warning, using only %u entries in %u key cache\n",
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001285 ATH_KEYMAX, common->keymax);
1286 common->keymax = ATH_KEYMAX;
Sujithff37e332008-11-24 12:07:55 +05301287 }
1288
1289 /*
1290 * Reset the key cache since some parts do not
1291 * reset the contents on initial power up.
1292 */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001293 for (i = 0; i < common->keymax; i++)
Sujithff37e332008-11-24 12:07:55 +05301294 ath9k_hw_keyreset(ah, (u16) i);
Sujithff37e332008-11-24 12:07:55 +05301295
Sujithff37e332008-11-24 12:07:55 +05301296 /* default to MONITOR mode */
Sujith2660b812009-02-09 13:27:26 +05301297 sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR;
Colin McCabed97809d2008-12-01 13:38:55 -08001298
Sujithff37e332008-11-24 12:07:55 +05301299 /*
1300 * Allocate hardware transmit queues: one queue for
1301 * beacon frames and one data queue for each QoS
1302 * priority. Note that the hal handles reseting
1303 * these queues at the needed time.
1304 */
Luis R. Rodriguez536b3a72009-10-06 21:19:11 -04001305 sc->beacon.beaconq = ath9k_hw_beaconq_setup(ah);
Sujithb77f4832008-12-07 21:44:03 +05301306 if (sc->beacon.beaconq == -1) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001307 ath_print(common, ATH_DBG_FATAL,
1308 "Unable to setup a beacon xmit queue\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001309 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301310 goto bad2;
1311 }
Sujithb77f4832008-12-07 21:44:03 +05301312 sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
1313 if (sc->beacon.cabq == NULL) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001314 ath_print(common, ATH_DBG_FATAL,
1315 "Unable to setup CAB xmit queue\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001316 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301317 goto bad2;
1318 }
1319
Sujith17d79042009-02-09 13:27:03 +05301320 sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
Sujithff37e332008-11-24 12:07:55 +05301321 ath_cabq_update(sc);
1322
Sujithb77f4832008-12-07 21:44:03 +05301323 for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++)
1324 sc->tx.hwq_map[i] = -1;
Sujithff37e332008-11-24 12:07:55 +05301325
1326 /* Setup data queues */
1327 /* NB: ensure BK queue is the lowest priority h/w queue */
1328 if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001329 ath_print(common, ATH_DBG_FATAL,
1330 "Unable to setup xmit queue for BK traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001331 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301332 goto bad2;
1333 }
1334
1335 if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001336 ath_print(common, ATH_DBG_FATAL,
1337 "Unable to setup xmit queue for BE traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001338 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301339 goto bad2;
1340 }
1341 if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001342 ath_print(common, ATH_DBG_FATAL,
1343 "Unable to setup xmit queue for VI traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001344 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301345 goto bad2;
1346 }
1347 if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001348 ath_print(common, ATH_DBG_FATAL,
1349 "Unable to setup xmit queue for VO traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001350 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301351 goto bad2;
1352 }
1353
1354 /* Initializes the noise floor to a reasonable default value.
1355 * Later on this will be updated during ANI processing. */
1356
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08001357 common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR;
1358 setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
Sujithff37e332008-11-24 12:07:55 +05301359
1360 if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1361 ATH9K_CIPHER_TKIP, NULL)) {
1362 /*
1363 * Whether we should enable h/w TKIP MIC.
1364 * XXX: if we don't support WME TKIP MIC, then we wouldn't
1365 * report WMM capable, so it's always safe to turn on
1366 * TKIP MIC in this case.
1367 */
1368 ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
1369 0, 1, NULL);
1370 }
1371
1372 /*
1373 * Check whether the separate key cache entries
1374 * are required to handle both tx+rx MIC keys.
1375 * With split mic keys the number of stations is limited
1376 * to 27 otherwise 59.
1377 */
1378 if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1379 ATH9K_CIPHER_TKIP, NULL)
1380 && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1381 ATH9K_CIPHER_MIC, NULL)
1382 && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
1383 0, NULL))
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001384 common->splitmic = 1;
Sujithff37e332008-11-24 12:07:55 +05301385
1386 /* turn on mcast key search if possible */
1387 if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
1388 (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
1389 1, NULL);
1390
Sujith17d79042009-02-09 13:27:03 +05301391 sc->config.txpowlimit = ATH_TXPOWER_MAX;
Sujithff37e332008-11-24 12:07:55 +05301392
1393 /* 11n Capabilities */
Sujith2660b812009-02-09 13:27:26 +05301394 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
Sujithff37e332008-11-24 12:07:55 +05301395 sc->sc_flags |= SC_OP_TXAGGR;
1396 sc->sc_flags |= SC_OP_RXAGGR;
1397 }
1398
Luis R. Rodriguez43c27612009-09-13 21:07:07 -07001399 common->tx_chainmask = ah->caps.tx_chainmask;
1400 common->rx_chainmask = ah->caps.rx_chainmask;
Sujithff37e332008-11-24 12:07:55 +05301401
1402 ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
Sujithb77f4832008-12-07 21:44:03 +05301403 sc->rx.defant = ath9k_hw_getdefantenna(ah);
Sujithff37e332008-11-24 12:07:55 +05301404
Jouni Malinen8ca21f02009-03-03 19:23:27 +02001405 if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001406 memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
Sujithff37e332008-11-24 12:07:55 +05301407
Sujithb77f4832008-12-07 21:44:03 +05301408 sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */
Sujithff37e332008-11-24 12:07:55 +05301409
1410 /* initialize beacon slots */
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001411 for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001412 sc->beacon.bslot[i] = NULL;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001413 sc->beacon.bslot_aphy[i] = NULL;
1414 }
Sujithff37e332008-11-24 12:07:55 +05301415
Sujithff37e332008-11-24 12:07:55 +05301416 /* setup channels and rates */
1417
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001418 if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) {
1419 sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable;
1420 sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
1421 sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
1422 ARRAY_SIZE(ath9k_2ghz_chantable);
1423 sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
1424 sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
1425 ARRAY_SIZE(ath9k_legacy_rates);
1426 }
Sujithff37e332008-11-24 12:07:55 +05301427
Sujith2660b812009-02-09 13:27:26 +05301428 if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) {
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001429 sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable;
Sujithff37e332008-11-24 12:07:55 +05301430 sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001431 sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
1432 ARRAY_SIZE(ath9k_5ghz_chantable);
Felix Fietkau545750d2009-11-23 22:21:01 +01001433 sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
1434 ath9k_legacy_rates + 4;
1435 sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
1436 ARRAY_SIZE(ath9k_legacy_rates) - 4;
Sujithff37e332008-11-24 12:07:55 +05301437 }
1438
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001439 switch (ah->btcoex_hw.scheme) {
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001440 case ATH_BTCOEX_CFG_NONE:
1441 break;
1442 case ATH_BTCOEX_CFG_2WIRE:
1443 ath9k_hw_btcoex_init_2wire(ah);
1444 break;
1445 case ATH_BTCOEX_CFG_3WIRE:
1446 ath9k_hw_btcoex_init_3wire(ah);
1447 r = ath_init_btcoex_timer(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301448 if (r)
1449 goto bad2;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001450 qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001451 ath9k_hw_init_btcoex_hw(ah, qnum);
Luis R. Rodrigueze08a6ac2009-09-09 14:26:15 -07001452 sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001453 break;
1454 default:
1455 WARN_ON(1);
1456 break;
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301457 }
Vasanthakumar Thiagarajanc97c92d2009-01-02 15:35:46 +05301458
Sujithff37e332008-11-24 12:07:55 +05301459 return 0;
1460bad2:
1461 /* cleanup tx queues */
1462 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1463 if (ATH_TXQ_SETUP(sc, i))
Sujithb77f4832008-12-07 21:44:03 +05301464 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
Sujithff37e332008-11-24 12:07:55 +05301465
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001466bad_free_hw:
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001467 ath9k_uninit_hw(sc);
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001468 return r;
Sujithff37e332008-11-24 12:07:55 +05301469}
1470
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001471void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301472{
Luis R. Rodriguez14acdde2009-12-18 11:26:04 -05001473 struct ath_hw *ah = sc->sc_ah;
1474
Sujith9c84b792008-10-29 10:17:13 +05301475 hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
1476 IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
1477 IEEE80211_HW_SIGNAL_DBM |
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301478 IEEE80211_HW_AMPDU_AGGREGATION |
1479 IEEE80211_HW_SUPPORTS_PS |
Sujitheeee1322009-03-10 10:39:53 +05301480 IEEE80211_HW_PS_NULLFUNC_STACK |
1481 IEEE80211_HW_SPECTRUM_MGMT;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301482
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02001483 if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt)
Jouni Malinen0ced0e12009-01-08 13:32:13 +02001484 hw->flags |= IEEE80211_HW_MFP_CAPABLE;
1485
Sujith9c84b792008-10-29 10:17:13 +05301486 hw->wiphy->interface_modes =
1487 BIT(NL80211_IFTYPE_AP) |
1488 BIT(NL80211_IFTYPE_STATION) |
Pat Erley9cb54122009-03-20 22:59:59 -04001489 BIT(NL80211_IFTYPE_ADHOC) |
1490 BIT(NL80211_IFTYPE_MESH_POINT);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301491
Luis R. Rodriguez14acdde2009-12-18 11:26:04 -05001492 if (AR_SREV_5416(ah))
1493 hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
John W. Linville18b6c9a2009-11-23 16:15:19 -05001494
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301495 hw->queues = 4;
Sujithe63835b2008-11-18 09:07:53 +05301496 hw->max_rates = 4;
Sujith171387e2009-02-17 15:36:25 +05301497 hw->channel_change_time = 5000;
Jouni Malinen465ca842009-03-03 19:23:34 +02001498 hw->max_listen_interval = 10;
Luis R. Rodriguezdd190182009-07-14 20:13:56 -04001499 /* Hardware supports 10 but we use 4 */
1500 hw->max_rate_tries = 4;
Sujith528f0c62008-10-29 10:14:26 +05301501 hw->sta_data_size = sizeof(struct ath_node);
Sujith17d79042009-02-09 13:27:03 +05301502 hw->vif_data_size = sizeof(struct ath_vif);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301503
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301504 hw->rate_control_algorithm = "ath9k_rate_control";
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301505
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001506 if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
1507 hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
1508 &sc->sbands[IEEE80211_BAND_2GHZ];
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001509 if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
1510 hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
1511 &sc->sbands[IEEE80211_BAND_5GHZ];
1512}
1513
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001514/* Device driver core initialization */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001515int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
1516 const struct ath_bus_ops *bus_ops)
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001517{
1518 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001519 struct ath_common *common;
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001520 struct ath_hw *ah;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001521 int error = 0, i;
Bob Copeland3a702e42009-03-30 22:30:29 -04001522 struct ath_regulatory *reg;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001523
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001524 dev_dbg(sc->dev, "Attach ATH hw\n");
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001525
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001526 error = ath_init_softc(devid, sc, subsysid, bus_ops);
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001527 if (error != 0)
1528 return error;
1529
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001530 ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001531 common = ath9k_hw_common(ah);
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001532
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001533 /* get mac address from hardware and set in mac80211 */
1534
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001535 SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001536
1537 ath_set_hw_capab(sc, hw);
1538
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001539 error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
Luis R. Rodriguezc26c2e52009-05-19 18:27:11 -04001540 ath9k_reg_notifier);
1541 if (error)
1542 return error;
1543
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001544 reg = &common->regulatory;
Luis R. Rodriguezc26c2e52009-05-19 18:27:11 -04001545
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001546 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001547 if (test_bit(ATH9K_MODE_11G, ah->caps.wireless_modes))
1548 setup_ht_cap(sc,
1549 &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001550 if (test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes))
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001551 setup_ht_cap(sc,
1552 &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
Sujith9c84b792008-10-29 10:17:13 +05301553 }
1554
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301555 /* initialize tx/rx engine */
1556 error = ath_tx_init(sc, ATH_TXBUF);
1557 if (error != 0)
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301558 goto error_attach;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301559
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301560 error = ath_rx_init(sc, ATH_RXBUF);
1561 if (error != 0)
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301562 goto error_attach;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301563
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001564 INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
Jouni Malinenf98c3bd2009-03-03 19:23:39 +02001565 INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);
1566 sc->wiphy_scheduler_int = msecs_to_jiffies(500);
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001567
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301568 error = ieee80211_register_hw(hw);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301569
Bob Copeland3a702e42009-03-30 22:30:29 -04001570 if (!ath_is_world_regd(reg)) {
Bob Copelandc02cf372009-03-30 22:30:28 -04001571 error = regulatory_hint(hw->wiphy, reg->alpha2);
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05001572 if (error)
1573 goto error_attach;
1574 }
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001575
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301576 /* Initialize LED control */
1577 ath_init_leds(sc);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301578
Johannes Berg3b319aa2009-06-13 14:50:26 +05301579 ath_start_rfkill_poll(sc);
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001580
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301581 return 0;
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301582
1583error_attach:
1584 /* cleanup tx queues */
1585 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1586 if (ATH_TXQ_SETUP(sc, i))
1587 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
1588
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001589 ath9k_uninit_hw(sc);
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301590
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301591 return error;
1592}
1593
Sujithff37e332008-11-24 12:07:55 +05301594int ath_reset(struct ath_softc *sc, bool retry_tx)
1595{
Sujithcbe61d8a2009-02-09 13:27:12 +05301596 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001597 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez030bb492008-12-23 15:58:37 -08001598 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001599 int r;
Sujithff37e332008-11-24 12:07:55 +05301600
Sujith2ab81d42009-12-14 16:34:56 +05301601 /* Stop ANI */
1602 del_timer_sync(&common->ani.timer);
1603
Sujithff37e332008-11-24 12:07:55 +05301604 ath9k_hw_set_interrupts(ah, 0);
Sujith043a0402009-01-16 21:38:47 +05301605 ath_drain_all_txq(sc, retry_tx);
Sujithff37e332008-11-24 12:07:55 +05301606 ath_stoprecv(sc);
1607 ath_flushrecv(sc);
1608
1609 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301610 r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001611 if (r)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001612 ath_print(common, ATH_DBG_FATAL,
1613 "Unable to reset hardware; reset status %d\n", r);
Sujithff37e332008-11-24 12:07:55 +05301614 spin_unlock_bh(&sc->sc_resetlock);
1615
1616 if (ath_startrecv(sc) != 0)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001617 ath_print(common, ATH_DBG_FATAL,
1618 "Unable to start recv logic\n");
Sujithff37e332008-11-24 12:07:55 +05301619
1620 /*
1621 * We may be doing a reset in response to a request
1622 * that changes the channel so update any state that
1623 * might change as a result.
1624 */
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -08001625 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +05301626
1627 ath_update_txpow(sc);
1628
1629 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001630 ath_beacon_config(sc, NULL); /* restart beacons */
Sujithff37e332008-11-24 12:07:55 +05301631
Sujith17d79042009-02-09 13:27:03 +05301632 ath9k_hw_set_interrupts(ah, sc->imask);
Sujithff37e332008-11-24 12:07:55 +05301633
1634 if (retry_tx) {
1635 int i;
1636 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
1637 if (ATH_TXQ_SETUP(sc, i)) {
Sujithb77f4832008-12-07 21:44:03 +05301638 spin_lock_bh(&sc->tx.txq[i].axq_lock);
1639 ath_txq_schedule(sc, &sc->tx.txq[i]);
1640 spin_unlock_bh(&sc->tx.txq[i].axq_lock);
Sujithff37e332008-11-24 12:07:55 +05301641 }
1642 }
1643 }
1644
Sujith2ab81d42009-12-14 16:34:56 +05301645 /* Start ANI */
1646 ath_start_ani(common);
1647
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001648 return r;
Sujithff37e332008-11-24 12:07:55 +05301649}
1650
1651/*
1652 * This function will allocate both the DMA descriptor structure, and the
1653 * buffers it contains. These are used to contain the descriptors used
1654 * by the system.
1655*/
1656int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
1657 struct list_head *head, const char *name,
1658 int nbuf, int ndesc)
1659{
1660#define DS2PHYS(_dd, _ds) \
1661 ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
1662#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
1663#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001664 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujithff37e332008-11-24 12:07:55 +05301665 struct ath_desc *ds;
1666 struct ath_buf *bf;
1667 int i, bsize, error;
1668
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001669 ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n",
1670 name, nbuf, ndesc);
Sujithff37e332008-11-24 12:07:55 +05301671
Senthil Balasubramanianb03a9db2009-03-06 11:24:09 +05301672 INIT_LIST_HEAD(head);
Sujithff37e332008-11-24 12:07:55 +05301673 /* ath_desc must be a multiple of DWORDs */
1674 if ((sizeof(struct ath_desc) % 4) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001675 ath_print(common, ATH_DBG_FATAL,
1676 "ath_desc not DWORD aligned\n");
Luis R. Rodriguez9680e8a2009-09-13 23:28:00 -07001677 BUG_ON((sizeof(struct ath_desc) % 4) != 0);
Sujithff37e332008-11-24 12:07:55 +05301678 error = -ENOMEM;
1679 goto fail;
1680 }
1681
Sujithff37e332008-11-24 12:07:55 +05301682 dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
1683
1684 /*
1685 * Need additional DMA memory because we can't use
1686 * descriptors that cross the 4K page boundary. Assume
1687 * one skipped descriptor per 4K page.
1688 */
Sujith2660b812009-02-09 13:27:26 +05301689 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) {
Sujithff37e332008-11-24 12:07:55 +05301690 u32 ndesc_skipped =
1691 ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len);
1692 u32 dma_len;
1693
1694 while (ndesc_skipped) {
1695 dma_len = ndesc_skipped * sizeof(struct ath_desc);
1696 dd->dd_desc_len += dma_len;
1697
1698 ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len);
1699 };
1700 }
1701
1702 /* allocate descriptors */
Gabor Juhos7da3c552009-01-14 20:17:03 +01001703 dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len,
Senthil Balasubramanianf0e6ce12009-03-06 11:24:08 +05301704 &dd->dd_desc_paddr, GFP_KERNEL);
Sujithff37e332008-11-24 12:07:55 +05301705 if (dd->dd_desc == NULL) {
1706 error = -ENOMEM;
1707 goto fail;
1708 }
1709 ds = dd->dd_desc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001710 ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
1711 name, ds, (u32) dd->dd_desc_len,
1712 ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
Sujithff37e332008-11-24 12:07:55 +05301713
1714 /* allocate buffers */
1715 bsize = sizeof(struct ath_buf) * nbuf;
Senthil Balasubramanianf0e6ce12009-03-06 11:24:08 +05301716 bf = kzalloc(bsize, GFP_KERNEL);
Sujithff37e332008-11-24 12:07:55 +05301717 if (bf == NULL) {
1718 error = -ENOMEM;
1719 goto fail2;
1720 }
Sujithff37e332008-11-24 12:07:55 +05301721 dd->dd_bufptr = bf;
1722
Sujithff37e332008-11-24 12:07:55 +05301723 for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
1724 bf->bf_desc = ds;
1725 bf->bf_daddr = DS2PHYS(dd, ds);
1726
Sujith2660b812009-02-09 13:27:26 +05301727 if (!(sc->sc_ah->caps.hw_caps &
Sujithff37e332008-11-24 12:07:55 +05301728 ATH9K_HW_CAP_4KB_SPLITTRANS)) {
1729 /*
1730 * Skip descriptor addresses which can cause 4KB
1731 * boundary crossing (addr + length) with a 32 dword
1732 * descriptor fetch.
1733 */
1734 while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
Luis R. Rodriguez9680e8a2009-09-13 23:28:00 -07001735 BUG_ON((caddr_t) bf->bf_desc >=
Sujithff37e332008-11-24 12:07:55 +05301736 ((caddr_t) dd->dd_desc +
1737 dd->dd_desc_len));
1738
1739 ds += ndesc;
1740 bf->bf_desc = ds;
1741 bf->bf_daddr = DS2PHYS(dd, ds);
1742 }
1743 }
1744 list_add_tail(&bf->list, head);
1745 }
1746 return 0;
1747fail2:
Gabor Juhos7da3c552009-01-14 20:17:03 +01001748 dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
1749 dd->dd_desc_paddr);
Sujithff37e332008-11-24 12:07:55 +05301750fail:
1751 memset(dd, 0, sizeof(*dd));
1752 return error;
1753#undef ATH_DESC_4KB_BOUND_CHECK
1754#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
1755#undef DS2PHYS
1756}
1757
1758void ath_descdma_cleanup(struct ath_softc *sc,
1759 struct ath_descdma *dd,
1760 struct list_head *head)
1761{
Gabor Juhos7da3c552009-01-14 20:17:03 +01001762 dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
1763 dd->dd_desc_paddr);
Sujithff37e332008-11-24 12:07:55 +05301764
1765 INIT_LIST_HEAD(head);
1766 kfree(dd->dd_bufptr);
1767 memset(dd, 0, sizeof(*dd));
1768}
1769
1770int ath_get_hal_qnum(u16 queue, struct ath_softc *sc)
1771{
1772 int qnum;
1773
1774 switch (queue) {
1775 case 0:
Sujithb77f4832008-12-07 21:44:03 +05301776 qnum = sc->tx.hwq_map[ATH9K_WME_AC_VO];
Sujithff37e332008-11-24 12:07:55 +05301777 break;
1778 case 1:
Sujithb77f4832008-12-07 21:44:03 +05301779 qnum = sc->tx.hwq_map[ATH9K_WME_AC_VI];
Sujithff37e332008-11-24 12:07:55 +05301780 break;
1781 case 2:
Sujithb77f4832008-12-07 21:44:03 +05301782 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +05301783 break;
1784 case 3:
Sujithb77f4832008-12-07 21:44:03 +05301785 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BK];
Sujithff37e332008-11-24 12:07:55 +05301786 break;
1787 default:
Sujithb77f4832008-12-07 21:44:03 +05301788 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +05301789 break;
1790 }
1791
1792 return qnum;
1793}
1794
1795int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
1796{
1797 int qnum;
1798
1799 switch (queue) {
1800 case ATH9K_WME_AC_VO:
1801 qnum = 0;
1802 break;
1803 case ATH9K_WME_AC_VI:
1804 qnum = 1;
1805 break;
1806 case ATH9K_WME_AC_BE:
1807 qnum = 2;
1808 break;
1809 case ATH9K_WME_AC_BK:
1810 qnum = 3;
1811 break;
1812 default:
1813 qnum = -1;
1814 break;
1815 }
1816
1817 return qnum;
1818}
1819
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001820/* XXX: Remove me once we don't depend on ath9k_channel for all
1821 * this redundant data */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001822void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
1823 struct ath9k_channel *ichan)
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001824{
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001825 struct ieee80211_channel *chan = hw->conf.channel;
1826 struct ieee80211_conf *conf = &hw->conf;
1827
1828 ichan->channel = chan->center_freq;
1829 ichan->chan = chan;
1830
1831 if (chan->band == IEEE80211_BAND_2GHZ) {
1832 ichan->chanmode = CHANNEL_G;
Sujith88132622009-09-03 12:08:53 +05301833 ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001834 } else {
1835 ichan->chanmode = CHANNEL_A;
1836 ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
1837 }
1838
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -07001839 if (conf_is_ht(conf))
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001840 ichan->chanmode = ath_get_extchanmode(sc, chan,
1841 conf->channel_type);
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001842}
1843
Sujithff37e332008-11-24 12:07:55 +05301844/**********************/
1845/* mac80211 callbacks */
1846/**********************/
1847
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001848static int ath9k_start(struct ieee80211_hw *hw)
1849{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001850 struct ath_wiphy *aphy = hw->priv;
1851 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001852 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001853 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001854 struct ieee80211_channel *curchan = hw->conf.channel;
Sujithff37e332008-11-24 12:07:55 +05301855 struct ath9k_channel *init_channel;
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05301856 int r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001857
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001858 ath_print(common, ATH_DBG_CONFIG,
1859 "Starting driver with initial channel: %d MHz\n",
1860 curchan->center_freq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001861
Sujith141b38b2009-02-04 08:10:07 +05301862 mutex_lock(&sc->mutex);
1863
Jouni Malinen9580a222009-03-03 19:23:33 +02001864 if (ath9k_wiphy_started(sc)) {
1865 if (sc->chan_idx == curchan->hw_value) {
1866 /*
1867 * Already on the operational channel, the new wiphy
1868 * can be marked active.
1869 */
1870 aphy->state = ATH_WIPHY_ACTIVE;
1871 ieee80211_wake_queues(hw);
1872 } else {
1873 /*
1874 * Another wiphy is on another channel, start the new
1875 * wiphy in paused state.
1876 */
1877 aphy->state = ATH_WIPHY_PAUSED;
1878 ieee80211_stop_queues(hw);
1879 }
1880 mutex_unlock(&sc->mutex);
1881 return 0;
1882 }
1883 aphy->state = ATH_WIPHY_ACTIVE;
1884
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001885 /* setup initial channel */
1886
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05301887 sc->chan_idx = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001888
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05301889 init_channel = ath_get_curchannel(sc, hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001890
Sujithff37e332008-11-24 12:07:55 +05301891 /* Reset SERDES registers */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001892 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithff37e332008-11-24 12:07:55 +05301893
1894 /*
1895 * The basic interface to setting the hardware in a good
1896 * state is ``reset''. On return the hardware is known to
1897 * be powered up and with interrupts disabled. This must
1898 * be followed by initialization of the appropriate bits
1899 * and then setup of the interrupt mask.
1900 */
1901 spin_lock_bh(&sc->sc_resetlock);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001902 r = ath9k_hw_reset(ah, init_channel, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001903 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001904 ath_print(common, ATH_DBG_FATAL,
1905 "Unable to reset hardware; reset status %d "
1906 "(freq %u MHz)\n", r,
1907 curchan->center_freq);
Sujithff37e332008-11-24 12:07:55 +05301908 spin_unlock_bh(&sc->sc_resetlock);
Sujith141b38b2009-02-04 08:10:07 +05301909 goto mutex_unlock;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001910 }
Sujithff37e332008-11-24 12:07:55 +05301911 spin_unlock_bh(&sc->sc_resetlock);
1912
1913 /*
1914 * This is needed only to setup initial state
1915 * but it's best done after a reset.
1916 */
1917 ath_update_txpow(sc);
1918
1919 /*
1920 * Setup the hardware after reset:
1921 * The receive engine is set going.
1922 * Frame transmit is handled entirely
1923 * in the frame output path; there's nothing to do
1924 * here except setup the interrupt mask.
1925 */
1926 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001927 ath_print(common, ATH_DBG_FATAL,
1928 "Unable to start recv logic\n");
Sujith141b38b2009-02-04 08:10:07 +05301929 r = -EIO;
1930 goto mutex_unlock;
Sujithff37e332008-11-24 12:07:55 +05301931 }
1932
1933 /* Setup our intr mask. */
Sujith17d79042009-02-09 13:27:03 +05301934 sc->imask = ATH9K_INT_RX | ATH9K_INT_TX
Sujithff37e332008-11-24 12:07:55 +05301935 | ATH9K_INT_RXEOL | ATH9K_INT_RXORN
1936 | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
1937
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001938 if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
Sujith17d79042009-02-09 13:27:03 +05301939 sc->imask |= ATH9K_INT_GTT;
Sujithff37e332008-11-24 12:07:55 +05301940
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001941 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
Sujith17d79042009-02-09 13:27:03 +05301942 sc->imask |= ATH9K_INT_CST;
Sujithff37e332008-11-24 12:07:55 +05301943
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -08001944 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +05301945
1946 sc->sc_flags &= ~SC_OP_INVALID;
1947
1948 /* Disable BMISS interrupt when we're not associated */
Sujith17d79042009-02-09 13:27:03 +05301949 sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001950 ath9k_hw_set_interrupts(ah, sc->imask);
Sujithff37e332008-11-24 12:07:55 +05301951
Jouni Malinenbce048d2009-03-03 19:23:28 +02001952 ieee80211_wake_queues(hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001953
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04001954 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
Senthil Balasubramanian164ace32009-07-14 20:17:09 -04001955
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001956 if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
1957 !ah->btcoex_hw.enabled) {
Luis R. Rodriguez5e197292009-09-09 15:15:55 -07001958 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1959 AR_STOMP_LOW_WLAN_WGHT);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001960 ath9k_hw_btcoex_enable(ah);
Vasanthakumar Thiagarajanf985ad12009-08-26 21:08:43 +05301961
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001962 if (common->bus_ops->bt_coex_prep)
1963 common->bus_ops->bt_coex_prep(common);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001964 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001965 ath9k_btcoex_timer_resume(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301966 }
1967
Sujith141b38b2009-02-04 08:10:07 +05301968mutex_unlock:
1969 mutex_unlock(&sc->mutex);
1970
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001971 return r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001972}
1973
1974static int ath9k_tx(struct ieee80211_hw *hw,
1975 struct sk_buff *skb)
1976{
Jouni Malinen147583c2008-08-11 14:01:50 +03001977 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
Jouni Malinenbce048d2009-03-03 19:23:28 +02001978 struct ath_wiphy *aphy = hw->priv;
1979 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001980 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith528f0c62008-10-29 10:14:26 +05301981 struct ath_tx_control txctl;
Benoit Papillault1bc14882009-11-24 15:49:18 +01001982 int padpos, padsize;
1983 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
Sujith528f0c62008-10-29 10:14:26 +05301984
Jouni Malinen8089cc42009-03-03 19:23:38 +02001985 if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001986 ath_print(common, ATH_DBG_XMIT,
1987 "ath9k: %s: TX in unexpected wiphy state "
1988 "%d\n", wiphy_name(hw->wiphy), aphy->state);
Jouni Malinenee166a02009-03-03 19:23:36 +02001989 goto exit;
1990 }
1991
Gabor Juhos96148322009-07-24 17:27:21 +02001992 if (sc->ps_enabled) {
Jouni Malinendc8c4582009-05-19 17:01:42 +03001993 /*
1994 * mac80211 does not set PM field for normal data frames, so we
1995 * need to update that based on the current PS mode.
1996 */
1997 if (ieee80211_is_data(hdr->frame_control) &&
1998 !ieee80211_is_nullfunc(hdr->frame_control) &&
1999 !ieee80211_has_pm(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002000 ath_print(common, ATH_DBG_PS, "Add PM=1 for a TX frame "
2001 "while in PS mode\n");
Jouni Malinendc8c4582009-05-19 17:01:42 +03002002 hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
2003 }
2004 }
2005
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002006 if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) {
2007 /*
2008 * We are using PS-Poll and mac80211 can request TX while in
2009 * power save mode. Need to wake up hardware for the TX to be
2010 * completed and if needed, also for RX of buffered frames.
2011 */
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002012 ath9k_ps_wakeup(sc);
2013 ath9k_hw_setrxabort(sc->sc_ah, 0);
2014 if (ieee80211_is_pspoll(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002015 ath_print(common, ATH_DBG_PS,
2016 "Sending PS-Poll to pick a buffered frame\n");
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002017 sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA;
2018 } else {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002019 ath_print(common, ATH_DBG_PS,
2020 "Wake up to complete TX\n");
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002021 sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK;
2022 }
2023 /*
2024 * The actual restore operation will happen only after
2025 * the sc_flags bit is cleared. We are just dropping
2026 * the ps_usecount here.
2027 */
2028 ath9k_ps_restore(sc);
2029 }
2030
Sujith528f0c62008-10-29 10:14:26 +05302031 memset(&txctl, 0, sizeof(struct ath_tx_control));
Jouni Malinen147583c2008-08-11 14:01:50 +03002032
2033 /*
2034 * As a temporary workaround, assign seq# here; this will likely need
2035 * to be cleaned up to work better with Beacon transmission and virtual
2036 * BSSes.
2037 */
2038 if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
Jouni Malinen147583c2008-08-11 14:01:50 +03002039 if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
Sujithb77f4832008-12-07 21:44:03 +05302040 sc->tx.seq_no += 0x10;
Jouni Malinen147583c2008-08-11 14:01:50 +03002041 hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
Sujithb77f4832008-12-07 21:44:03 +05302042 hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
Jouni Malinen147583c2008-08-11 14:01:50 +03002043 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002044
2045 /* Add the padding after the header if this is not already done */
Benoit Papillault1bc14882009-11-24 15:49:18 +01002046 padpos = ath9k_cmn_padpos(hdr->frame_control);
2047 padsize = padpos & 3;
2048 if (padsize && skb->len>padpos) {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002049 if (skb_headroom(skb) < padsize)
2050 return -1;
2051 skb_push(skb, padsize);
Benoit Papillault1bc14882009-11-24 15:49:18 +01002052 memmove(skb->data, skb->data + padsize, padpos);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002053 }
2054
Sujith528f0c62008-10-29 10:14:26 +05302055 /* Check if a tx queue is available */
2056
2057 txctl.txq = ath_test_get_txq(sc, skb);
2058 if (!txctl.txq)
2059 goto exit;
2060
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002061 ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002062
Jouni Malinenc52f33d2009-03-03 19:23:29 +02002063 if (ath_tx_start(hw, skb, &txctl) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002064 ath_print(common, ATH_DBG_XMIT, "TX failed\n");
Sujith528f0c62008-10-29 10:14:26 +05302065 goto exit;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002066 }
2067
2068 return 0;
Sujith528f0c62008-10-29 10:14:26 +05302069exit:
2070 dev_kfree_skb_any(skb);
2071 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002072}
2073
2074static void ath9k_stop(struct ieee80211_hw *hw)
2075{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002076 struct ath_wiphy *aphy = hw->priv;
2077 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002078 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002079 struct ath_common *common = ath9k_hw_common(ah);
Sujith9c84b792008-10-29 10:17:13 +05302080
Sujith4c483812009-08-18 10:51:52 +05302081 mutex_lock(&sc->mutex);
2082
Jouni Malinen9580a222009-03-03 19:23:33 +02002083 aphy->state = ATH_WIPHY_INACTIVE;
2084
Luis R. Rodriguezc94dbff2009-07-27 11:53:04 -07002085 cancel_delayed_work_sync(&sc->ath_led_blink_work);
2086 cancel_delayed_work_sync(&sc->tx_complete_work);
2087
2088 if (!sc->num_sec_wiphy) {
2089 cancel_delayed_work_sync(&sc->wiphy_work);
2090 cancel_work_sync(&sc->chan_work);
2091 }
2092
Sujith9c84b792008-10-29 10:17:13 +05302093 if (sc->sc_flags & SC_OP_INVALID) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002094 ath_print(common, ATH_DBG_ANY, "Device not present\n");
Sujith4c483812009-08-18 10:51:52 +05302095 mutex_unlock(&sc->mutex);
Sujith9c84b792008-10-29 10:17:13 +05302096 return;
2097 }
2098
Jouni Malinen9580a222009-03-03 19:23:33 +02002099 if (ath9k_wiphy_started(sc)) {
2100 mutex_unlock(&sc->mutex);
2101 return; /* another wiphy still in use */
2102 }
2103
Sujith3867cf62009-12-23 20:03:27 -05002104 /* Ensure HW is awake when we try to shut it down. */
2105 ath9k_ps_wakeup(sc);
2106
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002107 if (ah->btcoex_hw.enabled) {
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002108 ath9k_hw_btcoex_disable(ah);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002109 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002110 ath9k_btcoex_timer_pause(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05302111 }
2112
Sujithff37e332008-11-24 12:07:55 +05302113 /* make sure h/w will not generate any interrupt
2114 * before setting the invalid flag. */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002115 ath9k_hw_set_interrupts(ah, 0);
Sujithff37e332008-11-24 12:07:55 +05302116
2117 if (!(sc->sc_flags & SC_OP_INVALID)) {
Sujith043a0402009-01-16 21:38:47 +05302118 ath_drain_all_txq(sc, false);
Sujithff37e332008-11-24 12:07:55 +05302119 ath_stoprecv(sc);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002120 ath9k_hw_phy_disable(ah);
Sujithff37e332008-11-24 12:07:55 +05302121 } else
Sujithb77f4832008-12-07 21:44:03 +05302122 sc->rx.rxlink = NULL;
Sujithff37e332008-11-24 12:07:55 +05302123
Sujithff37e332008-11-24 12:07:55 +05302124 /* disable HAL and put h/w to sleep */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002125 ath9k_hw_disable(ah);
2126 ath9k_hw_configpcipowersave(ah, 1, 1);
Sujith3867cf62009-12-23 20:03:27 -05002127 ath9k_ps_restore(sc);
2128
2129 /* Finally, put the chip in FULL SLEEP mode */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07002130 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Sujithff37e332008-11-24 12:07:55 +05302131
2132 sc->sc_flags |= SC_OP_INVALID;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002133
Sujith141b38b2009-02-04 08:10:07 +05302134 mutex_unlock(&sc->mutex);
2135
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002136 ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002137}
2138
2139static int ath9k_add_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01002140 struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002141{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002142 struct ath_wiphy *aphy = hw->priv;
2143 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002144 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berg1ed32e42009-12-23 13:15:45 +01002145 struct ath_vif *avp = (void *)vif->drv_priv;
Colin McCabed97809d2008-12-01 13:38:55 -08002146 enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002147 int ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002148
Sujith141b38b2009-02-04 08:10:07 +05302149 mutex_lock(&sc->mutex);
2150
Jouni Malinen8ca21f02009-03-03 19:23:27 +02002151 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
2152 sc->nvifs > 0) {
2153 ret = -ENOBUFS;
2154 goto out;
2155 }
2156
Johannes Berg1ed32e42009-12-23 13:15:45 +01002157 switch (vif->type) {
Johannes Berg05c914f2008-09-11 00:01:58 +02002158 case NL80211_IFTYPE_STATION:
Colin McCabed97809d2008-12-01 13:38:55 -08002159 ic_opmode = NL80211_IFTYPE_STATION;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002160 break;
Johannes Berg05c914f2008-09-11 00:01:58 +02002161 case NL80211_IFTYPE_ADHOC:
Johannes Berg05c914f2008-09-11 00:01:58 +02002162 case NL80211_IFTYPE_AP:
Pat Erley9cb54122009-03-20 22:59:59 -04002163 case NL80211_IFTYPE_MESH_POINT:
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002164 if (sc->nbcnvifs >= ATH_BCBUF) {
2165 ret = -ENOBUFS;
2166 goto out;
2167 }
Johannes Berg1ed32e42009-12-23 13:15:45 +01002168 ic_opmode = vif->type;
Jouni Malinen2ad67de2008-08-11 14:01:47 +03002169 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002170 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002171 ath_print(common, ATH_DBG_FATAL,
Johannes Berg1ed32e42009-12-23 13:15:45 +01002172 "Interface type %d not yet supported\n", vif->type);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002173 ret = -EOPNOTSUPP;
2174 goto out;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002175 }
2176
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002177 ath_print(common, ATH_DBG_CONFIG,
2178 "Attach a VIF of type: %d\n", ic_opmode);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002179
Sujith17d79042009-02-09 13:27:03 +05302180 /* Set the VIF opmode */
Sujith5640b082008-10-29 10:16:06 +05302181 avp->av_opmode = ic_opmode;
2182 avp->av_bslot = -1;
2183
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002184 sc->nvifs++;
Jouni Malinen8ca21f02009-03-03 19:23:27 +02002185
2186 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
2187 ath9k_set_bssid_mask(hw);
2188
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002189 if (sc->nvifs > 1)
2190 goto out; /* skip global settings for secondary vif */
2191
Sujithb238e902009-03-03 10:16:56 +05302192 if (ic_opmode == NL80211_IFTYPE_AP) {
Sujith5640b082008-10-29 10:16:06 +05302193 ath9k_hw_set_tsfadjust(sc->sc_ah, 1);
Sujithb238e902009-03-03 10:16:56 +05302194 sc->sc_flags |= SC_OP_TSF_RESET;
2195 }
Sujith5640b082008-10-29 10:16:06 +05302196
Sujith5640b082008-10-29 10:16:06 +05302197 /* Set the device opmode */
Sujith2660b812009-02-09 13:27:26 +05302198 sc->sc_ah->opmode = ic_opmode;
Sujith5640b082008-10-29 10:16:06 +05302199
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05302200 /*
2201 * Enable MIB interrupts when there are hardware phy counters.
2202 * Note we only do this (at the moment) for station mode.
2203 */
Johannes Berg1ed32e42009-12-23 13:15:45 +01002204 if ((vif->type == NL80211_IFTYPE_STATION) ||
2205 (vif->type == NL80211_IFTYPE_ADHOC) ||
2206 (vif->type == NL80211_IFTYPE_MESH_POINT)) {
Sujith1aa8e842009-08-13 09:34:25 +05302207 sc->imask |= ATH9K_INT_MIB;
Sujith4af9cf42009-02-12 10:06:47 +05302208 sc->imask |= ATH9K_INT_TSFOOR;
2209 }
2210
Sujith17d79042009-02-09 13:27:03 +05302211 ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05302212
Johannes Berg1ed32e42009-12-23 13:15:45 +01002213 if (vif->type == NL80211_IFTYPE_AP ||
2214 vif->type == NL80211_IFTYPE_ADHOC ||
2215 vif->type == NL80211_IFTYPE_MONITOR)
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08002216 ath_start_ani(common);
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07002217
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002218out:
Sujith141b38b2009-02-04 08:10:07 +05302219 mutex_unlock(&sc->mutex);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002220 return ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002221}
2222
2223static void ath9k_remove_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01002224 struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002225{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002226 struct ath_wiphy *aphy = hw->priv;
2227 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002228 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berg1ed32e42009-12-23 13:15:45 +01002229 struct ath_vif *avp = (void *)vif->drv_priv;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002230 int i;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002231
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002232 ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002233
Sujith141b38b2009-02-04 08:10:07 +05302234 mutex_lock(&sc->mutex);
2235
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07002236 /* Stop ANI */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08002237 del_timer_sync(&common->ani.timer);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002238
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002239 /* Reclaim beacon resources */
Pat Erley9cb54122009-03-20 22:59:59 -04002240 if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
2241 (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
2242 (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
Luis R. Rodriguez5f70a882009-12-23 20:03:28 -05002243 ath9k_ps_wakeup(sc);
Sujithb77f4832008-12-07 21:44:03 +05302244 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002245 ath_beacon_return(sc, avp);
Luis R. Rodriguez5f70a882009-12-23 20:03:28 -05002246 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002247 }
2248
Sujith672840a2008-08-11 14:05:08 +05302249 sc->sc_flags &= ~SC_OP_BEACONS;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002250
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002251 for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
Johannes Berg1ed32e42009-12-23 13:15:45 +01002252 if (sc->beacon.bslot[i] == vif) {
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002253 printk(KERN_DEBUG "%s: vif had allocated beacon "
2254 "slot\n", __func__);
2255 sc->beacon.bslot[i] = NULL;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02002256 sc->beacon.bslot_aphy[i] = NULL;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002257 }
2258 }
2259
Sujith17d79042009-02-09 13:27:03 +05302260 sc->nvifs--;
Sujith141b38b2009-02-04 08:10:07 +05302261
2262 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002263}
2264
Johannes Berge8975582008-10-09 12:18:51 +02002265static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002266{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002267 struct ath_wiphy *aphy = hw->priv;
2268 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002269 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berge8975582008-10-09 12:18:51 +02002270 struct ieee80211_conf *conf = &hw->conf;
Vivek Natarajan8782b412009-03-30 14:17:00 +05302271 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002272 bool disable_radio;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002273
Sujithaa33de02008-12-18 11:40:16 +05302274 mutex_lock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05302275
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002276 /*
2277 * Leave this as the first check because we need to turn on the
2278 * radio if it was disabled before prior to processing the rest
2279 * of the changes. Likewise we must only disable the radio towards
2280 * the end.
2281 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002282 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002283 bool enable_radio;
2284 bool all_wiphys_idle;
2285 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002286
2287 spin_lock_bh(&sc->wiphy_lock);
2288 all_wiphys_idle = ath9k_all_wiphys_idle(sc);
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002289 ath9k_set_wiphy_idle(aphy, idle);
2290
2291 if (!idle && all_wiphys_idle)
2292 enable_radio = true;
2293
2294 /*
2295 * After we unlock here its possible another wiphy
2296 * can be re-renabled so to account for that we will
2297 * only disable the radio toward the end of this routine
2298 * if by then all wiphys are still idle.
2299 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002300 spin_unlock_bh(&sc->wiphy_lock);
2301
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002302 if (enable_radio) {
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08002303 ath_radio_enable(sc, hw);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002304 ath_print(common, ATH_DBG_CONFIG,
2305 "not-idle: enabling radio\n");
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002306 }
2307 }
2308
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002309 /*
2310 * We just prepare to enable PS. We have to wait until our AP has
2311 * ACK'd our null data frame to disable RX otherwise we'll ignore
2312 * those ACKs and end up retransmitting the same null data frames.
2313 * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
2314 */
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302315 if (changed & IEEE80211_CONF_CHANGE_PS) {
2316 if (conf->flags & IEEE80211_CONF_PS) {
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002317 sc->sc_flags |= SC_OP_PS_ENABLED;
Vivek Natarajan8782b412009-03-30 14:17:00 +05302318 if (!(ah->caps.hw_caps &
2319 ATH9K_HW_CAP_AUTOSLEEP)) {
2320 if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
2321 sc->imask |= ATH9K_INT_TIM_TIMER;
2322 ath9k_hw_set_interrupts(sc->sc_ah,
2323 sc->imask);
2324 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302325 }
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002326 /*
2327 * At this point we know hardware has received an ACK
2328 * of a previously sent null data frame.
2329 */
2330 if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) {
2331 sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
2332 sc->ps_enabled = true;
2333 ath9k_hw_setrxabort(sc->sc_ah, 1);
2334 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302335 } else {
Gabor Juhos96148322009-07-24 17:27:21 +02002336 sc->ps_enabled = false;
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002337 sc->sc_flags &= ~(SC_OP_PS_ENABLED |
2338 SC_OP_NULLFUNC_COMPLETED);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07002339 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vivek Natarajan8782b412009-03-30 14:17:00 +05302340 if (!(ah->caps.hw_caps &
2341 ATH9K_HW_CAP_AUTOSLEEP)) {
2342 ath9k_hw_setrxabort(sc->sc_ah, 0);
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002343 sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON |
2344 SC_OP_WAIT_FOR_CAB |
2345 SC_OP_WAIT_FOR_PSPOLL_DATA |
2346 SC_OP_WAIT_FOR_TX_ACK);
Vivek Natarajan8782b412009-03-30 14:17:00 +05302347 if (sc->imask & ATH9K_INT_TIM_TIMER) {
2348 sc->imask &= ~ATH9K_INT_TIM_TIMER;
2349 ath9k_hw_set_interrupts(sc->sc_ah,
2350 sc->imask);
2351 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302352 }
2353 }
2354 }
2355
Johannes Berg47979382009-01-07 10:13:27 +01002356 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
Sujith99405f92008-11-24 12:08:35 +05302357 struct ieee80211_channel *curchan = hw->conf.channel;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002358 int pos = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002359
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002360 aphy->chan_idx = pos;
2361 aphy->chan_is_ht = conf_is_ht(conf);
2362
Jouni Malinen8089cc42009-03-03 19:23:38 +02002363 if (aphy->state == ATH_WIPHY_SCAN ||
2364 aphy->state == ATH_WIPHY_ACTIVE)
2365 ath9k_wiphy_pause_all_forced(sc, aphy);
2366 else {
2367 /*
2368 * Do not change operational channel based on a paused
2369 * wiphy changes.
2370 */
2371 goto skip_chan_change;
2372 }
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002373
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002374 ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
2375 curchan->center_freq);
Johannes Bergae5eb022008-10-14 16:58:37 +02002376
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002377 /* XXX: remove me eventualy */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002378 ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
Sujithe11602b2008-11-27 09:46:27 +05302379
Luis R. Rodriguezecf70442008-12-23 15:58:43 -08002380 ath_update_chainmask(sc, conf_is_ht(conf));
Sujith86060f02009-01-07 14:25:29 +05302381
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002382 if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002383 ath_print(common, ATH_DBG_FATAL,
2384 "Unable to set channel\n");
Sujithaa33de02008-12-18 11:40:16 +05302385 mutex_unlock(&sc->mutex);
Sujithe11602b2008-11-27 09:46:27 +05302386 return -EINVAL;
2387 }
Sujith094d05d2008-12-12 11:57:43 +05302388 }
Sujith86b89ee2008-08-07 10:54:57 +05302389
Jouni Malinen8089cc42009-03-03 19:23:38 +02002390skip_chan_change:
Luis R. Rodriguez5c020dc2008-10-22 13:28:45 -07002391 if (changed & IEEE80211_CONF_CHANGE_POWER)
Sujith17d79042009-02-09 13:27:03 +05302392 sc->config.txpowlimit = 2 * conf->power_level;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002393
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002394 spin_lock_bh(&sc->wiphy_lock);
2395 disable_radio = ath9k_all_wiphys_idle(sc);
2396 spin_unlock_bh(&sc->wiphy_lock);
2397
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002398 if (disable_radio) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002399 ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08002400 ath_radio_disable(sc, hw);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002401 }
2402
Sujithaa33de02008-12-18 11:40:16 +05302403 mutex_unlock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05302404
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002405 return 0;
2406}
2407
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002408#define SUPPORTED_FILTERS \
2409 (FIF_PROMISC_IN_BSS | \
2410 FIF_ALLMULTI | \
2411 FIF_CONTROL | \
Luis R. Rodriguezaf6a3fc2009-08-08 21:55:16 -04002412 FIF_PSPOLL | \
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002413 FIF_OTHER_BSS | \
2414 FIF_BCN_PRBRESP_PROMISC | \
2415 FIF_FCSFAIL)
2416
Sujith7dcfdcd2008-08-11 14:03:13 +05302417/* FIXME: sc->sc_full_reset ? */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002418static void ath9k_configure_filter(struct ieee80211_hw *hw,
2419 unsigned int changed_flags,
2420 unsigned int *total_flags,
Johannes Berg3ac64be2009-08-17 16:16:53 +02002421 u64 multicast)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002422{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002423 struct ath_wiphy *aphy = hw->priv;
2424 struct ath_softc *sc = aphy->sc;
Sujith7dcfdcd2008-08-11 14:03:13 +05302425 u32 rfilt;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002426
2427 changed_flags &= SUPPORTED_FILTERS;
2428 *total_flags &= SUPPORTED_FILTERS;
2429
Sujithb77f4832008-12-07 21:44:03 +05302430 sc->rx.rxfilter = *total_flags;
Jouni Malinenaa68aea2009-05-19 17:01:41 +03002431 ath9k_ps_wakeup(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05302432 rfilt = ath_calcrxfilter(sc);
2433 ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
Jouni Malinenaa68aea2009-05-19 17:01:41 +03002434 ath9k_ps_restore(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05302435
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002436 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
2437 "Set HW RX filter: 0x%x\n", rfilt);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002438}
2439
2440static void ath9k_sta_notify(struct ieee80211_hw *hw,
2441 struct ieee80211_vif *vif,
2442 enum sta_notify_cmd cmd,
Johannes Berg17741cd2008-09-11 00:02:02 +02002443 struct ieee80211_sta *sta)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002444{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002445 struct ath_wiphy *aphy = hw->priv;
2446 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002447
2448 switch (cmd) {
2449 case STA_NOTIFY_ADD:
Sujith5640b082008-10-29 10:16:06 +05302450 ath_node_attach(sc, sta);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002451 break;
2452 case STA_NOTIFY_REMOVE:
Sujithb5aa9bf2008-10-29 10:13:31 +05302453 ath_node_detach(sc, sta);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002454 break;
2455 default:
2456 break;
2457 }
2458}
2459
Sujith141b38b2009-02-04 08:10:07 +05302460static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002461 const struct ieee80211_tx_queue_params *params)
2462{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002463 struct ath_wiphy *aphy = hw->priv;
2464 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002465 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujithea9880f2008-08-07 10:53:10 +05302466 struct ath9k_tx_queue_info qi;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002467 int ret = 0, qnum;
2468
2469 if (queue >= WME_NUM_AC)
2470 return 0;
2471
Sujith141b38b2009-02-04 08:10:07 +05302472 mutex_lock(&sc->mutex);
2473
Sujith1ffb0612009-03-30 15:28:46 +05302474 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
2475
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002476 qi.tqi_aifs = params->aifs;
2477 qi.tqi_cwmin = params->cw_min;
2478 qi.tqi_cwmax = params->cw_max;
2479 qi.tqi_burstTime = params->txop;
2480 qnum = ath_get_hal_qnum(queue, sc);
2481
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002482 ath_print(common, ATH_DBG_CONFIG,
2483 "Configure tx [queue/halq] [%d/%d], "
2484 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
2485 queue, qnum, params->aifs, params->cw_min,
2486 params->cw_max, params->txop);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002487
2488 ret = ath_txq_update(sc, qnum, &qi);
2489 if (ret)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002490 ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002491
Vivek Natarajan94db2932009-11-25 12:01:54 +05302492 if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)
2493 if ((qnum == sc->tx.hwq_map[ATH9K_WME_AC_BE]) && !ret)
2494 ath_beaconq_config(sc);
2495
Sujith141b38b2009-02-04 08:10:07 +05302496 mutex_unlock(&sc->mutex);
2497
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002498 return ret;
2499}
2500
2501static int ath9k_set_key(struct ieee80211_hw *hw,
2502 enum set_key_cmd cmd,
Johannes Bergdc822b52008-12-29 12:55:09 +01002503 struct ieee80211_vif *vif,
2504 struct ieee80211_sta *sta,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002505 struct ieee80211_key_conf *key)
2506{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002507 struct ath_wiphy *aphy = hw->priv;
2508 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002509 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002510 int ret = 0;
2511
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02002512 if (modparam_nohwcrypt)
2513 return -ENOSPC;
2514
Sujith141b38b2009-02-04 08:10:07 +05302515 mutex_lock(&sc->mutex);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302516 ath9k_ps_wakeup(sc);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002517 ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002518
2519 switch (cmd) {
2520 case SET_KEY:
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08002521 ret = ath_key_config(common, vif, sta, key);
Jouni Malinen6ace2892008-12-17 13:32:17 +02002522 if (ret >= 0) {
2523 key->hw_key_idx = ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002524 /* push IV and Michael MIC generation to stack */
2525 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Senthil Balasubramanian1b961752008-09-01 19:45:21 +05302526 if (key->alg == ALG_TKIP)
2527 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Jouni Malinen0ced0e12009-01-08 13:32:13 +02002528 if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
2529 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
Jouni Malinen6ace2892008-12-17 13:32:17 +02002530 ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002531 }
2532 break;
2533 case DISABLE_KEY:
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08002534 ath_key_delete(common, key);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002535 break;
2536 default:
2537 ret = -EINVAL;
2538 }
2539
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302540 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05302541 mutex_unlock(&sc->mutex);
2542
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002543 return ret;
2544}
2545
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002546static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
2547 struct ieee80211_vif *vif,
2548 struct ieee80211_bss_conf *bss_conf,
2549 u32 changed)
2550{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002551 struct ath_wiphy *aphy = hw->priv;
2552 struct ath_softc *sc = aphy->sc;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002553 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07002554 struct ath_common *common = ath9k_hw_common(ah);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002555 struct ath_vif *avp = (void *)vif->drv_priv;
Sujithc6089cc2009-11-16 11:40:48 +05302556 int error;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002557
Sujith141b38b2009-02-04 08:10:07 +05302558 mutex_lock(&sc->mutex);
2559
Sujithc6089cc2009-11-16 11:40:48 +05302560 if (changed & BSS_CHANGED_BSSID) {
2561 /* Set BSSID */
2562 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
2563 memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
Luis R. Rodriguez15107182009-09-10 09:22:37 -07002564 common->curaid = 0;
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -07002565 ath9k_hw_write_associd(ah);
Sujithc6089cc2009-11-16 11:40:48 +05302566
2567 /* Set aggregation protection mode parameters */
2568 sc->config.ath_aggr_prot = 0;
2569
2570 /* Only legacy IBSS for now */
2571 if (vif->type == NL80211_IFTYPE_ADHOC)
2572 ath_update_chainmask(sc, 0);
2573
2574 ath_print(common, ATH_DBG_CONFIG,
2575 "BSSID: %pM aid: 0x%x\n",
2576 common->curbssid, common->curaid);
2577
2578 /* need to reconfigure the beacon */
2579 sc->sc_flags &= ~SC_OP_BEACONS ;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002580 }
2581
Sujithc6089cc2009-11-16 11:40:48 +05302582 /* Enable transmission of beacons (AP, IBSS, MESH) */
2583 if ((changed & BSS_CHANGED_BEACON) ||
2584 ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) {
2585 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
2586 error = ath_beacon_alloc(aphy, vif);
2587 if (!error)
2588 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002589 }
2590
Sujithc6089cc2009-11-16 11:40:48 +05302591 /* Disable transmission of beacons */
2592 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon)
2593 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
2594
2595 if (changed & BSS_CHANGED_BEACON_INT) {
2596 sc->beacon_interval = bss_conf->beacon_int;
2597 /*
2598 * In case of AP mode, the HW TSF has to be reset
2599 * when the beacon interval changes.
2600 */
2601 if (vif->type == NL80211_IFTYPE_AP) {
2602 sc->sc_flags |= SC_OP_TSF_RESET;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002603 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002604 error = ath_beacon_alloc(aphy, vif);
2605 if (!error)
2606 ath_beacon_config(sc, vif);
Sujithc6089cc2009-11-16 11:40:48 +05302607 } else {
2608 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002609 }
2610 }
2611
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002612 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002613 ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
2614 bss_conf->use_short_preamble);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002615 if (bss_conf->use_short_preamble)
Sujith672840a2008-08-11 14:05:08 +05302616 sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002617 else
Sujith672840a2008-08-11 14:05:08 +05302618 sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002619 }
2620
2621 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002622 ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
2623 bss_conf->use_cts_prot);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002624 if (bss_conf->use_cts_prot &&
2625 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
Sujith672840a2008-08-11 14:05:08 +05302626 sc->sc_flags |= SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002627 else
Sujith672840a2008-08-11 14:05:08 +05302628 sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002629 }
2630
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002631 if (changed & BSS_CHANGED_ASSOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002632 ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002633 bss_conf->assoc);
Sujith5640b082008-10-29 10:16:06 +05302634 ath9k_bss_assoc_info(sc, vif, bss_conf);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002635 }
Sujith141b38b2009-02-04 08:10:07 +05302636
2637 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002638}
2639
2640static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
2641{
2642 u64 tsf;
Jouni Malinenbce048d2009-03-03 19:23:28 +02002643 struct ath_wiphy *aphy = hw->priv;
2644 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002645
Sujith141b38b2009-02-04 08:10:07 +05302646 mutex_lock(&sc->mutex);
2647 tsf = ath9k_hw_gettsf64(sc->sc_ah);
2648 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002649
2650 return tsf;
2651}
2652
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01002653static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
2654{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002655 struct ath_wiphy *aphy = hw->priv;
2656 struct ath_softc *sc = aphy->sc;
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01002657
Sujith141b38b2009-02-04 08:10:07 +05302658 mutex_lock(&sc->mutex);
2659 ath9k_hw_settsf64(sc->sc_ah, tsf);
2660 mutex_unlock(&sc->mutex);
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01002661}
2662
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002663static void ath9k_reset_tsf(struct ieee80211_hw *hw)
2664{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002665 struct ath_wiphy *aphy = hw->priv;
2666 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002667
Sujith141b38b2009-02-04 08:10:07 +05302668 mutex_lock(&sc->mutex);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07002669
2670 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05302671 ath9k_hw_reset_tsf(sc->sc_ah);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07002672 ath9k_ps_restore(sc);
2673
Sujith141b38b2009-02-04 08:10:07 +05302674 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002675}
2676
2677static int ath9k_ampdu_action(struct ieee80211_hw *hw,
Johannes Bergc951ad32009-11-16 12:00:38 +01002678 struct ieee80211_vif *vif,
Sujith141b38b2009-02-04 08:10:07 +05302679 enum ieee80211_ampdu_mlme_action action,
2680 struct ieee80211_sta *sta,
2681 u16 tid, u16 *ssn)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002682{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002683 struct ath_wiphy *aphy = hw->priv;
2684 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002685 int ret = 0;
2686
2687 switch (action) {
2688 case IEEE80211_AMPDU_RX_START:
Sujithdca3edb2008-10-29 10:19:01 +05302689 if (!(sc->sc_flags & SC_OP_RXAGGR))
2690 ret = -ENOTSUPP;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002691 break;
2692 case IEEE80211_AMPDU_RX_STOP:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002693 break;
2694 case IEEE80211_AMPDU_TX_START:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05002695 ath9k_ps_wakeup(sc);
Sujithf83da962009-07-23 15:32:37 +05302696 ath_tx_aggr_start(sc, sta, tid, ssn);
Johannes Bergc951ad32009-11-16 12:00:38 +01002697 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05002698 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002699 break;
2700 case IEEE80211_AMPDU_TX_STOP:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05002701 ath9k_ps_wakeup(sc);
Sujithf83da962009-07-23 15:32:37 +05302702 ath_tx_aggr_stop(sc, sta, tid);
Johannes Bergc951ad32009-11-16 12:00:38 +01002703 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05002704 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002705 break;
Johannes Bergb1720232009-03-23 17:28:39 +01002706 case IEEE80211_AMPDU_TX_OPERATIONAL:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05002707 ath9k_ps_wakeup(sc);
Sujith8469cde2008-10-29 10:19:28 +05302708 ath_tx_aggr_resume(sc, sta, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05002709 ath9k_ps_restore(sc);
Sujith8469cde2008-10-29 10:19:28 +05302710 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002711 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002712 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
2713 "Unknown AMPDU action\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002714 }
2715
2716 return ret;
2717}
2718
Sujith0c98de62009-03-03 10:16:45 +05302719static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
2720{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002721 struct ath_wiphy *aphy = hw->priv;
2722 struct ath_softc *sc = aphy->sc;
Sujith05c78d62009-12-14 14:57:04 +05302723 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith0c98de62009-03-03 10:16:45 +05302724
Sujith3d832612009-08-21 12:00:28 +05302725 mutex_lock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02002726 if (ath9k_wiphy_scanning(sc)) {
2727 printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the "
2728 "same time\n");
2729 /*
2730 * Do not allow the concurrent scanning state for now. This
2731 * could be improved with scanning control moved into ath9k.
2732 */
Sujith3d832612009-08-21 12:00:28 +05302733 mutex_unlock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02002734 return;
2735 }
2736
2737 aphy->state = ATH_WIPHY_SCAN;
2738 ath9k_wiphy_pause_all_forced(sc, aphy);
Sujith0c98de62009-03-03 10:16:45 +05302739 sc->sc_flags |= SC_OP_SCANNING;
Sujith05c78d62009-12-14 14:57:04 +05302740 del_timer_sync(&common->ani.timer);
Sujithb6ce5c32009-12-14 14:57:06 +05302741 cancel_delayed_work_sync(&sc->tx_complete_work);
Sujith3d832612009-08-21 12:00:28 +05302742 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05302743}
2744
2745static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
2746{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002747 struct ath_wiphy *aphy = hw->priv;
2748 struct ath_softc *sc = aphy->sc;
Sujith05c78d62009-12-14 14:57:04 +05302749 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith0c98de62009-03-03 10:16:45 +05302750
Sujith3d832612009-08-21 12:00:28 +05302751 mutex_lock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02002752 aphy->state = ATH_WIPHY_ACTIVE;
Sujith0c98de62009-03-03 10:16:45 +05302753 sc->sc_flags &= ~SC_OP_SCANNING;
Sujith9c07a772009-04-13 21:56:36 +05302754 sc->sc_flags |= SC_OP_FULL_RESET;
Sujith05c78d62009-12-14 14:57:04 +05302755 ath_start_ani(common);
Sujithb6ce5c32009-12-14 14:57:06 +05302756 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
Vivek Natarajand0bec342009-09-02 15:50:55 +05302757 ath_beacon_config(sc, NULL);
Sujith3d832612009-08-21 12:00:28 +05302758 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05302759}
2760
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002761struct ieee80211_ops ath9k_ops = {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002762 .tx = ath9k_tx,
2763 .start = ath9k_start,
2764 .stop = ath9k_stop,
2765 .add_interface = ath9k_add_interface,
2766 .remove_interface = ath9k_remove_interface,
2767 .config = ath9k_config,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002768 .configure_filter = ath9k_configure_filter,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002769 .sta_notify = ath9k_sta_notify,
2770 .conf_tx = ath9k_conf_tx,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002771 .bss_info_changed = ath9k_bss_info_changed,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002772 .set_key = ath9k_set_key,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002773 .get_tsf = ath9k_get_tsf,
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01002774 .set_tsf = ath9k_set_tsf,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002775 .reset_tsf = ath9k_reset_tsf,
Johannes Berg4233df62008-10-13 13:35:05 +02002776 .ampdu_action = ath9k_ampdu_action,
Sujith0c98de62009-03-03 10:16:45 +05302777 .sw_scan_start = ath9k_sw_scan_start,
2778 .sw_scan_complete = ath9k_sw_scan_complete,
Johannes Berg3b319aa2009-06-13 14:50:26 +05302779 .rfkill_poll = ath9k_rfkill_poll_state,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002780};
2781
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002782static int __init ath9k_init(void)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002783{
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05302784 int error;
2785
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05302786 /* Register rate control algorithm */
2787 error = ath_rate_control_register();
2788 if (error != 0) {
2789 printk(KERN_ERR
Luis R. Rodriguezb51bb3c2009-01-26 07:30:03 -08002790 "ath9k: Unable to register rate control "
2791 "algorithm: %d\n",
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05302792 error);
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002793 goto err_out;
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05302794 }
2795
Gabor Juhos19d8bc22009-03-05 16:55:18 +01002796 error = ath9k_debug_create_root();
2797 if (error) {
2798 printk(KERN_ERR
2799 "ath9k: Unable to create debugfs root: %d\n",
2800 error);
2801 goto err_rate_unregister;
2802 }
2803
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002804 error = ath_pci_init();
2805 if (error < 0) {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002806 printk(KERN_ERR
Luis R. Rodriguezb51bb3c2009-01-26 07:30:03 -08002807 "ath9k: No PCI devices found, driver not installed.\n");
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002808 error = -ENODEV;
Gabor Juhos19d8bc22009-03-05 16:55:18 +01002809 goto err_remove_root;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002810 }
2811
Gabor Juhos09329d32009-01-14 20:17:07 +01002812 error = ath_ahb_init();
2813 if (error < 0) {
2814 error = -ENODEV;
2815 goto err_pci_exit;
2816 }
2817
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002818 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002819
Gabor Juhos09329d32009-01-14 20:17:07 +01002820 err_pci_exit:
2821 ath_pci_exit();
2822
Gabor Juhos19d8bc22009-03-05 16:55:18 +01002823 err_remove_root:
2824 ath9k_debug_remove_root();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002825 err_rate_unregister:
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05302826 ath_rate_control_unregister();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002827 err_out:
2828 return error;
2829}
2830module_init(ath9k_init);
2831
2832static void __exit ath9k_exit(void)
2833{
Gabor Juhos09329d32009-01-14 20:17:07 +01002834 ath_ahb_exit();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002835 ath_pci_exit();
Gabor Juhos19d8bc22009-03-05 16:55:18 +01002836 ath9k_debug_remove_root();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002837 ath_rate_control_unregister();
Sujith04bd46382008-11-28 22:18:05 +05302838 printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002839}
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002840module_exit(ath9k_exit);