blob: 2ec7451350f0c8b71d3bc1d1a91171bd066e64c4 [file] [log] [blame]
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001/*
Sujithcee075a2009-03-13 09:07:23 +05302 * Copyright (c) 2008-2009 Atheros Communications Inc.
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Luis R. Rodriguezf078f202008-08-04 00:16:41 -070017#include <linux/nl80211.h>
Sujith394cf0a2009-02-09 13:26:54 +053018#include "ath9k.h"
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -070019#include "btcoex.h"
Luis R. Rodriguezf078f202008-08-04 00:16:41 -070020
Luis R. Rodriguezf078f202008-08-04 00:16:41 -070021static char *dev_info = "ath9k";
22
23MODULE_AUTHOR("Atheros Communications");
24MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
25MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
26MODULE_LICENSE("Dual BSD/GPL");
27
Jouni Malinenb3bd89c2009-02-24 13:42:01 +020028static int modparam_nohwcrypt;
29module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
30MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
31
Luis R. Rodriguezfaa27fa2009-10-06 21:19:06 -040032static unsigned int ath9k_debug = ATH_DBG_DEFAULT;
33module_param_named(debug, ath9k_debug, uint, 0);
Luis R. Rodriguezaf1fc672009-10-08 01:00:18 -040034MODULE_PARM_DESC(debug, "Debugging mask");
Luis R. Rodriguezfaa27fa2009-10-06 21:19:06 -040035
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -080036/* We use the hw_value as an index into our private channel structure */
37
38#define CHAN2G(_freq, _idx) { \
39 .center_freq = (_freq), \
40 .hw_value = (_idx), \
Luis R. Rodriguezeeddfd92009-05-19 17:49:46 -040041 .max_power = 20, \
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -080042}
43
44#define CHAN5G(_freq, _idx) { \
45 .band = IEEE80211_BAND_5GHZ, \
46 .center_freq = (_freq), \
47 .hw_value = (_idx), \
Luis R. Rodriguezeeddfd92009-05-19 17:49:46 -040048 .max_power = 20, \
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -080049}
50
51/* Some 2 GHz radios are actually tunable on 2312-2732
52 * on 5 MHz steps, we support the channels which we know
53 * we have calibration data for all cards though to make
54 * this static */
55static struct ieee80211_channel ath9k_2ghz_chantable[] = {
56 CHAN2G(2412, 0), /* Channel 1 */
57 CHAN2G(2417, 1), /* Channel 2 */
58 CHAN2G(2422, 2), /* Channel 3 */
59 CHAN2G(2427, 3), /* Channel 4 */
60 CHAN2G(2432, 4), /* Channel 5 */
61 CHAN2G(2437, 5), /* Channel 6 */
62 CHAN2G(2442, 6), /* Channel 7 */
63 CHAN2G(2447, 7), /* Channel 8 */
64 CHAN2G(2452, 8), /* Channel 9 */
65 CHAN2G(2457, 9), /* Channel 10 */
66 CHAN2G(2462, 10), /* Channel 11 */
67 CHAN2G(2467, 11), /* Channel 12 */
68 CHAN2G(2472, 12), /* Channel 13 */
69 CHAN2G(2484, 13), /* Channel 14 */
70};
71
72/* Some 5 GHz radios are actually tunable on XXXX-YYYY
73 * on 5 MHz steps, we support the channels which we know
74 * we have calibration data for all cards though to make
75 * this static */
76static struct ieee80211_channel ath9k_5ghz_chantable[] = {
77 /* _We_ call this UNII 1 */
78 CHAN5G(5180, 14), /* Channel 36 */
79 CHAN5G(5200, 15), /* Channel 40 */
80 CHAN5G(5220, 16), /* Channel 44 */
81 CHAN5G(5240, 17), /* Channel 48 */
82 /* _We_ call this UNII 2 */
83 CHAN5G(5260, 18), /* Channel 52 */
84 CHAN5G(5280, 19), /* Channel 56 */
85 CHAN5G(5300, 20), /* Channel 60 */
86 CHAN5G(5320, 21), /* Channel 64 */
87 /* _We_ call this "Middle band" */
88 CHAN5G(5500, 22), /* Channel 100 */
89 CHAN5G(5520, 23), /* Channel 104 */
90 CHAN5G(5540, 24), /* Channel 108 */
91 CHAN5G(5560, 25), /* Channel 112 */
92 CHAN5G(5580, 26), /* Channel 116 */
93 CHAN5G(5600, 27), /* Channel 120 */
94 CHAN5G(5620, 28), /* Channel 124 */
95 CHAN5G(5640, 29), /* Channel 128 */
96 CHAN5G(5660, 30), /* Channel 132 */
97 CHAN5G(5680, 31), /* Channel 136 */
98 CHAN5G(5700, 32), /* Channel 140 */
99 /* _We_ call this UNII 3 */
100 CHAN5G(5745, 33), /* Channel 149 */
101 CHAN5G(5765, 34), /* Channel 153 */
102 CHAN5G(5785, 35), /* Channel 157 */
103 CHAN5G(5805, 36), /* Channel 161 */
104 CHAN5G(5825, 37), /* Channel 165 */
105};
106
Felix Fietkau545750d2009-11-23 22:21:01 +0100107/* Atheros hardware rate code addition for short premble */
108#define SHPCHECK(__hw_rate, __flags) \
109 ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0)
110
111#define RATE(_bitrate, _hw_rate, _flags) { \
112 .bitrate = (_bitrate), \
113 .flags = (_flags), \
114 .hw_value = (_hw_rate), \
115 .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \
116}
117
118static struct ieee80211_rate ath9k_legacy_rates[] = {
119 RATE(10, 0x1b, 0),
120 RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
121 RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
122 RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
123 RATE(60, 0x0b, 0),
124 RATE(90, 0x0f, 0),
125 RATE(120, 0x0a, 0),
126 RATE(180, 0x0e, 0),
127 RATE(240, 0x09, 0),
128 RATE(360, 0x0d, 0),
129 RATE(480, 0x08, 0),
130 RATE(540, 0x0c, 0),
131};
132
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -0800133static void ath_cache_conf_rate(struct ath_softc *sc,
134 struct ieee80211_conf *conf)
Sujithff37e332008-11-24 12:07:55 +0530135{
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800136 switch (conf->channel->band) {
137 case IEEE80211_BAND_2GHZ:
138 if (conf_is_ht20(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100139 sc->cur_rate_mode = ATH9K_MODE_11NG_HT20;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800140 else if (conf_is_ht40_minus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100141 sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800142 else if (conf_is_ht40_plus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100143 sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS;
Luis R. Rodriguez96742252008-12-23 15:58:38 -0800144 else
Felix Fietkau545750d2009-11-23 22:21:01 +0100145 sc->cur_rate_mode = ATH9K_MODE_11G;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800146 break;
147 case IEEE80211_BAND_5GHZ:
148 if (conf_is_ht20(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100149 sc->cur_rate_mode = ATH9K_MODE_11NA_HT20;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800150 else if (conf_is_ht40_minus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100151 sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800152 else if (conf_is_ht40_plus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +0100153 sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800154 else
Felix Fietkau545750d2009-11-23 22:21:01 +0100155 sc->cur_rate_mode = ATH9K_MODE_11A;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800156 break;
157 default:
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -0800158 BUG_ON(1);
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800159 break;
160 }
Sujithff37e332008-11-24 12:07:55 +0530161}
162
163static void ath_update_txpow(struct ath_softc *sc)
164{
Sujithcbe61d82009-02-09 13:27:12 +0530165 struct ath_hw *ah = sc->sc_ah;
Sujithff37e332008-11-24 12:07:55 +0530166 u32 txpow;
167
Sujith17d79042009-02-09 13:27:03 +0530168 if (sc->curtxpow != sc->config.txpowlimit) {
169 ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit);
Sujithff37e332008-11-24 12:07:55 +0530170 /* read back in case value is clamped */
171 ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow);
Sujith17d79042009-02-09 13:27:03 +0530172 sc->curtxpow = txpow;
Sujithff37e332008-11-24 12:07:55 +0530173 }
174}
175
176static u8 parse_mpdudensity(u8 mpdudensity)
177{
178 /*
179 * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
180 * 0 for no restriction
181 * 1 for 1/4 us
182 * 2 for 1/2 us
183 * 3 for 1 us
184 * 4 for 2 us
185 * 5 for 4 us
186 * 6 for 8 us
187 * 7 for 16 us
188 */
189 switch (mpdudensity) {
190 case 0:
191 return 0;
192 case 1:
193 case 2:
194 case 3:
195 /* Our lower layer calculations limit our precision to
196 1 microsecond */
197 return 1;
198 case 4:
199 return 2;
200 case 5:
201 return 4;
202 case 6:
203 return 8;
204 case 7:
205 return 16;
206 default:
207 return 0;
208 }
209}
210
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +0530211static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
212 struct ieee80211_hw *hw)
213{
214 struct ieee80211_channel *curchan = hw->conf.channel;
215 struct ath9k_channel *channel;
216 u8 chan_idx;
217
218 chan_idx = curchan->hw_value;
219 channel = &sc->sc_ah->channels[chan_idx];
220 ath9k_update_ichannel(sc, hw, channel);
221 return channel;
222}
223
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700224static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -0700225{
226 unsigned long flags;
227 bool ret;
228
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700229 spin_lock_irqsave(&sc->sc_pm_lock, flags);
230 ret = ath9k_hw_setpower(sc->sc_ah, mode);
231 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -0700232
233 return ret;
234}
235
Luis R. Rodrigueza91d75a2009-09-09 20:29:18 -0700236void ath9k_ps_wakeup(struct ath_softc *sc)
237{
238 unsigned long flags;
239
240 spin_lock_irqsave(&sc->sc_pm_lock, flags);
241 if (++sc->ps_usecount != 1)
242 goto unlock;
243
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700244 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
Luis R. Rodrigueza91d75a2009-09-09 20:29:18 -0700245
246 unlock:
247 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
248}
249
250void ath9k_ps_restore(struct ath_softc *sc)
251{
252 unsigned long flags;
253
254 spin_lock_irqsave(&sc->sc_pm_lock, flags);
255 if (--sc->ps_usecount != 0)
256 goto unlock;
257
258 if (sc->ps_enabled &&
259 !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
260 SC_OP_WAIT_FOR_CAB |
261 SC_OP_WAIT_FOR_PSPOLL_DATA |
262 SC_OP_WAIT_FOR_TX_ACK)))
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700263 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
Luis R. Rodrigueza91d75a2009-09-09 20:29:18 -0700264
265 unlock:
266 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
267}
268
Sujithff37e332008-11-24 12:07:55 +0530269/*
270 * Set/change channels. If the channel is really being changed, it's done
271 * by reseting the chip. To accomplish this we must first cleanup any pending
272 * DMA, then restart stuff.
273*/
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200274int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
275 struct ath9k_channel *hchan)
Sujithff37e332008-11-24 12:07:55 +0530276{
Sujithcbe61d82009-02-09 13:27:12 +0530277 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700278 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700279 struct ieee80211_conf *conf = &common->hw->conf;
Sujithff37e332008-11-24 12:07:55 +0530280 bool fastcc = true, stopped;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800281 struct ieee80211_channel *channel = hw->conf.channel;
282 int r;
Sujithff37e332008-11-24 12:07:55 +0530283
284 if (sc->sc_flags & SC_OP_INVALID)
285 return -EIO;
286
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530287 ath9k_ps_wakeup(sc);
288
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800289 /*
290 * This is only performed if the channel settings have
291 * actually changed.
292 *
293 * To switch channels clear any pending DMA operations;
294 * wait long enough for the RX fifo to drain, reset the
295 * hardware at the new frequency, and then re-enable
296 * the relevant bits of the h/w.
297 */
298 ath9k_hw_set_interrupts(ah, 0);
Sujith043a0402009-01-16 21:38:47 +0530299 ath_drain_all_txq(sc, false);
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800300 stopped = ath_stoprecv(sc);
Sujithff37e332008-11-24 12:07:55 +0530301
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800302 /* XXX: do not flush receive queue here. We don't want
303 * to flush data frames already in queue because of
304 * changing channel. */
Sujithff37e332008-11-24 12:07:55 +0530305
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800306 if (!stopped || (sc->sc_flags & SC_OP_FULL_RESET))
307 fastcc = false;
Sujithff37e332008-11-24 12:07:55 +0530308
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700309 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700310 "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n",
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700311 sc->sc_ah->curchan->channel,
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700312 channel->center_freq, conf_is_ht40(conf));
Sujith99405f92008-11-24 12:08:35 +0530313
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800314 spin_lock_bh(&sc->sc_resetlock);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800315
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800316 r = ath9k_hw_reset(ah, hchan, fastcc);
317 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700318 ath_print(common, ATH_DBG_FATAL,
319 "Unable to reset channel (%u Mhz) "
320 "reset status %d\n",
321 channel->center_freq, r);
Sujithff37e332008-11-24 12:07:55 +0530322 spin_unlock_bh(&sc->sc_resetlock);
Gabor Juhos39892792009-06-15 17:49:09 +0200323 goto ps_restore;
Sujithff37e332008-11-24 12:07:55 +0530324 }
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800325 spin_unlock_bh(&sc->sc_resetlock);
326
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800327 sc->sc_flags &= ~SC_OP_FULL_RESET;
328
329 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700330 ath_print(common, ATH_DBG_FATAL,
331 "Unable to restart recv logic\n");
Gabor Juhos39892792009-06-15 17:49:09 +0200332 r = -EIO;
333 goto ps_restore;
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800334 }
335
336 ath_cache_conf_rate(sc, &hw->conf);
337 ath_update_txpow(sc);
Sujith17d79042009-02-09 13:27:03 +0530338 ath9k_hw_set_interrupts(ah, sc->imask);
Gabor Juhos39892792009-06-15 17:49:09 +0200339
340 ps_restore:
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530341 ath9k_ps_restore(sc);
Gabor Juhos39892792009-06-15 17:49:09 +0200342 return r;
Sujithff37e332008-11-24 12:07:55 +0530343}
344
345/*
346 * This routine performs the periodic noise floor calibration function
347 * that is used to adjust and optimize the chip performance. This
348 * takes environmental changes (location, temperature) into account.
349 * When the task is complete, it reschedules itself depending on the
350 * appropriate interval that was calculated.
351 */
352static void ath_ani_calibrate(unsigned long data)
353{
Sujith20977d32009-02-20 15:13:28 +0530354 struct ath_softc *sc = (struct ath_softc *)data;
355 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700356 struct ath_common *common = ath9k_hw_common(ah);
Sujithff37e332008-11-24 12:07:55 +0530357 bool longcal = false;
358 bool shortcal = false;
359 bool aniflag = false;
360 unsigned int timestamp = jiffies_to_msecs(jiffies);
Sujith20977d32009-02-20 15:13:28 +0530361 u32 cal_interval, short_cal_interval;
Sujithff37e332008-11-24 12:07:55 +0530362
Sujith20977d32009-02-20 15:13:28 +0530363 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
364 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithff37e332008-11-24 12:07:55 +0530365
366 /*
367 * don't calibrate when we're scanning.
368 * we are most likely not on our home channel.
369 */
Senthil Balasubramaniane5f09212009-06-24 18:56:41 +0530370 spin_lock(&sc->ani_lock);
Sujith0c98de62009-03-03 10:16:45 +0530371 if (sc->sc_flags & SC_OP_SCANNING)
Sujith20977d32009-02-20 15:13:28 +0530372 goto set_timer;
Sujithff37e332008-11-24 12:07:55 +0530373
Jouni Malinen1ffc1c62009-05-19 17:01:39 +0300374 /* Only calibrate if awake */
375 if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
376 goto set_timer;
377
378 ath9k_ps_wakeup(sc);
379
Sujithff37e332008-11-24 12:07:55 +0530380 /* Long calibration runs independently of short calibration. */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800381 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
Sujithff37e332008-11-24 12:07:55 +0530382 longcal = true;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700383 ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800384 common->ani.longcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530385 }
386
Sujith17d79042009-02-09 13:27:03 +0530387 /* Short calibration applies only while caldone is false */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800388 if (!common->ani.caldone) {
389 if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
Sujithff37e332008-11-24 12:07:55 +0530390 shortcal = true;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700391 ath_print(common, ATH_DBG_ANI,
392 "shortcal @%lu\n", jiffies);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800393 common->ani.shortcal_timer = timestamp;
394 common->ani.resetcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530395 }
396 } else {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800397 if ((timestamp - common->ani.resetcal_timer) >=
Sujithff37e332008-11-24 12:07:55 +0530398 ATH_RESTART_CALINTERVAL) {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800399 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
400 if (common->ani.caldone)
401 common->ani.resetcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530402 }
403 }
404
405 /* Verify whether we must check ANI */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800406 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
Sujithff37e332008-11-24 12:07:55 +0530407 aniflag = true;
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800408 common->ani.checkani_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530409 }
410
411 /* Skip all processing if there's nothing to do. */
412 if (longcal || shortcal || aniflag) {
413 /* Call ANI routine if necessary */
414 if (aniflag)
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530415 ath9k_hw_ani_monitor(ah, ah->curchan);
Sujithff37e332008-11-24 12:07:55 +0530416
417 /* Perform calibration if necessary */
418 if (longcal || shortcal) {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800419 common->ani.caldone =
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700420 ath9k_hw_calibrate(ah,
421 ah->curchan,
422 common->rx_chainmask,
423 longcal);
Sujithff37e332008-11-24 12:07:55 +0530424
Sujith379f0442009-04-13 21:56:48 +0530425 if (longcal)
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800426 common->ani.noise_floor = ath9k_hw_getchan_noise(ah,
Sujith379f0442009-04-13 21:56:48 +0530427 ah->curchan);
Sujithff37e332008-11-24 12:07:55 +0530428
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700429 ath_print(common, ATH_DBG_ANI,
430 " calibrate chan %u/%x nf: %d\n",
431 ah->curchan->channel,
432 ah->curchan->channelFlags,
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800433 common->ani.noise_floor);
Sujithff37e332008-11-24 12:07:55 +0530434 }
435 }
436
Jouni Malinen1ffc1c62009-05-19 17:01:39 +0300437 ath9k_ps_restore(sc);
438
Sujith20977d32009-02-20 15:13:28 +0530439set_timer:
Senthil Balasubramaniane5f09212009-06-24 18:56:41 +0530440 spin_unlock(&sc->ani_lock);
Sujithff37e332008-11-24 12:07:55 +0530441 /*
442 * Set timer interval based on previous results.
443 * The interval must be the shortest necessary to satisfy ANI,
444 * short calibration and long calibration.
445 */
Sujithaac92072008-12-02 18:37:54 +0530446 cal_interval = ATH_LONG_CALINTERVAL;
Sujith2660b812009-02-09 13:27:26 +0530447 if (sc->sc_ah->config.enable_ani)
Sujithaac92072008-12-02 18:37:54 +0530448 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800449 if (!common->ani.caldone)
Sujith20977d32009-02-20 15:13:28 +0530450 cal_interval = min(cal_interval, (u32)short_cal_interval);
Sujithff37e332008-11-24 12:07:55 +0530451
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800452 mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
Sujithff37e332008-11-24 12:07:55 +0530453}
454
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800455static void ath_start_ani(struct ath_common *common)
Sujith415f7382009-04-13 21:56:46 +0530456{
457 unsigned long timestamp = jiffies_to_msecs(jiffies);
458
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800459 common->ani.longcal_timer = timestamp;
460 common->ani.shortcal_timer = timestamp;
461 common->ani.checkani_timer = timestamp;
Sujith415f7382009-04-13 21:56:46 +0530462
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800463 mod_timer(&common->ani.timer,
Sujith415f7382009-04-13 21:56:46 +0530464 jiffies + msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
465}
466
Sujithff37e332008-11-24 12:07:55 +0530467/*
468 * Update tx/rx chainmask. For legacy association,
469 * hard code chainmask to 1x1, for 11n association, use
Vasanthakumar Thiagarajanc97c92d2009-01-02 15:35:46 +0530470 * the chainmask configuration, for bt coexistence, use
471 * the chainmask configuration even in legacy mode.
Sujithff37e332008-11-24 12:07:55 +0530472 */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200473void ath_update_chainmask(struct ath_softc *sc, int is_ht)
Sujithff37e332008-11-24 12:07:55 +0530474{
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700475 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700476 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700477
Sujith3d832612009-08-21 12:00:28 +0530478 if ((sc->sc_flags & SC_OP_SCANNING) || is_ht ||
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -0700479 (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE)) {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700480 common->tx_chainmask = ah->caps.tx_chainmask;
481 common->rx_chainmask = ah->caps.rx_chainmask;
Sujithff37e332008-11-24 12:07:55 +0530482 } else {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700483 common->tx_chainmask = 1;
484 common->rx_chainmask = 1;
Sujithff37e332008-11-24 12:07:55 +0530485 }
486
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700487 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700488 "tx chmask: %d, rx chmask: %d\n",
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700489 common->tx_chainmask,
490 common->rx_chainmask);
Sujithff37e332008-11-24 12:07:55 +0530491}
492
493static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
494{
495 struct ath_node *an;
496
497 an = (struct ath_node *)sta->drv_priv;
498
Sujith87792ef2009-03-30 15:28:48 +0530499 if (sc->sc_flags & SC_OP_TXAGGR) {
Sujithff37e332008-11-24 12:07:55 +0530500 ath_tx_node_init(sc, an);
Sujith9e98ac62009-07-23 15:32:34 +0530501 an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
Sujith87792ef2009-03-30 15:28:48 +0530502 sta->ht_cap.ampdu_factor);
503 an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
Senthil Balasubramaniana59b5a52009-07-14 20:17:07 -0400504 an->last_rssi = ATH_RSSI_DUMMY_MARKER;
Sujith87792ef2009-03-30 15:28:48 +0530505 }
Sujithff37e332008-11-24 12:07:55 +0530506}
507
508static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
509{
510 struct ath_node *an = (struct ath_node *)sta->drv_priv;
511
512 if (sc->sc_flags & SC_OP_TXAGGR)
513 ath_tx_node_cleanup(sc, an);
514}
515
516static void ath9k_tasklet(unsigned long data)
517{
518 struct ath_softc *sc = (struct ath_softc *)data;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700519 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700520 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700521
Sujith17d79042009-02-09 13:27:03 +0530522 u32 status = sc->intrstatus;
Sujithff37e332008-11-24 12:07:55 +0530523
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400524 ath9k_ps_wakeup(sc);
525
Sujithff37e332008-11-24 12:07:55 +0530526 if (status & ATH9K_INT_FATAL) {
Sujithff37e332008-11-24 12:07:55 +0530527 ath_reset(sc, false);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400528 ath9k_ps_restore(sc);
Sujithff37e332008-11-24 12:07:55 +0530529 return;
Sujithff37e332008-11-24 12:07:55 +0530530 }
531
Sujith063d8be2009-03-30 15:28:49 +0530532 if (status & (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) {
533 spin_lock_bh(&sc->rx.rxflushlock);
534 ath_rx_tasklet(sc, 0);
535 spin_unlock_bh(&sc->rx.rxflushlock);
536 }
537
538 if (status & ATH9K_INT_TX)
539 ath_tx_tasklet(sc);
540
Gabor Juhos96148322009-07-24 17:27:21 +0200541 if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
Jouni Malinen54ce8462009-05-19 17:01:40 +0300542 /*
543 * TSF sync does not look correct; remain awake to sync with
544 * the next Beacon.
545 */
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700546 ath_print(common, ATH_DBG_PS,
547 "TSFOOR - Sync with next Beacon\n");
Jouni Malinenccdfeab2009-05-20 21:59:08 +0300548 sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC;
Jouni Malinen54ce8462009-05-19 17:01:40 +0300549 }
550
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -0700551 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Vasanthakumar Thiagarajanebb8e1d2009-09-01 17:46:32 +0530552 if (status & ATH9K_INT_GENTIMER)
553 ath_gen_timer_isr(sc->sc_ah);
554
Sujithff37e332008-11-24 12:07:55 +0530555 /* re-enable hardware interrupt */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700556 ath9k_hw_set_interrupts(ah, sc->imask);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400557 ath9k_ps_restore(sc);
Sujithff37e332008-11-24 12:07:55 +0530558}
559
Gabor Juhos6baff7f2009-01-14 20:17:06 +0100560irqreturn_t ath_isr(int irq, void *dev)
Sujithff37e332008-11-24 12:07:55 +0530561{
Sujith063d8be2009-03-30 15:28:49 +0530562#define SCHED_INTR ( \
563 ATH9K_INT_FATAL | \
564 ATH9K_INT_RXORN | \
565 ATH9K_INT_RXEOL | \
566 ATH9K_INT_RX | \
567 ATH9K_INT_TX | \
568 ATH9K_INT_BMISS | \
569 ATH9K_INT_CST | \
Vasanthakumar Thiagarajanebb8e1d2009-09-01 17:46:32 +0530570 ATH9K_INT_TSFOOR | \
571 ATH9K_INT_GENTIMER)
Sujith063d8be2009-03-30 15:28:49 +0530572
Sujithff37e332008-11-24 12:07:55 +0530573 struct ath_softc *sc = dev;
Sujithcbe61d82009-02-09 13:27:12 +0530574 struct ath_hw *ah = sc->sc_ah;
Sujithff37e332008-11-24 12:07:55 +0530575 enum ath9k_int status;
576 bool sched = false;
577
Sujith063d8be2009-03-30 15:28:49 +0530578 /*
579 * The hardware is not ready/present, don't
580 * touch anything. Note this can happen early
581 * on if the IRQ is shared.
582 */
583 if (sc->sc_flags & SC_OP_INVALID)
584 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530585
Sujithff37e332008-11-24 12:07:55 +0530586
Sujith063d8be2009-03-30 15:28:49 +0530587 /* shared irq, not for us */
Sujithff37e332008-11-24 12:07:55 +0530588
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400589 if (!ath9k_hw_intrpend(ah))
Sujith063d8be2009-03-30 15:28:49 +0530590 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530591
Sujith063d8be2009-03-30 15:28:49 +0530592 /*
593 * Figure out the reason(s) for the interrupt. Note
594 * that the hal returns a pseudo-ISR that may include
595 * bits we haven't explicitly enabled so we mask the
596 * value to insure we only process bits we requested.
597 */
598 ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
599 status &= sc->imask; /* discard unasked-for bits */
600
601 /*
602 * If there are no status bits set, then this interrupt was not
603 * for me (should have been caught above).
604 */
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400605 if (!status)
Sujith063d8be2009-03-30 15:28:49 +0530606 return IRQ_NONE;
Sujith063d8be2009-03-30 15:28:49 +0530607
608 /* Cache the status */
609 sc->intrstatus = status;
610
611 if (status & SCHED_INTR)
612 sched = true;
613
614 /*
615 * If a FATAL or RXORN interrupt is received, we have to reset the
616 * chip immediately.
617 */
618 if (status & (ATH9K_INT_FATAL | ATH9K_INT_RXORN))
619 goto chip_reset;
620
621 if (status & ATH9K_INT_SWBA)
622 tasklet_schedule(&sc->bcon_tasklet);
623
624 if (status & ATH9K_INT_TXURN)
625 ath9k_hw_updatetxtriglevel(ah, true);
626
627 if (status & ATH9K_INT_MIB) {
628 /*
629 * Disable interrupts until we service the MIB
630 * interrupt; otherwise it will continue to
631 * fire.
632 */
633 ath9k_hw_set_interrupts(ah, 0);
634 /*
635 * Let the hal handle the event. We assume
636 * it will clear whatever condition caused
637 * the interrupt.
638 */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530639 ath9k_hw_procmibevent(ah);
Sujith063d8be2009-03-30 15:28:49 +0530640 ath9k_hw_set_interrupts(ah, sc->imask);
641 }
642
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400643 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
644 if (status & ATH9K_INT_TIM_TIMER) {
Sujith063d8be2009-03-30 15:28:49 +0530645 /* Clear RxAbort bit so that we can
646 * receive frames */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700647 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400648 ath9k_hw_setrxabort(sc->sc_ah, 0);
Sujith063d8be2009-03-30 15:28:49 +0530649 sc->sc_flags |= SC_OP_WAIT_FOR_BEACON;
650 }
Sujith063d8be2009-03-30 15:28:49 +0530651
652chip_reset:
653
Sujith817e11d2008-12-07 21:42:44 +0530654 ath_debug_stat_interrupt(sc, status);
655
Sujithff37e332008-11-24 12:07:55 +0530656 if (sched) {
657 /* turn off every interrupt except SWBA */
Sujith17d79042009-02-09 13:27:03 +0530658 ath9k_hw_set_interrupts(ah, (sc->imask & ATH9K_INT_SWBA));
Sujithff37e332008-11-24 12:07:55 +0530659 tasklet_schedule(&sc->intr_tq);
660 }
661
662 return IRQ_HANDLED;
Sujith063d8be2009-03-30 15:28:49 +0530663
664#undef SCHED_INTR
Sujithff37e332008-11-24 12:07:55 +0530665}
666
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700667static u32 ath_get_extchanmode(struct ath_softc *sc,
Sujith99405f92008-11-24 12:08:35 +0530668 struct ieee80211_channel *chan,
Sujith094d05d2008-12-12 11:57:43 +0530669 enum nl80211_channel_type channel_type)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700670{
671 u32 chanmode = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700672
673 switch (chan->band) {
674 case IEEE80211_BAND_2GHZ:
Sujith094d05d2008-12-12 11:57:43 +0530675 switch(channel_type) {
676 case NL80211_CHAN_NO_HT:
677 case NL80211_CHAN_HT20:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700678 chanmode = CHANNEL_G_HT20;
Sujith094d05d2008-12-12 11:57:43 +0530679 break;
680 case NL80211_CHAN_HT40PLUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700681 chanmode = CHANNEL_G_HT40PLUS;
Sujith094d05d2008-12-12 11:57:43 +0530682 break;
683 case NL80211_CHAN_HT40MINUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700684 chanmode = CHANNEL_G_HT40MINUS;
Sujith094d05d2008-12-12 11:57:43 +0530685 break;
686 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700687 break;
688 case IEEE80211_BAND_5GHZ:
Sujith094d05d2008-12-12 11:57:43 +0530689 switch(channel_type) {
690 case NL80211_CHAN_NO_HT:
691 case NL80211_CHAN_HT20:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700692 chanmode = CHANNEL_A_HT20;
Sujith094d05d2008-12-12 11:57:43 +0530693 break;
694 case NL80211_CHAN_HT40PLUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700695 chanmode = CHANNEL_A_HT40PLUS;
Sujith094d05d2008-12-12 11:57:43 +0530696 break;
697 case NL80211_CHAN_HT40MINUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700698 chanmode = CHANNEL_A_HT40MINUS;
Sujith094d05d2008-12-12 11:57:43 +0530699 break;
700 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700701 break;
702 default:
703 break;
704 }
705
706 return chanmode;
707}
708
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800709static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200710 struct ath9k_keyval *hk, const u8 *addr,
711 bool authenticator)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700712{
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800713 struct ath_hw *ah = common->ah;
Jouni Malinen6ace2892008-12-17 13:32:17 +0200714 const u8 *key_rxmic;
715 const u8 *key_txmic;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700716
Jouni Malinen6ace2892008-12-17 13:32:17 +0200717 key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
718 key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700719
720 if (addr == NULL) {
Jouni Malinend216aaa2009-03-03 13:11:53 +0200721 /*
722 * Group key installation - only two key cache entries are used
723 * regardless of splitmic capability since group key is only
724 * used either for TX or RX.
725 */
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200726 if (authenticator) {
727 memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
728 memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
729 } else {
730 memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
731 memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
732 }
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800733 return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700734 }
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800735 if (!common->splitmic) {
Jouni Malinend216aaa2009-03-03 13:11:53 +0200736 /* TX and RX keys share the same key cache entry. */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700737 memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
738 memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800739 return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700740 }
Jouni Malinend216aaa2009-03-03 13:11:53 +0200741
742 /* Separate key cache entries for TX and RX */
743
744 /* TX key goes at first index, RX key at +32. */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700745 memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800746 if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) {
Jouni Malinend216aaa2009-03-03 13:11:53 +0200747 /* TX MIC entry failed. No need to proceed further */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800748 ath_print(common, ATH_DBG_FATAL,
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700749 "Setting TX MIC Key Failed\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700750 return 0;
751 }
752
753 memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
754 /* XXX delete tx key on failure? */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800755 return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200756}
757
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800758static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
Jouni Malinen6ace2892008-12-17 13:32:17 +0200759{
760 int i;
761
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800762 for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
763 if (test_bit(i, common->keymap) ||
764 test_bit(i + 64, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200765 continue; /* At least one part of TKIP key allocated */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800766 if (common->splitmic &&
767 (test_bit(i + 32, common->keymap) ||
768 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200769 continue; /* At least one part of TKIP key allocated */
770
771 /* Found a free slot for a TKIP key */
772 return i;
773 }
774 return -1;
775}
776
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800777static int ath_reserve_key_cache_slot(struct ath_common *common)
Jouni Malinen6ace2892008-12-17 13:32:17 +0200778{
779 int i;
780
781 /* First, try to find slots that would not be available for TKIP. */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800782 if (common->splitmic) {
783 for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
784 if (!test_bit(i, common->keymap) &&
785 (test_bit(i + 32, common->keymap) ||
786 test_bit(i + 64, common->keymap) ||
787 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200788 return i;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800789 if (!test_bit(i + 32, common->keymap) &&
790 (test_bit(i, common->keymap) ||
791 test_bit(i + 64, common->keymap) ||
792 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200793 return i + 32;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800794 if (!test_bit(i + 64, common->keymap) &&
795 (test_bit(i , common->keymap) ||
796 test_bit(i + 32, common->keymap) ||
797 test_bit(i + 64 + 32, common->keymap)))
Jouni Malinenea612132008-12-18 14:31:10 +0200798 return i + 64;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800799 if (!test_bit(i + 64 + 32, common->keymap) &&
800 (test_bit(i, common->keymap) ||
801 test_bit(i + 32, common->keymap) ||
802 test_bit(i + 64, common->keymap)))
Jouni Malinenea612132008-12-18 14:31:10 +0200803 return i + 64 + 32;
Jouni Malinen6ace2892008-12-17 13:32:17 +0200804 }
805 } else {
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800806 for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
807 if (!test_bit(i, common->keymap) &&
808 test_bit(i + 64, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200809 return i;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800810 if (test_bit(i, common->keymap) &&
811 !test_bit(i + 64, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200812 return i + 64;
813 }
814 }
815
816 /* No partially used TKIP slots, pick any available slot */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800817 for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
Jouni Malinenbe2864c2008-12-18 14:33:00 +0200818 /* Do not allow slots that could be needed for TKIP group keys
819 * to be used. This limitation could be removed if we know that
820 * TKIP will not be used. */
821 if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
822 continue;
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800823 if (common->splitmic) {
Jouni Malinenbe2864c2008-12-18 14:33:00 +0200824 if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
825 continue;
826 if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
827 continue;
828 }
829
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800830 if (!test_bit(i, common->keymap))
Jouni Malinen6ace2892008-12-17 13:32:17 +0200831 return i; /* Found a free slot for a key */
832 }
833
834 /* No free slot found */
835 return -1;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700836}
837
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800838static int ath_key_config(struct ath_common *common,
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200839 struct ieee80211_vif *vif,
Johannes Bergdc822b52008-12-29 12:55:09 +0100840 struct ieee80211_sta *sta,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700841 struct ieee80211_key_conf *key)
842{
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800843 struct ath_hw *ah = common->ah;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700844 struct ath9k_keyval hk;
845 const u8 *mac = NULL;
846 int ret = 0;
Jouni Malinen6ace2892008-12-17 13:32:17 +0200847 int idx;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700848
849 memset(&hk, 0, sizeof(hk));
850
851 switch (key->alg) {
852 case ALG_WEP:
853 hk.kv_type = ATH9K_CIPHER_WEP;
854 break;
855 case ALG_TKIP:
856 hk.kv_type = ATH9K_CIPHER_TKIP;
857 break;
858 case ALG_CCMP:
859 hk.kv_type = ATH9K_CIPHER_AES_CCM;
860 break;
861 default:
Jouni Malinenca470b22009-01-08 13:32:12 +0200862 return -EOPNOTSUPP;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700863 }
864
Jouni Malinen6ace2892008-12-17 13:32:17 +0200865 hk.kv_len = key->keylen;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700866 memcpy(hk.kv_val, key->key, key->keylen);
867
Jouni Malinen6ace2892008-12-17 13:32:17 +0200868 if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
869 /* For now, use the default keys for broadcast keys. This may
870 * need to change with virtual interfaces. */
871 idx = key->keyidx;
872 } else if (key->keyidx) {
Johannes Bergdc822b52008-12-29 12:55:09 +0100873 if (WARN_ON(!sta))
874 return -EOPNOTSUPP;
875 mac = sta->addr;
876
Jouni Malinen6ace2892008-12-17 13:32:17 +0200877 if (vif->type != NL80211_IFTYPE_AP) {
878 /* Only keyidx 0 should be used with unicast key, but
879 * allow this for client mode for now. */
880 idx = key->keyidx;
881 } else
882 return -EIO;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700883 } else {
Johannes Bergdc822b52008-12-29 12:55:09 +0100884 if (WARN_ON(!sta))
885 return -EOPNOTSUPP;
886 mac = sta->addr;
887
Jouni Malinen6ace2892008-12-17 13:32:17 +0200888 if (key->alg == ALG_TKIP)
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800889 idx = ath_reserve_key_cache_slot_tkip(common);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200890 else
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800891 idx = ath_reserve_key_cache_slot(common);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200892 if (idx < 0)
Jouni Malinenca470b22009-01-08 13:32:12 +0200893 return -ENOSPC; /* no free key cache entries */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700894 }
895
896 if (key->alg == ALG_TKIP)
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800897 ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
Jouni Malinen3f53dd62009-02-26 11:18:46 +0200898 vif->type == NL80211_IFTYPE_AP);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700899 else
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800900 ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700901
902 if (!ret)
903 return -EIO;
904
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800905 set_bit(idx, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200906 if (key->alg == ALG_TKIP) {
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800907 set_bit(idx + 64, common->keymap);
908 if (common->splitmic) {
909 set_bit(idx + 32, common->keymap);
910 set_bit(idx + 64 + 32, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200911 }
912 }
913
914 return idx;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700915}
916
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800917static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700918{
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800919 struct ath_hw *ah = common->ah;
920
921 ath9k_hw_keyreset(ah, key->hw_key_idx);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200922 if (key->hw_key_idx < IEEE80211_WEP_NKID)
923 return;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700924
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800925 clear_bit(key->hw_key_idx, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200926 if (key->alg != ALG_TKIP)
927 return;
928
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -0800929 clear_bit(key->hw_key_idx + 64, common->keymap);
930 if (common->splitmic) {
931 clear_bit(key->hw_key_idx + 32, common->keymap);
932 clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
Jouni Malinen6ace2892008-12-17 13:32:17 +0200933 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700934}
935
Sujitheb2599c2009-01-23 11:20:44 +0530936static void setup_ht_cap(struct ath_softc *sc,
937 struct ieee80211_sta_ht_cap *ht_info)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700938{
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700939 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530940 u8 tx_streams, rx_streams;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700941
Johannes Bergd9fe60d2008-10-09 12:13:49 +0200942 ht_info->ht_supported = true;
943 ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
944 IEEE80211_HT_CAP_SM_PS |
945 IEEE80211_HT_CAP_SGI_40 |
946 IEEE80211_HT_CAP_DSSSCCK40;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700947
Sujith9e98ac62009-07-23 15:32:34 +0530948 ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
949 ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
Sujitheb2599c2009-01-23 11:20:44 +0530950
Johannes Bergd9fe60d2008-10-09 12:13:49 +0200951 /* set up supported mcs set */
952 memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700953 tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ?
954 1 : 2;
955 rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ?
956 1 : 2;
Sujitheb2599c2009-01-23 11:20:44 +0530957
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530958 if (tx_streams != rx_streams) {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700959 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700960 "TX streams %d, RX streams: %d\n",
961 tx_streams, rx_streams);
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530962 ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF;
963 ht_info->mcs.tx_params |= ((tx_streams - 1) <<
964 IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT);
Sujitheb2599c2009-01-23 11:20:44 +0530965 }
966
Senthil Balasubramanian140add22009-06-24 18:56:42 +0530967 ht_info->mcs.rx_mask[0] = 0xff;
968 if (rx_streams >= 2)
969 ht_info->mcs.rx_mask[1] = 0xff;
970
971 ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700972}
973
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530974static void ath9k_bss_assoc_info(struct ath_softc *sc,
Sujith5640b082008-10-29 10:16:06 +0530975 struct ieee80211_vif *vif,
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530976 struct ieee80211_bss_conf *bss_conf)
977{
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -0700978 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700979 struct ath_common *common = ath9k_hw_common(ah);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530980
981 if (bss_conf->assoc) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700982 ath_print(common, ATH_DBG_CONFIG,
983 "Bss Info ASSOC %d, bssid: %pM\n",
984 bss_conf->aid, common->curbssid);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530985
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530986 /* New association, store aid */
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700987 common->curaid = bss_conf->aid;
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -0700988 ath9k_hw_write_associd(ah);
Jouni Malinenccdfeab2009-05-20 21:59:08 +0300989
Senthil Balasubramanian2664f202009-06-24 18:56:39 +0530990 /*
991 * Request a re-configuration of Beacon related timers
992 * on the receipt of the first Beacon frame (i.e.,
993 * after time sync with the AP).
994 */
995 sc->sc_flags |= SC_OP_BEACON_SYNC;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530996
997 /* Configure the beacon */
Jouni Malinen2c3db3d2009-03-03 19:23:26 +0200998 ath_beacon_config(sc, vif);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530999
1000 /* Reset rssi stats */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +05301001 sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301002
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08001003 ath_start_ani(common);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301004 } else {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001005 ath_print(common, ATH_DBG_CONFIG, "Bss Info DISASSOC\n");
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001006 common->curaid = 0;
Senthil Balasubramanianf38faa32009-06-24 18:56:40 +05301007 /* Stop ANI */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08001008 del_timer_sync(&common->ani.timer);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301009 }
1010}
1011
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301012/********************************/
1013/* LED functions */
1014/********************************/
1015
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301016static void ath_led_blink_work(struct work_struct *work)
1017{
1018 struct ath_softc *sc = container_of(work, struct ath_softc,
1019 ath_led_blink_work.work);
1020
1021 if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED))
1022 return;
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301023
1024 if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
1025 (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301026 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301027 else
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301028 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301029 (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0);
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301030
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04001031 ieee80211_queue_delayed_work(sc->hw,
1032 &sc->ath_led_blink_work,
1033 (sc->sc_flags & SC_OP_LED_ON) ?
1034 msecs_to_jiffies(sc->led_off_duration) :
1035 msecs_to_jiffies(sc->led_on_duration));
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301036
Vasanthakumar Thiagarajan85067c02009-03-14 19:59:41 +05301037 sc->led_on_duration = sc->led_on_cnt ?
1038 max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) :
1039 ATH_LED_ON_DURATION_IDLE;
1040 sc->led_off_duration = sc->led_off_cnt ?
1041 max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) :
1042 ATH_LED_OFF_DURATION_IDLE;
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301043 sc->led_on_cnt = sc->led_off_cnt = 0;
1044 if (sc->sc_flags & SC_OP_LED_ON)
1045 sc->sc_flags &= ~SC_OP_LED_ON;
1046 else
1047 sc->sc_flags |= SC_OP_LED_ON;
1048}
1049
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301050static void ath_led_brightness(struct led_classdev *led_cdev,
1051 enum led_brightness brightness)
1052{
1053 struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
1054 struct ath_softc *sc = led->sc;
1055
1056 switch (brightness) {
1057 case LED_OFF:
1058 if (led->led_type == ATH_LED_ASSOC ||
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301059 led->led_type == ATH_LED_RADIO) {
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301060 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin,
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301061 (led->led_type == ATH_LED_RADIO));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301062 sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301063 if (led->led_type == ATH_LED_RADIO)
1064 sc->sc_flags &= ~SC_OP_LED_ON;
1065 } else {
1066 sc->led_off_cnt++;
1067 }
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301068 break;
1069 case LED_FULL:
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301070 if (led->led_type == ATH_LED_ASSOC) {
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301071 sc->sc_flags |= SC_OP_LED_ASSOCIATED;
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04001072 ieee80211_queue_delayed_work(sc->hw,
1073 &sc->ath_led_blink_work, 0);
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301074 } else if (led->led_type == ATH_LED_RADIO) {
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301075 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0);
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301076 sc->sc_flags |= SC_OP_LED_ON;
1077 } else {
1078 sc->led_on_cnt++;
1079 }
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301080 break;
1081 default:
1082 break;
1083 }
1084}
1085
1086static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
1087 char *trigger)
1088{
1089 int ret;
1090
1091 led->sc = sc;
1092 led->led_cdev.name = led->name;
1093 led->led_cdev.default_trigger = trigger;
1094 led->led_cdev.brightness_set = ath_led_brightness;
1095
1096 ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev);
1097 if (ret)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001098 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
1099 "Failed to register led:%s", led->name);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301100 else
1101 led->registered = 1;
1102 return ret;
1103}
1104
1105static void ath_unregister_led(struct ath_led *led)
1106{
1107 if (led->registered) {
1108 led_classdev_unregister(&led->led_cdev);
1109 led->registered = 0;
1110 }
1111}
1112
1113static void ath_deinit_leds(struct ath_softc *sc)
1114{
1115 ath_unregister_led(&sc->assoc_led);
1116 sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
1117 ath_unregister_led(&sc->tx_led);
1118 ath_unregister_led(&sc->rx_led);
1119 ath_unregister_led(&sc->radio_led);
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301120 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301121}
1122
1123static void ath_init_leds(struct ath_softc *sc)
1124{
1125 char *trigger;
1126 int ret;
1127
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301128 if (AR_SREV_9287(sc->sc_ah))
1129 sc->sc_ah->led_pin = ATH_LED_PIN_9287;
1130 else
1131 sc->sc_ah->led_pin = ATH_LED_PIN_DEF;
1132
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301133 /* Configure gpio 1 for output */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301134 ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin,
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301135 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
1136 /* LED off, active low */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301137 ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301138
Vasanthakumar Thiagarajanf2bffa72009-01-29 17:52:19 +05301139 INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work);
1140
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301141 trigger = ieee80211_get_radio_led_name(sc->hw);
1142 snprintf(sc->radio_led.name, sizeof(sc->radio_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001143 "ath9k-%s::radio", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301144 ret = ath_register_led(sc, &sc->radio_led, trigger);
1145 sc->radio_led.led_type = ATH_LED_RADIO;
1146 if (ret)
1147 goto fail;
1148
1149 trigger = ieee80211_get_assoc_led_name(sc->hw);
1150 snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001151 "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301152 ret = ath_register_led(sc, &sc->assoc_led, trigger);
1153 sc->assoc_led.led_type = ATH_LED_ASSOC;
1154 if (ret)
1155 goto fail;
1156
1157 trigger = ieee80211_get_tx_led_name(sc->hw);
1158 snprintf(sc->tx_led.name, sizeof(sc->tx_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001159 "ath9k-%s::tx", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301160 ret = ath_register_led(sc, &sc->tx_led, trigger);
1161 sc->tx_led.led_type = ATH_LED_TX;
1162 if (ret)
1163 goto fail;
1164
1165 trigger = ieee80211_get_rx_led_name(sc->hw);
1166 snprintf(sc->rx_led.name, sizeof(sc->rx_led.name),
Danny Kukawka0818cb82009-01-31 15:52:09 +01001167 "ath9k-%s::rx", wiphy_name(sc->hw->wiphy));
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301168 ret = ath_register_led(sc, &sc->rx_led, trigger);
1169 sc->rx_led.led_type = ATH_LED_RX;
1170 if (ret)
1171 goto fail;
1172
1173 return;
1174
1175fail:
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001176 cancel_delayed_work_sync(&sc->ath_led_blink_work);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301177 ath_deinit_leds(sc);
1178}
1179
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001180void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301181{
Sujithcbe61d82009-02-09 13:27:12 +05301182 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001183 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001184 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001185 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301186
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301187 ath9k_ps_wakeup(sc);
Vivek Natarajan93b1b372009-09-17 09:24:58 +05301188 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithd2f5b3a2009-04-13 21:56:25 +05301189
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301190 if (!ah->curchan)
1191 ah->curchan = ath_get_curchannel(sc, sc->hw);
1192
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301193 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301194 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001195 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001196 ath_print(common, ATH_DBG_FATAL,
1197 "Unable to reset channel %u (%uMhz) ",
1198 "reset status %d\n",
1199 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301200 }
1201 spin_unlock_bh(&sc->sc_resetlock);
1202
1203 ath_update_txpow(sc);
1204 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001205 ath_print(common, ATH_DBG_FATAL,
1206 "Unable to restart recv logic\n");
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301207 return;
1208 }
1209
1210 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001211 ath_beacon_config(sc, NULL); /* restart beacons */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301212
1213 /* Re-Enable interrupts */
Sujith17d79042009-02-09 13:27:03 +05301214 ath9k_hw_set_interrupts(ah, sc->imask);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301215
1216 /* Enable LED */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301217 ath9k_hw_cfg_output(ah, ah->led_pin,
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301218 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301219 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301220
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001221 ieee80211_wake_queues(hw);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301222 ath9k_ps_restore(sc);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301223}
1224
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001225void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301226{
Sujithcbe61d82009-02-09 13:27:12 +05301227 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001228 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001229 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301230
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301231 ath9k_ps_wakeup(sc);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001232 ieee80211_stop_queues(hw);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301233
1234 /* Disable LED */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +05301235 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
1236 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301237
1238 /* Disable interrupts */
1239 ath9k_hw_set_interrupts(ah, 0);
1240
Sujith043a0402009-01-16 21:38:47 +05301241 ath_drain_all_txq(sc, false); /* clear pending tx frames */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301242 ath_stoprecv(sc); /* turn off frame recv */
1243 ath_flushrecv(sc); /* flush recv queue */
1244
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301245 if (!ah->curchan)
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001246 ah->curchan = ath_get_curchannel(sc, hw);
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +05301247
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301248 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301249 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001250 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001251 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
1252 "Unable to reset channel %u (%uMhz) "
1253 "reset status %d\n",
1254 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301255 }
1256 spin_unlock_bh(&sc->sc_resetlock);
1257
1258 ath9k_hw_phy_disable(ah);
Vivek Natarajan93b1b372009-09-17 09:24:58 +05301259 ath9k_hw_configpcipowersave(ah, 1, 1);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301260 ath9k_ps_restore(sc);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001261 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301262}
1263
Gabor Juhos5077fd32009-03-06 11:17:55 +01001264/*******************/
1265/* Rfkill */
1266/*******************/
1267
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301268static bool ath_is_rfkill_set(struct ath_softc *sc)
1269{
Sujithcbe61d82009-02-09 13:27:12 +05301270 struct ath_hw *ah = sc->sc_ah;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301271
Sujith2660b812009-02-09 13:27:26 +05301272 return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) ==
1273 ah->rfkill_polarity;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301274}
1275
Johannes Berg3b319aa2009-06-13 14:50:26 +05301276static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301277{
Johannes Berg3b319aa2009-06-13 14:50:26 +05301278 struct ath_wiphy *aphy = hw->priv;
1279 struct ath_softc *sc = aphy->sc;
1280 bool blocked = !!ath_is_rfkill_set(sc);
1281
1282 wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
Johannes Berg19d337d2009-06-02 13:01:37 +02001283}
1284
Johannes Berg3b319aa2009-06-13 14:50:26 +05301285static void ath_start_rfkill_poll(struct ath_softc *sc)
Johannes Berg19d337d2009-06-02 13:01:37 +02001286{
Johannes Berg3b319aa2009-06-13 14:50:26 +05301287 struct ath_hw *ah = sc->sc_ah;
Johannes Berg19d337d2009-06-02 13:01:37 +02001288
Johannes Berg3b319aa2009-06-13 14:50:26 +05301289 if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
1290 wiphy_rfkill_start_polling(sc->hw->wiphy);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +05301291}
1292
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001293static void ath9k_uninit_hw(struct ath_softc *sc)
1294{
1295 struct ath_hw *ah = sc->sc_ah;
1296
1297 BUG_ON(!ah);
1298
1299 ath9k_exit_debug(ah);
1300 ath9k_hw_detach(ah);
1301 sc->sc_ah = NULL;
1302}
1303
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001304static void ath_clean_core(struct ath_softc *sc)
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301305{
1306 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001307 struct ath_hw *ah = sc->sc_ah;
Sujith9c84b792008-10-29 10:17:13 +05301308 int i = 0;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301309
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301310 ath9k_ps_wakeup(sc);
1311
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001312 dev_dbg(sc->dev, "Detach ATH hw\n");
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301313
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001314 ath_deinit_leds(sc);
Sujithe31f7b92009-09-23 13:49:12 +05301315 wiphy_rfkill_stop_polling(sc->hw->wiphy);
Luis R. Rodriguez35c95ab2009-07-27 11:53:03 -07001316
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001317 for (i = 0; i < sc->num_sec_wiphy; i++) {
1318 struct ath_wiphy *aphy = sc->sec_wiphy[i];
1319 if (aphy == NULL)
1320 continue;
1321 sc->sec_wiphy[i] = NULL;
1322 ieee80211_unregister_hw(aphy->hw);
1323 ieee80211_free_hw(aphy->hw);
1324 }
Vasanthakumar Thiagarajan3fcdfb42008-11-18 01:19:56 +05301325 ieee80211_unregister_hw(hw);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301326 ath_rx_cleanup(sc);
1327 ath_tx_cleanup(sc);
1328
Sujith9c84b792008-10-29 10:17:13 +05301329 tasklet_kill(&sc->intr_tq);
1330 tasklet_kill(&sc->bcon_tasklet);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301331
Sujith9c84b792008-10-29 10:17:13 +05301332 if (!(sc->sc_flags & SC_OP_INVALID))
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001333 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301334
Sujith9c84b792008-10-29 10:17:13 +05301335 /* cleanup tx queues */
1336 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1337 if (ATH_TXQ_SETUP(sc, i))
Sujithb77f4832008-12-07 21:44:03 +05301338 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
Sujith9c84b792008-10-29 10:17:13 +05301339
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001340 if ((sc->btcoex.no_stomp_timer) &&
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001341 ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001342 ath_gen_timer_free(ah, sc->btcoex.no_stomp_timer);
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001343}
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301344
Luis R. Rodriguez25688352009-10-06 21:19:09 -04001345void ath_detach(struct ath_softc *sc)
1346{
1347 ath_clean_core(sc);
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001348 ath9k_uninit_hw(sc);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301349}
1350
Luis R. Rodriguezbd96d392009-10-06 21:19:10 -04001351void ath_cleanup(struct ath_softc *sc)
1352{
1353 struct ath_hw *ah = sc->sc_ah;
1354 struct ath_common *common = ath9k_hw_common(ah);
1355
1356 ath_clean_core(sc);
1357 free_irq(sc->irq, sc);
1358 ath_bus_cleanup(common);
1359 kfree(sc->sec_wiphy);
1360 ieee80211_free_hw(sc->hw);
1361
1362 ath9k_uninit_hw(sc);
1363}
1364
Bob Copelande3bb2492009-03-30 22:30:30 -04001365static int ath9k_reg_notifier(struct wiphy *wiphy,
1366 struct regulatory_request *request)
1367{
1368 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
1369 struct ath_wiphy *aphy = hw->priv;
1370 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001371 struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah);
Bob Copelande3bb2492009-03-30 22:30:30 -04001372
1373 return ath_reg_notifier_apply(wiphy, request, reg);
1374}
1375
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001376/*
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001377 * Detects if there is any priority bt traffic
1378 */
1379static void ath_detect_bt_priority(struct ath_softc *sc)
1380{
1381 struct ath_btcoex *btcoex = &sc->btcoex;
1382 struct ath_hw *ah = sc->sc_ah;
1383
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001384 if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio))
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001385 btcoex->bt_priority_cnt++;
1386
1387 if (time_after(jiffies, btcoex->bt_priority_time +
1388 msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) {
1389 if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001390 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX,
1391 "BT priority traffic detected");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001392 sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED;
1393 } else {
1394 sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
1395 }
1396
1397 btcoex->bt_priority_cnt = 0;
1398 btcoex->bt_priority_time = jiffies;
1399 }
1400}
1401
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001402/*
1403 * Configures appropriate weight based on stomp type.
1404 */
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001405static void ath9k_btcoex_bt_stomp(struct ath_softc *sc,
1406 enum ath_stomp_type stomp_type)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001407{
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001408 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001409
1410 switch (stomp_type) {
1411 case ATH_BTCOEX_STOMP_ALL:
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001412 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1413 AR_STOMP_ALL_WLAN_WGHT);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001414 break;
1415 case ATH_BTCOEX_STOMP_LOW:
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001416 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1417 AR_STOMP_LOW_WLAN_WGHT);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001418 break;
1419 case ATH_BTCOEX_STOMP_NONE:
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001420 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1421 AR_STOMP_NONE_WLAN_WGHT);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001422 break;
1423 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001424 ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
1425 "Invalid Stomptype\n");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001426 break;
1427 }
1428
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001429 ath9k_hw_btcoex_enable(ah);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001430}
1431
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001432static void ath9k_gen_timer_start(struct ath_hw *ah,
1433 struct ath_gen_timer *timer,
1434 u32 timer_next,
1435 u32 timer_period)
1436{
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001437 struct ath_common *common = ath9k_hw_common(ah);
1438 struct ath_softc *sc = (struct ath_softc *) common->priv;
1439
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001440 ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period);
1441
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001442 if ((sc->imask & ATH9K_INT_GENTIMER) == 0) {
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001443 ath9k_hw_set_interrupts(ah, 0);
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001444 sc->imask |= ATH9K_INT_GENTIMER;
1445 ath9k_hw_set_interrupts(ah, sc->imask);
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001446 }
1447}
1448
1449static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
1450{
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001451 struct ath_common *common = ath9k_hw_common(ah);
1452 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001453 struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
1454
1455 ath9k_hw_gen_timer_stop(ah, timer);
1456
1457 /* if no timer is enabled, turn off interrupt mask */
1458 if (timer_table->timer_mask.val == 0) {
1459 ath9k_hw_set_interrupts(ah, 0);
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001460 sc->imask &= ~ATH9K_INT_GENTIMER;
1461 ath9k_hw_set_interrupts(ah, sc->imask);
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001462 }
1463}
1464
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001465/*
1466 * This is the master bt coex timer which runs for every
1467 * 45ms, bt traffic will be given priority during 55% of this
1468 * period while wlan gets remaining 45%
1469 */
1470static void ath_btcoex_period_timer(unsigned long data)
1471{
1472 struct ath_softc *sc = (struct ath_softc *) data;
1473 struct ath_hw *ah = sc->sc_ah;
1474 struct ath_btcoex *btcoex = &sc->btcoex;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001475
1476 ath_detect_bt_priority(sc);
1477
1478 spin_lock_bh(&btcoex->btcoex_lock);
1479
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001480 ath9k_btcoex_bt_stomp(sc, btcoex->bt_stomp_type);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001481
1482 spin_unlock_bh(&btcoex->btcoex_lock);
1483
1484 if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) {
1485 if (btcoex->hw_timer_enabled)
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001486 ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001487
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07001488 ath9k_gen_timer_start(ah,
1489 btcoex->no_stomp_timer,
1490 (ath9k_hw_gettsf32(ah) +
1491 btcoex->btcoex_no_stomp),
1492 btcoex->btcoex_no_stomp * 10);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001493 btcoex->hw_timer_enabled = true;
1494 }
1495
1496 mod_timer(&btcoex->period_timer, jiffies +
1497 msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD));
1498}
1499
1500/*
1501 * Generic tsf based hw timer which configures weight
1502 * registers to time slice between wlan and bt traffic
1503 */
1504static void ath_btcoex_no_stomp_timer(void *arg)
1505{
1506 struct ath_softc *sc = (struct ath_softc *)arg;
1507 struct ath_hw *ah = sc->sc_ah;
1508 struct ath_btcoex *btcoex = &sc->btcoex;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001509
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001510 ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
1511 "no stomp timer running \n");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001512
1513 spin_lock_bh(&btcoex->btcoex_lock);
1514
Luis R. Rodrigueze08a6ac2009-09-09 14:26:15 -07001515 if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW)
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001516 ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE);
Luis R. Rodrigueze08a6ac2009-09-09 14:26:15 -07001517 else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL)
Luis R. Rodriguez269ad812009-09-09 15:05:00 -07001518 ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001519
1520 spin_unlock_bh(&btcoex->btcoex_lock);
1521}
1522
1523static int ath_init_btcoex_timer(struct ath_softc *sc)
1524{
1525 struct ath_btcoex *btcoex = &sc->btcoex;
1526
1527 btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000;
1528 btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
1529 btcoex->btcoex_period / 100;
1530
1531 setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
1532 (unsigned long) sc);
1533
1534 spin_lock_init(&btcoex->btcoex_lock);
1535
1536 btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
1537 ath_btcoex_no_stomp_timer,
1538 ath_btcoex_no_stomp_timer,
1539 (void *) sc, AR_FIRST_NDP_TIMER);
1540
1541 if (!btcoex->no_stomp_timer)
1542 return -ENOMEM;
1543
1544 return 0;
1545}
1546
1547/*
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001548 * Read and write, they both share the same lock. We do this to serialize
1549 * reads and writes on Atheros 802.11n PCI devices only. This is required
1550 * as the FIFO on these devices can only accept sanely 2 requests. After
1551 * that the device goes bananas. Serializing the reads/writes prevents this
1552 * from happening.
1553 */
1554
1555static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
1556{
1557 struct ath_hw *ah = (struct ath_hw *) hw_priv;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001558 struct ath_common *common = ath9k_hw_common(ah);
1559 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001560
1561 if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
1562 unsigned long flags;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001563 spin_lock_irqsave(&sc->sc_serial_rw, flags);
1564 iowrite32(val, sc->mem + reg_offset);
1565 spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001566 } else
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001567 iowrite32(val, sc->mem + reg_offset);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001568}
1569
1570static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
1571{
1572 struct ath_hw *ah = (struct ath_hw *) hw_priv;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001573 struct ath_common *common = ath9k_hw_common(ah);
1574 struct ath_softc *sc = (struct ath_softc *) common->priv;
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001575 u32 val;
1576
1577 if (ah->config.serialize_regmode == SER_REG_MODE_ON) {
1578 unsigned long flags;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001579 spin_lock_irqsave(&sc->sc_serial_rw, flags);
1580 val = ioread32(sc->mem + reg_offset);
1581 spin_unlock_irqrestore(&sc->sc_serial_rw, flags);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001582 } else
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001583 val = ioread32(sc->mem + reg_offset);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001584 return val;
1585}
1586
Luis R. Rodriguez2ddb5c82009-09-14 02:09:38 -07001587static const struct ath_ops ath9k_common_ops = {
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001588 .read = ath9k_ioread32,
1589 .write = ath9k_iowrite32,
1590};
1591
1592/*
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001593 * Initialize and fill ath_softc, ath_sofct is the
1594 * "Software Carrier" struct. Historically it has existed
1595 * to allow the separation between hardware specific
1596 * variables (now in ath_hw) and driver specific variables.
1597 */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001598static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid,
1599 const struct ath_bus_ops *bus_ops)
Sujithff37e332008-11-24 12:07:55 +05301600{
Sujithcbe61d82009-02-09 13:27:12 +05301601 struct ath_hw *ah = NULL;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001602 struct ath_common *common;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001603 int r = 0, i;
Sujithff37e332008-11-24 12:07:55 +05301604 int csz = 0;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001605 int qnum;
Sujithff37e332008-11-24 12:07:55 +05301606
1607 /* XXX: hardware will not be ready until ath_open() being called */
1608 sc->sc_flags |= SC_OP_INVALID;
Sujith88b126a2008-11-28 22:19:02 +05301609
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001610 spin_lock_init(&sc->wiphy_lock);
Sujithff37e332008-11-24 12:07:55 +05301611 spin_lock_init(&sc->sc_resetlock);
Luis R. Rodriguez61584252009-03-12 18:18:49 -04001612 spin_lock_init(&sc->sc_serial_rw);
Senthil Balasubramaniane5f09212009-06-24 18:56:41 +05301613 spin_lock_init(&sc->ani_lock);
Gabor Juhos04717cc2009-07-14 20:17:13 -04001614 spin_lock_init(&sc->sc_pm_lock);
Sujithaa33de02008-12-18 11:40:16 +05301615 mutex_init(&sc->mutex);
Sujithff37e332008-11-24 12:07:55 +05301616 tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
Sujith9fc9ab02009-03-03 10:16:51 +05301617 tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet,
Sujithff37e332008-11-24 12:07:55 +05301618 (unsigned long)sc);
1619
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001620 ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001621 if (!ah)
1622 return -ENOMEM;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001623
Luis R. Rodriguez8df5d1b2009-08-03 12:24:37 -07001624 ah->hw_version.devid = devid;
Vasanthakumar Thiagarajanaeac3552009-09-09 15:25:49 +05301625 ah->hw_version.subsysid = subsysid;
Luis R. Rodrigueze1e2f932009-08-03 12:24:38 -07001626 sc->sc_ah = ah;
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001627
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001628 common = ath9k_hw_common(ah);
Luis R. Rodriguez9e4bffd2009-09-10 16:11:21 -07001629 common->ops = &ath9k_common_ops;
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001630 common->bus_ops = bus_ops;
Luis R. Rodriguez13b81552009-09-10 17:52:45 -07001631 common->ah = ah;
Luis R. Rodriguezb002a4a2009-09-13 00:03:27 -07001632 common->hw = sc->hw;
Luis R. Rodriguezbc974f42009-09-28 02:54:40 -04001633 common->priv = sc;
Luis R. Rodriguezfaa27fa2009-10-06 21:19:06 -04001634 common->debug_mask = ath9k_debug;
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001635
1636 /*
1637 * Cache line size is used to size and align various
1638 * structures used to communicate with the hardware.
1639 */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001640 ath_read_cachesize(common, &csz);
Luis R. Rodriguez27c51f12009-09-10 11:08:14 -07001641 /* XXX assert csz is non-zero */
1642 common->cachelsz = csz << 2; /* convert to bytes */
1643
Luis R. Rodriguezf637cfd2009-08-03 12:24:46 -07001644 r = ath9k_hw_init(ah);
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001645 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001646 ath_print(common, ATH_DBG_FATAL,
1647 "Unable to initialize hardware; "
1648 "initialization status: %d\n", r);
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001649 goto bad_free_hw;
1650 }
1651
1652 if (ath9k_init_debug(ah) < 0) {
1653 ath_print(common, ATH_DBG_FATAL,
1654 "Unable to create debugfs files\n");
1655 goto bad_free_hw;
Sujithff37e332008-11-24 12:07:55 +05301656 }
Sujithff37e332008-11-24 12:07:55 +05301657
1658 /* Get the hardware key cache size. */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001659 common->keymax = ah->caps.keycache_size;
1660 if (common->keymax > ATH_KEYMAX) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001661 ath_print(common, ATH_DBG_ANY,
1662 "Warning, using only %u entries in %u key cache\n",
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001663 ATH_KEYMAX, common->keymax);
1664 common->keymax = ATH_KEYMAX;
Sujithff37e332008-11-24 12:07:55 +05301665 }
1666
1667 /*
1668 * Reset the key cache since some parts do not
1669 * reset the contents on initial power up.
1670 */
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001671 for (i = 0; i < common->keymax; i++)
Sujithff37e332008-11-24 12:07:55 +05301672 ath9k_hw_keyreset(ah, (u16) i);
Sujithff37e332008-11-24 12:07:55 +05301673
Sujithff37e332008-11-24 12:07:55 +05301674 /* default to MONITOR mode */
Sujith2660b812009-02-09 13:27:26 +05301675 sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR;
Colin McCabed97809d2008-12-01 13:38:55 -08001676
Sujithff37e332008-11-24 12:07:55 +05301677 /*
1678 * Allocate hardware transmit queues: one queue for
1679 * beacon frames and one data queue for each QoS
1680 * priority. Note that the hal handles reseting
1681 * these queues at the needed time.
1682 */
Luis R. Rodriguez536b3a72009-10-06 21:19:11 -04001683 sc->beacon.beaconq = ath9k_hw_beaconq_setup(ah);
Sujithb77f4832008-12-07 21:44:03 +05301684 if (sc->beacon.beaconq == -1) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001685 ath_print(common, ATH_DBG_FATAL,
1686 "Unable to setup a beacon xmit queue\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001687 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301688 goto bad2;
1689 }
Sujithb77f4832008-12-07 21:44:03 +05301690 sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
1691 if (sc->beacon.cabq == NULL) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001692 ath_print(common, ATH_DBG_FATAL,
1693 "Unable to setup CAB xmit queue\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001694 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301695 goto bad2;
1696 }
1697
Sujith17d79042009-02-09 13:27:03 +05301698 sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
Sujithff37e332008-11-24 12:07:55 +05301699 ath_cabq_update(sc);
1700
Sujithb77f4832008-12-07 21:44:03 +05301701 for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++)
1702 sc->tx.hwq_map[i] = -1;
Sujithff37e332008-11-24 12:07:55 +05301703
1704 /* Setup data queues */
1705 /* NB: ensure BK queue is the lowest priority h/w queue */
1706 if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001707 ath_print(common, ATH_DBG_FATAL,
1708 "Unable to setup xmit queue for BK traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001709 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301710 goto bad2;
1711 }
1712
1713 if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001714 ath_print(common, ATH_DBG_FATAL,
1715 "Unable to setup xmit queue for BE traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001716 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301717 goto bad2;
1718 }
1719 if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001720 ath_print(common, ATH_DBG_FATAL,
1721 "Unable to setup xmit queue for VI traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001722 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301723 goto bad2;
1724 }
1725 if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001726 ath_print(common, ATH_DBG_FATAL,
1727 "Unable to setup xmit queue for VO traffic\n");
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001728 r = -EIO;
Sujithff37e332008-11-24 12:07:55 +05301729 goto bad2;
1730 }
1731
1732 /* Initializes the noise floor to a reasonable default value.
1733 * Later on this will be updated during ANI processing. */
1734
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08001735 common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR;
1736 setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
Sujithff37e332008-11-24 12:07:55 +05301737
1738 if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1739 ATH9K_CIPHER_TKIP, NULL)) {
1740 /*
1741 * Whether we should enable h/w TKIP MIC.
1742 * XXX: if we don't support WME TKIP MIC, then we wouldn't
1743 * report WMM capable, so it's always safe to turn on
1744 * TKIP MIC in this case.
1745 */
1746 ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC,
1747 0, 1, NULL);
1748 }
1749
1750 /*
1751 * Check whether the separate key cache entries
1752 * are required to handle both tx+rx MIC keys.
1753 * With split mic keys the number of stations is limited
1754 * to 27 otherwise 59.
1755 */
1756 if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1757 ATH9K_CIPHER_TKIP, NULL)
1758 && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER,
1759 ATH9K_CIPHER_MIC, NULL)
1760 && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT,
1761 0, NULL))
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08001762 common->splitmic = 1;
Sujithff37e332008-11-24 12:07:55 +05301763
1764 /* turn on mcast key search if possible */
1765 if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
1766 (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1,
1767 1, NULL);
1768
Sujith17d79042009-02-09 13:27:03 +05301769 sc->config.txpowlimit = ATH_TXPOWER_MAX;
Sujithff37e332008-11-24 12:07:55 +05301770
1771 /* 11n Capabilities */
Sujith2660b812009-02-09 13:27:26 +05301772 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
Sujithff37e332008-11-24 12:07:55 +05301773 sc->sc_flags |= SC_OP_TXAGGR;
1774 sc->sc_flags |= SC_OP_RXAGGR;
1775 }
1776
Luis R. Rodriguez43c27612009-09-13 21:07:07 -07001777 common->tx_chainmask = ah->caps.tx_chainmask;
1778 common->rx_chainmask = ah->caps.rx_chainmask;
Sujithff37e332008-11-24 12:07:55 +05301779
1780 ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL);
Sujithb77f4832008-12-07 21:44:03 +05301781 sc->rx.defant = ath9k_hw_getdefantenna(ah);
Sujithff37e332008-11-24 12:07:55 +05301782
Jouni Malinen8ca21f02009-03-03 19:23:27 +02001783 if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001784 memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
Sujithff37e332008-11-24 12:07:55 +05301785
Sujithb77f4832008-12-07 21:44:03 +05301786 sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */
Sujithff37e332008-11-24 12:07:55 +05301787
1788 /* initialize beacon slots */
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001789 for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001790 sc->beacon.bslot[i] = NULL;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001791 sc->beacon.bslot_aphy[i] = NULL;
1792 }
Sujithff37e332008-11-24 12:07:55 +05301793
Sujithff37e332008-11-24 12:07:55 +05301794 /* setup channels and rates */
1795
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001796 if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) {
1797 sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable;
1798 sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
1799 sc->sbands[IEEE80211_BAND_2GHZ].n_channels =
1800 ARRAY_SIZE(ath9k_2ghz_chantable);
1801 sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
1802 sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
1803 ARRAY_SIZE(ath9k_legacy_rates);
1804 }
Sujithff37e332008-11-24 12:07:55 +05301805
Sujith2660b812009-02-09 13:27:26 +05301806 if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) {
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001807 sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable;
Sujithff37e332008-11-24 12:07:55 +05301808 sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001809 sc->sbands[IEEE80211_BAND_5GHZ].n_channels =
1810 ARRAY_SIZE(ath9k_5ghz_chantable);
Felix Fietkau545750d2009-11-23 22:21:01 +01001811 sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
1812 ath9k_legacy_rates + 4;
1813 sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates =
1814 ARRAY_SIZE(ath9k_legacy_rates) - 4;
Sujithff37e332008-11-24 12:07:55 +05301815 }
1816
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001817 switch (ah->btcoex_hw.scheme) {
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001818 case ATH_BTCOEX_CFG_NONE:
1819 break;
1820 case ATH_BTCOEX_CFG_2WIRE:
1821 ath9k_hw_btcoex_init_2wire(ah);
1822 break;
1823 case ATH_BTCOEX_CFG_3WIRE:
1824 ath9k_hw_btcoex_init_3wire(ah);
1825 r = ath_init_btcoex_timer(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301826 if (r)
1827 goto bad2;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001828 qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001829 ath9k_hw_init_btcoex_hw(ah, qnum);
Luis R. Rodrigueze08a6ac2009-09-09 14:26:15 -07001830 sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW;
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001831 break;
1832 default:
1833 WARN_ON(1);
1834 break;
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301835 }
Vasanthakumar Thiagarajanc97c92d2009-01-02 15:35:46 +05301836
Sujithff37e332008-11-24 12:07:55 +05301837 return 0;
1838bad2:
1839 /* cleanup tx queues */
1840 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1841 if (ATH_TXQ_SETUP(sc, i))
Sujithb77f4832008-12-07 21:44:03 +05301842 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
Sujithff37e332008-11-24 12:07:55 +05301843
Luis R. Rodriguez211f5852009-10-06 21:19:07 -04001844bad_free_hw:
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001845 ath9k_uninit_hw(sc);
Luis R. Rodriguez4f3acf82009-08-03 12:24:36 -07001846 return r;
Sujithff37e332008-11-24 12:07:55 +05301847}
1848
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001849void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301850{
Sujith9c84b792008-10-29 10:17:13 +05301851 hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
1852 IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
1853 IEEE80211_HW_SIGNAL_DBM |
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301854 IEEE80211_HW_AMPDU_AGGREGATION |
1855 IEEE80211_HW_SUPPORTS_PS |
Sujitheeee1322009-03-10 10:39:53 +05301856 IEEE80211_HW_PS_NULLFUNC_STACK |
1857 IEEE80211_HW_SPECTRUM_MGMT;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301858
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02001859 if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt)
Jouni Malinen0ced0e12009-01-08 13:32:13 +02001860 hw->flags |= IEEE80211_HW_MFP_CAPABLE;
1861
Sujith9c84b792008-10-29 10:17:13 +05301862 hw->wiphy->interface_modes =
1863 BIT(NL80211_IFTYPE_AP) |
1864 BIT(NL80211_IFTYPE_STATION) |
Pat Erley9cb54122009-03-20 22:59:59 -04001865 BIT(NL80211_IFTYPE_ADHOC) |
1866 BIT(NL80211_IFTYPE_MESH_POINT);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301867
John W. Linville18b6c9a2009-11-23 16:15:19 -05001868 hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
1869
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301870 hw->queues = 4;
Sujithe63835b2008-11-18 09:07:53 +05301871 hw->max_rates = 4;
Sujith171387e2009-02-17 15:36:25 +05301872 hw->channel_change_time = 5000;
Jouni Malinen465ca842009-03-03 19:23:34 +02001873 hw->max_listen_interval = 10;
Luis R. Rodriguezdd190182009-07-14 20:13:56 -04001874 /* Hardware supports 10 but we use 4 */
1875 hw->max_rate_tries = 4;
Sujith528f0c62008-10-29 10:14:26 +05301876 hw->sta_data_size = sizeof(struct ath_node);
Sujith17d79042009-02-09 13:27:03 +05301877 hw->vif_data_size = sizeof(struct ath_vif);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301878
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301879 hw->rate_control_algorithm = "ath9k_rate_control";
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301880
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001881 if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes))
1882 hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
1883 &sc->sbands[IEEE80211_BAND_2GHZ];
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001884 if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes))
1885 hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
1886 &sc->sbands[IEEE80211_BAND_5GHZ];
1887}
1888
Luis R. Rodriguez1e40bcf2009-08-03 12:24:47 -07001889/* Device driver core initialization */
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001890int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
1891 const struct ath_bus_ops *bus_ops)
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001892{
1893 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001894 struct ath_common *common;
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001895 struct ath_hw *ah;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001896 int error = 0, i;
Bob Copeland3a702e42009-03-30 22:30:29 -04001897 struct ath_regulatory *reg;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001898
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001899 dev_dbg(sc->dev, "Attach ATH hw\n");
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001900
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001901 error = ath_init_softc(devid, sc, subsysid, bus_ops);
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001902 if (error != 0)
1903 return error;
1904
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001905 ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001906 common = ath9k_hw_common(ah);
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001907
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001908 /* get mac address from hardware and set in mac80211 */
1909
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001910 SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001911
1912 ath_set_hw_capab(sc, hw);
1913
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001914 error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
Luis R. Rodriguezc26c2e52009-05-19 18:27:11 -04001915 ath9k_reg_notifier);
1916 if (error)
1917 return error;
1918
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001919 reg = &common->regulatory;
Luis R. Rodriguezc26c2e52009-05-19 18:27:11 -04001920
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001921 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001922 if (test_bit(ATH9K_MODE_11G, ah->caps.wireless_modes))
1923 setup_ht_cap(sc,
1924 &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap);
Luis R. Rodriguez4d6b2282009-09-07 04:52:26 -07001925 if (test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes))
Gabor Juhosa9a29ce2009-11-27 12:01:35 +01001926 setup_ht_cap(sc,
1927 &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap);
Sujith9c84b792008-10-29 10:17:13 +05301928 }
1929
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301930 /* initialize tx/rx engine */
1931 error = ath_tx_init(sc, ATH_TXBUF);
1932 if (error != 0)
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301933 goto error_attach;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301934
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301935 error = ath_rx_init(sc, ATH_RXBUF);
1936 if (error != 0)
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301937 goto error_attach;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301938
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001939 INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
Jouni Malinenf98c3bd2009-03-03 19:23:39 +02001940 INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);
1941 sc->wiphy_scheduler_int = msecs_to_jiffies(500);
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001942
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301943 error = ieee80211_register_hw(hw);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301944
Bob Copeland3a702e42009-03-30 22:30:29 -04001945 if (!ath_is_world_regd(reg)) {
Bob Copelandc02cf372009-03-30 22:30:28 -04001946 error = regulatory_hint(hw->wiphy, reg->alpha2);
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05001947 if (error)
1948 goto error_attach;
1949 }
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001950
Senthil Balasubramaniandb93e7b2008-11-13 18:01:08 +05301951 /* Initialize LED control */
1952 ath_init_leds(sc);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301953
Johannes Berg3b319aa2009-06-13 14:50:26 +05301954 ath_start_rfkill_poll(sc);
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001955
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301956 return 0;
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301957
1958error_attach:
1959 /* cleanup tx queues */
1960 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++)
1961 if (ATH_TXQ_SETUP(sc, i))
1962 ath_tx_cleanupq(sc, &sc->tx.txq[i]);
1963
Luis R. Rodriguez7fda1662009-10-06 21:19:08 -04001964 ath9k_uninit_hw(sc);
Vasanthakumar Thiagarajan40b130a2009-02-16 13:55:07 +05301965
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +05301966 return error;
1967}
1968
Sujithff37e332008-11-24 12:07:55 +05301969int ath_reset(struct ath_softc *sc, bool retry_tx)
1970{
Sujithcbe61d82009-02-09 13:27:12 +05301971 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001972 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez030bb492008-12-23 15:58:37 -08001973 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001974 int r;
Sujithff37e332008-11-24 12:07:55 +05301975
Sujith2ab81d42009-12-14 16:34:56 +05301976 /* Stop ANI */
1977 del_timer_sync(&common->ani.timer);
1978
Sujithff37e332008-11-24 12:07:55 +05301979 ath9k_hw_set_interrupts(ah, 0);
Sujith043a0402009-01-16 21:38:47 +05301980 ath_drain_all_txq(sc, retry_tx);
Sujithff37e332008-11-24 12:07:55 +05301981 ath_stoprecv(sc);
1982 ath_flushrecv(sc);
1983
1984 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +05301985 r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001986 if (r)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001987 ath_print(common, ATH_DBG_FATAL,
1988 "Unable to reset hardware; reset status %d\n", r);
Sujithff37e332008-11-24 12:07:55 +05301989 spin_unlock_bh(&sc->sc_resetlock);
1990
1991 if (ath_startrecv(sc) != 0)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001992 ath_print(common, ATH_DBG_FATAL,
1993 "Unable to start recv logic\n");
Sujithff37e332008-11-24 12:07:55 +05301994
1995 /*
1996 * We may be doing a reset in response to a request
1997 * that changes the channel so update any state that
1998 * might change as a result.
1999 */
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -08002000 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +05302001
2002 ath_update_txpow(sc);
2003
2004 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002005 ath_beacon_config(sc, NULL); /* restart beacons */
Sujithff37e332008-11-24 12:07:55 +05302006
Sujith17d79042009-02-09 13:27:03 +05302007 ath9k_hw_set_interrupts(ah, sc->imask);
Sujithff37e332008-11-24 12:07:55 +05302008
2009 if (retry_tx) {
2010 int i;
2011 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
2012 if (ATH_TXQ_SETUP(sc, i)) {
Sujithb77f4832008-12-07 21:44:03 +05302013 spin_lock_bh(&sc->tx.txq[i].axq_lock);
2014 ath_txq_schedule(sc, &sc->tx.txq[i]);
2015 spin_unlock_bh(&sc->tx.txq[i].axq_lock);
Sujithff37e332008-11-24 12:07:55 +05302016 }
2017 }
2018 }
2019
Sujith2ab81d42009-12-14 16:34:56 +05302020 /* Start ANI */
2021 ath_start_ani(common);
2022
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08002023 return r;
Sujithff37e332008-11-24 12:07:55 +05302024}
2025
2026/*
2027 * This function will allocate both the DMA descriptor structure, and the
2028 * buffers it contains. These are used to contain the descriptors used
2029 * by the system.
2030*/
2031int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
2032 struct list_head *head, const char *name,
2033 int nbuf, int ndesc)
2034{
2035#define DS2PHYS(_dd, _ds) \
2036 ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
2037#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
2038#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002039 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujithff37e332008-11-24 12:07:55 +05302040 struct ath_desc *ds;
2041 struct ath_buf *bf;
2042 int i, bsize, error;
2043
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002044 ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n",
2045 name, nbuf, ndesc);
Sujithff37e332008-11-24 12:07:55 +05302046
Senthil Balasubramanianb03a9db2009-03-06 11:24:09 +05302047 INIT_LIST_HEAD(head);
Sujithff37e332008-11-24 12:07:55 +05302048 /* ath_desc must be a multiple of DWORDs */
2049 if ((sizeof(struct ath_desc) % 4) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002050 ath_print(common, ATH_DBG_FATAL,
2051 "ath_desc not DWORD aligned\n");
Luis R. Rodriguez9680e8a2009-09-13 23:28:00 -07002052 BUG_ON((sizeof(struct ath_desc) % 4) != 0);
Sujithff37e332008-11-24 12:07:55 +05302053 error = -ENOMEM;
2054 goto fail;
2055 }
2056
Sujithff37e332008-11-24 12:07:55 +05302057 dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
2058
2059 /*
2060 * Need additional DMA memory because we can't use
2061 * descriptors that cross the 4K page boundary. Assume
2062 * one skipped descriptor per 4K page.
2063 */
Sujith2660b812009-02-09 13:27:26 +05302064 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) {
Sujithff37e332008-11-24 12:07:55 +05302065 u32 ndesc_skipped =
2066 ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len);
2067 u32 dma_len;
2068
2069 while (ndesc_skipped) {
2070 dma_len = ndesc_skipped * sizeof(struct ath_desc);
2071 dd->dd_desc_len += dma_len;
2072
2073 ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len);
2074 };
2075 }
2076
2077 /* allocate descriptors */
Gabor Juhos7da3c552009-01-14 20:17:03 +01002078 dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len,
Senthil Balasubramanianf0e6ce12009-03-06 11:24:08 +05302079 &dd->dd_desc_paddr, GFP_KERNEL);
Sujithff37e332008-11-24 12:07:55 +05302080 if (dd->dd_desc == NULL) {
2081 error = -ENOMEM;
2082 goto fail;
2083 }
2084 ds = dd->dd_desc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002085 ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n",
2086 name, ds, (u32) dd->dd_desc_len,
2087 ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
Sujithff37e332008-11-24 12:07:55 +05302088
2089 /* allocate buffers */
2090 bsize = sizeof(struct ath_buf) * nbuf;
Senthil Balasubramanianf0e6ce12009-03-06 11:24:08 +05302091 bf = kzalloc(bsize, GFP_KERNEL);
Sujithff37e332008-11-24 12:07:55 +05302092 if (bf == NULL) {
2093 error = -ENOMEM;
2094 goto fail2;
2095 }
Sujithff37e332008-11-24 12:07:55 +05302096 dd->dd_bufptr = bf;
2097
Sujithff37e332008-11-24 12:07:55 +05302098 for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
2099 bf->bf_desc = ds;
2100 bf->bf_daddr = DS2PHYS(dd, ds);
2101
Sujith2660b812009-02-09 13:27:26 +05302102 if (!(sc->sc_ah->caps.hw_caps &
Sujithff37e332008-11-24 12:07:55 +05302103 ATH9K_HW_CAP_4KB_SPLITTRANS)) {
2104 /*
2105 * Skip descriptor addresses which can cause 4KB
2106 * boundary crossing (addr + length) with a 32 dword
2107 * descriptor fetch.
2108 */
2109 while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
Luis R. Rodriguez9680e8a2009-09-13 23:28:00 -07002110 BUG_ON((caddr_t) bf->bf_desc >=
Sujithff37e332008-11-24 12:07:55 +05302111 ((caddr_t) dd->dd_desc +
2112 dd->dd_desc_len));
2113
2114 ds += ndesc;
2115 bf->bf_desc = ds;
2116 bf->bf_daddr = DS2PHYS(dd, ds);
2117 }
2118 }
2119 list_add_tail(&bf->list, head);
2120 }
2121 return 0;
2122fail2:
Gabor Juhos7da3c552009-01-14 20:17:03 +01002123 dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
2124 dd->dd_desc_paddr);
Sujithff37e332008-11-24 12:07:55 +05302125fail:
2126 memset(dd, 0, sizeof(*dd));
2127 return error;
2128#undef ATH_DESC_4KB_BOUND_CHECK
2129#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED
2130#undef DS2PHYS
2131}
2132
2133void ath_descdma_cleanup(struct ath_softc *sc,
2134 struct ath_descdma *dd,
2135 struct list_head *head)
2136{
Gabor Juhos7da3c552009-01-14 20:17:03 +01002137 dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc,
2138 dd->dd_desc_paddr);
Sujithff37e332008-11-24 12:07:55 +05302139
2140 INIT_LIST_HEAD(head);
2141 kfree(dd->dd_bufptr);
2142 memset(dd, 0, sizeof(*dd));
2143}
2144
2145int ath_get_hal_qnum(u16 queue, struct ath_softc *sc)
2146{
2147 int qnum;
2148
2149 switch (queue) {
2150 case 0:
Sujithb77f4832008-12-07 21:44:03 +05302151 qnum = sc->tx.hwq_map[ATH9K_WME_AC_VO];
Sujithff37e332008-11-24 12:07:55 +05302152 break;
2153 case 1:
Sujithb77f4832008-12-07 21:44:03 +05302154 qnum = sc->tx.hwq_map[ATH9K_WME_AC_VI];
Sujithff37e332008-11-24 12:07:55 +05302155 break;
2156 case 2:
Sujithb77f4832008-12-07 21:44:03 +05302157 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +05302158 break;
2159 case 3:
Sujithb77f4832008-12-07 21:44:03 +05302160 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BK];
Sujithff37e332008-11-24 12:07:55 +05302161 break;
2162 default:
Sujithb77f4832008-12-07 21:44:03 +05302163 qnum = sc->tx.hwq_map[ATH9K_WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +05302164 break;
2165 }
2166
2167 return qnum;
2168}
2169
2170int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
2171{
2172 int qnum;
2173
2174 switch (queue) {
2175 case ATH9K_WME_AC_VO:
2176 qnum = 0;
2177 break;
2178 case ATH9K_WME_AC_VI:
2179 qnum = 1;
2180 break;
2181 case ATH9K_WME_AC_BE:
2182 qnum = 2;
2183 break;
2184 case ATH9K_WME_AC_BK:
2185 qnum = 3;
2186 break;
2187 default:
2188 qnum = -1;
2189 break;
2190 }
2191
2192 return qnum;
2193}
2194
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002195/* XXX: Remove me once we don't depend on ath9k_channel for all
2196 * this redundant data */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002197void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
2198 struct ath9k_channel *ichan)
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002199{
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002200 struct ieee80211_channel *chan = hw->conf.channel;
2201 struct ieee80211_conf *conf = &hw->conf;
2202
2203 ichan->channel = chan->center_freq;
2204 ichan->chan = chan;
2205
2206 if (chan->band == IEEE80211_BAND_2GHZ) {
2207 ichan->chanmode = CHANNEL_G;
Sujith88132622009-09-03 12:08:53 +05302208 ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002209 } else {
2210 ichan->chanmode = CHANNEL_A;
2211 ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
2212 }
2213
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -07002214 if (conf_is_ht(conf))
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002215 ichan->chanmode = ath_get_extchanmode(sc, chan,
2216 conf->channel_type);
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002217}
2218
Sujithff37e332008-11-24 12:07:55 +05302219/**********************/
2220/* mac80211 callbacks */
2221/**********************/
2222
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002223/*
2224 * (Re)start btcoex timers
2225 */
2226static void ath9k_btcoex_timer_resume(struct ath_softc *sc)
2227{
2228 struct ath_btcoex *btcoex = &sc->btcoex;
2229 struct ath_hw *ah = sc->sc_ah;
2230
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002231 ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX,
2232 "Starting btcoex timers");
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002233
2234 /* make sure duty cycle timer is also stopped when resuming */
2235 if (btcoex->hw_timer_enabled)
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07002236 ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002237
2238 btcoex->bt_priority_cnt = 0;
2239 btcoex->bt_priority_time = jiffies;
2240 sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED;
2241
2242 mod_timer(&btcoex->period_timer, jiffies);
2243}
2244
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002245static int ath9k_start(struct ieee80211_hw *hw)
2246{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002247 struct ath_wiphy *aphy = hw->priv;
2248 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002249 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002250 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002251 struct ieee80211_channel *curchan = hw->conf.channel;
Sujithff37e332008-11-24 12:07:55 +05302252 struct ath9k_channel *init_channel;
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05302253 int r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002254
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002255 ath_print(common, ATH_DBG_CONFIG,
2256 "Starting driver with initial channel: %d MHz\n",
2257 curchan->center_freq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002258
Sujith141b38b2009-02-04 08:10:07 +05302259 mutex_lock(&sc->mutex);
2260
Jouni Malinen9580a222009-03-03 19:23:33 +02002261 if (ath9k_wiphy_started(sc)) {
2262 if (sc->chan_idx == curchan->hw_value) {
2263 /*
2264 * Already on the operational channel, the new wiphy
2265 * can be marked active.
2266 */
2267 aphy->state = ATH_WIPHY_ACTIVE;
2268 ieee80211_wake_queues(hw);
2269 } else {
2270 /*
2271 * Another wiphy is on another channel, start the new
2272 * wiphy in paused state.
2273 */
2274 aphy->state = ATH_WIPHY_PAUSED;
2275 ieee80211_stop_queues(hw);
2276 }
2277 mutex_unlock(&sc->mutex);
2278 return 0;
2279 }
2280 aphy->state = ATH_WIPHY_ACTIVE;
2281
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002282 /* setup initial channel */
2283
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05302284 sc->chan_idx = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002285
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05302286 init_channel = ath_get_curchannel(sc, hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002287
Sujithff37e332008-11-24 12:07:55 +05302288 /* Reset SERDES registers */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002289 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithff37e332008-11-24 12:07:55 +05302290
2291 /*
2292 * The basic interface to setting the hardware in a good
2293 * state is ``reset''. On return the hardware is known to
2294 * be powered up and with interrupts disabled. This must
2295 * be followed by initialization of the appropriate bits
2296 * and then setup of the interrupt mask.
2297 */
2298 spin_lock_bh(&sc->sc_resetlock);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002299 r = ath9k_hw_reset(ah, init_channel, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08002300 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002301 ath_print(common, ATH_DBG_FATAL,
2302 "Unable to reset hardware; reset status %d "
2303 "(freq %u MHz)\n", r,
2304 curchan->center_freq);
Sujithff37e332008-11-24 12:07:55 +05302305 spin_unlock_bh(&sc->sc_resetlock);
Sujith141b38b2009-02-04 08:10:07 +05302306 goto mutex_unlock;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002307 }
Sujithff37e332008-11-24 12:07:55 +05302308 spin_unlock_bh(&sc->sc_resetlock);
2309
2310 /*
2311 * This is needed only to setup initial state
2312 * but it's best done after a reset.
2313 */
2314 ath_update_txpow(sc);
2315
2316 /*
2317 * Setup the hardware after reset:
2318 * The receive engine is set going.
2319 * Frame transmit is handled entirely
2320 * in the frame output path; there's nothing to do
2321 * here except setup the interrupt mask.
2322 */
2323 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002324 ath_print(common, ATH_DBG_FATAL,
2325 "Unable to start recv logic\n");
Sujith141b38b2009-02-04 08:10:07 +05302326 r = -EIO;
2327 goto mutex_unlock;
Sujithff37e332008-11-24 12:07:55 +05302328 }
2329
2330 /* Setup our intr mask. */
Sujith17d79042009-02-09 13:27:03 +05302331 sc->imask = ATH9K_INT_RX | ATH9K_INT_TX
Sujithff37e332008-11-24 12:07:55 +05302332 | ATH9K_INT_RXEOL | ATH9K_INT_RXORN
2333 | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL;
2334
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002335 if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
Sujith17d79042009-02-09 13:27:03 +05302336 sc->imask |= ATH9K_INT_GTT;
Sujithff37e332008-11-24 12:07:55 +05302337
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002338 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
Sujith17d79042009-02-09 13:27:03 +05302339 sc->imask |= ATH9K_INT_CST;
Sujithff37e332008-11-24 12:07:55 +05302340
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -08002341 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +05302342
2343 sc->sc_flags &= ~SC_OP_INVALID;
2344
2345 /* Disable BMISS interrupt when we're not associated */
Sujith17d79042009-02-09 13:27:03 +05302346 sc->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002347 ath9k_hw_set_interrupts(ah, sc->imask);
Sujithff37e332008-11-24 12:07:55 +05302348
Jouni Malinenbce048d2009-03-03 19:23:28 +02002349 ieee80211_wake_queues(hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002350
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04002351 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
Senthil Balasubramanian164ace32009-07-14 20:17:09 -04002352
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002353 if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
2354 !ah->btcoex_hw.enabled) {
Luis R. Rodriguez5e197292009-09-09 15:15:55 -07002355 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
2356 AR_STOMP_LOW_WLAN_WGHT);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002357 ath9k_hw_btcoex_enable(ah);
Vasanthakumar Thiagarajanf985ad12009-08-26 21:08:43 +05302358
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07002359 if (common->bus_ops->bt_coex_prep)
2360 common->bus_ops->bt_coex_prep(common);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002361 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002362 ath9k_btcoex_timer_resume(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05302363 }
2364
Sujith141b38b2009-02-04 08:10:07 +05302365mutex_unlock:
2366 mutex_unlock(&sc->mutex);
2367
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08002368 return r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002369}
2370
2371static int ath9k_tx(struct ieee80211_hw *hw,
2372 struct sk_buff *skb)
2373{
Jouni Malinen147583c2008-08-11 14:01:50 +03002374 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
Jouni Malinenbce048d2009-03-03 19:23:28 +02002375 struct ath_wiphy *aphy = hw->priv;
2376 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002377 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith528f0c62008-10-29 10:14:26 +05302378 struct ath_tx_control txctl;
Benoit Papillault1bc14882009-11-24 15:49:18 +01002379 int padpos, padsize;
2380 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
Sujith528f0c62008-10-29 10:14:26 +05302381
Jouni Malinen8089cc42009-03-03 19:23:38 +02002382 if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002383 ath_print(common, ATH_DBG_XMIT,
2384 "ath9k: %s: TX in unexpected wiphy state "
2385 "%d\n", wiphy_name(hw->wiphy), aphy->state);
Jouni Malinenee166a02009-03-03 19:23:36 +02002386 goto exit;
2387 }
2388
Gabor Juhos96148322009-07-24 17:27:21 +02002389 if (sc->ps_enabled) {
Jouni Malinendc8c4582009-05-19 17:01:42 +03002390 /*
2391 * mac80211 does not set PM field for normal data frames, so we
2392 * need to update that based on the current PS mode.
2393 */
2394 if (ieee80211_is_data(hdr->frame_control) &&
2395 !ieee80211_is_nullfunc(hdr->frame_control) &&
2396 !ieee80211_has_pm(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002397 ath_print(common, ATH_DBG_PS, "Add PM=1 for a TX frame "
2398 "while in PS mode\n");
Jouni Malinendc8c4582009-05-19 17:01:42 +03002399 hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
2400 }
2401 }
2402
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002403 if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) {
2404 /*
2405 * We are using PS-Poll and mac80211 can request TX while in
2406 * power save mode. Need to wake up hardware for the TX to be
2407 * completed and if needed, also for RX of buffered frames.
2408 */
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002409 ath9k_ps_wakeup(sc);
2410 ath9k_hw_setrxabort(sc->sc_ah, 0);
2411 if (ieee80211_is_pspoll(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002412 ath_print(common, ATH_DBG_PS,
2413 "Sending PS-Poll to pick a buffered frame\n");
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002414 sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA;
2415 } else {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002416 ath_print(common, ATH_DBG_PS,
2417 "Wake up to complete TX\n");
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002418 sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK;
2419 }
2420 /*
2421 * The actual restore operation will happen only after
2422 * the sc_flags bit is cleared. We are just dropping
2423 * the ps_usecount here.
2424 */
2425 ath9k_ps_restore(sc);
2426 }
2427
Sujith528f0c62008-10-29 10:14:26 +05302428 memset(&txctl, 0, sizeof(struct ath_tx_control));
Jouni Malinen147583c2008-08-11 14:01:50 +03002429
2430 /*
2431 * As a temporary workaround, assign seq# here; this will likely need
2432 * to be cleaned up to work better with Beacon transmission and virtual
2433 * BSSes.
2434 */
2435 if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
Jouni Malinen147583c2008-08-11 14:01:50 +03002436 if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
Sujithb77f4832008-12-07 21:44:03 +05302437 sc->tx.seq_no += 0x10;
Jouni Malinen147583c2008-08-11 14:01:50 +03002438 hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
Sujithb77f4832008-12-07 21:44:03 +05302439 hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
Jouni Malinen147583c2008-08-11 14:01:50 +03002440 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002441
2442 /* Add the padding after the header if this is not already done */
Benoit Papillault1bc14882009-11-24 15:49:18 +01002443 padpos = ath9k_cmn_padpos(hdr->frame_control);
2444 padsize = padpos & 3;
2445 if (padsize && skb->len>padpos) {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002446 if (skb_headroom(skb) < padsize)
2447 return -1;
2448 skb_push(skb, padsize);
Benoit Papillault1bc14882009-11-24 15:49:18 +01002449 memmove(skb->data, skb->data + padsize, padpos);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002450 }
2451
Sujith528f0c62008-10-29 10:14:26 +05302452 /* Check if a tx queue is available */
2453
2454 txctl.txq = ath_test_get_txq(sc, skb);
2455 if (!txctl.txq)
2456 goto exit;
2457
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002458 ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002459
Jouni Malinenc52f33d2009-03-03 19:23:29 +02002460 if (ath_tx_start(hw, skb, &txctl) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002461 ath_print(common, ATH_DBG_XMIT, "TX failed\n");
Sujith528f0c62008-10-29 10:14:26 +05302462 goto exit;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002463 }
2464
2465 return 0;
Sujith528f0c62008-10-29 10:14:26 +05302466exit:
2467 dev_kfree_skb_any(skb);
2468 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002469}
2470
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002471/*
2472 * Pause btcoex timer and bt duty cycle timer
2473 */
2474static void ath9k_btcoex_timer_pause(struct ath_softc *sc)
2475{
2476 struct ath_btcoex *btcoex = &sc->btcoex;
2477 struct ath_hw *ah = sc->sc_ah;
2478
2479 del_timer_sync(&btcoex->period_timer);
2480
2481 if (btcoex->hw_timer_enabled)
Luis R. Rodriguezcd9bf682009-09-13 02:08:34 -07002482 ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002483
2484 btcoex->hw_timer_enabled = false;
2485}
2486
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002487static void ath9k_stop(struct ieee80211_hw *hw)
2488{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002489 struct ath_wiphy *aphy = hw->priv;
2490 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002491 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002492 struct ath_common *common = ath9k_hw_common(ah);
Sujith9c84b792008-10-29 10:17:13 +05302493
Sujith4c483812009-08-18 10:51:52 +05302494 mutex_lock(&sc->mutex);
2495
Jouni Malinen9580a222009-03-03 19:23:33 +02002496 aphy->state = ATH_WIPHY_INACTIVE;
2497
Luis R. Rodriguezc94dbff2009-07-27 11:53:04 -07002498 cancel_delayed_work_sync(&sc->ath_led_blink_work);
2499 cancel_delayed_work_sync(&sc->tx_complete_work);
2500
2501 if (!sc->num_sec_wiphy) {
2502 cancel_delayed_work_sync(&sc->wiphy_work);
2503 cancel_work_sync(&sc->chan_work);
2504 }
2505
Sujith9c84b792008-10-29 10:17:13 +05302506 if (sc->sc_flags & SC_OP_INVALID) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002507 ath_print(common, ATH_DBG_ANY, "Device not present\n");
Sujith4c483812009-08-18 10:51:52 +05302508 mutex_unlock(&sc->mutex);
Sujith9c84b792008-10-29 10:17:13 +05302509 return;
2510 }
2511
Jouni Malinen9580a222009-03-03 19:23:33 +02002512 if (ath9k_wiphy_started(sc)) {
2513 mutex_unlock(&sc->mutex);
2514 return; /* another wiphy still in use */
2515 }
2516
Sujith3867cf62009-12-23 20:03:27 -05002517 /* Ensure HW is awake when we try to shut it down. */
2518 ath9k_ps_wakeup(sc);
2519
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002520 if (ah->btcoex_hw.enabled) {
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002521 ath9k_hw_btcoex_disable(ah);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07002522 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07002523 ath9k_btcoex_timer_pause(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05302524 }
2525
Sujithff37e332008-11-24 12:07:55 +05302526 /* make sure h/w will not generate any interrupt
2527 * before setting the invalid flag. */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002528 ath9k_hw_set_interrupts(ah, 0);
Sujithff37e332008-11-24 12:07:55 +05302529
2530 if (!(sc->sc_flags & SC_OP_INVALID)) {
Sujith043a0402009-01-16 21:38:47 +05302531 ath_drain_all_txq(sc, false);
Sujithff37e332008-11-24 12:07:55 +05302532 ath_stoprecv(sc);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002533 ath9k_hw_phy_disable(ah);
Sujithff37e332008-11-24 12:07:55 +05302534 } else
Sujithb77f4832008-12-07 21:44:03 +05302535 sc->rx.rxlink = NULL;
Sujithff37e332008-11-24 12:07:55 +05302536
Sujithff37e332008-11-24 12:07:55 +05302537 /* disable HAL and put h/w to sleep */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07002538 ath9k_hw_disable(ah);
2539 ath9k_hw_configpcipowersave(ah, 1, 1);
Sujith3867cf62009-12-23 20:03:27 -05002540 ath9k_ps_restore(sc);
2541
2542 /* Finally, put the chip in FULL SLEEP mode */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07002543 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Sujithff37e332008-11-24 12:07:55 +05302544
2545 sc->sc_flags |= SC_OP_INVALID;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002546
Sujith141b38b2009-02-04 08:10:07 +05302547 mutex_unlock(&sc->mutex);
2548
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002549 ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002550}
2551
2552static int ath9k_add_interface(struct ieee80211_hw *hw,
2553 struct ieee80211_if_init_conf *conf)
2554{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002555 struct ath_wiphy *aphy = hw->priv;
2556 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002557 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith17d79042009-02-09 13:27:03 +05302558 struct ath_vif *avp = (void *)conf->vif->drv_priv;
Colin McCabed97809d2008-12-01 13:38:55 -08002559 enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002560 int ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002561
Sujith141b38b2009-02-04 08:10:07 +05302562 mutex_lock(&sc->mutex);
2563
Jouni Malinen8ca21f02009-03-03 19:23:27 +02002564 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
2565 sc->nvifs > 0) {
2566 ret = -ENOBUFS;
2567 goto out;
2568 }
2569
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002570 switch (conf->type) {
Johannes Berg05c914f2008-09-11 00:01:58 +02002571 case NL80211_IFTYPE_STATION:
Colin McCabed97809d2008-12-01 13:38:55 -08002572 ic_opmode = NL80211_IFTYPE_STATION;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002573 break;
Johannes Berg05c914f2008-09-11 00:01:58 +02002574 case NL80211_IFTYPE_ADHOC:
Johannes Berg05c914f2008-09-11 00:01:58 +02002575 case NL80211_IFTYPE_AP:
Pat Erley9cb54122009-03-20 22:59:59 -04002576 case NL80211_IFTYPE_MESH_POINT:
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002577 if (sc->nbcnvifs >= ATH_BCBUF) {
2578 ret = -ENOBUFS;
2579 goto out;
2580 }
Pat Erley9cb54122009-03-20 22:59:59 -04002581 ic_opmode = conf->type;
Jouni Malinen2ad67de2008-08-11 14:01:47 +03002582 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002583 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002584 ath_print(common, ATH_DBG_FATAL,
Sujith04bd4632008-11-28 22:18:05 +05302585 "Interface type %d not yet supported\n", conf->type);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002586 ret = -EOPNOTSUPP;
2587 goto out;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002588 }
2589
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002590 ath_print(common, ATH_DBG_CONFIG,
2591 "Attach a VIF of type: %d\n", ic_opmode);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002592
Sujith17d79042009-02-09 13:27:03 +05302593 /* Set the VIF opmode */
Sujith5640b082008-10-29 10:16:06 +05302594 avp->av_opmode = ic_opmode;
2595 avp->av_bslot = -1;
2596
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002597 sc->nvifs++;
Jouni Malinen8ca21f02009-03-03 19:23:27 +02002598
2599 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
2600 ath9k_set_bssid_mask(hw);
2601
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002602 if (sc->nvifs > 1)
2603 goto out; /* skip global settings for secondary vif */
2604
Sujithb238e902009-03-03 10:16:56 +05302605 if (ic_opmode == NL80211_IFTYPE_AP) {
Sujith5640b082008-10-29 10:16:06 +05302606 ath9k_hw_set_tsfadjust(sc->sc_ah, 1);
Sujithb238e902009-03-03 10:16:56 +05302607 sc->sc_flags |= SC_OP_TSF_RESET;
2608 }
Sujith5640b082008-10-29 10:16:06 +05302609
Sujith5640b082008-10-29 10:16:06 +05302610 /* Set the device opmode */
Sujith2660b812009-02-09 13:27:26 +05302611 sc->sc_ah->opmode = ic_opmode;
Sujith5640b082008-10-29 10:16:06 +05302612
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05302613 /*
2614 * Enable MIB interrupts when there are hardware phy counters.
2615 * Note we only do this (at the moment) for station mode.
2616 */
Sujith4af9cf42009-02-12 10:06:47 +05302617 if ((conf->type == NL80211_IFTYPE_STATION) ||
Pat Erley9cb54122009-03-20 22:59:59 -04002618 (conf->type == NL80211_IFTYPE_ADHOC) ||
2619 (conf->type == NL80211_IFTYPE_MESH_POINT)) {
Sujith1aa8e842009-08-13 09:34:25 +05302620 sc->imask |= ATH9K_INT_MIB;
Sujith4af9cf42009-02-12 10:06:47 +05302621 sc->imask |= ATH9K_INT_TSFOOR;
2622 }
2623
Sujith17d79042009-02-09 13:27:03 +05302624 ath9k_hw_set_interrupts(sc->sc_ah, sc->imask);
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05302625
Senthil Balasubramanianf38faa32009-06-24 18:56:40 +05302626 if (conf->type == NL80211_IFTYPE_AP ||
2627 conf->type == NL80211_IFTYPE_ADHOC ||
2628 conf->type == NL80211_IFTYPE_MONITOR)
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08002629 ath_start_ani(common);
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07002630
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002631out:
Sujith141b38b2009-02-04 08:10:07 +05302632 mutex_unlock(&sc->mutex);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002633 return ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002634}
2635
2636static void ath9k_remove_interface(struct ieee80211_hw *hw,
2637 struct ieee80211_if_init_conf *conf)
2638{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002639 struct ath_wiphy *aphy = hw->priv;
2640 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002641 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith17d79042009-02-09 13:27:03 +05302642 struct ath_vif *avp = (void *)conf->vif->drv_priv;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002643 int i;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002644
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002645 ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002646
Sujith141b38b2009-02-04 08:10:07 +05302647 mutex_lock(&sc->mutex);
2648
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07002649 /* Stop ANI */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08002650 del_timer_sync(&common->ani.timer);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002651
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002652 /* Reclaim beacon resources */
Pat Erley9cb54122009-03-20 22:59:59 -04002653 if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
2654 (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
2655 (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
Sujithb77f4832008-12-07 21:44:03 +05302656 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002657 ath_beacon_return(sc, avp);
2658 }
2659
Sujith672840a2008-08-11 14:05:08 +05302660 sc->sc_flags &= ~SC_OP_BEACONS;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002661
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002662 for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
2663 if (sc->beacon.bslot[i] == conf->vif) {
2664 printk(KERN_DEBUG "%s: vif had allocated beacon "
2665 "slot\n", __func__);
2666 sc->beacon.bslot[i] = NULL;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02002667 sc->beacon.bslot_aphy[i] = NULL;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02002668 }
2669 }
2670
Sujith17d79042009-02-09 13:27:03 +05302671 sc->nvifs--;
Sujith141b38b2009-02-04 08:10:07 +05302672
2673 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002674}
2675
Johannes Berge8975582008-10-09 12:18:51 +02002676static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002677{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002678 struct ath_wiphy *aphy = hw->priv;
2679 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002680 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berge8975582008-10-09 12:18:51 +02002681 struct ieee80211_conf *conf = &hw->conf;
Vivek Natarajan8782b412009-03-30 14:17:00 +05302682 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002683 bool disable_radio;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002684
Sujithaa33de02008-12-18 11:40:16 +05302685 mutex_lock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05302686
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002687 /*
2688 * Leave this as the first check because we need to turn on the
2689 * radio if it was disabled before prior to processing the rest
2690 * of the changes. Likewise we must only disable the radio towards
2691 * the end.
2692 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002693 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002694 bool enable_radio;
2695 bool all_wiphys_idle;
2696 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002697
2698 spin_lock_bh(&sc->wiphy_lock);
2699 all_wiphys_idle = ath9k_all_wiphys_idle(sc);
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002700 ath9k_set_wiphy_idle(aphy, idle);
2701
2702 if (!idle && all_wiphys_idle)
2703 enable_radio = true;
2704
2705 /*
2706 * After we unlock here its possible another wiphy
2707 * can be re-renabled so to account for that we will
2708 * only disable the radio toward the end of this routine
2709 * if by then all wiphys are still idle.
2710 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002711 spin_unlock_bh(&sc->wiphy_lock);
2712
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002713 if (enable_radio) {
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08002714 ath_radio_enable(sc, hw);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002715 ath_print(common, ATH_DBG_CONFIG,
2716 "not-idle: enabling radio\n");
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002717 }
2718 }
2719
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002720 /*
2721 * We just prepare to enable PS. We have to wait until our AP has
2722 * ACK'd our null data frame to disable RX otherwise we'll ignore
2723 * those ACKs and end up retransmitting the same null data frames.
2724 * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
2725 */
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302726 if (changed & IEEE80211_CONF_CHANGE_PS) {
2727 if (conf->flags & IEEE80211_CONF_PS) {
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002728 sc->sc_flags |= SC_OP_PS_ENABLED;
Vivek Natarajan8782b412009-03-30 14:17:00 +05302729 if (!(ah->caps.hw_caps &
2730 ATH9K_HW_CAP_AUTOSLEEP)) {
2731 if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) {
2732 sc->imask |= ATH9K_INT_TIM_TIMER;
2733 ath9k_hw_set_interrupts(sc->sc_ah,
2734 sc->imask);
2735 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302736 }
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002737 /*
2738 * At this point we know hardware has received an ACK
2739 * of a previously sent null data frame.
2740 */
2741 if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) {
2742 sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED;
2743 sc->ps_enabled = true;
2744 ath9k_hw_setrxabort(sc->sc_ah, 1);
2745 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302746 } else {
Gabor Juhos96148322009-07-24 17:27:21 +02002747 sc->ps_enabled = false;
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05002748 sc->sc_flags &= ~(SC_OP_PS_ENABLED |
2749 SC_OP_NULLFUNC_COMPLETED);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07002750 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vivek Natarajan8782b412009-03-30 14:17:00 +05302751 if (!(ah->caps.hw_caps &
2752 ATH9K_HW_CAP_AUTOSLEEP)) {
2753 ath9k_hw_setrxabort(sc->sc_ah, 0);
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03002754 sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON |
2755 SC_OP_WAIT_FOR_CAB |
2756 SC_OP_WAIT_FOR_PSPOLL_DATA |
2757 SC_OP_WAIT_FOR_TX_ACK);
Vivek Natarajan8782b412009-03-30 14:17:00 +05302758 if (sc->imask & ATH9K_INT_TIM_TIMER) {
2759 sc->imask &= ~ATH9K_INT_TIM_TIMER;
2760 ath9k_hw_set_interrupts(sc->sc_ah,
2761 sc->imask);
2762 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302763 }
2764 }
2765 }
2766
Johannes Berg47979382009-01-07 10:13:27 +01002767 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
Sujith99405f92008-11-24 12:08:35 +05302768 struct ieee80211_channel *curchan = hw->conf.channel;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002769 int pos = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002770
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002771 aphy->chan_idx = pos;
2772 aphy->chan_is_ht = conf_is_ht(conf);
2773
Jouni Malinen8089cc42009-03-03 19:23:38 +02002774 if (aphy->state == ATH_WIPHY_SCAN ||
2775 aphy->state == ATH_WIPHY_ACTIVE)
2776 ath9k_wiphy_pause_all_forced(sc, aphy);
2777 else {
2778 /*
2779 * Do not change operational channel based on a paused
2780 * wiphy changes.
2781 */
2782 goto skip_chan_change;
2783 }
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002784
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002785 ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
2786 curchan->center_freq);
Johannes Bergae5eb022008-10-14 16:58:37 +02002787
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08002788 /* XXX: remove me eventualy */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002789 ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
Sujithe11602b2008-11-27 09:46:27 +05302790
Luis R. Rodriguezecf70442008-12-23 15:58:43 -08002791 ath_update_chainmask(sc, conf_is_ht(conf));
Sujith86060f02009-01-07 14:25:29 +05302792
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02002793 if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002794 ath_print(common, ATH_DBG_FATAL,
2795 "Unable to set channel\n");
Sujithaa33de02008-12-18 11:40:16 +05302796 mutex_unlock(&sc->mutex);
Sujithe11602b2008-11-27 09:46:27 +05302797 return -EINVAL;
2798 }
Sujith094d05d2008-12-12 11:57:43 +05302799 }
Sujith86b89ee2008-08-07 10:54:57 +05302800
Jouni Malinen8089cc42009-03-03 19:23:38 +02002801skip_chan_change:
Luis R. Rodriguez5c020dc2008-10-22 13:28:45 -07002802 if (changed & IEEE80211_CONF_CHANGE_POWER)
Sujith17d79042009-02-09 13:27:03 +05302803 sc->config.txpowlimit = 2 * conf->power_level;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002804
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07002805 spin_lock_bh(&sc->wiphy_lock);
2806 disable_radio = ath9k_all_wiphys_idle(sc);
2807 spin_unlock_bh(&sc->wiphy_lock);
2808
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002809 if (disable_radio) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002810 ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08002811 ath_radio_disable(sc, hw);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04002812 }
2813
Sujithaa33de02008-12-18 11:40:16 +05302814 mutex_unlock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05302815
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002816 return 0;
2817}
2818
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002819#define SUPPORTED_FILTERS \
2820 (FIF_PROMISC_IN_BSS | \
2821 FIF_ALLMULTI | \
2822 FIF_CONTROL | \
Luis R. Rodriguezaf6a3fc2009-08-08 21:55:16 -04002823 FIF_PSPOLL | \
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002824 FIF_OTHER_BSS | \
2825 FIF_BCN_PRBRESP_PROMISC | \
2826 FIF_FCSFAIL)
2827
Sujith7dcfdcd2008-08-11 14:03:13 +05302828/* FIXME: sc->sc_full_reset ? */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002829static void ath9k_configure_filter(struct ieee80211_hw *hw,
2830 unsigned int changed_flags,
2831 unsigned int *total_flags,
Johannes Berg3ac64be2009-08-17 16:16:53 +02002832 u64 multicast)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002833{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002834 struct ath_wiphy *aphy = hw->priv;
2835 struct ath_softc *sc = aphy->sc;
Sujith7dcfdcd2008-08-11 14:03:13 +05302836 u32 rfilt;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002837
2838 changed_flags &= SUPPORTED_FILTERS;
2839 *total_flags &= SUPPORTED_FILTERS;
2840
Sujithb77f4832008-12-07 21:44:03 +05302841 sc->rx.rxfilter = *total_flags;
Jouni Malinenaa68aea2009-05-19 17:01:41 +03002842 ath9k_ps_wakeup(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05302843 rfilt = ath_calcrxfilter(sc);
2844 ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
Jouni Malinenaa68aea2009-05-19 17:01:41 +03002845 ath9k_ps_restore(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05302846
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002847 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
2848 "Set HW RX filter: 0x%x\n", rfilt);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002849}
2850
2851static void ath9k_sta_notify(struct ieee80211_hw *hw,
2852 struct ieee80211_vif *vif,
2853 enum sta_notify_cmd cmd,
Johannes Berg17741cd2008-09-11 00:02:02 +02002854 struct ieee80211_sta *sta)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002855{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002856 struct ath_wiphy *aphy = hw->priv;
2857 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002858
2859 switch (cmd) {
2860 case STA_NOTIFY_ADD:
Sujith5640b082008-10-29 10:16:06 +05302861 ath_node_attach(sc, sta);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002862 break;
2863 case STA_NOTIFY_REMOVE:
Sujithb5aa9bf2008-10-29 10:13:31 +05302864 ath_node_detach(sc, sta);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002865 break;
2866 default:
2867 break;
2868 }
2869}
2870
Sujith141b38b2009-02-04 08:10:07 +05302871static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002872 const struct ieee80211_tx_queue_params *params)
2873{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002874 struct ath_wiphy *aphy = hw->priv;
2875 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002876 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujithea9880f2008-08-07 10:53:10 +05302877 struct ath9k_tx_queue_info qi;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002878 int ret = 0, qnum;
2879
2880 if (queue >= WME_NUM_AC)
2881 return 0;
2882
Sujith141b38b2009-02-04 08:10:07 +05302883 mutex_lock(&sc->mutex);
2884
Sujith1ffb0612009-03-30 15:28:46 +05302885 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
2886
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002887 qi.tqi_aifs = params->aifs;
2888 qi.tqi_cwmin = params->cw_min;
2889 qi.tqi_cwmax = params->cw_max;
2890 qi.tqi_burstTime = params->txop;
2891 qnum = ath_get_hal_qnum(queue, sc);
2892
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002893 ath_print(common, ATH_DBG_CONFIG,
2894 "Configure tx [queue/halq] [%d/%d], "
2895 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
2896 queue, qnum, params->aifs, params->cw_min,
2897 params->cw_max, params->txop);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002898
2899 ret = ath_txq_update(sc, qnum, &qi);
2900 if (ret)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002901 ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002902
Vivek Natarajan94db2932009-11-25 12:01:54 +05302903 if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)
2904 if ((qnum == sc->tx.hwq_map[ATH9K_WME_AC_BE]) && !ret)
2905 ath_beaconq_config(sc);
2906
Sujith141b38b2009-02-04 08:10:07 +05302907 mutex_unlock(&sc->mutex);
2908
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002909 return ret;
2910}
2911
2912static int ath9k_set_key(struct ieee80211_hw *hw,
2913 enum set_key_cmd cmd,
Johannes Bergdc822b52008-12-29 12:55:09 +01002914 struct ieee80211_vif *vif,
2915 struct ieee80211_sta *sta,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002916 struct ieee80211_key_conf *key)
2917{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002918 struct ath_wiphy *aphy = hw->priv;
2919 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002920 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002921 int ret = 0;
2922
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02002923 if (modparam_nohwcrypt)
2924 return -ENOSPC;
2925
Sujith141b38b2009-02-04 08:10:07 +05302926 mutex_lock(&sc->mutex);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302927 ath9k_ps_wakeup(sc);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07002928 ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002929
2930 switch (cmd) {
2931 case SET_KEY:
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08002932 ret = ath_key_config(common, vif, sta, key);
Jouni Malinen6ace2892008-12-17 13:32:17 +02002933 if (ret >= 0) {
2934 key->hw_key_idx = ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002935 /* push IV and Michael MIC generation to stack */
2936 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Senthil Balasubramanian1b961752008-09-01 19:45:21 +05302937 if (key->alg == ALG_TKIP)
2938 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Jouni Malinen0ced0e12009-01-08 13:32:13 +02002939 if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
2940 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
Jouni Malinen6ace2892008-12-17 13:32:17 +02002941 ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002942 }
2943 break;
2944 case DISABLE_KEY:
Luis R. Rodriguez7e86c102009-11-04 17:21:01 -08002945 ath_key_delete(common, key);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002946 break;
2947 default:
2948 ret = -EINVAL;
2949 }
2950
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05302951 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05302952 mutex_unlock(&sc->mutex);
2953
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002954 return ret;
2955}
2956
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002957static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
2958 struct ieee80211_vif *vif,
2959 struct ieee80211_bss_conf *bss_conf,
2960 u32 changed)
2961{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002962 struct ath_wiphy *aphy = hw->priv;
2963 struct ath_softc *sc = aphy->sc;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002964 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07002965 struct ath_common *common = ath9k_hw_common(ah);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002966 struct ath_vif *avp = (void *)vif->drv_priv;
Sujithc6089cc2009-11-16 11:40:48 +05302967 int error;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002968
Sujith141b38b2009-02-04 08:10:07 +05302969 mutex_lock(&sc->mutex);
2970
Sujithc6089cc2009-11-16 11:40:48 +05302971 if (changed & BSS_CHANGED_BSSID) {
2972 /* Set BSSID */
2973 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
2974 memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
Luis R. Rodriguez15107182009-09-10 09:22:37 -07002975 common->curaid = 0;
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -07002976 ath9k_hw_write_associd(ah);
Sujithc6089cc2009-11-16 11:40:48 +05302977
2978 /* Set aggregation protection mode parameters */
2979 sc->config.ath_aggr_prot = 0;
2980
2981 /* Only legacy IBSS for now */
2982 if (vif->type == NL80211_IFTYPE_ADHOC)
2983 ath_update_chainmask(sc, 0);
2984
2985 ath_print(common, ATH_DBG_CONFIG,
2986 "BSSID: %pM aid: 0x%x\n",
2987 common->curbssid, common->curaid);
2988
2989 /* need to reconfigure the beacon */
2990 sc->sc_flags &= ~SC_OP_BEACONS ;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02002991 }
2992
Sujithc6089cc2009-11-16 11:40:48 +05302993 /* Enable transmission of beacons (AP, IBSS, MESH) */
2994 if ((changed & BSS_CHANGED_BEACON) ||
2995 ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) {
2996 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
2997 error = ath_beacon_alloc(aphy, vif);
2998 if (!error)
2999 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02003000 }
3001
Sujithc6089cc2009-11-16 11:40:48 +05303002 /* Disable transmission of beacons */
3003 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon)
3004 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
3005
3006 if (changed & BSS_CHANGED_BEACON_INT) {
3007 sc->beacon_interval = bss_conf->beacon_int;
3008 /*
3009 * In case of AP mode, the HW TSF has to be reset
3010 * when the beacon interval changes.
3011 */
3012 if (vif->type == NL80211_IFTYPE_AP) {
3013 sc->sc_flags |= SC_OP_TSF_RESET;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02003014 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02003015 error = ath_beacon_alloc(aphy, vif);
3016 if (!error)
3017 ath_beacon_config(sc, vif);
Sujithc6089cc2009-11-16 11:40:48 +05303018 } else {
3019 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02003020 }
3021 }
3022
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003023 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003024 ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
3025 bss_conf->use_short_preamble);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003026 if (bss_conf->use_short_preamble)
Sujith672840a2008-08-11 14:05:08 +05303027 sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003028 else
Sujith672840a2008-08-11 14:05:08 +05303029 sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003030 }
3031
3032 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003033 ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
3034 bss_conf->use_cts_prot);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003035 if (bss_conf->use_cts_prot &&
3036 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
Sujith672840a2008-08-11 14:05:08 +05303037 sc->sc_flags |= SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003038 else
Sujith672840a2008-08-11 14:05:08 +05303039 sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003040 }
3041
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003042 if (changed & BSS_CHANGED_ASSOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003043 ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003044 bss_conf->assoc);
Sujith5640b082008-10-29 10:16:06 +05303045 ath9k_bss_assoc_info(sc, vif, bss_conf);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003046 }
Sujith141b38b2009-02-04 08:10:07 +05303047
3048 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003049}
3050
3051static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
3052{
3053 u64 tsf;
Jouni Malinenbce048d2009-03-03 19:23:28 +02003054 struct ath_wiphy *aphy = hw->priv;
3055 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003056
Sujith141b38b2009-02-04 08:10:07 +05303057 mutex_lock(&sc->mutex);
3058 tsf = ath9k_hw_gettsf64(sc->sc_ah);
3059 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003060
3061 return tsf;
3062}
3063
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003064static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
3065{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003066 struct ath_wiphy *aphy = hw->priv;
3067 struct ath_softc *sc = aphy->sc;
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003068
Sujith141b38b2009-02-04 08:10:07 +05303069 mutex_lock(&sc->mutex);
3070 ath9k_hw_settsf64(sc->sc_ah, tsf);
3071 mutex_unlock(&sc->mutex);
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003072}
3073
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003074static void ath9k_reset_tsf(struct ieee80211_hw *hw)
3075{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003076 struct ath_wiphy *aphy = hw->priv;
3077 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003078
Sujith141b38b2009-02-04 08:10:07 +05303079 mutex_lock(&sc->mutex);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07003080
3081 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05303082 ath9k_hw_reset_tsf(sc->sc_ah);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07003083 ath9k_ps_restore(sc);
3084
Sujith141b38b2009-02-04 08:10:07 +05303085 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003086}
3087
3088static int ath9k_ampdu_action(struct ieee80211_hw *hw,
Johannes Bergc951ad32009-11-16 12:00:38 +01003089 struct ieee80211_vif *vif,
Sujith141b38b2009-02-04 08:10:07 +05303090 enum ieee80211_ampdu_mlme_action action,
3091 struct ieee80211_sta *sta,
3092 u16 tid, u16 *ssn)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003093{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003094 struct ath_wiphy *aphy = hw->priv;
3095 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003096 int ret = 0;
3097
3098 switch (action) {
3099 case IEEE80211_AMPDU_RX_START:
Sujithdca3edb2008-10-29 10:19:01 +05303100 if (!(sc->sc_flags & SC_OP_RXAGGR))
3101 ret = -ENOTSUPP;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003102 break;
3103 case IEEE80211_AMPDU_RX_STOP:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003104 break;
3105 case IEEE80211_AMPDU_TX_START:
Sujithf83da962009-07-23 15:32:37 +05303106 ath_tx_aggr_start(sc, sta, tid, ssn);
Johannes Bergc951ad32009-11-16 12:00:38 +01003107 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003108 break;
3109 case IEEE80211_AMPDU_TX_STOP:
Sujithf83da962009-07-23 15:32:37 +05303110 ath_tx_aggr_stop(sc, sta, tid);
Johannes Bergc951ad32009-11-16 12:00:38 +01003111 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003112 break;
Johannes Bergb1720232009-03-23 17:28:39 +01003113 case IEEE80211_AMPDU_TX_OPERATIONAL:
Sujith8469cde2008-10-29 10:19:28 +05303114 ath_tx_aggr_resume(sc, sta, tid);
3115 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003116 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07003117 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
3118 "Unknown AMPDU action\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003119 }
3120
3121 return ret;
3122}
3123
Sujith0c98de62009-03-03 10:16:45 +05303124static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
3125{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003126 struct ath_wiphy *aphy = hw->priv;
3127 struct ath_softc *sc = aphy->sc;
Sujith0c98de62009-03-03 10:16:45 +05303128
Sujith3d832612009-08-21 12:00:28 +05303129 mutex_lock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02003130 if (ath9k_wiphy_scanning(sc)) {
3131 printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the "
3132 "same time\n");
3133 /*
3134 * Do not allow the concurrent scanning state for now. This
3135 * could be improved with scanning control moved into ath9k.
3136 */
Sujith3d832612009-08-21 12:00:28 +05303137 mutex_unlock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02003138 return;
3139 }
3140
3141 aphy->state = ATH_WIPHY_SCAN;
3142 ath9k_wiphy_pause_all_forced(sc, aphy);
3143
Senthil Balasubramaniane5f09212009-06-24 18:56:41 +05303144 spin_lock_bh(&sc->ani_lock);
Sujith0c98de62009-03-03 10:16:45 +05303145 sc->sc_flags |= SC_OP_SCANNING;
Senthil Balasubramaniane5f09212009-06-24 18:56:41 +05303146 spin_unlock_bh(&sc->ani_lock);
Sujith3d832612009-08-21 12:00:28 +05303147 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05303148}
3149
3150static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
3151{
Jouni Malinenbce048d2009-03-03 19:23:28 +02003152 struct ath_wiphy *aphy = hw->priv;
3153 struct ath_softc *sc = aphy->sc;
Sujith0c98de62009-03-03 10:16:45 +05303154
Sujith3d832612009-08-21 12:00:28 +05303155 mutex_lock(&sc->mutex);
Senthil Balasubramaniane5f09212009-06-24 18:56:41 +05303156 spin_lock_bh(&sc->ani_lock);
Jouni Malinen8089cc42009-03-03 19:23:38 +02003157 aphy->state = ATH_WIPHY_ACTIVE;
Sujith0c98de62009-03-03 10:16:45 +05303158 sc->sc_flags &= ~SC_OP_SCANNING;
Sujith9c07a772009-04-13 21:56:36 +05303159 sc->sc_flags |= SC_OP_FULL_RESET;
Senthil Balasubramaniane5f09212009-06-24 18:56:41 +05303160 spin_unlock_bh(&sc->ani_lock);
Vivek Natarajand0bec342009-09-02 15:50:55 +05303161 ath_beacon_config(sc, NULL);
Sujith3d832612009-08-21 12:00:28 +05303162 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05303163}
3164
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003165struct ieee80211_ops ath9k_ops = {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003166 .tx = ath9k_tx,
3167 .start = ath9k_start,
3168 .stop = ath9k_stop,
3169 .add_interface = ath9k_add_interface,
3170 .remove_interface = ath9k_remove_interface,
3171 .config = ath9k_config,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003172 .configure_filter = ath9k_configure_filter,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003173 .sta_notify = ath9k_sta_notify,
3174 .conf_tx = ath9k_conf_tx,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003175 .bss_info_changed = ath9k_bss_info_changed,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003176 .set_key = ath9k_set_key,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003177 .get_tsf = ath9k_get_tsf,
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01003178 .set_tsf = ath9k_set_tsf,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003179 .reset_tsf = ath9k_reset_tsf,
Johannes Berg4233df62008-10-13 13:35:05 +02003180 .ampdu_action = ath9k_ampdu_action,
Sujith0c98de62009-03-03 10:16:45 +05303181 .sw_scan_start = ath9k_sw_scan_start,
3182 .sw_scan_complete = ath9k_sw_scan_complete,
Johannes Berg3b319aa2009-06-13 14:50:26 +05303183 .rfkill_poll = ath9k_rfkill_poll_state,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003184};
3185
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003186static int __init ath9k_init(void)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003187{
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303188 int error;
3189
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303190 /* Register rate control algorithm */
3191 error = ath_rate_control_register();
3192 if (error != 0) {
3193 printk(KERN_ERR
Luis R. Rodriguezb51bb3c2009-01-26 07:30:03 -08003194 "ath9k: Unable to register rate control "
3195 "algorithm: %d\n",
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303196 error);
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003197 goto err_out;
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303198 }
3199
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003200 error = ath9k_debug_create_root();
3201 if (error) {
3202 printk(KERN_ERR
3203 "ath9k: Unable to create debugfs root: %d\n",
3204 error);
3205 goto err_rate_unregister;
3206 }
3207
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003208 error = ath_pci_init();
3209 if (error < 0) {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003210 printk(KERN_ERR
Luis R. Rodriguezb51bb3c2009-01-26 07:30:03 -08003211 "ath9k: No PCI devices found, driver not installed.\n");
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003212 error = -ENODEV;
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003213 goto err_remove_root;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003214 }
3215
Gabor Juhos09329d32009-01-14 20:17:07 +01003216 error = ath_ahb_init();
3217 if (error < 0) {
3218 error = -ENODEV;
3219 goto err_pci_exit;
3220 }
3221
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003222 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003223
Gabor Juhos09329d32009-01-14 20:17:07 +01003224 err_pci_exit:
3225 ath_pci_exit();
3226
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003227 err_remove_root:
3228 ath9k_debug_remove_root();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003229 err_rate_unregister:
Vasanthakumar Thiagarajanca8a8562008-12-16 12:37:38 +05303230 ath_rate_control_unregister();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003231 err_out:
3232 return error;
3233}
3234module_init(ath9k_init);
3235
3236static void __exit ath9k_exit(void)
3237{
Gabor Juhos09329d32009-01-14 20:17:07 +01003238 ath_ahb_exit();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003239 ath_pci_exit();
Gabor Juhos19d8bc22009-03-05 16:55:18 +01003240 ath9k_debug_remove_root();
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003241 ath_rate_control_unregister();
Sujith04bd4632008-11-28 22:18:05 +05303242 printk(KERN_INFO "%s: Driver unloaded\n", dev_info);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07003243}
Gabor Juhos6baff7f2009-01-14 20:17:06 +01003244module_exit(ath9k_exit);