blob: 8387ad5b4897288bcc423f05eaf616f575177b7f [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. Rodriguezce111ba2008-12-23 15:58:39 -080021static void ath_cache_conf_rate(struct ath_softc *sc,
22 struct ieee80211_conf *conf)
Sujithff37e332008-11-24 12:07:55 +053023{
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080024 switch (conf->channel->band) {
25 case IEEE80211_BAND_2GHZ:
26 if (conf_is_ht20(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +010027 sc->cur_rate_mode = ATH9K_MODE_11NG_HT20;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080028 else if (conf_is_ht40_minus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +010029 sc->cur_rate_mode = ATH9K_MODE_11NG_HT40MINUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080030 else if (conf_is_ht40_plus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +010031 sc->cur_rate_mode = ATH9K_MODE_11NG_HT40PLUS;
Luis R. Rodriguez96742252008-12-23 15:58:38 -080032 else
Felix Fietkau545750d2009-11-23 22:21:01 +010033 sc->cur_rate_mode = ATH9K_MODE_11G;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080034 break;
35 case IEEE80211_BAND_5GHZ:
36 if (conf_is_ht20(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +010037 sc->cur_rate_mode = ATH9K_MODE_11NA_HT20;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080038 else if (conf_is_ht40_minus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +010039 sc->cur_rate_mode = ATH9K_MODE_11NA_HT40MINUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080040 else if (conf_is_ht40_plus(conf))
Felix Fietkau545750d2009-11-23 22:21:01 +010041 sc->cur_rate_mode = ATH9K_MODE_11NA_HT40PLUS;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080042 else
Felix Fietkau545750d2009-11-23 22:21:01 +010043 sc->cur_rate_mode = ATH9K_MODE_11A;
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080044 break;
45 default:
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -080046 BUG_ON(1);
Luis R. Rodriguez030bb492008-12-23 15:58:37 -080047 break;
48 }
Sujithff37e332008-11-24 12:07:55 +053049}
50
51static void ath_update_txpow(struct ath_softc *sc)
52{
Sujithcbe61d82009-02-09 13:27:12 +053053 struct ath_hw *ah = sc->sc_ah;
Sujithff37e332008-11-24 12:07:55 +053054
Sujith17d79042009-02-09 13:27:03 +053055 if (sc->curtxpow != sc->config.txpowlimit) {
56 ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit);
Sujithff37e332008-11-24 12:07:55 +053057 /* read back in case value is clamped */
Felix Fietkau9cc32712010-06-12 17:22:29 +020058 sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
Sujithff37e332008-11-24 12:07:55 +053059 }
60}
61
62static u8 parse_mpdudensity(u8 mpdudensity)
63{
64 /*
65 * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
66 * 0 for no restriction
67 * 1 for 1/4 us
68 * 2 for 1/2 us
69 * 3 for 1 us
70 * 4 for 2 us
71 * 5 for 4 us
72 * 6 for 8 us
73 * 7 for 16 us
74 */
75 switch (mpdudensity) {
76 case 0:
77 return 0;
78 case 1:
79 case 2:
80 case 3:
81 /* Our lower layer calculations limit our precision to
82 1 microsecond */
83 return 1;
84 case 4:
85 return 2;
86 case 5:
87 return 4;
88 case 6:
89 return 8;
90 case 7:
91 return 16;
92 default:
93 return 0;
94 }
95}
96
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +053097static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc,
98 struct ieee80211_hw *hw)
99{
100 struct ieee80211_channel *curchan = hw->conf.channel;
101 struct ath9k_channel *channel;
102 u8 chan_idx;
103
104 chan_idx = curchan->hw_value;
105 channel = &sc->sc_ah->channels[chan_idx];
106 ath9k_update_ichannel(sc, hw, channel);
107 return channel;
108}
109
Sujith55624202010-01-08 10:36:02 +0530110bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -0700111{
112 unsigned long flags;
113 bool ret;
114
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700115 spin_lock_irqsave(&sc->sc_pm_lock, flags);
116 ret = ath9k_hw_setpower(sc->sc_ah, mode);
117 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -0700118
119 return ret;
120}
121
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700122void ath9k_ps_wakeup(struct ath_softc *sc)
123{
124 unsigned long flags;
125
126 spin_lock_irqsave(&sc->sc_pm_lock, flags);
127 if (++sc->ps_usecount != 1)
128 goto unlock;
129
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700130 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700131
132 unlock:
133 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
134}
135
136void ath9k_ps_restore(struct ath_softc *sc)
137{
138 unsigned long flags;
139
140 spin_lock_irqsave(&sc->sc_pm_lock, flags);
141 if (--sc->ps_usecount != 0)
142 goto unlock;
143
Vivek Natarajan1dbfd9d2010-01-29 16:56:51 +0530144 if (sc->ps_idle)
145 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
146 else if (sc->ps_enabled &&
147 !(sc->ps_flags & (PS_WAIT_FOR_BEACON |
Sujith1b04b932010-01-08 10:36:05 +0530148 PS_WAIT_FOR_CAB |
149 PS_WAIT_FOR_PSPOLL_DATA |
150 PS_WAIT_FOR_TX_ACK)))
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700151 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700152
153 unlock:
154 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
155}
156
Felix Fietkau5ee08652010-07-31 00:11:59 +0200157static void ath_start_ani(struct ath_common *common)
158{
159 struct ath_hw *ah = common->ah;
160 unsigned long timestamp = jiffies_to_msecs(jiffies);
161 struct ath_softc *sc = (struct ath_softc *) common->priv;
162
163 if (!(sc->sc_flags & SC_OP_ANI_RUN))
164 return;
165
166 if (sc->sc_flags & SC_OP_OFFCHANNEL)
167 return;
168
169 common->ani.longcal_timer = timestamp;
170 common->ani.shortcal_timer = timestamp;
171 common->ani.checkani_timer = timestamp;
172
173 mod_timer(&common->ani.timer,
174 jiffies +
175 msecs_to_jiffies((u32)ah->config.ani_poll_interval));
176}
177
Sujithff37e332008-11-24 12:07:55 +0530178/*
179 * Set/change channels. If the channel is really being changed, it's done
180 * by reseting the chip. To accomplish this we must first cleanup any pending
181 * DMA, then restart stuff.
182*/
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200183int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
184 struct ath9k_channel *hchan)
Sujithff37e332008-11-24 12:07:55 +0530185{
Sujithcbe61d82009-02-09 13:27:12 +0530186 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700187 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700188 struct ieee80211_conf *conf = &common->hw->conf;
Sujithff37e332008-11-24 12:07:55 +0530189 bool fastcc = true, stopped;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800190 struct ieee80211_channel *channel = hw->conf.channel;
191 int r;
Sujithff37e332008-11-24 12:07:55 +0530192
193 if (sc->sc_flags & SC_OP_INVALID)
194 return -EIO;
195
Felix Fietkau5ee08652010-07-31 00:11:59 +0200196 del_timer_sync(&common->ani.timer);
197 cancel_work_sync(&sc->paprd_work);
198 cancel_work_sync(&sc->hw_check_work);
199 cancel_delayed_work_sync(&sc->tx_complete_work);
200
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530201 ath9k_ps_wakeup(sc);
202
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800203 /*
204 * This is only performed if the channel settings have
205 * actually changed.
206 *
207 * To switch channels clear any pending DMA operations;
208 * wait long enough for the RX fifo to drain, reset the
209 * hardware at the new frequency, and then re-enable
210 * the relevant bits of the h/w.
211 */
212 ath9k_hw_set_interrupts(ah, 0);
Sujith043a0402009-01-16 21:38:47 +0530213 ath_drain_all_txq(sc, false);
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800214 stopped = ath_stoprecv(sc);
Sujithff37e332008-11-24 12:07:55 +0530215
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800216 /* XXX: do not flush receive queue here. We don't want
217 * to flush data frames already in queue because of
218 * changing channel. */
Sujithff37e332008-11-24 12:07:55 +0530219
Felix Fietkau5ee08652010-07-31 00:11:59 +0200220 if (!stopped || !(sc->sc_flags & SC_OP_OFFCHANNEL))
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800221 fastcc = false;
Sujithff37e332008-11-24 12:07:55 +0530222
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700223 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700224 "(%u MHz) -> (%u MHz), conf_is_ht40: %d\n",
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700225 sc->sc_ah->curchan->channel,
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -0700226 channel->center_freq, conf_is_ht40(conf));
Sujith99405f92008-11-24 12:08:35 +0530227
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800228 spin_lock_bh(&sc->sc_resetlock);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800229
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800230 r = ath9k_hw_reset(ah, hchan, fastcc);
231 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700232 ath_print(common, ATH_DBG_FATAL,
Pavel Roskinf643e512010-01-29 17:22:12 -0500233 "Unable to reset channel (%u MHz), "
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700234 "reset status %d\n",
235 channel->center_freq, r);
Sujithff37e332008-11-24 12:07:55 +0530236 spin_unlock_bh(&sc->sc_resetlock);
Gabor Juhos39892792009-06-15 17:49:09 +0200237 goto ps_restore;
Sujithff37e332008-11-24 12:07:55 +0530238 }
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800239 spin_unlock_bh(&sc->sc_resetlock);
240
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800241 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700242 ath_print(common, ATH_DBG_FATAL,
243 "Unable to restart recv logic\n");
Gabor Juhos39892792009-06-15 17:49:09 +0200244 r = -EIO;
245 goto ps_restore;
Luis R. Rodriguezc0d7c7a2008-12-23 15:58:50 -0800246 }
247
248 ath_cache_conf_rate(sc, &hw->conf);
249 ath_update_txpow(sc);
Pavel Roskin30691682010-03-31 18:05:31 -0400250 ath9k_hw_set_interrupts(ah, ah->imask);
Gabor Juhos39892792009-06-15 17:49:09 +0200251
Felix Fietkau5ee08652010-07-31 00:11:59 +0200252 if (!(sc->sc_flags & (SC_OP_OFFCHANNEL | SC_OP_SCANNING))) {
253 ath_start_ani(common);
254 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
255 ath_beacon_config(sc, NULL);
256 }
257
Gabor Juhos39892792009-06-15 17:49:09 +0200258 ps_restore:
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530259 ath9k_ps_restore(sc);
Gabor Juhos39892792009-06-15 17:49:09 +0200260 return r;
Sujithff37e332008-11-24 12:07:55 +0530261}
262
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400263static void ath_paprd_activate(struct ath_softc *sc)
264{
265 struct ath_hw *ah = sc->sc_ah;
266 int chain;
267
268 if (!ah->curchan->paprd_done)
269 return;
270
271 ath9k_ps_wakeup(sc);
Felix Fietkauddfef7922010-07-30 21:02:11 +0200272 ar9003_paprd_enable(ah, false);
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400273 for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
274 if (!(ah->caps.tx_chainmask & BIT(chain)))
275 continue;
276
277 ar9003_paprd_populate_single_table(ah, ah->curchan, chain);
278 }
279
280 ar9003_paprd_enable(ah, true);
281 ath9k_ps_restore(sc);
282}
283
284void ath_paprd_calibrate(struct work_struct *work)
285{
286 struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work);
287 struct ieee80211_hw *hw = sc->hw;
288 struct ath_hw *ah = sc->sc_ah;
289 struct ieee80211_hdr *hdr;
290 struct sk_buff *skb = NULL;
291 struct ieee80211_tx_info *tx_info;
292 int band = hw->conf.channel->band;
293 struct ieee80211_supported_band *sband = &sc->sbands[band];
294 struct ath_tx_control txctl;
295 int qnum, ftype;
296 int chain_ok = 0;
297 int chain;
298 int len = 1800;
299 int time_left;
300 int i;
301
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400302 skb = alloc_skb(len, GFP_KERNEL);
303 if (!skb)
304 return;
305
306 tx_info = IEEE80211_SKB_CB(skb);
307
308 skb_put(skb, len);
309 memset(skb->data, 0, len);
310 hdr = (struct ieee80211_hdr *)skb->data;
311 ftype = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC;
312 hdr->frame_control = cpu_to_le16(ftype);
John W. Linvillea3d3da12010-07-20 13:15:31 -0400313 hdr->duration_id = cpu_to_le16(10);
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400314 memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN);
315 memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN);
316 memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
317
318 memset(&txctl, 0, sizeof(txctl));
319 qnum = sc->tx.hwq_map[WME_AC_BE];
320 txctl.txq = &sc->tx.txq[qnum];
321
Vasanthakumar Thiagarajan47399f12010-06-24 04:09:27 -0700322 ath9k_ps_wakeup(sc);
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400323 ar9003_paprd_init_table(ah);
324 for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
325 if (!(ah->caps.tx_chainmask & BIT(chain)))
326 continue;
327
328 chain_ok = 0;
329 memset(tx_info, 0, sizeof(*tx_info));
330 tx_info->band = band;
331
332 for (i = 0; i < 4; i++) {
333 tx_info->control.rates[i].idx = sband->n_bitrates - 1;
334 tx_info->control.rates[i].count = 6;
335 }
336
337 init_completion(&sc->paprd_complete);
338 ar9003_paprd_setup_gain_table(ah, chain);
339 txctl.paprd = BIT(chain);
340 if (ath_tx_start(hw, skb, &txctl) != 0)
341 break;
342
343 time_left = wait_for_completion_timeout(&sc->paprd_complete,
Vasanthakumar Thiagarajanca369eb2010-06-24 02:42:44 -0700344 msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400345 if (!time_left) {
346 ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
347 "Timeout waiting for paprd training on "
348 "TX chain %d\n",
349 chain);
Vasanthakumar Thiagarajanca369eb2010-06-24 02:42:44 -0700350 goto fail_paprd;
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400351 }
352
353 if (!ar9003_paprd_is_done(ah))
354 break;
355
356 if (ar9003_paprd_create_curve(ah, ah->curchan, chain) != 0)
357 break;
358
359 chain_ok = 1;
360 }
361 kfree_skb(skb);
362
363 if (chain_ok) {
364 ah->curchan->paprd_done = true;
365 ath_paprd_activate(sc);
366 }
367
Vasanthakumar Thiagarajanca369eb2010-06-24 02:42:44 -0700368fail_paprd:
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400369 ath9k_ps_restore(sc);
370}
371
Sujithff37e332008-11-24 12:07:55 +0530372/*
373 * This routine performs the periodic noise floor calibration function
374 * that is used to adjust and optimize the chip performance. This
375 * takes environmental changes (location, temperature) into account.
376 * When the task is complete, it reschedules itself depending on the
377 * appropriate interval that was calculated.
378 */
Sujith55624202010-01-08 10:36:02 +0530379void ath_ani_calibrate(unsigned long data)
Sujithff37e332008-11-24 12:07:55 +0530380{
Sujith20977d32009-02-20 15:13:28 +0530381 struct ath_softc *sc = (struct ath_softc *)data;
382 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700383 struct ath_common *common = ath9k_hw_common(ah);
Sujithff37e332008-11-24 12:07:55 +0530384 bool longcal = false;
385 bool shortcal = false;
386 bool aniflag = false;
387 unsigned int timestamp = jiffies_to_msecs(jiffies);
Sujith20977d32009-02-20 15:13:28 +0530388 u32 cal_interval, short_cal_interval;
Sujithff37e332008-11-24 12:07:55 +0530389
Sujith20977d32009-02-20 15:13:28 +0530390 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
391 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithff37e332008-11-24 12:07:55 +0530392
Jouni Malinen1ffc1c62009-05-19 17:01:39 +0300393 /* Only calibrate if awake */
394 if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
395 goto set_timer;
396
397 ath9k_ps_wakeup(sc);
398
Sujithff37e332008-11-24 12:07:55 +0530399 /* Long calibration runs independently of short calibration. */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800400 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
Sujithff37e332008-11-24 12:07:55 +0530401 longcal = true;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700402 ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800403 common->ani.longcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530404 }
405
Sujith17d79042009-02-09 13:27:03 +0530406 /* Short calibration applies only while caldone is false */
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800407 if (!common->ani.caldone) {
408 if ((timestamp - common->ani.shortcal_timer) >= short_cal_interval) {
Sujithff37e332008-11-24 12:07:55 +0530409 shortcal = true;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700410 ath_print(common, ATH_DBG_ANI,
411 "shortcal @%lu\n", jiffies);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800412 common->ani.shortcal_timer = timestamp;
413 common->ani.resetcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530414 }
415 } else {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800416 if ((timestamp - common->ani.resetcal_timer) >=
Sujithff37e332008-11-24 12:07:55 +0530417 ATH_RESTART_CALINTERVAL) {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800418 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
419 if (common->ani.caldone)
420 common->ani.resetcal_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530421 }
422 }
423
424 /* Verify whether we must check ANI */
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400425 if ((timestamp - common->ani.checkani_timer) >=
426 ah->config.ani_poll_interval) {
Sujithff37e332008-11-24 12:07:55 +0530427 aniflag = true;
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800428 common->ani.checkani_timer = timestamp;
Sujithff37e332008-11-24 12:07:55 +0530429 }
430
431 /* Skip all processing if there's nothing to do. */
432 if (longcal || shortcal || aniflag) {
433 /* Call ANI routine if necessary */
434 if (aniflag)
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530435 ath9k_hw_ani_monitor(ah, ah->curchan);
Sujithff37e332008-11-24 12:07:55 +0530436
437 /* Perform calibration if necessary */
438 if (longcal || shortcal) {
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800439 common->ani.caldone =
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700440 ath9k_hw_calibrate(ah,
441 ah->curchan,
442 common->rx_chainmask,
443 longcal);
Sujithff37e332008-11-24 12:07:55 +0530444
Sujith379f0442009-04-13 21:56:48 +0530445 if (longcal)
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800446 common->ani.noise_floor = ath9k_hw_getchan_noise(ah,
Sujith379f0442009-04-13 21:56:48 +0530447 ah->curchan);
Sujithff37e332008-11-24 12:07:55 +0530448
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700449 ath_print(common, ATH_DBG_ANI,
450 " calibrate chan %u/%x nf: %d\n",
451 ah->curchan->channel,
452 ah->curchan->channelFlags,
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800453 common->ani.noise_floor);
Sujithff37e332008-11-24 12:07:55 +0530454 }
455 }
456
Jouni Malinen1ffc1c62009-05-19 17:01:39 +0300457 ath9k_ps_restore(sc);
458
Sujith20977d32009-02-20 15:13:28 +0530459set_timer:
Sujithff37e332008-11-24 12:07:55 +0530460 /*
461 * Set timer interval based on previous results.
462 * The interval must be the shortest necessary to satisfy ANI,
463 * short calibration and long calibration.
464 */
Sujithaac92072008-12-02 18:37:54 +0530465 cal_interval = ATH_LONG_CALINTERVAL;
Sujith2660b812009-02-09 13:27:26 +0530466 if (sc->sc_ah->config.enable_ani)
Luis R. Rodrigueze36b27a2010-06-12 00:33:45 -0400467 cal_interval = min(cal_interval,
468 (u32)ah->config.ani_poll_interval);
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800469 if (!common->ani.caldone)
Sujith20977d32009-02-20 15:13:28 +0530470 cal_interval = min(cal_interval, (u32)short_cal_interval);
Sujithff37e332008-11-24 12:07:55 +0530471
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800472 mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
Felix Fietkau5ee08652010-07-31 00:11:59 +0200473 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) {
Felix Fietkau9f42c2b2010-06-12 00:34:01 -0400474 if (!sc->sc_ah->curchan->paprd_done)
475 ieee80211_queue_work(sc->hw, &sc->paprd_work);
476 else
477 ath_paprd_activate(sc);
478 }
Sujithff37e332008-11-24 12:07:55 +0530479}
480
481/*
482 * Update tx/rx chainmask. For legacy association,
483 * hard code chainmask to 1x1, for 11n association, use
Vasanthakumar Thiagarajanc97c92d2009-01-02 15:35:46 +0530484 * the chainmask configuration, for bt coexistence, use
485 * the chainmask configuration even in legacy mode.
Sujithff37e332008-11-24 12:07:55 +0530486 */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200487void ath_update_chainmask(struct ath_softc *sc, int is_ht)
Sujithff37e332008-11-24 12:07:55 +0530488{
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700489 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700490 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700491
Felix Fietkau5ee08652010-07-31 00:11:59 +0200492 if ((sc->sc_flags & SC_OP_OFFCHANNEL) || is_ht ||
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -0700493 (ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE)) {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700494 common->tx_chainmask = ah->caps.tx_chainmask;
495 common->rx_chainmask = ah->caps.rx_chainmask;
Sujithff37e332008-11-24 12:07:55 +0530496 } else {
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700497 common->tx_chainmask = 1;
498 common->rx_chainmask = 1;
Sujithff37e332008-11-24 12:07:55 +0530499 }
500
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700501 ath_print(common, ATH_DBG_CONFIG,
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700502 "tx chmask: %d, rx chmask: %d\n",
Luis R. Rodriguez43c27612009-09-13 21:07:07 -0700503 common->tx_chainmask,
504 common->rx_chainmask);
Sujithff37e332008-11-24 12:07:55 +0530505}
506
507static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta)
508{
509 struct ath_node *an;
510
511 an = (struct ath_node *)sta->drv_priv;
512
Sujith87792ef2009-03-30 15:28:48 +0530513 if (sc->sc_flags & SC_OP_TXAGGR) {
Sujithff37e332008-11-24 12:07:55 +0530514 ath_tx_node_init(sc, an);
Sujith9e98ac62009-07-23 15:32:34 +0530515 an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
Sujith87792ef2009-03-30 15:28:48 +0530516 sta->ht_cap.ampdu_factor);
517 an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density);
Senthil Balasubramaniana59b5a52009-07-14 20:17:07 -0400518 an->last_rssi = ATH_RSSI_DUMMY_MARKER;
Sujith87792ef2009-03-30 15:28:48 +0530519 }
Sujithff37e332008-11-24 12:07:55 +0530520}
521
522static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
523{
524 struct ath_node *an = (struct ath_node *)sta->drv_priv;
525
526 if (sc->sc_flags & SC_OP_TXAGGR)
527 ath_tx_node_cleanup(sc, an);
528}
529
Felix Fietkau347809f2010-07-02 00:09:52 +0200530void ath_hw_check(struct work_struct *work)
531{
532 struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
533 int i;
534
535 ath9k_ps_wakeup(sc);
536
537 for (i = 0; i < 3; i++) {
538 if (ath9k_hw_check_alive(sc->sc_ah))
539 goto out;
540
541 msleep(1);
542 }
543 ath_reset(sc, false);
544
545out:
546 ath9k_ps_restore(sc);
547}
548
Sujith55624202010-01-08 10:36:02 +0530549void ath9k_tasklet(unsigned long data)
Sujithff37e332008-11-24 12:07:55 +0530550{
551 struct ath_softc *sc = (struct ath_softc *)data;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700552 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700553 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700554
Sujith17d79042009-02-09 13:27:03 +0530555 u32 status = sc->intrstatus;
Felix Fietkaub5c804752010-04-15 17:38:48 -0400556 u32 rxmask;
Sujithff37e332008-11-24 12:07:55 +0530557
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400558 ath9k_ps_wakeup(sc);
559
Felix Fietkau347809f2010-07-02 00:09:52 +0200560 if (status & ATH9K_INT_FATAL) {
Sujithff37e332008-11-24 12:07:55 +0530561 ath_reset(sc, false);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400562 ath9k_ps_restore(sc);
Sujithff37e332008-11-24 12:07:55 +0530563 return;
Sujithff37e332008-11-24 12:07:55 +0530564 }
565
Felix Fietkau347809f2010-07-02 00:09:52 +0200566 if (!ath9k_hw_check_alive(ah))
567 ieee80211_queue_work(sc->hw, &sc->hw_check_work);
568
Felix Fietkaub5c804752010-04-15 17:38:48 -0400569 if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
570 rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL |
571 ATH9K_INT_RXORN);
572 else
573 rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
574
575 if (status & rxmask) {
Sujith063d8be2009-03-30 15:28:49 +0530576 spin_lock_bh(&sc->rx.rxflushlock);
Felix Fietkaub5c804752010-04-15 17:38:48 -0400577
578 /* Check for high priority Rx first */
579 if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
580 (status & ATH9K_INT_RXHP))
581 ath_rx_tasklet(sc, 0, true);
582
583 ath_rx_tasklet(sc, 0, false);
Sujith063d8be2009-03-30 15:28:49 +0530584 spin_unlock_bh(&sc->rx.rxflushlock);
585 }
586
Vasanthakumar Thiagarajane5003242010-04-15 17:39:36 -0400587 if (status & ATH9K_INT_TX) {
588 if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
589 ath_tx_edma_tasklet(sc);
590 else
591 ath_tx_tasklet(sc);
592 }
Sujith063d8be2009-03-30 15:28:49 +0530593
Gabor Juhos96148322009-07-24 17:27:21 +0200594 if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
Jouni Malinen54ce8462009-05-19 17:01:40 +0300595 /*
596 * TSF sync does not look correct; remain awake to sync with
597 * the next Beacon.
598 */
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700599 ath_print(common, ATH_DBG_PS,
600 "TSFOOR - Sync with next Beacon\n");
Sujith1b04b932010-01-08 10:36:05 +0530601 sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC;
Jouni Malinen54ce8462009-05-19 17:01:40 +0300602 }
603
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -0700604 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Vasanthakumar Thiagarajanebb8e1d2009-09-01 17:46:32 +0530605 if (status & ATH9K_INT_GENTIMER)
606 ath_gen_timer_isr(sc->sc_ah);
607
Sujithff37e332008-11-24 12:07:55 +0530608 /* re-enable hardware interrupt */
Pavel Roskin30691682010-03-31 18:05:31 -0400609 ath9k_hw_set_interrupts(ah, ah->imask);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400610 ath9k_ps_restore(sc);
Sujithff37e332008-11-24 12:07:55 +0530611}
612
Gabor Juhos6baff7f2009-01-14 20:17:06 +0100613irqreturn_t ath_isr(int irq, void *dev)
Sujithff37e332008-11-24 12:07:55 +0530614{
Sujith063d8be2009-03-30 15:28:49 +0530615#define SCHED_INTR ( \
616 ATH9K_INT_FATAL | \
617 ATH9K_INT_RXORN | \
618 ATH9K_INT_RXEOL | \
619 ATH9K_INT_RX | \
Felix Fietkaub5c804752010-04-15 17:38:48 -0400620 ATH9K_INT_RXLP | \
621 ATH9K_INT_RXHP | \
Sujith063d8be2009-03-30 15:28:49 +0530622 ATH9K_INT_TX | \
623 ATH9K_INT_BMISS | \
624 ATH9K_INT_CST | \
Vasanthakumar Thiagarajanebb8e1d2009-09-01 17:46:32 +0530625 ATH9K_INT_TSFOOR | \
626 ATH9K_INT_GENTIMER)
Sujith063d8be2009-03-30 15:28:49 +0530627
Sujithff37e332008-11-24 12:07:55 +0530628 struct ath_softc *sc = dev;
Sujithcbe61d82009-02-09 13:27:12 +0530629 struct ath_hw *ah = sc->sc_ah;
Sujithff37e332008-11-24 12:07:55 +0530630 enum ath9k_int status;
631 bool sched = false;
632
Sujith063d8be2009-03-30 15:28:49 +0530633 /*
634 * The hardware is not ready/present, don't
635 * touch anything. Note this can happen early
636 * on if the IRQ is shared.
637 */
638 if (sc->sc_flags & SC_OP_INVALID)
639 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530640
Sujithff37e332008-11-24 12:07:55 +0530641
Sujith063d8be2009-03-30 15:28:49 +0530642 /* shared irq, not for us */
Sujithff37e332008-11-24 12:07:55 +0530643
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400644 if (!ath9k_hw_intrpend(ah))
Sujith063d8be2009-03-30 15:28:49 +0530645 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530646
Sujith063d8be2009-03-30 15:28:49 +0530647 /*
648 * Figure out the reason(s) for the interrupt. Note
649 * that the hal returns a pseudo-ISR that may include
650 * bits we haven't explicitly enabled so we mask the
651 * value to insure we only process bits we requested.
652 */
653 ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */
Pavel Roskin30691682010-03-31 18:05:31 -0400654 status &= ah->imask; /* discard unasked-for bits */
Sujith063d8be2009-03-30 15:28:49 +0530655
656 /*
657 * If there are no status bits set, then this interrupt was not
658 * for me (should have been caught above).
659 */
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400660 if (!status)
Sujith063d8be2009-03-30 15:28:49 +0530661 return IRQ_NONE;
Sujith063d8be2009-03-30 15:28:49 +0530662
663 /* Cache the status */
664 sc->intrstatus = status;
665
666 if (status & SCHED_INTR)
667 sched = true;
668
669 /*
670 * If a FATAL or RXORN interrupt is received, we have to reset the
671 * chip immediately.
672 */
Felix Fietkaub5c804752010-04-15 17:38:48 -0400673 if ((status & ATH9K_INT_FATAL) || ((status & ATH9K_INT_RXORN) &&
674 !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)))
Sujith063d8be2009-03-30 15:28:49 +0530675 goto chip_reset;
676
Luis R. Rodriguez08578b82010-05-13 13:33:44 -0400677 if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
678 (status & ATH9K_INT_BB_WATCHDOG)) {
679 ar9003_hw_bb_watchdog_dbg_info(ah);
680 goto chip_reset;
681 }
682
Sujith063d8be2009-03-30 15:28:49 +0530683 if (status & ATH9K_INT_SWBA)
684 tasklet_schedule(&sc->bcon_tasklet);
685
686 if (status & ATH9K_INT_TXURN)
687 ath9k_hw_updatetxtriglevel(ah, true);
688
Felix Fietkaub5c804752010-04-15 17:38:48 -0400689 if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
690 if (status & ATH9K_INT_RXEOL) {
691 ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
692 ath9k_hw_set_interrupts(ah, ah->imask);
693 }
694 }
695
Sujith063d8be2009-03-30 15:28:49 +0530696 if (status & ATH9K_INT_MIB) {
697 /*
698 * Disable interrupts until we service the MIB
699 * interrupt; otherwise it will continue to
700 * fire.
701 */
702 ath9k_hw_set_interrupts(ah, 0);
703 /*
704 * Let the hal handle the event. We assume
705 * it will clear whatever condition caused
706 * the interrupt.
707 */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530708 ath9k_hw_procmibevent(ah);
Pavel Roskin30691682010-03-31 18:05:31 -0400709 ath9k_hw_set_interrupts(ah, ah->imask);
Sujith063d8be2009-03-30 15:28:49 +0530710 }
711
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400712 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
713 if (status & ATH9K_INT_TIM_TIMER) {
Sujith063d8be2009-03-30 15:28:49 +0530714 /* Clear RxAbort bit so that we can
715 * receive frames */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700716 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400717 ath9k_hw_setrxabort(sc->sc_ah, 0);
Sujith1b04b932010-01-08 10:36:05 +0530718 sc->ps_flags |= PS_WAIT_FOR_BEACON;
Sujith063d8be2009-03-30 15:28:49 +0530719 }
Sujith063d8be2009-03-30 15:28:49 +0530720
721chip_reset:
722
Sujith817e11d2008-12-07 21:42:44 +0530723 ath_debug_stat_interrupt(sc, status);
724
Sujithff37e332008-11-24 12:07:55 +0530725 if (sched) {
726 /* turn off every interrupt except SWBA */
Pavel Roskin30691682010-03-31 18:05:31 -0400727 ath9k_hw_set_interrupts(ah, (ah->imask & ATH9K_INT_SWBA));
Sujithff37e332008-11-24 12:07:55 +0530728 tasklet_schedule(&sc->intr_tq);
729 }
730
731 return IRQ_HANDLED;
Sujith063d8be2009-03-30 15:28:49 +0530732
733#undef SCHED_INTR
Sujithff37e332008-11-24 12:07:55 +0530734}
735
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700736static u32 ath_get_extchanmode(struct ath_softc *sc,
Sujith99405f92008-11-24 12:08:35 +0530737 struct ieee80211_channel *chan,
Sujith094d05d2008-12-12 11:57:43 +0530738 enum nl80211_channel_type channel_type)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700739{
740 u32 chanmode = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700741
742 switch (chan->band) {
743 case IEEE80211_BAND_2GHZ:
Sujith094d05d2008-12-12 11:57:43 +0530744 switch(channel_type) {
745 case NL80211_CHAN_NO_HT:
746 case NL80211_CHAN_HT20:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700747 chanmode = CHANNEL_G_HT20;
Sujith094d05d2008-12-12 11:57:43 +0530748 break;
749 case NL80211_CHAN_HT40PLUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700750 chanmode = CHANNEL_G_HT40PLUS;
Sujith094d05d2008-12-12 11:57:43 +0530751 break;
752 case NL80211_CHAN_HT40MINUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700753 chanmode = CHANNEL_G_HT40MINUS;
Sujith094d05d2008-12-12 11:57:43 +0530754 break;
755 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700756 break;
757 case IEEE80211_BAND_5GHZ:
Sujith094d05d2008-12-12 11:57:43 +0530758 switch(channel_type) {
759 case NL80211_CHAN_NO_HT:
760 case NL80211_CHAN_HT20:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700761 chanmode = CHANNEL_A_HT20;
Sujith094d05d2008-12-12 11:57:43 +0530762 break;
763 case NL80211_CHAN_HT40PLUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700764 chanmode = CHANNEL_A_HT40PLUS;
Sujith094d05d2008-12-12 11:57:43 +0530765 break;
766 case NL80211_CHAN_HT40MINUS:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700767 chanmode = CHANNEL_A_HT40MINUS;
Sujith094d05d2008-12-12 11:57:43 +0530768 break;
769 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700770 break;
771 default:
772 break;
773 }
774
775 return chanmode;
776}
777
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530778static void ath9k_bss_assoc_info(struct ath_softc *sc,
Sujith5640b082008-10-29 10:16:06 +0530779 struct ieee80211_vif *vif,
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530780 struct ieee80211_bss_conf *bss_conf)
781{
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -0700782 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700783 struct ath_common *common = ath9k_hw_common(ah);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530784
785 if (bss_conf->assoc) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700786 ath_print(common, ATH_DBG_CONFIG,
787 "Bss Info ASSOC %d, bssid: %pM\n",
788 bss_conf->aid, common->curbssid);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530789
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530790 /* New association, store aid */
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700791 common->curaid = bss_conf->aid;
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -0700792 ath9k_hw_write_associd(ah);
Jouni Malinenccdfeab2009-05-20 21:59:08 +0300793
Senthil Balasubramanian2664f202009-06-24 18:56:39 +0530794 /*
795 * Request a re-configuration of Beacon related timers
796 * on the receipt of the first Beacon frame (i.e.,
797 * after time sync with the AP).
798 */
Sujith1b04b932010-01-08 10:36:05 +0530799 sc->ps_flags |= PS_BEACON_SYNC;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530800
801 /* Configure the beacon */
Jouni Malinen2c3db3d2009-03-03 19:23:26 +0200802 ath_beacon_config(sc, vif);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530803
804 /* Reset rssi stats */
Vasanthakumar Thiagarajan22e66a42009-08-19 16:23:40 +0530805 sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530806
Vasanthakumar Thiagarajan6c3118e2010-06-23 06:49:21 -0700807 sc->sc_flags |= SC_OP_ANI_RUN;
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800808 ath_start_ani(common);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530809 } else {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700810 ath_print(common, ATH_DBG_CONFIG, "Bss Info DISASSOC\n");
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700811 common->curaid = 0;
Senthil Balasubramanianf38faa32009-06-24 18:56:40 +0530812 /* Stop ANI */
Vasanthakumar Thiagarajan6c3118e2010-06-23 06:49:21 -0700813 sc->sc_flags &= ~SC_OP_ANI_RUN;
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -0800814 del_timer_sync(&common->ani.timer);
Vasanthakumar Thiagarajan8feceb62008-09-10 18:49:27 +0530815 }
816}
817
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800818void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530819{
Sujithcbe61d82009-02-09 13:27:12 +0530820 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700821 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800822 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800823 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530824
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530825 ath9k_ps_wakeup(sc);
Vivek Natarajan93b1b372009-09-17 09:24:58 +0530826 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithd2f5b3a2009-04-13 21:56:25 +0530827
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +0530828 if (!ah->curchan)
829 ah->curchan = ath_get_curchannel(sc, sc->hw);
830
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530831 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +0530832 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800833 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700834 ath_print(common, ATH_DBG_FATAL,
Pavel Roskinf643e512010-01-29 17:22:12 -0500835 "Unable to reset channel (%u MHz), "
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700836 "reset status %d\n",
837 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530838 }
839 spin_unlock_bh(&sc->sc_resetlock);
840
841 ath_update_txpow(sc);
842 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700843 ath_print(common, ATH_DBG_FATAL,
844 "Unable to restart recv logic\n");
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530845 return;
846 }
847
848 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +0200849 ath_beacon_config(sc, NULL); /* restart beacons */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530850
851 /* Re-Enable interrupts */
Pavel Roskin30691682010-03-31 18:05:31 -0400852 ath9k_hw_set_interrupts(ah, ah->imask);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530853
854 /* Enable LED */
Vivek Natarajan08fc5c12009-08-14 11:30:52 +0530855 ath9k_hw_cfg_output(ah, ah->led_pin,
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530856 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
Vivek Natarajan08fc5c12009-08-14 11:30:52 +0530857 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530858
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800859 ieee80211_wake_queues(hw);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530860 ath9k_ps_restore(sc);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530861}
862
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800863void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530864{
Sujithcbe61d82009-02-09 13:27:12 +0530865 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800866 struct ieee80211_channel *channel = hw->conf.channel;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800867 int r;
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530868
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530869 ath9k_ps_wakeup(sc);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800870 ieee80211_stop_queues(hw);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530871
Vivek Natarajan982723d2010-06-23 12:08:29 +0530872 /*
873 * Keep the LED on when the radio is disabled
874 * during idle unassociated state.
875 */
876 if (!sc->ps_idle) {
877 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
878 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
879 }
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530880
881 /* Disable interrupts */
882 ath9k_hw_set_interrupts(ah, 0);
883
Sujith043a0402009-01-16 21:38:47 +0530884 ath_drain_all_txq(sc, false); /* clear pending tx frames */
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530885 ath_stoprecv(sc); /* turn off frame recv */
886 ath_flushrecv(sc); /* flush recv queue */
887
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +0530888 if (!ah->curchan)
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800889 ah->curchan = ath_get_curchannel(sc, hw);
Vasanthakumar Thiagarajan159cd462009-06-13 14:50:25 +0530890
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530891 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +0530892 r = ath9k_hw_reset(ah, ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800893 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700894 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
Pavel Roskinf643e512010-01-29 17:22:12 -0500895 "Unable to reset channel (%u MHz), "
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700896 "reset status %d\n",
897 channel->center_freq, r);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530898 }
899 spin_unlock_bh(&sc->sc_resetlock);
900
901 ath9k_hw_phy_disable(ah);
Vivek Natarajan93b1b372009-09-17 09:24:58 +0530902 ath9k_hw_configpcipowersave(ah, 1, 1);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +0530903 ath9k_ps_restore(sc);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700904 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Vasanthakumar Thiagarajan500c0642008-09-10 18:50:17 +0530905}
906
Sujithff37e332008-11-24 12:07:55 +0530907int ath_reset(struct ath_softc *sc, bool retry_tx)
908{
Sujithcbe61d82009-02-09 13:27:12 +0530909 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700910 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguez030bb492008-12-23 15:58:37 -0800911 struct ieee80211_hw *hw = sc->hw;
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800912 int r;
Sujithff37e332008-11-24 12:07:55 +0530913
Sujith2ab81d42009-12-14 16:34:56 +0530914 /* Stop ANI */
915 del_timer_sync(&common->ani.timer);
916
Sujithcc9c3782010-01-08 10:36:09 +0530917 ieee80211_stop_queues(hw);
918
Sujithff37e332008-11-24 12:07:55 +0530919 ath9k_hw_set_interrupts(ah, 0);
Sujith043a0402009-01-16 21:38:47 +0530920 ath_drain_all_txq(sc, retry_tx);
Sujithff37e332008-11-24 12:07:55 +0530921 ath_stoprecv(sc);
922 ath_flushrecv(sc);
923
924 spin_lock_bh(&sc->sc_resetlock);
Sujith2660b812009-02-09 13:27:26 +0530925 r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800926 if (r)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700927 ath_print(common, ATH_DBG_FATAL,
928 "Unable to reset hardware; reset status %d\n", r);
Sujithff37e332008-11-24 12:07:55 +0530929 spin_unlock_bh(&sc->sc_resetlock);
930
931 if (ath_startrecv(sc) != 0)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700932 ath_print(common, ATH_DBG_FATAL,
933 "Unable to start recv logic\n");
Sujithff37e332008-11-24 12:07:55 +0530934
935 /*
936 * We may be doing a reset in response to a request
937 * that changes the channel so update any state that
938 * might change as a result.
939 */
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -0800940 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +0530941
942 ath_update_txpow(sc);
943
944 if (sc->sc_flags & SC_OP_BEACONS)
Jouni Malinen2c3db3d2009-03-03 19:23:26 +0200945 ath_beacon_config(sc, NULL); /* restart beacons */
Sujithff37e332008-11-24 12:07:55 +0530946
Pavel Roskin30691682010-03-31 18:05:31 -0400947 ath9k_hw_set_interrupts(ah, ah->imask);
Sujithff37e332008-11-24 12:07:55 +0530948
949 if (retry_tx) {
950 int i;
951 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
952 if (ATH_TXQ_SETUP(sc, i)) {
Sujithb77f4832008-12-07 21:44:03 +0530953 spin_lock_bh(&sc->tx.txq[i].axq_lock);
954 ath_txq_schedule(sc, &sc->tx.txq[i]);
955 spin_unlock_bh(&sc->tx.txq[i].axq_lock);
Sujithff37e332008-11-24 12:07:55 +0530956 }
957 }
958 }
959
Sujithcc9c3782010-01-08 10:36:09 +0530960 ieee80211_wake_queues(hw);
961
Sujith2ab81d42009-12-14 16:34:56 +0530962 /* Start ANI */
963 ath_start_ani(common);
964
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800965 return r;
Sujithff37e332008-11-24 12:07:55 +0530966}
967
Felix Fietkauebe297c2010-06-12 00:33:53 -0400968static int ath_get_hal_qnum(u16 queue, struct ath_softc *sc)
Sujithff37e332008-11-24 12:07:55 +0530969{
970 int qnum;
971
972 switch (queue) {
973 case 0:
Felix Fietkau1d2231e2010-06-12 00:33:51 -0400974 qnum = sc->tx.hwq_map[WME_AC_VO];
Sujithff37e332008-11-24 12:07:55 +0530975 break;
976 case 1:
Felix Fietkau1d2231e2010-06-12 00:33:51 -0400977 qnum = sc->tx.hwq_map[WME_AC_VI];
Sujithff37e332008-11-24 12:07:55 +0530978 break;
979 case 2:
Felix Fietkau1d2231e2010-06-12 00:33:51 -0400980 qnum = sc->tx.hwq_map[WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +0530981 break;
982 case 3:
Felix Fietkau1d2231e2010-06-12 00:33:51 -0400983 qnum = sc->tx.hwq_map[WME_AC_BK];
Sujithff37e332008-11-24 12:07:55 +0530984 break;
985 default:
Felix Fietkau1d2231e2010-06-12 00:33:51 -0400986 qnum = sc->tx.hwq_map[WME_AC_BE];
Sujithff37e332008-11-24 12:07:55 +0530987 break;
988 }
989
990 return qnum;
991}
992
993int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc)
994{
995 int qnum;
996
997 switch (queue) {
Felix Fietkau1d2231e2010-06-12 00:33:51 -0400998 case WME_AC_VO:
Sujithff37e332008-11-24 12:07:55 +0530999 qnum = 0;
1000 break;
Felix Fietkau1d2231e2010-06-12 00:33:51 -04001001 case WME_AC_VI:
Sujithff37e332008-11-24 12:07:55 +05301002 qnum = 1;
1003 break;
Felix Fietkau1d2231e2010-06-12 00:33:51 -04001004 case WME_AC_BE:
Sujithff37e332008-11-24 12:07:55 +05301005 qnum = 2;
1006 break;
Felix Fietkau1d2231e2010-06-12 00:33:51 -04001007 case WME_AC_BK:
Sujithff37e332008-11-24 12:07:55 +05301008 qnum = 3;
1009 break;
1010 default:
1011 qnum = -1;
1012 break;
1013 }
1014
1015 return qnum;
1016}
1017
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001018/* XXX: Remove me once we don't depend on ath9k_channel for all
1019 * this redundant data */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001020void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw,
1021 struct ath9k_channel *ichan)
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001022{
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001023 struct ieee80211_channel *chan = hw->conf.channel;
1024 struct ieee80211_conf *conf = &hw->conf;
1025
1026 ichan->channel = chan->center_freq;
1027 ichan->chan = chan;
1028
1029 if (chan->band == IEEE80211_BAND_2GHZ) {
1030 ichan->chanmode = CHANNEL_G;
Sujith88132622009-09-03 12:08:53 +05301031 ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001032 } else {
1033 ichan->chanmode = CHANNEL_A;
1034 ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
1035 }
1036
Luis R. Rodriguez25c56ee2009-09-13 23:04:44 -07001037 if (conf_is_ht(conf))
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001038 ichan->chanmode = ath_get_extchanmode(sc, chan,
1039 conf->channel_type);
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001040}
1041
Sujithff37e332008-11-24 12:07:55 +05301042/**********************/
1043/* mac80211 callbacks */
1044/**********************/
1045
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001046static int ath9k_start(struct ieee80211_hw *hw)
1047{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001048 struct ath_wiphy *aphy = hw->priv;
1049 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001050 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001051 struct ath_common *common = ath9k_hw_common(ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001052 struct ieee80211_channel *curchan = hw->conf.channel;
Sujithff37e332008-11-24 12:07:55 +05301053 struct ath9k_channel *init_channel;
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05301054 int r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001055
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001056 ath_print(common, ATH_DBG_CONFIG,
1057 "Starting driver with initial channel: %d MHz\n",
1058 curchan->center_freq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001059
Sujith141b38b2009-02-04 08:10:07 +05301060 mutex_lock(&sc->mutex);
1061
Jouni Malinen9580a222009-03-03 19:23:33 +02001062 if (ath9k_wiphy_started(sc)) {
1063 if (sc->chan_idx == curchan->hw_value) {
1064 /*
1065 * Already on the operational channel, the new wiphy
1066 * can be marked active.
1067 */
1068 aphy->state = ATH_WIPHY_ACTIVE;
1069 ieee80211_wake_queues(hw);
1070 } else {
1071 /*
1072 * Another wiphy is on another channel, start the new
1073 * wiphy in paused state.
1074 */
1075 aphy->state = ATH_WIPHY_PAUSED;
1076 ieee80211_stop_queues(hw);
1077 }
1078 mutex_unlock(&sc->mutex);
1079 return 0;
1080 }
1081 aphy->state = ATH_WIPHY_ACTIVE;
1082
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001083 /* setup initial channel */
1084
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05301085 sc->chan_idx = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001086
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +05301087 init_channel = ath_get_curchannel(sc, hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001088
Sujithff37e332008-11-24 12:07:55 +05301089 /* Reset SERDES registers */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001090 ath9k_hw_configpcipowersave(ah, 0, 0);
Sujithff37e332008-11-24 12:07:55 +05301091
1092 /*
1093 * The basic interface to setting the hardware in a good
1094 * state is ``reset''. On return the hardware is known to
1095 * be powered up and with interrupts disabled. This must
1096 * be followed by initialization of the appropriate bits
1097 * and then setup of the interrupt mask.
1098 */
1099 spin_lock_bh(&sc->sc_resetlock);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001100 r = ath9k_hw_reset(ah, init_channel, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001101 if (r) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001102 ath_print(common, ATH_DBG_FATAL,
1103 "Unable to reset hardware; reset status %d "
1104 "(freq %u MHz)\n", r,
1105 curchan->center_freq);
Sujithff37e332008-11-24 12:07:55 +05301106 spin_unlock_bh(&sc->sc_resetlock);
Sujith141b38b2009-02-04 08:10:07 +05301107 goto mutex_unlock;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001108 }
Sujithff37e332008-11-24 12:07:55 +05301109 spin_unlock_bh(&sc->sc_resetlock);
1110
1111 /*
1112 * This is needed only to setup initial state
1113 * but it's best done after a reset.
1114 */
1115 ath_update_txpow(sc);
1116
1117 /*
1118 * Setup the hardware after reset:
1119 * The receive engine is set going.
1120 * Frame transmit is handled entirely
1121 * in the frame output path; there's nothing to do
1122 * here except setup the interrupt mask.
1123 */
1124 if (ath_startrecv(sc) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001125 ath_print(common, ATH_DBG_FATAL,
1126 "Unable to start recv logic\n");
Sujith141b38b2009-02-04 08:10:07 +05301127 r = -EIO;
1128 goto mutex_unlock;
Sujithff37e332008-11-24 12:07:55 +05301129 }
1130
1131 /* Setup our intr mask. */
Felix Fietkaub5c804752010-04-15 17:38:48 -04001132 ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
1133 ATH9K_INT_RXORN | ATH9K_INT_FATAL |
1134 ATH9K_INT_GLOBAL;
1135
1136 if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
Luis R. Rodriguez08578b82010-05-13 13:33:44 -04001137 ah->imask |= ATH9K_INT_RXHP |
1138 ATH9K_INT_RXLP |
1139 ATH9K_INT_BB_WATCHDOG;
Felix Fietkaub5c804752010-04-15 17:38:48 -04001140 else
1141 ah->imask |= ATH9K_INT_RX;
Sujithff37e332008-11-24 12:07:55 +05301142
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001143 if (ah->caps.hw_caps & ATH9K_HW_CAP_GTT)
Pavel Roskin30691682010-03-31 18:05:31 -04001144 ah->imask |= ATH9K_INT_GTT;
Sujithff37e332008-11-24 12:07:55 +05301145
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001146 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
Pavel Roskin30691682010-03-31 18:05:31 -04001147 ah->imask |= ATH9K_INT_CST;
Sujithff37e332008-11-24 12:07:55 +05301148
Luis R. Rodriguezce111ba2008-12-23 15:58:39 -08001149 ath_cache_conf_rate(sc, &hw->conf);
Sujithff37e332008-11-24 12:07:55 +05301150
1151 sc->sc_flags &= ~SC_OP_INVALID;
1152
1153 /* Disable BMISS interrupt when we're not associated */
Pavel Roskin30691682010-03-31 18:05:31 -04001154 ah->imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
1155 ath9k_hw_set_interrupts(ah, ah->imask);
Sujithff37e332008-11-24 12:07:55 +05301156
Jouni Malinenbce048d2009-03-03 19:23:28 +02001157 ieee80211_wake_queues(hw);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001158
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -04001159 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
Senthil Balasubramanian164ace32009-07-14 20:17:09 -04001160
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001161 if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) &&
1162 !ah->btcoex_hw.enabled) {
Luis R. Rodriguez5e197292009-09-09 15:15:55 -07001163 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1164 AR_STOMP_LOW_WLAN_WGHT);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001165 ath9k_hw_btcoex_enable(ah);
Vasanthakumar Thiagarajanf985ad12009-08-26 21:08:43 +05301166
Luis R. Rodriguez5bb12792009-09-14 00:55:09 -07001167 if (common->bus_ops->bt_coex_prep)
1168 common->bus_ops->bt_coex_prep(common);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001169 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001170 ath9k_btcoex_timer_resume(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301171 }
1172
Sujith141b38b2009-02-04 08:10:07 +05301173mutex_unlock:
1174 mutex_unlock(&sc->mutex);
1175
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -08001176 return r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001177}
1178
1179static int ath9k_tx(struct ieee80211_hw *hw,
1180 struct sk_buff *skb)
1181{
Jouni Malinen147583c2008-08-11 14:01:50 +03001182 struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
Jouni Malinenbce048d2009-03-03 19:23:28 +02001183 struct ath_wiphy *aphy = hw->priv;
1184 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001185 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith528f0c62008-10-29 10:14:26 +05301186 struct ath_tx_control txctl;
Benoit Papillault1bc14882009-11-24 15:49:18 +01001187 int padpos, padsize;
1188 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
Felix Fietkau84642d62010-06-01 21:33:13 +02001189 int qnum;
Sujith528f0c62008-10-29 10:14:26 +05301190
Jouni Malinen8089cc42009-03-03 19:23:38 +02001191 if (aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001192 ath_print(common, ATH_DBG_XMIT,
1193 "ath9k: %s: TX in unexpected wiphy state "
1194 "%d\n", wiphy_name(hw->wiphy), aphy->state);
Jouni Malinenee166a02009-03-03 19:23:36 +02001195 goto exit;
1196 }
1197
Gabor Juhos96148322009-07-24 17:27:21 +02001198 if (sc->ps_enabled) {
Jouni Malinendc8c4582009-05-19 17:01:42 +03001199 /*
1200 * mac80211 does not set PM field for normal data frames, so we
1201 * need to update that based on the current PS mode.
1202 */
1203 if (ieee80211_is_data(hdr->frame_control) &&
1204 !ieee80211_is_nullfunc(hdr->frame_control) &&
1205 !ieee80211_has_pm(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001206 ath_print(common, ATH_DBG_PS, "Add PM=1 for a TX frame "
1207 "while in PS mode\n");
Jouni Malinendc8c4582009-05-19 17:01:42 +03001208 hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
1209 }
1210 }
1211
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03001212 if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) {
1213 /*
1214 * We are using PS-Poll and mac80211 can request TX while in
1215 * power save mode. Need to wake up hardware for the TX to be
1216 * completed and if needed, also for RX of buffered frames.
1217 */
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03001218 ath9k_ps_wakeup(sc);
Vasanthakumar Thiagarajanfdf76622010-05-17 18:57:55 -07001219 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
1220 ath9k_hw_setrxabort(sc->sc_ah, 0);
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03001221 if (ieee80211_is_pspoll(hdr->frame_control)) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001222 ath_print(common, ATH_DBG_PS,
1223 "Sending PS-Poll to pick a buffered frame\n");
Sujith1b04b932010-01-08 10:36:05 +05301224 sc->ps_flags |= PS_WAIT_FOR_PSPOLL_DATA;
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03001225 } else {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001226 ath_print(common, ATH_DBG_PS,
1227 "Wake up to complete TX\n");
Sujith1b04b932010-01-08 10:36:05 +05301228 sc->ps_flags |= PS_WAIT_FOR_TX_ACK;
Jouni Malinen9a23f9c2009-05-19 17:01:38 +03001229 }
1230 /*
1231 * The actual restore operation will happen only after
1232 * the sc_flags bit is cleared. We are just dropping
1233 * the ps_usecount here.
1234 */
1235 ath9k_ps_restore(sc);
1236 }
1237
Sujith528f0c62008-10-29 10:14:26 +05301238 memset(&txctl, 0, sizeof(struct ath_tx_control));
Jouni Malinen147583c2008-08-11 14:01:50 +03001239
1240 /*
1241 * As a temporary workaround, assign seq# here; this will likely need
1242 * to be cleaned up to work better with Beacon transmission and virtual
1243 * BSSes.
1244 */
1245 if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
Jouni Malinen147583c2008-08-11 14:01:50 +03001246 if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
Sujithb77f4832008-12-07 21:44:03 +05301247 sc->tx.seq_no += 0x10;
Jouni Malinen147583c2008-08-11 14:01:50 +03001248 hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
Sujithb77f4832008-12-07 21:44:03 +05301249 hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
Jouni Malinen147583c2008-08-11 14:01:50 +03001250 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001251
1252 /* Add the padding after the header if this is not already done */
Benoit Papillault1bc14882009-11-24 15:49:18 +01001253 padpos = ath9k_cmn_padpos(hdr->frame_control);
1254 padsize = padpos & 3;
1255 if (padsize && skb->len>padpos) {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001256 if (skb_headroom(skb) < padsize)
1257 return -1;
1258 skb_push(skb, padsize);
Benoit Papillault1bc14882009-11-24 15:49:18 +01001259 memmove(skb->data, skb->data + padsize, padpos);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001260 }
1261
Felix Fietkau84642d62010-06-01 21:33:13 +02001262 qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc);
1263 txctl.txq = &sc->tx.txq[qnum];
Sujith528f0c62008-10-29 10:14:26 +05301264
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001265 ath_print(common, ATH_DBG_XMIT, "transmitting packet, skb: %p\n", skb);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001266
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001267 if (ath_tx_start(hw, skb, &txctl) != 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001268 ath_print(common, ATH_DBG_XMIT, "TX failed\n");
Sujith528f0c62008-10-29 10:14:26 +05301269 goto exit;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001270 }
1271
1272 return 0;
Sujith528f0c62008-10-29 10:14:26 +05301273exit:
1274 dev_kfree_skb_any(skb);
1275 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001276}
1277
1278static void ath9k_stop(struct ieee80211_hw *hw)
1279{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001280 struct ath_wiphy *aphy = hw->priv;
1281 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001282 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001283 struct ath_common *common = ath9k_hw_common(ah);
Rajkumar Manoharan447a42c2010-07-08 12:12:29 +05301284 int i;
Sujith9c84b792008-10-29 10:17:13 +05301285
Sujith4c483812009-08-18 10:51:52 +05301286 mutex_lock(&sc->mutex);
1287
Jouni Malinen9580a222009-03-03 19:23:33 +02001288 aphy->state = ATH_WIPHY_INACTIVE;
1289
Vivek Natarajan9a75c2f2010-06-22 11:52:37 +05301290 if (led_blink)
1291 cancel_delayed_work_sync(&sc->ath_led_blink_work);
1292
Luis R. Rodriguezc94dbff2009-07-27 11:53:04 -07001293 cancel_delayed_work_sync(&sc->tx_complete_work);
Felix Fietkau9f42c2b2010-06-12 00:34:01 -04001294 cancel_work_sync(&sc->paprd_work);
Felix Fietkau347809f2010-07-02 00:09:52 +02001295 cancel_work_sync(&sc->hw_check_work);
Luis R. Rodriguezc94dbff2009-07-27 11:53:04 -07001296
Rajkumar Manoharan447a42c2010-07-08 12:12:29 +05301297 for (i = 0; i < sc->num_sec_wiphy; i++) {
1298 if (sc->sec_wiphy[i])
1299 break;
1300 }
1301
1302 if (i == sc->num_sec_wiphy) {
Luis R. Rodriguezc94dbff2009-07-27 11:53:04 -07001303 cancel_delayed_work_sync(&sc->wiphy_work);
1304 cancel_work_sync(&sc->chan_work);
1305 }
1306
Sujith9c84b792008-10-29 10:17:13 +05301307 if (sc->sc_flags & SC_OP_INVALID) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001308 ath_print(common, ATH_DBG_ANY, "Device not present\n");
Sujith4c483812009-08-18 10:51:52 +05301309 mutex_unlock(&sc->mutex);
Sujith9c84b792008-10-29 10:17:13 +05301310 return;
1311 }
1312
Jouni Malinen9580a222009-03-03 19:23:33 +02001313 if (ath9k_wiphy_started(sc)) {
1314 mutex_unlock(&sc->mutex);
1315 return; /* another wiphy still in use */
1316 }
1317
Sujith3867cf62009-12-23 20:03:27 -05001318 /* Ensure HW is awake when we try to shut it down. */
1319 ath9k_ps_wakeup(sc);
1320
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001321 if (ah->btcoex_hw.enabled) {
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001322 ath9k_hw_btcoex_disable(ah);
Luis R. Rodriguez766ec4a2009-09-09 14:52:02 -07001323 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
Luis R. Rodriguez75d78392009-09-09 04:00:10 -07001324 ath9k_btcoex_timer_pause(sc);
Vasanthakumar Thiagarajan17739122009-08-26 21:08:50 +05301325 }
1326
Sujithff37e332008-11-24 12:07:55 +05301327 /* make sure h/w will not generate any interrupt
1328 * before setting the invalid flag. */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001329 ath9k_hw_set_interrupts(ah, 0);
Sujithff37e332008-11-24 12:07:55 +05301330
1331 if (!(sc->sc_flags & SC_OP_INVALID)) {
Sujith043a0402009-01-16 21:38:47 +05301332 ath_drain_all_txq(sc, false);
Sujithff37e332008-11-24 12:07:55 +05301333 ath_stoprecv(sc);
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001334 ath9k_hw_phy_disable(ah);
Sujithff37e332008-11-24 12:07:55 +05301335 } else
Sujithb77f4832008-12-07 21:44:03 +05301336 sc->rx.rxlink = NULL;
Sujithff37e332008-11-24 12:07:55 +05301337
Sujithff37e332008-11-24 12:07:55 +05301338 /* disable HAL and put h/w to sleep */
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -07001339 ath9k_hw_disable(ah);
1340 ath9k_hw_configpcipowersave(ah, 1, 1);
Sujith3867cf62009-12-23 20:03:27 -05001341 ath9k_ps_restore(sc);
1342
1343 /* Finally, put the chip in FULL SLEEP mode */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001344 ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Sujithff37e332008-11-24 12:07:55 +05301345
1346 sc->sc_flags |= SC_OP_INVALID;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001347
Sujith141b38b2009-02-04 08:10:07 +05301348 mutex_unlock(&sc->mutex);
1349
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001350 ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001351}
1352
1353static int ath9k_add_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01001354 struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001355{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001356 struct ath_wiphy *aphy = hw->priv;
1357 struct ath_softc *sc = aphy->sc;
Pavel Roskin30691682010-03-31 18:05:31 -04001358 struct ath_hw *ah = sc->sc_ah;
1359 struct ath_common *common = ath9k_hw_common(ah);
Johannes Berg1ed32e42009-12-23 13:15:45 +01001360 struct ath_vif *avp = (void *)vif->drv_priv;
Colin McCabed97809d2008-12-01 13:38:55 -08001361 enum nl80211_iftype ic_opmode = NL80211_IFTYPE_UNSPECIFIED;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001362 int ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001363
Sujith141b38b2009-02-04 08:10:07 +05301364 mutex_lock(&sc->mutex);
1365
Pavel Roskin30691682010-03-31 18:05:31 -04001366 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) &&
Jouni Malinen8ca21f02009-03-03 19:23:27 +02001367 sc->nvifs > 0) {
1368 ret = -ENOBUFS;
1369 goto out;
1370 }
1371
Johannes Berg1ed32e42009-12-23 13:15:45 +01001372 switch (vif->type) {
Johannes Berg05c914f2008-09-11 00:01:58 +02001373 case NL80211_IFTYPE_STATION:
Colin McCabed97809d2008-12-01 13:38:55 -08001374 ic_opmode = NL80211_IFTYPE_STATION;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001375 break;
Johannes Berg05c914f2008-09-11 00:01:58 +02001376 case NL80211_IFTYPE_ADHOC:
Johannes Berg05c914f2008-09-11 00:01:58 +02001377 case NL80211_IFTYPE_AP:
Pat Erley9cb54122009-03-20 22:59:59 -04001378 case NL80211_IFTYPE_MESH_POINT:
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001379 if (sc->nbcnvifs >= ATH_BCBUF) {
1380 ret = -ENOBUFS;
1381 goto out;
1382 }
Johannes Berg1ed32e42009-12-23 13:15:45 +01001383 ic_opmode = vif->type;
Jouni Malinen2ad67de2008-08-11 14:01:47 +03001384 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001385 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001386 ath_print(common, ATH_DBG_FATAL,
Johannes Berg1ed32e42009-12-23 13:15:45 +01001387 "Interface type %d not yet supported\n", vif->type);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001388 ret = -EOPNOTSUPP;
1389 goto out;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001390 }
1391
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001392 ath_print(common, ATH_DBG_CONFIG,
1393 "Attach a VIF of type: %d\n", ic_opmode);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001394
Sujith17d79042009-02-09 13:27:03 +05301395 /* Set the VIF opmode */
Sujith5640b082008-10-29 10:16:06 +05301396 avp->av_opmode = ic_opmode;
1397 avp->av_bslot = -1;
1398
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001399 sc->nvifs++;
Jouni Malinen8ca21f02009-03-03 19:23:27 +02001400
Pavel Roskin30691682010-03-31 18:05:31 -04001401 if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
Jouni Malinen8ca21f02009-03-03 19:23:27 +02001402 ath9k_set_bssid_mask(hw);
1403
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001404 if (sc->nvifs > 1)
1405 goto out; /* skip global settings for secondary vif */
1406
Sujithb238e902009-03-03 10:16:56 +05301407 if (ic_opmode == NL80211_IFTYPE_AP) {
Pavel Roskin30691682010-03-31 18:05:31 -04001408 ath9k_hw_set_tsfadjust(ah, 1);
Sujithb238e902009-03-03 10:16:56 +05301409 sc->sc_flags |= SC_OP_TSF_RESET;
1410 }
Sujith5640b082008-10-29 10:16:06 +05301411
Sujith5640b082008-10-29 10:16:06 +05301412 /* Set the device opmode */
Pavel Roskin30691682010-03-31 18:05:31 -04001413 ah->opmode = ic_opmode;
Sujith5640b082008-10-29 10:16:06 +05301414
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05301415 /*
1416 * Enable MIB interrupts when there are hardware phy counters.
1417 * Note we only do this (at the moment) for station mode.
1418 */
Johannes Berg1ed32e42009-12-23 13:15:45 +01001419 if ((vif->type == NL80211_IFTYPE_STATION) ||
1420 (vif->type == NL80211_IFTYPE_ADHOC) ||
1421 (vif->type == NL80211_IFTYPE_MESH_POINT)) {
Luis R. Rodriguez3448f912010-04-15 17:38:23 -04001422 if (ah->config.enable_ani)
1423 ah->imask |= ATH9K_INT_MIB;
Pavel Roskin30691682010-03-31 18:05:31 -04001424 ah->imask |= ATH9K_INT_TSFOOR;
Sujith4af9cf42009-02-12 10:06:47 +05301425 }
1426
Pavel Roskin30691682010-03-31 18:05:31 -04001427 ath9k_hw_set_interrupts(ah, ah->imask);
Vivek Natarajan4e30ffa2009-01-28 20:53:27 +05301428
Johannes Berg1ed32e42009-12-23 13:15:45 +01001429 if (vif->type == NL80211_IFTYPE_AP ||
1430 vif->type == NL80211_IFTYPE_ADHOC ||
Vasanthakumar Thiagarajan6c3118e2010-06-23 06:49:21 -07001431 vif->type == NL80211_IFTYPE_MONITOR) {
1432 sc->sc_flags |= SC_OP_ANI_RUN;
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08001433 ath_start_ani(common);
Vasanthakumar Thiagarajan6c3118e2010-06-23 06:49:21 -07001434 }
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07001435
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001436out:
Sujith141b38b2009-02-04 08:10:07 +05301437 mutex_unlock(&sc->mutex);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001438 return ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001439}
1440
1441static void ath9k_remove_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01001442 struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001443{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001444 struct ath_wiphy *aphy = hw->priv;
1445 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001446 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berg1ed32e42009-12-23 13:15:45 +01001447 struct ath_vif *avp = (void *)vif->drv_priv;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001448 int i;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001449
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001450 ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001451
Sujith141b38b2009-02-04 08:10:07 +05301452 mutex_lock(&sc->mutex);
1453
Luis R. Rodriguez6f255422008-10-03 15:45:27 -07001454 /* Stop ANI */
Vasanthakumar Thiagarajan6c3118e2010-06-23 06:49:21 -07001455 sc->sc_flags &= ~SC_OP_ANI_RUN;
Luis R. Rodriguez3d536ac2009-11-03 17:07:04 -08001456 del_timer_sync(&common->ani.timer);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001457
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001458 /* Reclaim beacon resources */
Pat Erley9cb54122009-03-20 22:59:59 -04001459 if ((sc->sc_ah->opmode == NL80211_IFTYPE_AP) ||
1460 (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC) ||
1461 (sc->sc_ah->opmode == NL80211_IFTYPE_MESH_POINT)) {
Luis R. Rodriguez5f70a882009-12-23 20:03:28 -05001462 ath9k_ps_wakeup(sc);
Sujithb77f4832008-12-07 21:44:03 +05301463 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Luis R. Rodriguez5f70a882009-12-23 20:03:28 -05001464 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001465 }
1466
Felix Fietkau74401772010-01-19 20:51:32 +01001467 ath_beacon_return(sc, avp);
Sujith672840a2008-08-11 14:05:08 +05301468 sc->sc_flags &= ~SC_OP_BEACONS;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001469
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001470 for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) {
Johannes Berg1ed32e42009-12-23 13:15:45 +01001471 if (sc->beacon.bslot[i] == vif) {
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001472 printk(KERN_DEBUG "%s: vif had allocated beacon "
1473 "slot\n", __func__);
1474 sc->beacon.bslot[i] = NULL;
Jouni Malinenc52f33d2009-03-03 19:23:29 +02001475 sc->beacon.bslot_aphy[i] = NULL;
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001476 }
1477 }
1478
Sujith17d79042009-02-09 13:27:03 +05301479 sc->nvifs--;
Sujith141b38b2009-02-04 08:10:07 +05301480
1481 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001482}
1483
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301484void ath9k_enable_ps(struct ath_softc *sc)
1485{
Pavel Roskin30691682010-03-31 18:05:31 -04001486 struct ath_hw *ah = sc->sc_ah;
1487
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301488 sc->ps_enabled = true;
Pavel Roskin30691682010-03-31 18:05:31 -04001489 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
1490 if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) {
1491 ah->imask |= ATH9K_INT_TIM_TIMER;
1492 ath9k_hw_set_interrupts(ah, ah->imask);
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301493 }
Vasanthakumar Thiagarajanfdf76622010-05-17 18:57:55 -07001494 ath9k_hw_setrxabort(ah, 1);
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301495 }
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301496}
1497
Johannes Berge8975582008-10-09 12:18:51 +02001498static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001499{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001500 struct ath_wiphy *aphy = hw->priv;
1501 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001502 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Johannes Berge8975582008-10-09 12:18:51 +02001503 struct ieee80211_conf *conf = &hw->conf;
Vivek Natarajan8782b412009-03-30 14:17:00 +05301504 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07001505 bool disable_radio;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001506
Sujithaa33de02008-12-18 11:40:16 +05301507 mutex_lock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05301508
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07001509 /*
1510 * Leave this as the first check because we need to turn on the
1511 * radio if it was disabled before prior to processing the rest
1512 * of the changes. Likewise we must only disable the radio towards
1513 * the end.
1514 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001515 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07001516 bool enable_radio;
1517 bool all_wiphys_idle;
1518 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001519
1520 spin_lock_bh(&sc->wiphy_lock);
1521 all_wiphys_idle = ath9k_all_wiphys_idle(sc);
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07001522 ath9k_set_wiphy_idle(aphy, idle);
1523
Felix Fietkau11446012010-04-06 12:05:01 -07001524 enable_radio = (!idle && all_wiphys_idle);
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07001525
1526 /*
1527 * After we unlock here its possible another wiphy
1528 * can be re-renabled so to account for that we will
1529 * only disable the radio toward the end of this routine
1530 * if by then all wiphys are still idle.
1531 */
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001532 spin_unlock_bh(&sc->wiphy_lock);
1533
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07001534 if (enable_radio) {
Vivek Natarajan1dbfd9d2010-01-29 16:56:51 +05301535 sc->ps_idle = false;
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001536 ath_radio_enable(sc, hw);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001537 ath_print(common, ATH_DBG_CONFIG,
1538 "not-idle: enabling radio\n");
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001539 }
1540 }
1541
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05001542 /*
1543 * We just prepare to enable PS. We have to wait until our AP has
1544 * ACK'd our null data frame to disable RX otherwise we'll ignore
1545 * those ACKs and end up retransmitting the same null data frames.
1546 * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
1547 */
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301548 if (changed & IEEE80211_CONF_CHANGE_PS) {
1549 if (conf->flags & IEEE80211_CONF_PS) {
Sujith1b04b932010-01-08 10:36:05 +05301550 sc->ps_flags |= PS_ENABLED;
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05001551 /*
1552 * At this point we know hardware has received an ACK
1553 * of a previously sent null data frame.
1554 */
Sujith1b04b932010-01-08 10:36:05 +05301555 if ((sc->ps_flags & PS_NULLFUNC_COMPLETED)) {
1556 sc->ps_flags &= ~PS_NULLFUNC_COMPLETED;
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301557 ath9k_enable_ps(sc);
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05001558 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301559 } else {
Gabor Juhos96148322009-07-24 17:27:21 +02001560 sc->ps_enabled = false;
Sujith1b04b932010-01-08 10:36:05 +05301561 sc->ps_flags &= ~(PS_ENABLED |
1562 PS_NULLFUNC_COMPLETED);
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -07001563 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Vivek Natarajan8782b412009-03-30 14:17:00 +05301564 if (!(ah->caps.hw_caps &
1565 ATH9K_HW_CAP_AUTOSLEEP)) {
1566 ath9k_hw_setrxabort(sc->sc_ah, 0);
Sujith1b04b932010-01-08 10:36:05 +05301567 sc->ps_flags &= ~(PS_WAIT_FOR_BEACON |
1568 PS_WAIT_FOR_CAB |
1569 PS_WAIT_FOR_PSPOLL_DATA |
1570 PS_WAIT_FOR_TX_ACK);
Pavel Roskin30691682010-03-31 18:05:31 -04001571 if (ah->imask & ATH9K_INT_TIM_TIMER) {
1572 ah->imask &= ~ATH9K_INT_TIM_TIMER;
Vivek Natarajan8782b412009-03-30 14:17:00 +05301573 ath9k_hw_set_interrupts(sc->sc_ah,
Pavel Roskin30691682010-03-31 18:05:31 -04001574 ah->imask);
Vivek Natarajan8782b412009-03-30 14:17:00 +05301575 }
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301576 }
1577 }
1578 }
1579
Sujith199afd92010-01-08 10:36:13 +05301580 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1581 if (conf->flags & IEEE80211_CONF_MONITOR) {
1582 ath_print(common, ATH_DBG_CONFIG,
1583 "HW opmode set to Monitor mode\n");
1584 sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR;
1585 }
1586 }
1587
Johannes Berg47979382009-01-07 10:13:27 +01001588 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
Sujith99405f92008-11-24 12:08:35 +05301589 struct ieee80211_channel *curchan = hw->conf.channel;
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001590 int pos = curchan->hw_value;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001591
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001592 aphy->chan_idx = pos;
1593 aphy->chan_is_ht = conf_is_ht(conf);
Felix Fietkau5ee08652010-07-31 00:11:59 +02001594 if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
1595 sc->sc_flags |= SC_OP_OFFCHANNEL;
1596 else
1597 sc->sc_flags &= ~SC_OP_OFFCHANNEL;
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001598
Jouni Malinen8089cc42009-03-03 19:23:38 +02001599 if (aphy->state == ATH_WIPHY_SCAN ||
1600 aphy->state == ATH_WIPHY_ACTIVE)
1601 ath9k_wiphy_pause_all_forced(sc, aphy);
1602 else {
1603 /*
1604 * Do not change operational channel based on a paused
1605 * wiphy changes.
1606 */
1607 goto skip_chan_change;
1608 }
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001609
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001610 ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1611 curchan->center_freq);
Johannes Bergae5eb022008-10-14 16:58:37 +02001612
Luis R. Rodriguez5f8e0772009-01-22 15:16:48 -08001613 /* XXX: remove me eventualy */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001614 ath9k_update_ichannel(sc, hw, &sc->sc_ah->channels[pos]);
Sujithe11602b2008-11-27 09:46:27 +05301615
Luis R. Rodriguezecf70442008-12-23 15:58:43 -08001616 ath_update_chainmask(sc, conf_is_ht(conf));
Sujith86060f02009-01-07 14:25:29 +05301617
Jouni Malinen0e2dedf2009-03-03 19:23:32 +02001618 if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001619 ath_print(common, ATH_DBG_FATAL,
1620 "Unable to set channel\n");
Sujithaa33de02008-12-18 11:40:16 +05301621 mutex_unlock(&sc->mutex);
Sujithe11602b2008-11-27 09:46:27 +05301622 return -EINVAL;
1623 }
Sujith094d05d2008-12-12 11:57:43 +05301624 }
Sujith86b89ee2008-08-07 10:54:57 +05301625
Jouni Malinen8089cc42009-03-03 19:23:38 +02001626skip_chan_change:
Luis R. Rodriguezc9f6a652010-01-19 14:04:19 -05001627 if (changed & IEEE80211_CONF_CHANGE_POWER) {
Sujith17d79042009-02-09 13:27:03 +05301628 sc->config.txpowlimit = 2 * conf->power_level;
Luis R. Rodriguezc9f6a652010-01-19 14:04:19 -05001629 ath_update_txpow(sc);
1630 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001631
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -07001632 spin_lock_bh(&sc->wiphy_lock);
1633 disable_radio = ath9k_all_wiphys_idle(sc);
1634 spin_unlock_bh(&sc->wiphy_lock);
1635
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001636 if (disable_radio) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001637 ath_print(common, ATH_DBG_CONFIG, "idle: disabling radio\n");
Vivek Natarajan1dbfd9d2010-01-29 16:56:51 +05301638 sc->ps_idle = true;
Luis R. Rodriguez68a89112009-11-02 14:35:42 -08001639 ath_radio_disable(sc, hw);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001640 }
1641
Sujithaa33de02008-12-18 11:40:16 +05301642 mutex_unlock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05301643
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001644 return 0;
1645}
1646
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001647#define SUPPORTED_FILTERS \
1648 (FIF_PROMISC_IN_BSS | \
1649 FIF_ALLMULTI | \
1650 FIF_CONTROL | \
Luis R. Rodriguezaf6a3fc2009-08-08 21:55:16 -04001651 FIF_PSPOLL | \
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001652 FIF_OTHER_BSS | \
1653 FIF_BCN_PRBRESP_PROMISC | \
1654 FIF_FCSFAIL)
1655
Sujith7dcfdcd2008-08-11 14:03:13 +05301656/* FIXME: sc->sc_full_reset ? */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001657static void ath9k_configure_filter(struct ieee80211_hw *hw,
1658 unsigned int changed_flags,
1659 unsigned int *total_flags,
Johannes Berg3ac64be2009-08-17 16:16:53 +02001660 u64 multicast)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001661{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001662 struct ath_wiphy *aphy = hw->priv;
1663 struct ath_softc *sc = aphy->sc;
Sujith7dcfdcd2008-08-11 14:03:13 +05301664 u32 rfilt;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001665
1666 changed_flags &= SUPPORTED_FILTERS;
1667 *total_flags &= SUPPORTED_FILTERS;
1668
Sujithb77f4832008-12-07 21:44:03 +05301669 sc->rx.rxfilter = *total_flags;
Jouni Malinenaa68aea2009-05-19 17:01:41 +03001670 ath9k_ps_wakeup(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05301671 rfilt = ath_calcrxfilter(sc);
1672 ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
Jouni Malinenaa68aea2009-05-19 17:01:41 +03001673 ath9k_ps_restore(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05301674
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001675 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
1676 "Set HW RX filter: 0x%x\n", rfilt);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001677}
1678
Johannes Berg4ca77862010-02-19 19:06:56 +01001679static int ath9k_sta_add(struct ieee80211_hw *hw,
1680 struct ieee80211_vif *vif,
1681 struct ieee80211_sta *sta)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001682{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001683 struct ath_wiphy *aphy = hw->priv;
1684 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001685
Johannes Berg4ca77862010-02-19 19:06:56 +01001686 ath_node_attach(sc, sta);
1687
1688 return 0;
1689}
1690
1691static int ath9k_sta_remove(struct ieee80211_hw *hw,
1692 struct ieee80211_vif *vif,
1693 struct ieee80211_sta *sta)
1694{
1695 struct ath_wiphy *aphy = hw->priv;
1696 struct ath_softc *sc = aphy->sc;
1697
1698 ath_node_detach(sc, sta);
1699
1700 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001701}
1702
Sujith141b38b2009-02-04 08:10:07 +05301703static int ath9k_conf_tx(struct ieee80211_hw *hw, u16 queue,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001704 const struct ieee80211_tx_queue_params *params)
1705{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001706 struct ath_wiphy *aphy = hw->priv;
1707 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001708 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujithea9880f2008-08-07 10:53:10 +05301709 struct ath9k_tx_queue_info qi;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001710 int ret = 0, qnum;
1711
1712 if (queue >= WME_NUM_AC)
1713 return 0;
1714
Sujith141b38b2009-02-04 08:10:07 +05301715 mutex_lock(&sc->mutex);
1716
Sujith1ffb0612009-03-30 15:28:46 +05301717 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1718
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001719 qi.tqi_aifs = params->aifs;
1720 qi.tqi_cwmin = params->cw_min;
1721 qi.tqi_cwmax = params->cw_max;
1722 qi.tqi_burstTime = params->txop;
1723 qnum = ath_get_hal_qnum(queue, sc);
1724
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001725 ath_print(common, ATH_DBG_CONFIG,
1726 "Configure tx [queue/halq] [%d/%d], "
1727 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1728 queue, qnum, params->aifs, params->cw_min,
1729 params->cw_max, params->txop);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001730
1731 ret = ath_txq_update(sc, qnum, &qi);
1732 if (ret)
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001733 ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001734
Vivek Natarajan94db2932009-11-25 12:01:54 +05301735 if (sc->sc_ah->opmode == NL80211_IFTYPE_ADHOC)
Felix Fietkau1d2231e2010-06-12 00:33:51 -04001736 if ((qnum == sc->tx.hwq_map[WME_AC_BE]) && !ret)
Vivek Natarajan94db2932009-11-25 12:01:54 +05301737 ath_beaconq_config(sc);
1738
Sujith141b38b2009-02-04 08:10:07 +05301739 mutex_unlock(&sc->mutex);
1740
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001741 return ret;
1742}
1743
1744static int ath9k_set_key(struct ieee80211_hw *hw,
1745 enum set_key_cmd cmd,
Johannes Bergdc822b52008-12-29 12:55:09 +01001746 struct ieee80211_vif *vif,
1747 struct ieee80211_sta *sta,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001748 struct ieee80211_key_conf *key)
1749{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001750 struct ath_wiphy *aphy = hw->priv;
1751 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001752 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001753 int ret = 0;
1754
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02001755 if (modparam_nohwcrypt)
1756 return -ENOSPC;
1757
Sujith141b38b2009-02-04 08:10:07 +05301758 mutex_lock(&sc->mutex);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301759 ath9k_ps_wakeup(sc);
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001760 ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001761
1762 switch (cmd) {
1763 case SET_KEY:
Felix Fietkau1f03baa2010-05-25 19:42:46 +02001764 ret = ath9k_cmn_key_config(common, vif, sta, key);
Jouni Malinen6ace2892008-12-17 13:32:17 +02001765 if (ret >= 0) {
1766 key->hw_key_idx = ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001767 /* push IV and Michael MIC generation to stack */
1768 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Senthil Balasubramanian1b961752008-09-01 19:45:21 +05301769 if (key->alg == ALG_TKIP)
1770 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Jouni Malinen0ced0e12009-01-08 13:32:13 +02001771 if (sc->sc_ah->sw_mgmt_crypto && key->alg == ALG_CCMP)
1772 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
Jouni Malinen6ace2892008-12-17 13:32:17 +02001773 ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001774 }
1775 break;
1776 case DISABLE_KEY:
Felix Fietkau1f03baa2010-05-25 19:42:46 +02001777 ath9k_cmn_key_delete(common, key);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001778 break;
1779 default:
1780 ret = -EINVAL;
1781 }
1782
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301783 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05301784 mutex_unlock(&sc->mutex);
1785
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001786 return ret;
1787}
1788
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001789static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
1790 struct ieee80211_vif *vif,
1791 struct ieee80211_bss_conf *bss_conf,
1792 u32 changed)
1793{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001794 struct ath_wiphy *aphy = hw->priv;
1795 struct ath_softc *sc = aphy->sc;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001796 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001797 struct ath_common *common = ath9k_hw_common(ah);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001798 struct ath_vif *avp = (void *)vif->drv_priv;
Felix Fietkau0005baf2010-01-15 02:33:40 +01001799 int slottime;
Sujithc6089cc2009-11-16 11:40:48 +05301800 int error;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001801
Sujith141b38b2009-02-04 08:10:07 +05301802 mutex_lock(&sc->mutex);
1803
Sujithc6089cc2009-11-16 11:40:48 +05301804 if (changed & BSS_CHANGED_BSSID) {
1805 /* Set BSSID */
1806 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1807 memcpy(avp->bssid, bss_conf->bssid, ETH_ALEN);
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001808 common->curaid = 0;
Luis R. Rodriguezf2b21432009-09-10 08:50:20 -07001809 ath9k_hw_write_associd(ah);
Sujithc6089cc2009-11-16 11:40:48 +05301810
1811 /* Set aggregation protection mode parameters */
1812 sc->config.ath_aggr_prot = 0;
1813
1814 /* Only legacy IBSS for now */
1815 if (vif->type == NL80211_IFTYPE_ADHOC)
1816 ath_update_chainmask(sc, 0);
1817
1818 ath_print(common, ATH_DBG_CONFIG,
1819 "BSSID: %pM aid: 0x%x\n",
1820 common->curbssid, common->curaid);
1821
1822 /* need to reconfigure the beacon */
1823 sc->sc_flags &= ~SC_OP_BEACONS ;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001824 }
1825
Sujithc6089cc2009-11-16 11:40:48 +05301826 /* Enable transmission of beacons (AP, IBSS, MESH) */
1827 if ((changed & BSS_CHANGED_BEACON) ||
1828 ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) {
1829 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
1830 error = ath_beacon_alloc(aphy, vif);
1831 if (!error)
1832 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001833 }
1834
Felix Fietkau0005baf2010-01-15 02:33:40 +01001835 if (changed & BSS_CHANGED_ERP_SLOT) {
1836 if (bss_conf->use_short_slot)
1837 slottime = 9;
1838 else
1839 slottime = 20;
1840 if (vif->type == NL80211_IFTYPE_AP) {
1841 /*
1842 * Defer update, so that connected stations can adjust
1843 * their settings at the same time.
1844 * See beacon.c for more details
1845 */
1846 sc->beacon.slottime = slottime;
1847 sc->beacon.updateslot = UPDATE;
1848 } else {
1849 ah->slottime = slottime;
1850 ath9k_hw_init_global_settings(ah);
1851 }
1852 }
1853
Sujithc6089cc2009-11-16 11:40:48 +05301854 /* Disable transmission of beacons */
1855 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon)
1856 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
1857
1858 if (changed & BSS_CHANGED_BEACON_INT) {
1859 sc->beacon_interval = bss_conf->beacon_int;
1860 /*
1861 * In case of AP mode, the HW TSF has to be reset
1862 * when the beacon interval changes.
1863 */
1864 if (vif->type == NL80211_IFTYPE_AP) {
1865 sc->sc_flags |= SC_OP_TSF_RESET;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001866 ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001867 error = ath_beacon_alloc(aphy, vif);
1868 if (!error)
1869 ath_beacon_config(sc, vif);
Sujithc6089cc2009-11-16 11:40:48 +05301870 } else {
1871 ath_beacon_config(sc, vif);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001872 }
1873 }
1874
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001875 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001876 ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1877 bss_conf->use_short_preamble);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001878 if (bss_conf->use_short_preamble)
Sujith672840a2008-08-11 14:05:08 +05301879 sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001880 else
Sujith672840a2008-08-11 14:05:08 +05301881 sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001882 }
1883
1884 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001885 ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1886 bss_conf->use_cts_prot);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001887 if (bss_conf->use_cts_prot &&
1888 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
Sujith672840a2008-08-11 14:05:08 +05301889 sc->sc_flags |= SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001890 else
Sujith672840a2008-08-11 14:05:08 +05301891 sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001892 }
1893
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001894 if (changed & BSS_CHANGED_ASSOC) {
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001895 ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001896 bss_conf->assoc);
Sujith5640b082008-10-29 10:16:06 +05301897 ath9k_bss_assoc_info(sc, vif, bss_conf);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001898 }
Sujith141b38b2009-02-04 08:10:07 +05301899
1900 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001901}
1902
1903static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
1904{
1905 u64 tsf;
Jouni Malinenbce048d2009-03-03 19:23:28 +02001906 struct ath_wiphy *aphy = hw->priv;
1907 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001908
Sujith141b38b2009-02-04 08:10:07 +05301909 mutex_lock(&sc->mutex);
1910 tsf = ath9k_hw_gettsf64(sc->sc_ah);
1911 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001912
1913 return tsf;
1914}
1915
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01001916static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1917{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001918 struct ath_wiphy *aphy = hw->priv;
1919 struct ath_softc *sc = aphy->sc;
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01001920
Sujith141b38b2009-02-04 08:10:07 +05301921 mutex_lock(&sc->mutex);
1922 ath9k_hw_settsf64(sc->sc_ah, tsf);
1923 mutex_unlock(&sc->mutex);
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01001924}
1925
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001926static void ath9k_reset_tsf(struct ieee80211_hw *hw)
1927{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001928 struct ath_wiphy *aphy = hw->priv;
1929 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001930
Sujith141b38b2009-02-04 08:10:07 +05301931 mutex_lock(&sc->mutex);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07001932
1933 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05301934 ath9k_hw_reset_tsf(sc->sc_ah);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07001935 ath9k_ps_restore(sc);
1936
Sujith141b38b2009-02-04 08:10:07 +05301937 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001938}
1939
1940static int ath9k_ampdu_action(struct ieee80211_hw *hw,
Johannes Bergc951ad32009-11-16 12:00:38 +01001941 struct ieee80211_vif *vif,
Sujith141b38b2009-02-04 08:10:07 +05301942 enum ieee80211_ampdu_mlme_action action,
1943 struct ieee80211_sta *sta,
1944 u16 tid, u16 *ssn)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001945{
Jouni Malinenbce048d2009-03-03 19:23:28 +02001946 struct ath_wiphy *aphy = hw->priv;
1947 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001948 int ret = 0;
1949
Johannes Berg85ad1812010-06-10 10:21:49 +02001950 local_bh_disable();
1951
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001952 switch (action) {
1953 case IEEE80211_AMPDU_RX_START:
Sujithdca3edb2008-10-29 10:19:01 +05301954 if (!(sc->sc_flags & SC_OP_RXAGGR))
1955 ret = -ENOTSUPP;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001956 break;
1957 case IEEE80211_AMPDU_RX_STOP:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001958 break;
1959 case IEEE80211_AMPDU_TX_START:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001960 ath9k_ps_wakeup(sc);
Sujithf83da962009-07-23 15:32:37 +05301961 ath_tx_aggr_start(sc, sta, tid, ssn);
Johannes Bergc951ad32009-11-16 12:00:38 +01001962 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001963 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001964 break;
1965 case IEEE80211_AMPDU_TX_STOP:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001966 ath9k_ps_wakeup(sc);
Sujithf83da962009-07-23 15:32:37 +05301967 ath_tx_aggr_stop(sc, sta, tid);
Johannes Bergc951ad32009-11-16 12:00:38 +01001968 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001969 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001970 break;
Johannes Bergb1720232009-03-23 17:28:39 +01001971 case IEEE80211_AMPDU_TX_OPERATIONAL:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001972 ath9k_ps_wakeup(sc);
Sujith8469cde2008-10-29 10:19:28 +05301973 ath_tx_aggr_resume(sc, sta, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001974 ath9k_ps_restore(sc);
Sujith8469cde2008-10-29 10:19:28 +05301975 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001976 default:
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001977 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL,
1978 "Unknown AMPDU action\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001979 }
1980
Johannes Berg85ad1812010-06-10 10:21:49 +02001981 local_bh_enable();
1982
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001983 return ret;
1984}
1985
Benoit Papillault62dad5b2010-04-28 00:08:24 +02001986static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
1987 struct survey_info *survey)
1988{
1989 struct ath_wiphy *aphy = hw->priv;
1990 struct ath_softc *sc = aphy->sc;
1991 struct ath_hw *ah = sc->sc_ah;
1992 struct ath_common *common = ath9k_hw_common(ah);
1993 struct ieee80211_conf *conf = &hw->conf;
1994
1995 if (idx != 0)
1996 return -ENOENT;
1997
1998 survey->channel = conf->channel;
1999 survey->filled = SURVEY_INFO_NOISE_DBM;
2000 survey->noise = common->ani.noise_floor;
2001
2002 return 0;
2003}
2004
Sujith0c98de62009-03-03 10:16:45 +05302005static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
2006{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002007 struct ath_wiphy *aphy = hw->priv;
2008 struct ath_softc *sc = aphy->sc;
Sujith0c98de62009-03-03 10:16:45 +05302009
Sujith3d832612009-08-21 12:00:28 +05302010 mutex_lock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02002011 if (ath9k_wiphy_scanning(sc)) {
Jouni Malinen8089cc42009-03-03 19:23:38 +02002012 /*
Luis R. Rodriguez30888332010-07-27 16:33:06 -04002013 * There is a race here in mac80211 but fixing it requires
2014 * we revisit how we handle the scan complete callback.
2015 * After mac80211 fixes we will not have configured hardware
2016 * to the home channel nor would we have configured the RX
2017 * filter yet.
Jouni Malinen8089cc42009-03-03 19:23:38 +02002018 */
Sujith3d832612009-08-21 12:00:28 +05302019 mutex_unlock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02002020 return;
2021 }
2022
2023 aphy->state = ATH_WIPHY_SCAN;
2024 ath9k_wiphy_pause_all_forced(sc, aphy);
Sujith0c98de62009-03-03 10:16:45 +05302025 sc->sc_flags |= SC_OP_SCANNING;
Sujith3d832612009-08-21 12:00:28 +05302026 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05302027}
2028
Luis R. Rodriguez30888332010-07-27 16:33:06 -04002029/*
2030 * XXX: this requires a revisit after the driver
2031 * scan_complete gets moved to another place/removed in mac80211.
2032 */
Sujith0c98de62009-03-03 10:16:45 +05302033static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
2034{
Jouni Malinenbce048d2009-03-03 19:23:28 +02002035 struct ath_wiphy *aphy = hw->priv;
2036 struct ath_softc *sc = aphy->sc;
Sujith0c98de62009-03-03 10:16:45 +05302037
Sujith3d832612009-08-21 12:00:28 +05302038 mutex_lock(&sc->mutex);
Jouni Malinen8089cc42009-03-03 19:23:38 +02002039 aphy->state = ATH_WIPHY_ACTIVE;
Sujith0c98de62009-03-03 10:16:45 +05302040 sc->sc_flags &= ~SC_OP_SCANNING;
Sujith3d832612009-08-21 12:00:28 +05302041 mutex_unlock(&sc->mutex);
Sujith0c98de62009-03-03 10:16:45 +05302042}
2043
Felix Fietkaue239d852010-01-15 02:34:58 +01002044static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class)
2045{
2046 struct ath_wiphy *aphy = hw->priv;
2047 struct ath_softc *sc = aphy->sc;
2048 struct ath_hw *ah = sc->sc_ah;
2049
2050 mutex_lock(&sc->mutex);
2051 ah->coverage_class = coverage_class;
2052 ath9k_hw_init_global_settings(ah);
2053 mutex_unlock(&sc->mutex);
2054}
2055
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002056struct ieee80211_ops ath9k_ops = {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002057 .tx = ath9k_tx,
2058 .start = ath9k_start,
2059 .stop = ath9k_stop,
2060 .add_interface = ath9k_add_interface,
2061 .remove_interface = ath9k_remove_interface,
2062 .config = ath9k_config,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002063 .configure_filter = ath9k_configure_filter,
Johannes Berg4ca77862010-02-19 19:06:56 +01002064 .sta_add = ath9k_sta_add,
2065 .sta_remove = ath9k_sta_remove,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002066 .conf_tx = ath9k_conf_tx,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002067 .bss_info_changed = ath9k_bss_info_changed,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002068 .set_key = ath9k_set_key,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002069 .get_tsf = ath9k_get_tsf,
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01002070 .set_tsf = ath9k_set_tsf,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002071 .reset_tsf = ath9k_reset_tsf,
Johannes Berg4233df62008-10-13 13:35:05 +02002072 .ampdu_action = ath9k_ampdu_action,
Benoit Papillault62dad5b2010-04-28 00:08:24 +02002073 .get_survey = ath9k_get_survey,
Sujith0c98de62009-03-03 10:16:45 +05302074 .sw_scan_start = ath9k_sw_scan_start,
2075 .sw_scan_complete = ath9k_sw_scan_complete,
Johannes Berg3b319aa2009-06-13 14:50:26 +05302076 .rfkill_poll = ath9k_rfkill_poll_state,
Felix Fietkaue239d852010-01-15 02:34:58 +01002077 .set_coverage_class = ath9k_set_coverage_class,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002078};