blob: 3e485f7d88e639e1039667b216d15f4645366dae [file] [log] [blame]
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001/*
Sujith Manoharan5b681382011-05-17 13:36:18 +05302 * Copyright (c) 2008-2011 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>
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -080018#include <linux/delay.h>
Sujith394cf0a2009-02-09 13:26:54 +053019#include "ath9k.h"
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -070020#include "btcoex.h"
Luis R. Rodriguezf078f202008-08-04 00:16:41 -070021
Sven Eckelmann313eb872012-06-25 07:15:22 +020022u8 ath9k_parse_mpdudensity(u8 mpdudensity)
Sujithff37e332008-11-24 12:07:55 +053023{
24 /*
25 * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing":
26 * 0 for no restriction
27 * 1 for 1/4 us
28 * 2 for 1/2 us
29 * 3 for 1 us
30 * 4 for 2 us
31 * 5 for 4 us
32 * 6 for 8 us
33 * 7 for 16 us
34 */
35 switch (mpdudensity) {
36 case 0:
37 return 0;
38 case 1:
39 case 2:
40 case 3:
41 /* Our lower layer calculations limit our precision to
42 1 microsecond */
43 return 1;
44 case 4:
45 return 2;
46 case 5:
47 return 4;
48 case 6:
49 return 8;
50 case 7:
51 return 16;
52 default:
53 return 0;
54 }
55}
56
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -080057static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
58{
59 bool pending = false;
60
61 spin_lock_bh(&txq->axq_lock);
62
Felix Fietkau04535312014-06-11 16:17:51 +053063 if (txq->axq_depth)
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -080064 pending = true;
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -080065
Felix Fietkau04535312014-06-11 16:17:51 +053066 if (txq->mac80211_qnum >= 0) {
67 struct list_head *list;
68
69 list = &sc->cur_chan->acq[txq->mac80211_qnum];
70 if (!list_empty(list))
71 pending = true;
72 }
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -080073 spin_unlock_bh(&txq->axq_lock);
74 return pending;
75}
76
Mohammed Shafi Shajakhan6d79cb42011-05-19 17:40:46 +053077static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode)
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -070078{
79 unsigned long flags;
80 bool ret;
81
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -070082 spin_lock_irqsave(&sc->sc_pm_lock, flags);
83 ret = ath9k_hw_setpower(sc->sc_ah, mode);
84 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Luis R. Rodriguez8c77a562009-09-09 21:02:34 -070085
86 return ret;
87}
88
Felix Fietkaubf3dac52013-11-11 22:23:33 +010089void ath_ps_full_sleep(unsigned long data)
90{
91 struct ath_softc *sc = (struct ath_softc *) data;
92 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
93 bool reset;
94
95 spin_lock(&common->cc_lock);
96 ath_hw_cycle_counters_update(common);
97 spin_unlock(&common->cc_lock);
98
99 ath9k_hw_setrxabort(sc->sc_ah, 1);
100 ath9k_hw_stopdmarecv(sc->sc_ah, &reset);
101
102 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
103}
104
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700105void ath9k_ps_wakeup(struct ath_softc *sc)
106{
Felix Fietkau898c9142010-10-12 14:02:53 +0200107 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700108 unsigned long flags;
Felix Fietkaufbb078f2010-11-03 01:36:51 +0100109 enum ath9k_power_mode power_mode;
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700110
111 spin_lock_irqsave(&sc->sc_pm_lock, flags);
112 if (++sc->ps_usecount != 1)
113 goto unlock;
114
Felix Fietkaubf3dac52013-11-11 22:23:33 +0100115 del_timer_sync(&sc->sleep_timer);
Felix Fietkaufbb078f2010-11-03 01:36:51 +0100116 power_mode = sc->sc_ah->power_mode;
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700117 ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700118
Felix Fietkau898c9142010-10-12 14:02:53 +0200119 /*
120 * While the hardware is asleep, the cycle counters contain no
121 * useful data. Better clear them now so that they don't mess up
122 * survey data results.
123 */
Felix Fietkaufbb078f2010-11-03 01:36:51 +0100124 if (power_mode != ATH9K_PM_AWAKE) {
125 spin_lock(&common->cc_lock);
126 ath_hw_cycle_counters_update(common);
127 memset(&common->cc_survey, 0, sizeof(common->cc_survey));
Rajkumar Manoharanc9ae6ab2012-06-04 16:28:41 +0530128 memset(&common->cc_ani, 0, sizeof(common->cc_ani));
Felix Fietkaufbb078f2010-11-03 01:36:51 +0100129 spin_unlock(&common->cc_lock);
130 }
Felix Fietkau898c9142010-10-12 14:02:53 +0200131
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700132 unlock:
133 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
134}
135
136void ath9k_ps_restore(struct ath_softc *sc)
137{
Felix Fietkau898c9142010-10-12 14:02:53 +0200138 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkauc6c539f2011-09-14 21:24:24 +0200139 enum ath9k_power_mode mode;
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700140 unsigned long flags;
141
142 spin_lock_irqsave(&sc->sc_pm_lock, flags);
143 if (--sc->ps_usecount != 0)
144 goto unlock;
145
Sujith Manoharanad128862012-04-24 10:23:20 +0530146 if (sc->ps_idle) {
Felix Fietkaubf3dac52013-11-11 22:23:33 +0100147 mod_timer(&sc->sleep_timer, jiffies + HZ / 10);
148 goto unlock;
149 }
150
151 if (sc->ps_enabled &&
Sujith Manoharanad128862012-04-24 10:23:20 +0530152 !(sc->ps_flags & (PS_WAIT_FOR_BEACON |
153 PS_WAIT_FOR_CAB |
154 PS_WAIT_FOR_PSPOLL_DATA |
Rajkumar Manoharan424749c2012-10-10 23:03:02 +0530155 PS_WAIT_FOR_TX_ACK |
156 PS_WAIT_FOR_ANI))) {
Felix Fietkauc6c539f2011-09-14 21:24:24 +0200157 mode = ATH9K_PM_NETWORK_SLEEP;
Rajkumar Manoharan08d4df42012-07-01 19:53:54 +0530158 if (ath9k_hw_btcoex_is_enabled(sc->sc_ah))
159 ath9k_btcoex_stop_gen_timer(sc);
Sujith Manoharanad128862012-04-24 10:23:20 +0530160 } else {
Felix Fietkauc6c539f2011-09-14 21:24:24 +0200161 goto unlock;
Sujith Manoharanad128862012-04-24 10:23:20 +0530162 }
Felix Fietkauc6c539f2011-09-14 21:24:24 +0200163
164 spin_lock(&common->cc_lock);
165 ath_hw_cycle_counters_update(common);
166 spin_unlock(&common->cc_lock);
167
Felix Fietkau1a8f0d392011-09-22 08:04:32 -0600168 ath9k_hw_setpower(sc->sc_ah, mode);
Luis R. Rodrigueza91d75ae2009-09-09 20:29:18 -0700169
170 unlock:
171 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
172}
173
Felix Fietkau9adcf442011-09-03 01:40:26 +0200174static void __ath_cancel_work(struct ath_softc *sc)
175{
176 cancel_work_sync(&sc->paprd_work);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200177 cancel_delayed_work_sync(&sc->tx_complete_work);
178 cancel_delayed_work_sync(&sc->hw_pll_work);
Sujith Manoharanfad29cd2012-06-25 13:54:22 +0530179
Sujith Manoharanbf525922012-06-27 14:15:59 +0530180#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
Sujith Manoharanfad29cd2012-06-25 13:54:22 +0530181 if (ath9k_hw_mci_is_enabled(sc->sc_ah))
182 cancel_work_sync(&sc->mci_work);
Sujith Manoharanbf525922012-06-27 14:15:59 +0530183#endif
Felix Fietkau9adcf442011-09-03 01:40:26 +0200184}
185
Sujith Manoharane60001e2013-10-28 12:22:04 +0530186void ath_cancel_work(struct ath_softc *sc)
Felix Fietkau9adcf442011-09-03 01:40:26 +0200187{
188 __ath_cancel_work(sc);
189 cancel_work_sync(&sc->hw_reset_work);
190}
191
Sujith Manoharane60001e2013-10-28 12:22:04 +0530192void ath_restart_work(struct ath_softc *sc)
Sujith Manoharanaf68aba2012-06-04 20:23:43 +0530193{
Sujith Manoharanaf68aba2012-06-04 20:23:43 +0530194 ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
195
Sujith Manoharan19c36162013-08-20 10:05:59 +0530196 if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9330(sc->sc_ah))
Sujith Manoharanaf68aba2012-06-04 20:23:43 +0530197 ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
198 msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
199
Sujith Manoharanda0d45f2012-07-17 17:16:29 +0530200 ath_start_ani(sc);
Sujith Manoharanaf68aba2012-06-04 20:23:43 +0530201}
202
John W. Linville9ebea382013-01-28 13:54:03 -0500203static bool ath_prepare_reset(struct ath_softc *sc)
Felix Fietkau9adcf442011-09-03 01:40:26 +0200204{
205 struct ath_hw *ah = sc->sc_ah;
Felix Fietkauceea2a52012-05-24 14:32:19 +0200206 bool ret = true;
Felix Fietkau9adcf442011-09-03 01:40:26 +0200207
208 ieee80211_stop_queues(sc->hw);
Sujith Manoharanda0d45f2012-07-17 17:16:29 +0530209 ath_stop_ani(sc);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200210 ath9k_hw_disable_interrupts(ah);
211
Felix Fietkau13815592013-01-20 18:51:53 +0100212 if (!ath_drain_all_txq(sc))
Felix Fietkau9adcf442011-09-03 01:40:26 +0200213 ret = false;
214
Felix Fietkau0a62acb2013-01-20 18:51:52 +0100215 if (!ath_stoprecv(sc))
Felix Fietkauceea2a52012-05-24 14:32:19 +0200216 ret = false;
217
Felix Fietkau9adcf442011-09-03 01:40:26 +0200218 return ret;
219}
220
221static bool ath_complete_reset(struct ath_softc *sc, bool start)
222{
223 struct ath_hw *ah = sc->sc_ah;
224 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharan196fb862012-06-04 20:24:13 +0530225 unsigned long flags;
Felix Fietkau9adcf442011-09-03 01:40:26 +0200226
Sujith Manoharan9019f642014-09-05 08:03:15 +0530227 ath9k_calculate_summary_state(sc, sc->cur_chan);
Sujith Manoharan19ec4772014-09-05 08:03:16 +0530228 ath_startrecv(sc);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200229 ath9k_cmn_update_txpow(ah, sc->curtxpow,
Felix Fietkaubc7e1be2014-06-11 16:17:50 +0530230 sc->cur_chan->txpower, &sc->curtxpow);
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100231 clear_bit(ATH_OP_HW_RESET, &common->op_flags);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200232
Felix Fietkaufbbcd142014-06-11 16:17:49 +0530233 if (!sc->cur_chan->offchannel && start) {
Felix Fietkau8d7e09d2014-06-11 16:18:01 +0530234 /* restore per chanctx TSF timer */
235 if (sc->cur_chan->tsf_val) {
236 u32 offset;
237
238 offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts,
239 NULL);
240 ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset);
241 }
242
243
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100244 if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
Sujith Manoharan196fb862012-06-04 20:24:13 +0530245 goto work;
Felix Fietkau9adcf442011-09-03 01:40:26 +0200246
Sujith Manoharan196fb862012-06-04 20:24:13 +0530247 if (ah->opmode == NL80211_IFTYPE_STATION &&
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100248 test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
Sujith Manoharan196fb862012-06-04 20:24:13 +0530249 spin_lock_irqsave(&sc->sc_pm_lock, flags);
250 sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
251 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Sujith Manoharana6768282013-05-06 10:09:03 +0530252 } else {
253 ath9k_set_beacon(sc);
Sujith Manoharan196fb862012-06-04 20:24:13 +0530254 }
255 work:
Sujith Manoharanaf68aba2012-06-04 20:23:43 +0530256 ath_restart_work(sc);
Felix Fietkau04535312014-06-11 16:17:51 +0530257 ath_txq_schedule_all(sc);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200258 }
259
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530260 sc->gtt_cnt = 0;
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530261
262 ath9k_hw_set_interrupts(ah);
263 ath9k_hw_enable_interrupts(ah);
264
Sujith Manoharan499afac2014-08-22 20:39:31 +0530265 if (!ath9k_is_chanctx_enabled())
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +0530266 ieee80211_wake_queues(sc->hw);
Sujith Manoharan0e08b5f2014-08-23 13:29:19 +0530267 else
268 ath9k_chanctx_wake_queues(sc);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200269
Felix Fietkaud463af42014-04-06 00:37:03 +0200270 ath9k_p2p_ps_timer(sc);
271
Felix Fietkau9adcf442011-09-03 01:40:26 +0200272 return true;
273}
274
Felix Fietkaufbbcd142014-06-11 16:17:49 +0530275int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
Felix Fietkau9adcf442011-09-03 01:40:26 +0200276{
277 struct ath_hw *ah = sc->sc_ah;
278 struct ath_common *common = ath9k_hw_common(ah);
279 struct ath9k_hw_cal_data *caldata = NULL;
280 bool fastcc = true;
Felix Fietkau9adcf442011-09-03 01:40:26 +0200281 int r;
282
283 __ath_cancel_work(sc);
284
Felix Fietkau4668cce2013-01-14 16:56:46 +0100285 tasklet_disable(&sc->intr_tq);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200286 spin_lock_bh(&sc->sc_pcu_lock);
287
Felix Fietkaufbbcd142014-06-11 16:17:49 +0530288 if (!sc->cur_chan->offchannel) {
Felix Fietkau9adcf442011-09-03 01:40:26 +0200289 fastcc = false;
Felix Fietkaub01459e2014-06-11 16:17:59 +0530290 caldata = &sc->cur_chan->caldata;
Felix Fietkau9adcf442011-09-03 01:40:26 +0200291 }
292
293 if (!hchan) {
294 fastcc = false;
Felix Fietkau9adcf442011-09-03 01:40:26 +0200295 hchan = ah->curchan;
296 }
297
John W. Linville9ebea382013-01-28 13:54:03 -0500298 if (!ath_prepare_reset(sc))
Felix Fietkau9adcf442011-09-03 01:40:26 +0200299 fastcc = false;
300
Sujith Manoharan9ea35982014-08-27 12:07:23 +0530301 if (ath9k_is_chanctx_enabled())
302 fastcc = false;
303
Rajkumar Manoharand6067f02014-06-20 22:47:49 +0530304 spin_lock_bh(&sc->chan_lock);
305 sc->cur_chandef = sc->cur_chan->chandef;
306 spin_unlock_bh(&sc->chan_lock);
Felix Fietkaubff11762014-06-11 16:17:52 +0530307
Joe Perchesd2182b62011-12-15 14:55:53 -0800308 ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
Sujith Manoharanfeced202012-01-30 14:21:42 +0530309 hchan->channel, IS_CHAN_HT40(hchan), fastcc);
Felix Fietkau9adcf442011-09-03 01:40:26 +0200310
311 r = ath9k_hw_reset(ah, hchan, caldata, fastcc);
312 if (r) {
313 ath_err(common,
314 "Unable to reset channel, reset status %d\n", r);
Robert Shadef50b1cd2013-04-02 19:52:45 -0400315
316 ath9k_hw_enable_interrupts(ah);
317 ath9k_queue_reset(sc, RESET_TYPE_BB_HANG);
318
Felix Fietkau9adcf442011-09-03 01:40:26 +0200319 goto out;
320 }
321
Rajkumar Manoharane82cb032012-10-12 14:07:25 +0530322 if (ath9k_hw_mci_is_enabled(sc->sc_ah) &&
Felix Fietkaufbbcd142014-06-11 16:17:49 +0530323 sc->cur_chan->offchannel)
Rajkumar Manoharane82cb032012-10-12 14:07:25 +0530324 ath9k_mci_set_txpower(sc, true, false);
325
Felix Fietkau9adcf442011-09-03 01:40:26 +0200326 if (!ath_complete_reset(sc, true))
327 r = -EIO;
328
329out:
330 spin_unlock_bh(&sc->sc_pcu_lock);
Felix Fietkau4668cce2013-01-14 16:56:46 +0100331 tasklet_enable(&sc->intr_tq);
332
Felix Fietkau9adcf442011-09-03 01:40:26 +0200333 return r;
334}
335
Ben Greear7e1e3862011-11-03 11:33:13 -0700336static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
337 struct ieee80211_vif *vif)
Sujithff37e332008-11-24 12:07:55 +0530338{
339 struct ath_node *an;
Sujithff37e332008-11-24 12:07:55 +0530340 an = (struct ath_node *)sta->drv_priv;
341
Sujith Manoharana145daf2012-11-28 15:08:54 +0530342 an->sc = sc;
Ben Greear7f010c92011-01-09 23:11:49 -0800343 an->sta = sta;
Ben Greear7e1e3862011-11-03 11:33:13 -0700344 an->vif = vif;
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +0530345 memset(&an->key_idx, 0, sizeof(an->key_idx));
Sujith Manoharan3d4e20f2012-03-14 14:40:58 +0530346
Sujith Manoharandd5ee592013-02-04 15:38:23 +0530347 ath_tx_node_init(sc, an);
Lorenzo Bianconi44b47a72014-09-16 02:13:16 +0200348
349 ath_dynack_node_init(sc->sc_ah, an);
Sujithff37e332008-11-24 12:07:55 +0530350}
351
352static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
353{
354 struct ath_node *an = (struct ath_node *)sta->drv_priv;
Sujith Manoharandd5ee592013-02-04 15:38:23 +0530355 ath_tx_node_cleanup(sc, an);
Lorenzo Bianconi44b47a72014-09-16 02:13:16 +0200356
357 ath_dynack_node_deinit(sc->sc_ah, an);
Sujithff37e332008-11-24 12:07:55 +0530358}
359
Sujith55624202010-01-08 10:36:02 +0530360void ath9k_tasklet(unsigned long data)
Sujithff37e332008-11-24 12:07:55 +0530361{
362 struct ath_softc *sc = (struct ath_softc *)data;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700363 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700364 struct ath_common *common = ath9k_hw_common(ah);
Rajkumar Manoharan124b9792012-07-17 17:16:42 +0530365 enum ath_reset_type type;
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530366 unsigned long flags;
Sujith17d79042009-02-09 13:27:03 +0530367 u32 status = sc->intrstatus;
Felix Fietkaub5c804752010-04-15 17:38:48 -0400368 u32 rxmask;
Sujithff37e332008-11-24 12:07:55 +0530369
Felix Fietkaue3927002011-09-14 21:23:01 +0200370 ath9k_ps_wakeup(sc);
371 spin_lock(&sc->sc_pcu_lock);
372
Sujith Manoharan6549a862013-12-24 10:44:24 +0530373 if (status & ATH9K_INT_FATAL) {
374 type = RESET_TYPE_FATAL_INT;
Rajkumar Manoharan124b9792012-07-17 17:16:42 +0530375 ath9k_queue_reset(sc, type);
Sujith Manoharanc6cc47b2013-09-16 10:37:00 +0530376
377 /*
378 * Increment the ref. counter here so that
379 * interrupts are enabled in the reset routine.
380 */
381 atomic_inc(&ah->intr_ref_cnt);
Felix Fietkauaffad452014-02-22 14:52:49 +0100382 ath_dbg(common, RESET, "FATAL: Skipping interrupts\n");
Felix Fietkaue3927002011-09-14 21:23:01 +0200383 goto out;
Sujithff37e332008-11-24 12:07:55 +0530384 }
385
Sujith Manoharan6549a862013-12-24 10:44:24 +0530386 if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) &&
387 (status & ATH9K_INT_BB_WATCHDOG)) {
Sujith Manoharan0c759972013-12-24 10:44:26 +0530388 spin_lock(&common->cc_lock);
389 ath_hw_cycle_counters_update(common);
390 ar9003_hw_bb_watchdog_dbg_info(ah);
391 spin_unlock(&common->cc_lock);
392
Sujith Manoharan6549a862013-12-24 10:44:24 +0530393 if (ar9003_hw_bb_watchdog_check(ah)) {
394 type = RESET_TYPE_BB_WATCHDOG;
395 ath9k_queue_reset(sc, type);
396
397 /*
398 * Increment the ref. counter here so that
399 * interrupts are enabled in the reset routine.
400 */
401 atomic_inc(&ah->intr_ref_cnt);
Felix Fietkauaffad452014-02-22 14:52:49 +0100402 ath_dbg(common, RESET,
Sujith Manoharan6549a862013-12-24 10:44:24 +0530403 "BB_WATCHDOG: Skipping interrupts\n");
404 goto out;
405 }
406 }
407
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530408 if (status & ATH9K_INT_GTT) {
409 sc->gtt_cnt++;
410
411 if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) {
412 type = RESET_TYPE_TX_GTT;
413 ath9k_queue_reset(sc, type);
414 atomic_inc(&ah->intr_ref_cnt);
Felix Fietkauaffad452014-02-22 14:52:49 +0100415 ath_dbg(common, RESET,
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530416 "GTT: Skipping interrupts\n");
417 goto out;
418 }
419 }
420
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530421 spin_lock_irqsave(&sc->sc_pm_lock, flags);
Rajkumar Manoharan4105f802011-05-06 18:27:47 +0530422 if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
423 /*
424 * TSF sync does not look correct; remain awake to sync with
425 * the next Beacon.
426 */
Joe Perchesd2182b62011-12-15 14:55:53 -0800427 ath_dbg(common, PS, "TSFOOR - Sync with next Beacon\n");
Rajkumar Manoharane8fe7332011-08-05 18:59:41 +0530428 sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC;
Rajkumar Manoharan4105f802011-05-06 18:27:47 +0530429 }
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530430 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Rajkumar Manoharan4105f802011-05-06 18:27:47 +0530431
Felix Fietkaub5c804752010-04-15 17:38:48 -0400432 if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
433 rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL |
434 ATH9K_INT_RXORN);
435 else
436 rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
437
438 if (status & rxmask) {
Felix Fietkaub5c804752010-04-15 17:38:48 -0400439 /* Check for high priority Rx first */
440 if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
441 (status & ATH9K_INT_RXHP))
442 ath_rx_tasklet(sc, 0, true);
443
444 ath_rx_tasklet(sc, 0, false);
Sujith063d8be2009-03-30 15:28:49 +0530445 }
446
Vasanthakumar Thiagarajane5003242010-04-15 17:39:36 -0400447 if (status & ATH9K_INT_TX) {
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530448 if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
449 /*
450 * For EDMA chips, TX completion is enabled for the
451 * beacon queue, so if a beacon has been transmitted
452 * successfully after a GTT interrupt, the GTT counter
453 * gets reset to zero here.
454 */
Sujith Manoharan3b745c72014-01-20 13:25:28 +0530455 sc->gtt_cnt = 0;
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530456
Vasanthakumar Thiagarajane5003242010-04-15 17:39:36 -0400457 ath_tx_edma_tasklet(sc);
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530458 } else {
Vasanthakumar Thiagarajane5003242010-04-15 17:39:36 -0400459 ath_tx_tasklet(sc);
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530460 }
Felix Fietkau10e23182013-11-11 22:23:35 +0100461
462 wake_up(&sc->tx_wait);
Vasanthakumar Thiagarajane5003242010-04-15 17:39:36 -0400463 }
Sujith063d8be2009-03-30 15:28:49 +0530464
Felix Fietkauc67ce332013-12-14 18:03:38 +0100465 if (status & ATH9K_INT_GENTIMER)
466 ath_gen_timer_isr(sc->sc_ah);
467
Sujith Manoharan56ca0db2012-02-22 12:40:32 +0530468 ath9k_btcoex_handle_interrupt(sc, status);
Mohammed Shafi Shajakhan19686dd2011-11-30 10:41:28 +0530469
Sujithff37e332008-11-24 12:07:55 +0530470 /* re-enable hardware interrupt */
Felix Fietkau4df30712010-11-08 20:54:47 +0100471 ath9k_hw_enable_interrupts(ah);
Sujith Manoharanc6cc47b2013-09-16 10:37:00 +0530472out:
Senthil Balasubramanian52671e42010-12-23 21:06:57 +0530473 spin_unlock(&sc->sc_pcu_lock);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400474 ath9k_ps_restore(sc);
Sujithff37e332008-11-24 12:07:55 +0530475}
476
Gabor Juhos6baff7f2009-01-14 20:17:06 +0100477irqreturn_t ath_isr(int irq, void *dev)
Sujithff37e332008-11-24 12:07:55 +0530478{
Sujith063d8be2009-03-30 15:28:49 +0530479#define SCHED_INTR ( \
480 ATH9K_INT_FATAL | \
Rajkumar Manoharana4d86d92011-05-20 17:52:10 +0530481 ATH9K_INT_BB_WATCHDOG | \
Sujith063d8be2009-03-30 15:28:49 +0530482 ATH9K_INT_RXORN | \
483 ATH9K_INT_RXEOL | \
484 ATH9K_INT_RX | \
Felix Fietkaub5c804752010-04-15 17:38:48 -0400485 ATH9K_INT_RXLP | \
486 ATH9K_INT_RXHP | \
Sujith063d8be2009-03-30 15:28:49 +0530487 ATH9K_INT_TX | \
488 ATH9K_INT_BMISS | \
489 ATH9K_INT_CST | \
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530490 ATH9K_INT_GTT | \
Vasanthakumar Thiagarajanebb8e1d2009-09-01 17:46:32 +0530491 ATH9K_INT_TSFOOR | \
Mohammed Shafi Shajakhan40dc5392011-11-30 10:41:18 +0530492 ATH9K_INT_GENTIMER | \
493 ATH9K_INT_MCI)
Sujith063d8be2009-03-30 15:28:49 +0530494
Sujithff37e332008-11-24 12:07:55 +0530495 struct ath_softc *sc = dev;
Sujithcbe61d82009-02-09 13:27:12 +0530496 struct ath_hw *ah = sc->sc_ah;
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100497 struct ath_common *common = ath9k_hw_common(ah);
Sujithff37e332008-11-24 12:07:55 +0530498 enum ath9k_int status;
Sujith Manoharan78c8a952013-12-28 09:47:15 +0530499 u32 sync_cause = 0;
Sujithff37e332008-11-24 12:07:55 +0530500 bool sched = false;
501
Sujith063d8be2009-03-30 15:28:49 +0530502 /*
503 * The hardware is not ready/present, don't
504 * touch anything. Note this can happen early
505 * on if the IRQ is shared.
506 */
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100507 if (test_bit(ATH_OP_INVALID, &common->op_flags))
Sujith063d8be2009-03-30 15:28:49 +0530508 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530509
Sujith063d8be2009-03-30 15:28:49 +0530510 /* shared irq, not for us */
Sujithff37e332008-11-24 12:07:55 +0530511
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400512 if (!ath9k_hw_intrpend(ah))
Sujith063d8be2009-03-30 15:28:49 +0530513 return IRQ_NONE;
Sujithff37e332008-11-24 12:07:55 +0530514
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100515 if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
Felix Fietkauf41a9b32012-08-08 16:25:03 +0200516 ath9k_hw_kill_interrupts(ah);
Sujith Manoharanb74713d2012-06-04 20:24:01 +0530517 return IRQ_HANDLED;
Felix Fietkauf41a9b32012-08-08 16:25:03 +0200518 }
Sujith Manoharanb74713d2012-06-04 20:24:01 +0530519
Sujith063d8be2009-03-30 15:28:49 +0530520 /*
521 * Figure out the reason(s) for the interrupt. Note
522 * that the hal returns a pseudo-ISR that may include
523 * bits we haven't explicitly enabled so we mask the
524 * value to insure we only process bits we requested.
525 */
Felix Fietkau6a4d05d2013-12-19 18:01:48 +0100526 ath9k_hw_getisr(ah, &status, &sync_cause); /* NB: clears ISR too */
527 ath9k_debug_sync_cause(sc, sync_cause);
Pavel Roskin30691682010-03-31 18:05:31 -0400528 status &= ah->imask; /* discard unasked-for bits */
Sujith063d8be2009-03-30 15:28:49 +0530529
530 /*
531 * If there are no status bits set, then this interrupt was not
532 * for me (should have been caught above).
533 */
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400534 if (!status)
Sujith063d8be2009-03-30 15:28:49 +0530535 return IRQ_NONE;
Sujith063d8be2009-03-30 15:28:49 +0530536
537 /* Cache the status */
538 sc->intrstatus = status;
539
540 if (status & SCHED_INTR)
541 sched = true;
542
543 /*
544 * If a FATAL or RXORN interrupt is received, we have to reset the
545 * chip immediately.
546 */
Felix Fietkaub5c804752010-04-15 17:38:48 -0400547 if ((status & ATH9K_INT_FATAL) || ((status & ATH9K_INT_RXORN) &&
548 !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)))
Sujith063d8be2009-03-30 15:28:49 +0530549 goto chip_reset;
550
Sujith Manoharana6bb8602013-12-24 10:44:22 +0530551 if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) &&
Sujith Manoharan0c759972013-12-24 10:44:26 +0530552 (status & ATH9K_INT_BB_WATCHDOG))
Luis R. Rodriguez08578b82010-05-13 13:33:44 -0400553 goto chip_reset;
Sujith Manoharane60001e2013-10-28 12:22:04 +0530554
555#ifdef CONFIG_ATH9K_WOW
Rajkumar Manoharanca90ef42012-11-20 18:29:59 +0530556 if (status & ATH9K_INT_BMISS) {
557 if (atomic_read(&sc->wow_sleep_proc_intr) == 0) {
Rajkumar Manoharanca90ef42012-11-20 18:29:59 +0530558 atomic_inc(&sc->wow_got_bmiss_intr);
559 atomic_dec(&sc->wow_sleep_proc_intr);
560 }
561 }
562#endif
Sujith Manoharane60001e2013-10-28 12:22:04 +0530563
Sujith063d8be2009-03-30 15:28:49 +0530564 if (status & ATH9K_INT_SWBA)
565 tasklet_schedule(&sc->bcon_tasklet);
566
567 if (status & ATH9K_INT_TXURN)
568 ath9k_hw_updatetxtriglevel(ah, true);
569
Rajkumar Manoharan0682c9b2011-08-13 10:28:09 +0530570 if (status & ATH9K_INT_RXEOL) {
571 ah->imask &= ~(ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
Felix Fietkau72d874c2011-10-08 20:06:19 +0200572 ath9k_hw_set_interrupts(ah);
Felix Fietkaub5c804752010-04-15 17:38:48 -0400573 }
574
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400575 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
576 if (status & ATH9K_INT_TIM_TIMER) {
Luis R. Rodriguezff9f0b62010-12-07 15:13:22 -0800577 if (ATH_DBG_WARN_ON_ONCE(sc->ps_idle))
578 goto chip_reset;
Sujith063d8be2009-03-30 15:28:49 +0530579 /* Clear RxAbort bit so that we can
580 * receive frames */
Luis R. Rodriguez9ecdef42009-09-09 21:10:09 -0700581 ath9k_setpower(sc, ATH9K_PM_AWAKE);
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530582 spin_lock(&sc->sc_pm_lock);
Vasanthakumar Thiagarajan153e0802009-05-15 02:47:16 -0400583 ath9k_hw_setrxabort(sc->sc_ah, 0);
Sujith1b04b932010-01-08 10:36:05 +0530584 sc->ps_flags |= PS_WAIT_FOR_BEACON;
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530585 spin_unlock(&sc->sc_pm_lock);
Sujith063d8be2009-03-30 15:28:49 +0530586 }
Sujith063d8be2009-03-30 15:28:49 +0530587
588chip_reset:
589
Sujith817e11d2008-12-07 21:42:44 +0530590 ath_debug_stat_interrupt(sc, status);
591
Sujithff37e332008-11-24 12:07:55 +0530592 if (sched) {
Felix Fietkau4df30712010-11-08 20:54:47 +0100593 /* turn off every interrupt */
594 ath9k_hw_disable_interrupts(ah);
Sujithff37e332008-11-24 12:07:55 +0530595 tasklet_schedule(&sc->intr_tq);
596 }
597
598 return IRQ_HANDLED;
Sujith063d8be2009-03-30 15:28:49 +0530599
600#undef SCHED_INTR
Sujithff37e332008-11-24 12:07:55 +0530601}
602
Sujith Manoharanef6b19e2013-10-24 12:04:39 +0530603int ath_reset(struct ath_softc *sc)
Sujithff37e332008-11-24 12:07:55 +0530604{
Felix Fietkauec303262013-10-05 14:09:30 +0200605 int r;
Sujithff37e332008-11-24 12:07:55 +0530606
Felix Fietkau783cd012011-01-21 18:52:38 +0100607 ath9k_ps_wakeup(sc);
Felix Fietkau13815592013-01-20 18:51:53 +0100608 r = ath_reset_internal(sc, NULL);
Felix Fietkau783cd012011-01-21 18:52:38 +0100609 ath9k_ps_restore(sc);
Sujith2ab81d42009-12-14 16:34:56 +0530610
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800611 return r;
Sujithff37e332008-11-24 12:07:55 +0530612}
613
Rajkumar Manoharan124b9792012-07-17 17:16:42 +0530614void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type)
615{
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100616 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Rajkumar Manoharan124b9792012-07-17 17:16:42 +0530617#ifdef CONFIG_ATH9K_DEBUGFS
618 RESET_STAT_INC(sc, type);
619#endif
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100620 set_bit(ATH_OP_HW_RESET, &common->op_flags);
Rajkumar Manoharan124b9792012-07-17 17:16:42 +0530621 ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
622}
623
Felix Fietkau236de512011-09-03 01:40:25 +0200624void ath_reset_work(struct work_struct *work)
625{
626 struct ath_softc *sc = container_of(work, struct ath_softc, hw_reset_work);
627
Felix Fietkau13815592013-01-20 18:51:53 +0100628 ath_reset(sc);
Felix Fietkau236de512011-09-03 01:40:25 +0200629}
630
Sujithff37e332008-11-24 12:07:55 +0530631/**********************/
632/* mac80211 callbacks */
633/**********************/
634
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700635static int ath9k_start(struct ieee80211_hw *hw)
636{
Felix Fietkau9ac586152011-01-24 19:23:18 +0100637 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700638 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700639 struct ath_common *common = ath9k_hw_common(ah);
Felix Fietkau39305632014-06-11 16:17:57 +0530640 struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
Felix Fietkaufbbcd142014-06-11 16:17:49 +0530641 struct ath_chanctx *ctx = sc->cur_chan;
Sujithff37e332008-11-24 12:07:55 +0530642 struct ath9k_channel *init_channel;
Vasanthakumar Thiagarajan82880a72009-06-13 14:50:24 +0530643 int r;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700644
Joe Perchesd2182b62011-12-15 14:55:53 -0800645 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800646 "Starting driver with initial channel: %d MHz\n",
647 curchan->center_freq);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700648
Felix Fietkauf62d8162011-03-25 17:43:41 +0100649 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +0530650 mutex_lock(&sc->mutex);
651
Felix Fietkaufbbcd142014-06-11 16:17:49 +0530652 init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef);
Felix Fietkaubff11762014-06-11 16:17:52 +0530653 sc->cur_chandef = hw->conf.chandef;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700654
Sujithff37e332008-11-24 12:07:55 +0530655 /* Reset SERDES registers */
Stanislaw Gruszka84c87dc2011-08-05 13:10:32 +0200656 ath9k_hw_configpcipowersave(ah, false);
Sujithff37e332008-11-24 12:07:55 +0530657
658 /*
659 * The basic interface to setting the hardware in a good
660 * state is ``reset''. On return the hardware is known to
661 * be powered up and with interrupts disabled. This must
662 * be followed by initialization of the appropriate bits
663 * and then setup of the interrupt mask.
664 */
Luis R. Rodriguez4bdd1e92010-10-26 15:27:24 -0700665 spin_lock_bh(&sc->sc_pcu_lock);
Felix Fietkauc0c11742011-11-16 13:08:41 +0100666
667 atomic_set(&ah->intr_ref_cnt, -1);
668
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200669 r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Luis R. Rodriguezae8d2852008-12-23 15:58:40 -0800670 if (r) {
Joe Perches38002762010-12-02 19:12:36 -0800671 ath_err(common,
672 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
673 r, curchan->center_freq);
Felix Fietkauceb26a62012-10-03 21:07:51 +0200674 ah->reset_power_on = false;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700675 }
Sujithff37e332008-11-24 12:07:55 +0530676
Sujithff37e332008-11-24 12:07:55 +0530677 /* Setup our intr mask. */
Felix Fietkaub5c804752010-04-15 17:38:48 -0400678 ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
679 ATH9K_INT_RXORN | ATH9K_INT_FATAL |
680 ATH9K_INT_GLOBAL;
681
682 if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
Luis R. Rodriguez08578b82010-05-13 13:33:44 -0400683 ah->imask |= ATH9K_INT_RXHP |
Sujith Manoharana6bb8602013-12-24 10:44:22 +0530684 ATH9K_INT_RXLP;
Felix Fietkaub5c804752010-04-15 17:38:48 -0400685 else
686 ah->imask |= ATH9K_INT_RX;
Sujithff37e332008-11-24 12:07:55 +0530687
Sujith Manoharana6bb8602013-12-24 10:44:22 +0530688 if (ah->config.hw_hang_checks & HW_BB_WATCHDOG)
689 ah->imask |= ATH9K_INT_BB_WATCHDOG;
690
Sujith Manoharan071aa9a2014-01-13 13:55:11 +0530691 /*
692 * Enable GTT interrupts only for AR9003/AR9004 chips
693 * for now.
694 */
695 if (AR_SREV_9300_20_OR_LATER(ah))
696 ah->imask |= ATH9K_INT_GTT;
Sujithff37e332008-11-24 12:07:55 +0530697
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700698 if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
Pavel Roskin30691682010-03-31 18:05:31 -0400699 ah->imask |= ATH9K_INT_CST;
Sujithff37e332008-11-24 12:07:55 +0530700
Sujith Manoharane270e772012-06-04 16:27:19 +0530701 ath_mci_enable(sc);
Mohammed Shafi Shajakhan40dc5392011-11-30 10:41:18 +0530702
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100703 clear_bit(ATH_OP_INVALID, &common->op_flags);
Rajkumar Manoharan5f841b42010-10-27 18:31:15 +0530704 sc->sc_ah->is_monitoring = false;
Sujithff37e332008-11-24 12:07:55 +0530705
Felix Fietkauceb26a62012-10-03 21:07:51 +0200706 if (!ath_complete_reset(sc, false))
707 ah->reset_power_on = false;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700708
Felix Fietkauc0c11742011-11-16 13:08:41 +0100709 if (ah->led_pin >= 0) {
710 ath9k_hw_cfg_output(ah, ah->led_pin,
711 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
712 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
713 }
714
715 /*
716 * Reset key cache to sane defaults (all entries cleared) instead of
717 * semi-random values after suspend/resume.
718 */
719 ath9k_cmn_init_crypto(sc->sc_ah);
720
Felix Fietkaua35051c2013-12-14 18:03:45 +0100721 ath9k_hw_reset_tsf(ah);
722
Felix Fietkau9adcf442011-09-03 01:40:26 +0200723 spin_unlock_bh(&sc->sc_pcu_lock);
Senthil Balasubramanian164ace32009-07-14 20:17:09 -0400724
Sujith141b38b2009-02-04 08:10:07 +0530725 mutex_unlock(&sc->mutex);
726
Felix Fietkauf62d8162011-03-25 17:43:41 +0100727 ath9k_ps_restore(sc);
728
Felix Fietkauceb26a62012-10-03 21:07:51 +0200729 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700730}
731
Thomas Huehn36323f82012-07-23 21:33:42 +0200732static void ath9k_tx(struct ieee80211_hw *hw,
733 struct ieee80211_tx_control *control,
734 struct sk_buff *skb)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700735{
Felix Fietkau9ac586152011-01-24 19:23:18 +0100736 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700737 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Sujith528f0c62008-10-29 10:14:26 +0530738 struct ath_tx_control txctl;
Benoit Papillault1bc14882009-11-24 15:49:18 +0100739 struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530740 unsigned long flags;
Sujith528f0c62008-10-29 10:14:26 +0530741
Gabor Juhos96148322009-07-24 17:27:21 +0200742 if (sc->ps_enabled) {
Jouni Malinendc8c4582009-05-19 17:01:42 +0300743 /*
744 * mac80211 does not set PM field for normal data frames, so we
745 * need to update that based on the current PS mode.
746 */
747 if (ieee80211_is_data(hdr->frame_control) &&
748 !ieee80211_is_nullfunc(hdr->frame_control) &&
749 !ieee80211_has_pm(hdr->frame_control)) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800750 ath_dbg(common, PS,
Joe Perches226afe62010-12-02 19:12:37 -0800751 "Add PM=1 for a TX frame while in PS mode\n");
Jouni Malinendc8c4582009-05-19 17:01:42 +0300752 hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
753 }
754 }
755
Sujith Manoharanad128862012-04-24 10:23:20 +0530756 if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP)) {
Jouni Malinen9a23f9c2009-05-19 17:01:38 +0300757 /*
758 * We are using PS-Poll and mac80211 can request TX while in
759 * power save mode. Need to wake up hardware for the TX to be
760 * completed and if needed, also for RX of buffered frames.
761 */
Jouni Malinen9a23f9c2009-05-19 17:01:38 +0300762 ath9k_ps_wakeup(sc);
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530763 spin_lock_irqsave(&sc->sc_pm_lock, flags);
Vasanthakumar Thiagarajanfdf76622010-05-17 18:57:55 -0700764 if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
765 ath9k_hw_setrxabort(sc->sc_ah, 0);
Jouni Malinen9a23f9c2009-05-19 17:01:38 +0300766 if (ieee80211_is_pspoll(hdr->frame_control)) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800767 ath_dbg(common, PS,
Joe Perches226afe62010-12-02 19:12:37 -0800768 "Sending PS-Poll to pick a buffered frame\n");
Sujith1b04b932010-01-08 10:36:05 +0530769 sc->ps_flags |= PS_WAIT_FOR_PSPOLL_DATA;
Jouni Malinen9a23f9c2009-05-19 17:01:38 +0300770 } else {
Joe Perchesd2182b62011-12-15 14:55:53 -0800771 ath_dbg(common, PS, "Wake up to complete TX\n");
Sujith1b04b932010-01-08 10:36:05 +0530772 sc->ps_flags |= PS_WAIT_FOR_TX_ACK;
Jouni Malinen9a23f9c2009-05-19 17:01:38 +0300773 }
774 /*
775 * The actual restore operation will happen only after
Sujith Manoharanad128862012-04-24 10:23:20 +0530776 * the ps_flags bit is cleared. We are just dropping
Jouni Malinen9a23f9c2009-05-19 17:01:38 +0300777 * the ps_usecount here.
778 */
Sujith Manoharan07c15a32012-06-04 20:24:07 +0530779 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Jouni Malinen9a23f9c2009-05-19 17:01:38 +0300780 ath9k_ps_restore(sc);
781 }
782
Sujith Manoharanad128862012-04-24 10:23:20 +0530783 /*
784 * Cannot tx while the hardware is in full sleep, it first needs a full
785 * chip reset to recover from that
786 */
787 if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP)) {
788 ath_err(common, "TX while HW is in FULL_SLEEP mode\n");
789 goto exit;
790 }
791
Sujith528f0c62008-10-29 10:14:26 +0530792 memset(&txctl, 0, sizeof(struct ath_tx_control));
Felix Fietkau066dae92010-11-07 14:59:39 +0100793 txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)];
Thomas Huehn36323f82012-07-23 21:33:42 +0200794 txctl.sta = control->sta;
Sujith528f0c62008-10-29 10:14:26 +0530795
Joe Perchesd2182b62011-12-15 14:55:53 -0800796 ath_dbg(common, XMIT, "transmitting packet, skb: %p\n", skb);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700797
Jouni Malinenc52f33d2009-03-03 19:23:29 +0200798 if (ath_tx_start(hw, skb, &txctl) != 0) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800799 ath_dbg(common, XMIT, "TX failed\n");
Ben Greeara5a0bca2012-04-03 09:16:55 -0700800 TX_STAT_INC(txctl.txq->axq_qnum, txfailed);
Sujith528f0c62008-10-29 10:14:26 +0530801 goto exit;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700802 }
803
Johannes Berg7bb45682011-02-24 14:42:06 +0100804 return;
Sujith528f0c62008-10-29 10:14:26 +0530805exit:
Felix Fietkau249ee722012-10-03 21:07:52 +0200806 ieee80211_free_txskb(hw, skb);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700807}
808
809static void ath9k_stop(struct ieee80211_hw *hw)
810{
Felix Fietkau9ac586152011-01-24 19:23:18 +0100811 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezaf03abe2009-09-09 02:33:11 -0700812 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -0700813 struct ath_common *common = ath9k_hw_common(ah);
Felix Fietkauc0c11742011-11-16 13:08:41 +0100814 bool prev_idle;
Sujith9c84b792008-10-29 10:17:13 +0530815
Sujith Manoharanea22df22014-08-23 13:29:07 +0530816 ath9k_deinit_channel_context(sc);
817
Sujith4c483812009-08-18 10:51:52 +0530818 mutex_lock(&sc->mutex);
819
Felix Fietkau9adcf442011-09-03 01:40:26 +0200820 ath_cancel_work(sc);
Luis R. Rodriguezc94dbff2009-07-27 11:53:04 -0700821
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100822 if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800823 ath_dbg(common, ANY, "Device not present\n");
Sujith4c483812009-08-18 10:51:52 +0530824 mutex_unlock(&sc->mutex);
Sujith9c84b792008-10-29 10:17:13 +0530825 return;
826 }
827
Sujith3867cf62009-12-23 20:03:27 -0500828 /* Ensure HW is awake when we try to shut it down. */
829 ath9k_ps_wakeup(sc);
830
Luis R. Rodriguez6a6733f2010-10-26 15:27:25 -0700831 spin_lock_bh(&sc->sc_pcu_lock);
832
Stanislaw Gruszka203043f2011-01-25 14:08:40 +0100833 /* prevent tasklets to enable interrupts once we disable them */
834 ah->imask &= ~ATH9K_INT_GLOBAL;
835
Sujithff37e332008-11-24 12:07:55 +0530836 /* make sure h/w will not generate any interrupt
837 * before setting the invalid flag. */
Felix Fietkau4df30712010-11-08 20:54:47 +0100838 ath9k_hw_disable_interrupts(ah);
Sujithff37e332008-11-24 12:07:55 +0530839
Luis R. Rodriguez6a6733f2010-10-26 15:27:25 -0700840 spin_unlock_bh(&sc->sc_pcu_lock);
841
Stanislaw Gruszka203043f2011-01-25 14:08:40 +0100842 /* we can now sync irq and kill any running tasklets, since we already
843 * disabled interrupts and not holding a spin lock */
844 synchronize_irq(sc->irq);
845 tasklet_kill(&sc->intr_tq);
846 tasklet_kill(&sc->bcon_tasklet);
847
Felix Fietkauc0c11742011-11-16 13:08:41 +0100848 prev_idle = sc->ps_idle;
849 sc->ps_idle = true;
850
851 spin_lock_bh(&sc->sc_pcu_lock);
852
853 if (ah->led_pin >= 0) {
854 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
855 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
856 }
857
John W. Linville9ebea382013-01-28 13:54:03 -0500858 ath_prepare_reset(sc);
Felix Fietkauc0c11742011-11-16 13:08:41 +0100859
860 if (sc->rx.frag) {
861 dev_kfree_skb_any(sc->rx.frag);
862 sc->rx.frag = NULL;
863 }
864
865 if (!ah->curchan)
Felix Fietkaufbbcd142014-06-11 16:17:49 +0530866 ah->curchan = ath9k_cmn_get_channel(hw, ah,
867 &sc->cur_chan->chandef);
Felix Fietkauc0c11742011-11-16 13:08:41 +0100868
869 ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
870 ath9k_hw_phy_disable(ah);
871
872 ath9k_hw_configpcipowersave(ah, true);
873
874 spin_unlock_bh(&sc->sc_pcu_lock);
875
Sujith3867cf62009-12-23 20:03:27 -0500876 ath9k_ps_restore(sc);
877
Oleksij Rempeleefa01d2014-02-27 11:40:46 +0100878 set_bit(ATH_OP_INVALID, &common->op_flags);
Felix Fietkauc0c11742011-11-16 13:08:41 +0100879 sc->ps_idle = prev_idle;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700880
Sujith141b38b2009-02-04 08:10:07 +0530881 mutex_unlock(&sc->mutex);
882
Joe Perchesd2182b62011-12-15 14:55:53 -0800883 ath_dbg(common, CONFIG, "Driver halt\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700884}
885
Felix Fietkauc648ecb2013-10-11 23:31:00 +0200886static bool ath9k_uses_beacons(int type)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700887{
Ben Greear48014162011-01-15 19:13:48 +0000888 switch (type) {
Johannes Berg05c914f2008-09-11 00:01:58 +0200889 case NL80211_IFTYPE_AP:
Ben Greear48014162011-01-15 19:13:48 +0000890 case NL80211_IFTYPE_ADHOC:
Pat Erley9cb54122009-03-20 22:59:59 -0400891 case NL80211_IFTYPE_MESH_POINT:
Ben Greear48014162011-01-15 19:13:48 +0000892 return true;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700893 default:
Ben Greear48014162011-01-15 19:13:48 +0000894 return false;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700895 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -0700896}
897
Sujith Manoharan4b93fd22014-08-23 13:29:22 +0530898static void ath9k_vif_iter(struct ath9k_vif_iter_data *iter_data,
899 u8 *mac, struct ieee80211_vif *vif)
Ben Greear48014162011-01-15 19:13:48 +0000900{
Ben Greear48014162011-01-15 19:13:48 +0000901 int i;
902
Felix Fietkauab11bb22013-04-16 12:51:57 +0200903 if (iter_data->has_hw_macaddr) {
Ben Greear48014162011-01-15 19:13:48 +0000904 for (i = 0; i < ETH_ALEN; i++)
905 iter_data->mask[i] &=
906 ~(iter_data->hw_macaddr[i] ^ mac[i]);
Felix Fietkauab11bb22013-04-16 12:51:57 +0200907 } else {
908 memcpy(iter_data->hw_macaddr, mac, ETH_ALEN);
909 iter_data->has_hw_macaddr = true;
910 }
Ben Greear48014162011-01-15 19:13:48 +0000911
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530912 if (!vif->bss_conf.use_short_slot)
913 iter_data->slottime = ATH9K_SLOT_TIME_20;
914
Ben Greear48014162011-01-15 19:13:48 +0000915 switch (vif->type) {
916 case NL80211_IFTYPE_AP:
917 iter_data->naps++;
918 break;
919 case NL80211_IFTYPE_STATION:
920 iter_data->nstations++;
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530921 if (vif->bss_conf.assoc && !iter_data->primary_sta)
922 iter_data->primary_sta = vif;
Ben Greear48014162011-01-15 19:13:48 +0000923 break;
924 case NL80211_IFTYPE_ADHOC:
925 iter_data->nadhocs++;
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530926 if (vif->bss_conf.enable_beacon)
927 iter_data->beacons = true;
Ben Greear48014162011-01-15 19:13:48 +0000928 break;
929 case NL80211_IFTYPE_MESH_POINT:
930 iter_data->nmeshes++;
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530931 if (vif->bss_conf.enable_beacon)
932 iter_data->beacons = true;
Ben Greear48014162011-01-15 19:13:48 +0000933 break;
934 case NL80211_IFTYPE_WDS:
935 iter_data->nwds++;
936 break;
937 default:
Ben Greear48014162011-01-15 19:13:48 +0000938 break;
939 }
940}
941
942/* Called with sc->mutex held. */
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530943void ath9k_calculate_iter_data(struct ath_softc *sc,
944 struct ath_chanctx *ctx,
Ben Greear48014162011-01-15 19:13:48 +0000945 struct ath9k_vif_iter_data *iter_data)
946{
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530947 struct ath_vif *avp;
Ben Greear48014162011-01-15 19:13:48 +0000948
949 /*
Mathy Vanhoef657eb172013-11-28 12:21:45 +0100950 * Pick the MAC address of the first interface as the new hardware
951 * MAC address. The hardware will use it together with the BSSID mask
952 * when matching addresses.
Ben Greear48014162011-01-15 19:13:48 +0000953 */
954 memset(iter_data, 0, sizeof(*iter_data));
Ben Greear48014162011-01-15 19:13:48 +0000955 memset(&iter_data->mask, 0xff, ETH_ALEN);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530956 iter_data->slottime = ATH9K_SLOT_TIME_9;
Ben Greear48014162011-01-15 19:13:48 +0000957
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530958 list_for_each_entry(avp, &ctx->vifs, list)
959 ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530960}
961
962static void ath9k_set_assoc_state(struct ath_softc *sc,
963 struct ieee80211_vif *vif, bool changed)
964{
965 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
966 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
967 unsigned long flags;
968
969 set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +0530970
971 ether_addr_copy(common->curbssid, bss_conf->bssid);
972 common->curaid = bss_conf->aid;
973 ath9k_hw_write_associd(sc->sc_ah);
974
975 if (changed) {
976 common->last_rssi = ATH_RSSI_DUMMY_MARKER;
977 sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
978
979 spin_lock_irqsave(&sc->sc_pm_lock, flags);
980 sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
981 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
982 }
983
984 if (ath9k_hw_mci_is_enabled(sc->sc_ah))
985 ath9k_mci_update_wlan_channels(sc, false);
986
987 ath_dbg(common, CONFIG,
988 "Primary Station interface: %pM, BSSID: %pM\n",
989 vif->addr, common->curbssid);
Ben Greear48014162011-01-15 19:13:48 +0000990}
991
Sujith Manoharan4ee26de2014-09-15 11:25:52 +0530992#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
993static void ath9k_set_offchannel_state(struct ath_softc *sc)
994{
995 struct ath_hw *ah = sc->sc_ah;
996 struct ath_common *common = ath9k_hw_common(ah);
997 struct ieee80211_vif *vif = NULL;
998
999 ath9k_ps_wakeup(sc);
1000
1001 if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START)
1002 vif = sc->offchannel.scan_vif;
1003 else
1004 vif = sc->offchannel.roc_vif;
1005
1006 if (WARN_ON(!vif))
1007 goto exit;
1008
1009 eth_zero_addr(common->curbssid);
1010 eth_broadcast_addr(common->bssidmask);
1011 ether_addr_copy(common->macaddr, vif->addr);
1012 common->curaid = 0;
1013 ah->opmode = vif->type;
1014 ah->imask &= ~ATH9K_INT_SWBA;
1015 ah->imask &= ~ATH9K_INT_TSFOOR;
1016 ah->slottime = ATH9K_SLOT_TIME_9;
1017
1018 ath_hw_setbssidmask(common);
1019 ath9k_hw_setopmode(ah);
1020 ath9k_hw_write_associd(sc->sc_ah);
1021 ath9k_hw_set_interrupts(ah);
1022 ath9k_hw_init_global_settings(ah);
1023
1024exit:
1025 ath9k_ps_restore(sc);
1026}
1027#endif
1028
Ben Greear48014162011-01-15 19:13:48 +00001029/* Called with sc->mutex held. */
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301030void ath9k_calculate_summary_state(struct ath_softc *sc,
1031 struct ath_chanctx *ctx)
Ben Greear48014162011-01-15 19:13:48 +00001032{
Ben Greear48014162011-01-15 19:13:48 +00001033 struct ath_hw *ah = sc->sc_ah;
1034 struct ath_common *common = ath9k_hw_common(ah);
1035 struct ath9k_vif_iter_data iter_data;
Sujith Manoharan9bf30ff92014-09-05 08:03:11 +05301036 struct ath_beacon_config *cur_conf;
Ben Greear48014162011-01-15 19:13:48 +00001037
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301038 ath_chanctx_check_active(sc, ctx);
1039
1040 if (ctx != sc->cur_chan)
1041 return;
1042
Sujith Manoharan4ee26de2014-09-15 11:25:52 +05301043#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
1044 if (ctx == &sc->offchannel.chan)
1045 return ath9k_set_offchannel_state(sc);
1046#endif
1047
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301048 ath9k_ps_wakeup(sc);
1049 ath9k_calculate_iter_data(sc, ctx, &iter_data);
1050
1051 if (iter_data.has_hw_macaddr)
1052 ether_addr_copy(common->macaddr, iter_data.hw_macaddr);
Ben Greear48014162011-01-15 19:13:48 +00001053
Ben Greear48014162011-01-15 19:13:48 +00001054 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
1055 ath_hw_setbssidmask(common);
1056
Ben Greear48014162011-01-15 19:13:48 +00001057 if (iter_data.naps > 0) {
Sujith Manoharan9bf30ff92014-09-05 08:03:11 +05301058 cur_conf = &ctx->beacon;
Sujith Manoharan60ca9f82012-07-17 17:15:37 +05301059 ath9k_hw_set_tsfadjust(ah, true);
Ben Greear48014162011-01-15 19:13:48 +00001060 ah->opmode = NL80211_IFTYPE_AP;
Sujith Manoharan9bf30ff92014-09-05 08:03:11 +05301061 if (cur_conf->enable_beacon)
1062 iter_data.beacons = true;
Ben Greear48014162011-01-15 19:13:48 +00001063 } else {
Sujith Manoharan60ca9f82012-07-17 17:15:37 +05301064 ath9k_hw_set_tsfadjust(ah, false);
Ben Greear48014162011-01-15 19:13:48 +00001065
Javier Cardonafd5999c2011-05-03 16:57:19 -07001066 if (iter_data.nmeshes)
1067 ah->opmode = NL80211_IFTYPE_MESH_POINT;
1068 else if (iter_data.nwds)
Ben Greear48014162011-01-15 19:13:48 +00001069 ah->opmode = NL80211_IFTYPE_AP;
1070 else if (iter_data.nadhocs)
1071 ah->opmode = NL80211_IFTYPE_ADHOC;
1072 else
1073 ah->opmode = NL80211_IFTYPE_STATION;
1074 }
1075
Sujith Manoharandf35d292012-07-17 17:15:43 +05301076 ath9k_hw_setopmode(ah);
1077
Felix Fietkau748299f2014-06-11 16:18:04 +05301078 ctx->switch_after_beacon = false;
Felix Fietkau198823f2012-06-15 15:25:25 +02001079 if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0)
Ben Greear48014162011-01-15 19:13:48 +00001080 ah->imask |= ATH9K_INT_TSFOOR;
Felix Fietkau748299f2014-06-11 16:18:04 +05301081 else {
Ben Greear48014162011-01-15 19:13:48 +00001082 ah->imask &= ~ATH9K_INT_TSFOOR;
Felix Fietkau748299f2014-06-11 16:18:04 +05301083 if (iter_data.naps == 1 && iter_data.beacons)
1084 ctx->switch_after_beacon = true;
1085 }
Ben Greear48014162011-01-15 19:13:48 +00001086
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301087 ah->imask &= ~ATH9K_INT_SWBA;
1088 if (ah->opmode == NL80211_IFTYPE_STATION) {
1089 bool changed = (iter_data.primary_sta != ctx->primary_sta);
1090
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301091 if (iter_data.primary_sta) {
Sujith Manoharan602607b2014-09-05 08:03:10 +05301092 iter_data.beacons = true;
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301093 ath9k_set_assoc_state(sc, iter_data.primary_sta,
1094 changed);
Sujith Manoharan1030f9f2014-09-15 11:25:54 +05301095 ctx->primary_sta = iter_data.primary_sta;
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301096 } else {
1097 ctx->primary_sta = NULL;
1098 memset(common->curbssid, 0, ETH_ALEN);
1099 common->curaid = 0;
1100 ath9k_hw_write_associd(sc->sc_ah);
1101 if (ath9k_hw_mci_is_enabled(sc->sc_ah))
1102 ath9k_mci_update_wlan_channels(sc, true);
1103 }
1104 } else if (iter_data.beacons) {
1105 ah->imask |= ATH9K_INT_SWBA;
1106 }
Felix Fietkau72d874c2011-10-08 20:06:19 +02001107 ath9k_hw_set_interrupts(ah);
Sujith Manoharan6dcc3442012-07-17 17:16:36 +05301108
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301109 if (iter_data.beacons)
1110 set_bit(ATH_OP_BEACONS, &common->op_flags);
1111 else
1112 clear_bit(ATH_OP_BEACONS, &common->op_flags);
1113
1114 if (ah->slottime != iter_data.slottime) {
1115 ah->slottime = iter_data.slottime;
1116 ath9k_hw_init_global_settings(ah);
Sujith Manoharan6dcc3442012-07-17 17:16:36 +05301117 }
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301118
1119 if (iter_data.primary_sta)
1120 set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
1121 else
1122 clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
1123
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301124 ath9k_ps_restore(sc);
Ben Greear48014162011-01-15 19:13:48 +00001125}
1126
Sujith Manoharana4027642014-09-05 09:50:55 +05301127static void ath9k_assign_hw_queues(struct ieee80211_hw *hw,
1128 struct ieee80211_vif *vif)
1129{
1130 int i;
1131
1132 for (i = 0; i < IEEE80211_NUM_ACS; i++)
1133 vif->hw_queue[i] = i;
1134
1135 if (vif->type == NL80211_IFTYPE_AP)
1136 vif->cab_queue = hw->queues - 2;
1137 else
1138 vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
1139}
1140
Ben Greear48014162011-01-15 19:13:48 +00001141static int ath9k_add_interface(struct ieee80211_hw *hw,
1142 struct ieee80211_vif *vif)
1143{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001144 struct ath_softc *sc = hw->priv;
Ben Greear48014162011-01-15 19:13:48 +00001145 struct ath_hw *ah = sc->sc_ah;
1146 struct ath_common *common = ath9k_hw_common(ah);
Felix Fietkauf89d1bc2013-08-06 14:18:13 +02001147 struct ath_vif *avp = (void *)vif->drv_priv;
1148 struct ath_node *an = &avp->mcast_node;
Ben Greear48014162011-01-15 19:13:48 +00001149
1150 mutex_lock(&sc->mutex);
1151
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001152 if (config_enabled(CONFIG_ATH9K_TX99)) {
Sujith Manoharanca529c92014-09-05 08:03:19 +05301153 if (sc->cur_chan->nvifs >= 1) {
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001154 mutex_unlock(&sc->mutex);
1155 return -EOPNOTSUPP;
1156 }
1157 sc->tx99_vif = vif;
1158 }
1159
Joe Perchesd2182b62011-12-15 14:55:53 -08001160 ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
Sujith Manoharanca529c92014-09-05 08:03:19 +05301161 sc->cur_chan->nvifs++;
Ben Greear48014162011-01-15 19:13:48 +00001162
Sujith Manoharan130ef6e2012-07-17 17:15:30 +05301163 if (ath9k_uses_beacons(vif->type))
1164 ath9k_beacon_assign_slot(sc, vif);
1165
Felix Fietkaud463af42014-04-06 00:37:03 +02001166 avp->vif = vif;
Sujith Manoharan499afac2014-08-22 20:39:31 +05301167 if (!ath9k_is_chanctx_enabled()) {
Felix Fietkau39305632014-06-11 16:17:57 +05301168 avp->chanctx = sc->cur_chan;
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301169 list_add_tail(&avp->list, &avp->chanctx->vifs);
1170 }
Sujith Manoharana4027642014-09-05 09:50:55 +05301171
1172 ath9k_assign_hw_queues(hw, vif);
Felix Fietkau04535312014-06-11 16:17:51 +05301173
Felix Fietkauf89d1bc2013-08-06 14:18:13 +02001174 an->sc = sc;
1175 an->sta = NULL;
1176 an->vif = vif;
1177 an->no_ps_filter = true;
1178 ath_tx_node_init(sc, an);
1179
Ben Greear48014162011-01-15 19:13:48 +00001180 mutex_unlock(&sc->mutex);
Mohammed Shafi Shajakhan327967c2012-09-04 19:33:36 +05301181 return 0;
Ben Greear48014162011-01-15 19:13:48 +00001182}
1183
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05301184static int ath9k_change_interface(struct ieee80211_hw *hw,
1185 struct ieee80211_vif *vif,
1186 enum nl80211_iftype new_type,
1187 bool p2p)
1188{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001189 struct ath_softc *sc = hw->priv;
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05301190 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkauc083ce92014-06-11 16:17:54 +05301191 struct ath_vif *avp = (void *)vif->drv_priv;
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05301192
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05301193 mutex_lock(&sc->mutex);
Ben Greear48014162011-01-15 19:13:48 +00001194
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001195 if (config_enabled(CONFIG_ATH9K_TX99)) {
1196 mutex_unlock(&sc->mutex);
1197 return -EOPNOTSUPP;
1198 }
1199
1200 ath_dbg(common, CONFIG, "Change Interface\n");
1201
Ben Greear48014162011-01-15 19:13:48 +00001202 if (ath9k_uses_beacons(vif->type))
Sujith Manoharan130ef6e2012-07-17 17:15:30 +05301203 ath9k_beacon_remove_slot(sc, vif);
Ben Greear48014162011-01-15 19:13:48 +00001204
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05301205 vif->type = new_type;
1206 vif->p2p = p2p;
1207
Sujith Manoharan130ef6e2012-07-17 17:15:30 +05301208 if (ath9k_uses_beacons(vif->type))
1209 ath9k_beacon_assign_slot(sc, vif);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301210
Sujith Manoharana4027642014-09-05 09:50:55 +05301211 ath9k_assign_hw_queues(hw, vif);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301212 ath9k_calculate_summary_state(sc, avp->chanctx);
Sujith Manoharan130ef6e2012-07-17 17:15:30 +05301213
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05301214 mutex_unlock(&sc->mutex);
Mohammed Shafi Shajakhan327967c2012-09-04 19:33:36 +05301215 return 0;
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05301216}
1217
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001218static void ath9k_remove_interface(struct ieee80211_hw *hw,
Johannes Berg1ed32e42009-12-23 13:15:45 +01001219 struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001220{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001221 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001222 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkauf89d1bc2013-08-06 14:18:13 +02001223 struct ath_vif *avp = (void *)vif->drv_priv;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001224
Joe Perchesd2182b62011-12-15 14:55:53 -08001225 ath_dbg(common, CONFIG, "Detach Interface\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001226
Sujith141b38b2009-02-04 08:10:07 +05301227 mutex_lock(&sc->mutex);
1228
Sujith Manoharanc7dd40c2014-08-22 20:39:30 +05301229 ath9k_p2p_remove_vif(sc, vif);
Felix Fietkaud463af42014-04-06 00:37:03 +02001230
Sujith Manoharanca529c92014-09-05 08:03:19 +05301231 sc->cur_chan->nvifs--;
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001232 sc->tx99_vif = NULL;
Sujith Manoharan499afac2014-08-22 20:39:31 +05301233 if (!ath9k_is_chanctx_enabled())
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301234 list_del(&avp->list);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001235
Ben Greear48014162011-01-15 19:13:48 +00001236 if (ath9k_uses_beacons(vif->type))
Sujith Manoharan130ef6e2012-07-17 17:15:30 +05301237 ath9k_beacon_remove_slot(sc, vif);
Jouni Malinen2c3db3d2009-03-03 19:23:26 +02001238
Felix Fietkauf89d1bc2013-08-06 14:18:13 +02001239 ath_tx_node_cleanup(sc, &avp->mcast_node);
1240
Sujith141b38b2009-02-04 08:10:07 +05301241 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001242}
1243
Senthil Balasubramanianfbab7392010-10-05 20:36:40 +05301244static void ath9k_enable_ps(struct ath_softc *sc)
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301245{
Pavel Roskin30691682010-03-31 18:05:31 -04001246 struct ath_hw *ah = sc->sc_ah;
Sujith Manoharanad128862012-04-24 10:23:20 +05301247 struct ath_common *common = ath9k_hw_common(ah);
Pavel Roskin30691682010-03-31 18:05:31 -04001248
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001249 if (config_enabled(CONFIG_ATH9K_TX99))
1250 return;
1251
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301252 sc->ps_enabled = true;
Pavel Roskin30691682010-03-31 18:05:31 -04001253 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
1254 if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) {
1255 ah->imask |= ATH9K_INT_TIM_TIMER;
Felix Fietkau72d874c2011-10-08 20:06:19 +02001256 ath9k_hw_set_interrupts(ah);
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301257 }
Vasanthakumar Thiagarajanfdf76622010-05-17 18:57:55 -07001258 ath9k_hw_setrxabort(ah, 1);
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301259 }
Sujith Manoharanad128862012-04-24 10:23:20 +05301260 ath_dbg(common, PS, "PowerSave enabled\n");
Senthil Balasubramanian3f7c5c12010-02-03 22:51:13 +05301261}
1262
Senthil Balasubramanian845d7082010-10-05 20:36:41 +05301263static void ath9k_disable_ps(struct ath_softc *sc)
1264{
1265 struct ath_hw *ah = sc->sc_ah;
Sujith Manoharanad128862012-04-24 10:23:20 +05301266 struct ath_common *common = ath9k_hw_common(ah);
Senthil Balasubramanian845d7082010-10-05 20:36:41 +05301267
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001268 if (config_enabled(CONFIG_ATH9K_TX99))
1269 return;
1270
Senthil Balasubramanian845d7082010-10-05 20:36:41 +05301271 sc->ps_enabled = false;
1272 ath9k_hw_setpower(ah, ATH9K_PM_AWAKE);
1273 if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) {
1274 ath9k_hw_setrxabort(ah, 0);
1275 sc->ps_flags &= ~(PS_WAIT_FOR_BEACON |
1276 PS_WAIT_FOR_CAB |
1277 PS_WAIT_FOR_PSPOLL_DATA |
1278 PS_WAIT_FOR_TX_ACK);
1279 if (ah->imask & ATH9K_INT_TIM_TIMER) {
1280 ah->imask &= ~ATH9K_INT_TIM_TIMER;
Felix Fietkau72d874c2011-10-08 20:06:19 +02001281 ath9k_hw_set_interrupts(ah);
Senthil Balasubramanian845d7082010-10-05 20:36:41 +05301282 }
1283 }
Sujith Manoharanad128862012-04-24 10:23:20 +05301284 ath_dbg(common, PS, "PowerSave disabled\n");
Senthil Balasubramanian845d7082010-10-05 20:36:41 +05301285}
1286
Simon Wunderliche93d0832013-01-08 14:48:58 +01001287void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw)
1288{
1289 struct ath_softc *sc = hw->priv;
1290 struct ath_hw *ah = sc->sc_ah;
1291 struct ath_common *common = ath9k_hw_common(ah);
1292 u32 rxfilter;
1293
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001294 if (config_enabled(CONFIG_ATH9K_TX99))
1295 return;
1296
Simon Wunderliche93d0832013-01-08 14:48:58 +01001297 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
1298 ath_err(common, "spectrum analyzer not implemented on this hardware\n");
1299 return;
1300 }
1301
1302 ath9k_ps_wakeup(sc);
1303 rxfilter = ath9k_hw_getrxfilter(ah);
1304 ath9k_hw_setrxfilter(ah, rxfilter |
1305 ATH9K_RX_FILTER_PHYRADAR |
1306 ATH9K_RX_FILTER_PHYERR);
1307
1308 /* TODO: usually this should not be neccesary, but for some reason
1309 * (or in some mode?) the trigger must be called after the
1310 * configuration, otherwise the register will have its values reset
1311 * (on my ar9220 to value 0x01002310)
1312 */
1313 ath9k_spectral_scan_config(hw, sc->spectral_mode);
1314 ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
1315 ath9k_ps_restore(sc);
1316}
1317
1318int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
1319 enum spectral_mode spectral_mode)
1320{
1321 struct ath_softc *sc = hw->priv;
1322 struct ath_hw *ah = sc->sc_ah;
1323 struct ath_common *common = ath9k_hw_common(ah);
Simon Wunderliche93d0832013-01-08 14:48:58 +01001324
1325 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
1326 ath_err(common, "spectrum analyzer not implemented on this hardware\n");
1327 return -1;
1328 }
1329
Simon Wunderliche93d0832013-01-08 14:48:58 +01001330 switch (spectral_mode) {
1331 case SPECTRAL_DISABLED:
Simon Wunderlich04ccd4a2013-01-23 17:38:04 +01001332 sc->spec_config.enabled = 0;
Simon Wunderliche93d0832013-01-08 14:48:58 +01001333 break;
1334 case SPECTRAL_BACKGROUND:
1335 /* send endless samples.
1336 * TODO: is this really useful for "background"?
1337 */
Simon Wunderlich04ccd4a2013-01-23 17:38:04 +01001338 sc->spec_config.endless = 1;
1339 sc->spec_config.enabled = 1;
Simon Wunderliche93d0832013-01-08 14:48:58 +01001340 break;
1341 case SPECTRAL_CHANSCAN:
Simon Wunderliche93d0832013-01-08 14:48:58 +01001342 case SPECTRAL_MANUAL:
Simon Wunderlich04ccd4a2013-01-23 17:38:04 +01001343 sc->spec_config.endless = 0;
1344 sc->spec_config.enabled = 1;
Simon Wunderliche93d0832013-01-08 14:48:58 +01001345 break;
1346 default:
1347 return -1;
1348 }
1349
1350 ath9k_ps_wakeup(sc);
Simon Wunderlich04ccd4a2013-01-23 17:38:04 +01001351 ath9k_hw_ops(ah)->spectral_scan_config(ah, &sc->spec_config);
Simon Wunderliche93d0832013-01-08 14:48:58 +01001352 ath9k_ps_restore(sc);
1353
1354 sc->spectral_mode = spectral_mode;
1355
1356 return 0;
1357}
1358
Johannes Berge8975582008-10-09 12:18:51 +02001359static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001360{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001361 struct ath_softc *sc = hw->priv;
Felix Fietkau34300982010-10-10 18:21:52 +02001362 struct ath_hw *ah = sc->sc_ah;
1363 struct ath_common *common = ath9k_hw_common(ah);
Johannes Berge8975582008-10-09 12:18:51 +02001364 struct ieee80211_conf *conf = &hw->conf;
Felix Fietkaufbbcd142014-06-11 16:17:49 +05301365 struct ath_chanctx *ctx = sc->cur_chan;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001366
Felix Fietkauc0c11742011-11-16 13:08:41 +01001367 ath9k_ps_wakeup(sc);
Sujithaa33de02008-12-18 11:40:16 +05301368 mutex_lock(&sc->mutex);
Sujith141b38b2009-02-04 08:10:07 +05301369
Felix Fietkaudaa1b6e2011-11-16 13:08:43 +01001370 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
Felix Fietkau7545daf2011-01-24 19:23:16 +01001371 sc->ps_idle = !!(conf->flags & IEEE80211_CONF_IDLE);
Rajkumar Manoharanb73f3e72012-07-01 19:53:53 +05301372 if (sc->ps_idle) {
Felix Fietkaudaa1b6e2011-11-16 13:08:43 +01001373 ath_cancel_work(sc);
Rajkumar Manoharanb73f3e72012-07-01 19:53:53 +05301374 ath9k_stop_btcoex(sc);
1375 } else {
1376 ath9k_start_btcoex(sc);
Felix Fietkau75600ab2012-04-12 20:36:31 +02001377 /*
1378 * The chip needs a reset to properly wake up from
1379 * full sleep
1380 */
Felix Fietkau39305632014-06-11 16:17:57 +05301381 ath_chanctx_set_channel(sc, ctx, &ctx->chandef);
Rajkumar Manoharanb73f3e72012-07-01 19:53:53 +05301382 }
Felix Fietkaudaa1b6e2011-11-16 13:08:43 +01001383 }
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001384
Luis R. Rodrigueze7824a52009-11-24 02:53:25 -05001385 /*
1386 * We just prepare to enable PS. We have to wait until our AP has
1387 * ACK'd our null data frame to disable RX otherwise we'll ignore
1388 * those ACKs and end up retransmitting the same null data frames.
1389 * IEEE80211_CONF_CHANGE_PS is only passed by mac80211 for STA mode.
1390 */
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301391 if (changed & IEEE80211_CONF_CHANGE_PS) {
Luis R. Rodriguez8ab2cd02010-09-16 15:12:26 -04001392 unsigned long flags;
1393 spin_lock_irqsave(&sc->sc_pm_lock, flags);
Senthil Balasubramanianfbab7392010-10-05 20:36:40 +05301394 if (conf->flags & IEEE80211_CONF_PS)
1395 ath9k_enable_ps(sc);
Senthil Balasubramanian845d7082010-10-05 20:36:41 +05301396 else
1397 ath9k_disable_ps(sc);
Luis R. Rodriguez8ab2cd02010-09-16 15:12:26 -04001398 spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301399 }
1400
Sujith199afd92010-01-08 10:36:13 +05301401 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1402 if (conf->flags & IEEE80211_CONF_MONITOR) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001403 ath_dbg(common, CONFIG, "Monitor mode is enabled\n");
Rajkumar Manoharan5f841b42010-10-27 18:31:15 +05301404 sc->sc_ah->is_monitoring = true;
1405 } else {
Joe Perchesd2182b62011-12-15 14:55:53 -08001406 ath_dbg(common, CONFIG, "Monitor mode is disabled\n");
Rajkumar Manoharan5f841b42010-10-27 18:31:15 +05301407 sc->sc_ah->is_monitoring = false;
Sujith199afd92010-01-08 10:36:13 +05301408 }
1409 }
1410
Sujith Manoharan499afac2014-08-22 20:39:31 +05301411 if (!ath9k_is_chanctx_enabled() && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
Felix Fietkaufbbcd142014-06-11 16:17:49 +05301412 ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL);
Felix Fietkaubff11762014-06-11 16:17:52 +05301413 ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef);
Sujith094d05d2008-12-12 11:57:43 +05301414 }
Sujith86b89ee2008-08-07 10:54:57 +05301415
Luis R. Rodriguezc9f6a652010-01-19 14:04:19 -05001416 if (changed & IEEE80211_CONF_CHANGE_POWER) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001417 ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
Felix Fietkaubc7e1be2014-06-11 16:17:50 +05301418 sc->cur_chan->txpower = 2 * conf->power_level;
Rajkumar Manoharan5048e8c2011-01-31 23:47:44 +05301419 ath9k_cmn_update_txpow(ah, sc->curtxpow,
Felix Fietkaubc7e1be2014-06-11 16:17:50 +05301420 sc->cur_chan->txpower, &sc->curtxpow);
Luis R. Rodriguez64839172009-07-14 20:22:53 -04001421 }
1422
Sujithaa33de02008-12-18 11:40:16 +05301423 mutex_unlock(&sc->mutex);
Felix Fietkauc0c11742011-11-16 13:08:41 +01001424 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05301425
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001426 return 0;
1427}
1428
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001429#define SUPPORTED_FILTERS \
1430 (FIF_PROMISC_IN_BSS | \
1431 FIF_ALLMULTI | \
1432 FIF_CONTROL | \
Luis R. Rodriguezaf6a3fc2009-08-08 21:55:16 -04001433 FIF_PSPOLL | \
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001434 FIF_OTHER_BSS | \
1435 FIF_BCN_PRBRESP_PROMISC | \
Jouni Malinen9c1d8e42010-10-13 17:29:31 +03001436 FIF_PROBE_REQ | \
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001437 FIF_FCSFAIL)
1438
Sujith7dcfdcd2008-08-11 14:03:13 +05301439/* FIXME: sc->sc_full_reset ? */
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001440static void ath9k_configure_filter(struct ieee80211_hw *hw,
1441 unsigned int changed_flags,
1442 unsigned int *total_flags,
Johannes Berg3ac64be2009-08-17 16:16:53 +02001443 u64 multicast)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001444{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001445 struct ath_softc *sc = hw->priv;
Sujith7dcfdcd2008-08-11 14:03:13 +05301446 u32 rfilt;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001447
1448 changed_flags &= SUPPORTED_FILTERS;
1449 *total_flags &= SUPPORTED_FILTERS;
1450
Sujith Manoharanfce34432014-09-05 08:03:18 +05301451 spin_lock_bh(&sc->chan_lock);
1452 sc->cur_chan->rxfilter = *total_flags;
1453 spin_unlock_bh(&sc->chan_lock);
1454
Jouni Malinenaa68aea2009-05-19 17:01:41 +03001455 ath9k_ps_wakeup(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05301456 rfilt = ath_calcrxfilter(sc);
1457 ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
Jouni Malinenaa68aea2009-05-19 17:01:41 +03001458 ath9k_ps_restore(sc);
Sujith7dcfdcd2008-08-11 14:03:13 +05301459
Joe Perchesd2182b62011-12-15 14:55:53 -08001460 ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, "Set HW RX filter: 0x%x\n",
1461 rfilt);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001462}
1463
Johannes Berg4ca77862010-02-19 19:06:56 +01001464static int ath9k_sta_add(struct ieee80211_hw *hw,
1465 struct ieee80211_vif *vif,
1466 struct ieee80211_sta *sta)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001467{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001468 struct ath_softc *sc = hw->priv;
Felix Fietkau93ae2dd2011-04-17 23:28:10 +02001469 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
1470 struct ath_node *an = (struct ath_node *) sta->drv_priv;
1471 struct ieee80211_key_conf ps_key = { };
Felix Fietkau4ef69d02013-04-27 11:47:01 +02001472 int key;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001473
Ben Greear7e1e3862011-11-03 11:33:13 -07001474 ath_node_attach(sc, sta, vif);
Felix Fietkauf59a59f2011-05-10 20:52:22 +02001475
1476 if (vif->type != NL80211_IFTYPE_AP &&
1477 vif->type != NL80211_IFTYPE_AP_VLAN)
1478 return 0;
1479
Felix Fietkau4ef69d02013-04-27 11:47:01 +02001480 key = ath_key_config(common, vif, sta, &ps_key);
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301481 if (key > 0) {
Felix Fietkau4ef69d02013-04-27 11:47:01 +02001482 an->ps_key = key;
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301483 an->key_idx[0] = key;
1484 }
Johannes Berg4ca77862010-02-19 19:06:56 +01001485
1486 return 0;
1487}
1488
Felix Fietkau93ae2dd2011-04-17 23:28:10 +02001489static void ath9k_del_ps_key(struct ath_softc *sc,
1490 struct ieee80211_vif *vif,
1491 struct ieee80211_sta *sta)
1492{
1493 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
1494 struct ath_node *an = (struct ath_node *) sta->drv_priv;
1495 struct ieee80211_key_conf ps_key = { .hw_key_idx = an->ps_key };
1496
1497 if (!an->ps_key)
1498 return;
1499
1500 ath_key_delete(common, &ps_key);
Felix Fietkau4ef69d02013-04-27 11:47:01 +02001501 an->ps_key = 0;
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301502 an->key_idx[0] = 0;
Felix Fietkau93ae2dd2011-04-17 23:28:10 +02001503}
1504
Johannes Berg4ca77862010-02-19 19:06:56 +01001505static int ath9k_sta_remove(struct ieee80211_hw *hw,
1506 struct ieee80211_vif *vif,
1507 struct ieee80211_sta *sta)
1508{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001509 struct ath_softc *sc = hw->priv;
Johannes Berg4ca77862010-02-19 19:06:56 +01001510
Felix Fietkau93ae2dd2011-04-17 23:28:10 +02001511 ath9k_del_ps_key(sc, vif, sta);
Johannes Berg4ca77862010-02-19 19:06:56 +01001512 ath_node_detach(sc, sta);
1513
1514 return 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001515}
1516
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301517static void ath9k_sta_set_tx_filter(struct ath_hw *ah,
1518 struct ath_node *an,
1519 bool set)
1520{
1521 int i;
1522
1523 for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
1524 if (!an->key_idx[i])
1525 continue;
1526 ath9k_hw_set_tx_filter(ah, an->key_idx[i], set);
1527 }
1528}
1529
Felix Fietkau55195412011-04-17 23:28:09 +02001530static void ath9k_sta_notify(struct ieee80211_hw *hw,
1531 struct ieee80211_vif *vif,
1532 enum sta_notify_cmd cmd,
1533 struct ieee80211_sta *sta)
1534{
1535 struct ath_softc *sc = hw->priv;
1536 struct ath_node *an = (struct ath_node *) sta->drv_priv;
1537
1538 switch (cmd) {
1539 case STA_NOTIFY_SLEEP:
1540 an->sleeping = true;
Johannes Berg042ec452011-09-29 16:04:26 +02001541 ath_tx_aggr_sleep(sta, sc, an);
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301542 ath9k_sta_set_tx_filter(sc->sc_ah, an, true);
Felix Fietkau55195412011-04-17 23:28:09 +02001543 break;
1544 case STA_NOTIFY_AWAKE:
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301545 ath9k_sta_set_tx_filter(sc->sc_ah, an, false);
Felix Fietkau55195412011-04-17 23:28:09 +02001546 an->sleeping = false;
1547 ath_tx_aggr_wakeup(sc, an);
1548 break;
1549 }
1550}
1551
Eliad Peller8a3a3c82011-10-02 10:15:52 +02001552static int ath9k_conf_tx(struct ieee80211_hw *hw,
1553 struct ieee80211_vif *vif, u16 queue,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001554 const struct ieee80211_tx_queue_params *params)
1555{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001556 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001557 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau066dae92010-11-07 14:59:39 +01001558 struct ath_txq *txq;
Sujithea9880f2008-08-07 10:53:10 +05301559 struct ath9k_tx_queue_info qi;
Felix Fietkau066dae92010-11-07 14:59:39 +01001560 int ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001561
Sujith Manoharanbea843c2012-11-21 18:13:10 +05301562 if (queue >= IEEE80211_NUM_ACS)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001563 return 0;
1564
Felix Fietkau066dae92010-11-07 14:59:39 +01001565 txq = sc->tx.txq_map[queue];
1566
Felix Fietkau96f372c2011-04-07 19:07:17 +02001567 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05301568 mutex_lock(&sc->mutex);
1569
Sujith1ffb0612009-03-30 15:28:46 +05301570 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1571
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001572 qi.tqi_aifs = params->aifs;
1573 qi.tqi_cwmin = params->cw_min;
1574 qi.tqi_cwmax = params->cw_max;
Felix Fietkau531bd072012-07-15 19:53:34 +02001575 qi.tqi_burstTime = params->txop * 32;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001576
Joe Perchesd2182b62011-12-15 14:55:53 -08001577 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -08001578 "Configure tx [queue/halq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1579 queue, txq->axq_qnum, params->aifs, params->cw_min,
1580 params->cw_max, params->txop);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001581
Felix Fietkauaa5955c2012-07-15 19:53:36 +02001582 ath_update_max_aggr_framelen(sc, queue, qi.tqi_burstTime);
Felix Fietkau066dae92010-11-07 14:59:39 +01001583 ret = ath_txq_update(sc, txq->axq_qnum, &qi);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001584 if (ret)
Joe Perches38002762010-12-02 19:12:36 -08001585 ath_err(common, "TXQ Update failed\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001586
Sujith141b38b2009-02-04 08:10:07 +05301587 mutex_unlock(&sc->mutex);
Felix Fietkau96f372c2011-04-07 19:07:17 +02001588 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05301589
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001590 return ret;
1591}
1592
1593static int ath9k_set_key(struct ieee80211_hw *hw,
1594 enum set_key_cmd cmd,
Johannes Bergdc822b52008-12-29 12:55:09 +01001595 struct ieee80211_vif *vif,
1596 struct ieee80211_sta *sta,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001597 struct ieee80211_key_conf *key)
1598{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001599 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezc46917b2009-09-13 02:42:02 -07001600 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301601 struct ath_node *an = NULL;
1602 int ret = 0, i;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001603
John W. Linville3e6109c2011-01-05 09:39:17 -05001604 if (ath9k_modparam_nohwcrypt)
Jouni Malinenb3bd89c2009-02-24 13:42:01 +02001605 return -ENOSPC;
1606
Chun-Yeow Yeoh5bd5e9a2011-12-07 12:45:46 -08001607 if ((vif->type == NL80211_IFTYPE_ADHOC ||
1608 vif->type == NL80211_IFTYPE_MESH_POINT) &&
Jouni Malinencfdc9a82011-03-23 14:52:19 +02001609 (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
1610 key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
1611 !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
1612 /*
1613 * For now, disable hw crypto for the RSN IBSS group keys. This
1614 * could be optimized in the future to use a modified key cache
1615 * design to support per-STA RX GTK, but until that gets
1616 * implemented, use of software crypto for group addressed
1617 * frames is a acceptable to allow RSN IBSS to be used.
1618 */
1619 return -EOPNOTSUPP;
1620 }
1621
Sujith141b38b2009-02-04 08:10:07 +05301622 mutex_lock(&sc->mutex);
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301623 ath9k_ps_wakeup(sc);
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301624 ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd);
1625 if (sta)
1626 an = (struct ath_node *)sta->drv_priv;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001627
1628 switch (cmd) {
1629 case SET_KEY:
Felix Fietkau93ae2dd2011-04-17 23:28:10 +02001630 if (sta)
1631 ath9k_del_ps_key(sc, vif, sta);
1632
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301633 key->hw_key_idx = 0;
Bruno Randolf040e5392010-09-08 16:05:04 +09001634 ret = ath_key_config(common, vif, sta, key);
Jouni Malinen6ace2892008-12-17 13:32:17 +02001635 if (ret >= 0) {
1636 key->hw_key_idx = ret;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001637 /* push IV and Michael MIC generation to stack */
1638 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001639 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Senthil Balasubramanian1b961752008-09-01 19:45:21 +05301640 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001641 if (sc->sc_ah->sw_mgmt_crypto &&
1642 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Johannes Berge548c492012-09-04 17:08:23 +02001643 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
Jouni Malinen6ace2892008-12-17 13:32:17 +02001644 ret = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001645 }
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301646 if (an && key->hw_key_idx) {
1647 for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
1648 if (an->key_idx[i])
1649 continue;
1650 an->key_idx[i] = key->hw_key_idx;
1651 break;
1652 }
1653 WARN_ON(i == ARRAY_SIZE(an->key_idx));
1654 }
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001655 break;
1656 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001657 ath_key_delete(common, key);
Rajkumar Manoharan4bbf4412014-05-22 12:35:49 +05301658 if (an) {
1659 for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) {
1660 if (an->key_idx[i] != key->hw_key_idx)
1661 continue;
1662 an->key_idx[i] = 0;
1663 break;
1664 }
1665 }
1666 key->hw_key_idx = 0;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001667 break;
1668 default:
1669 ret = -EINVAL;
1670 }
1671
Vivek Natarajan3cbb5dd2009-01-20 11:17:08 +05301672 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05301673 mutex_unlock(&sc->mutex);
1674
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001675 return ret;
1676}
Sujith Manoharan6c43c0902012-07-17 17:15:50 +05301677
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001678static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
1679 struct ieee80211_vif *vif,
1680 struct ieee80211_bss_conf *bss_conf,
1681 u32 changed)
1682{
Sujith Manoharanda0d45f2012-07-17 17:16:29 +05301683#define CHECK_ANI \
1684 (BSS_CHANGED_ASSOC | \
1685 BSS_CHANGED_IBSS | \
1686 BSS_CHANGED_BEACON_ENABLED)
1687
Felix Fietkau9ac586152011-01-24 19:23:18 +01001688 struct ath_softc *sc = hw->priv;
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001689 struct ath_hw *ah = sc->sc_ah;
Luis R. Rodriguez15107182009-09-10 09:22:37 -07001690 struct ath_common *common = ath9k_hw_common(ah);
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001691 struct ath_vif *avp = (void *)vif->drv_priv;
Felix Fietkau0005baf2010-01-15 02:33:40 +01001692 int slottime;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001693
Felix Fietkau96f372c2011-04-07 19:07:17 +02001694 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05301695 mutex_lock(&sc->mutex);
1696
Rajkumar Manoharan9f619032012-03-10 05:06:49 +05301697 if (changed & BSS_CHANGED_ASSOC) {
Sujith Manoharan6c43c0902012-07-17 17:15:50 +05301698 ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n",
1699 bss_conf->bssid, bss_conf->assoc);
Sujithc6089cc2009-11-16 11:40:48 +05301700
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301701 ath9k_calculate_summary_state(sc, avp->chanctx);
Sujith Manoharan27babf92014-08-23 13:29:16 +05301702
1703 if (ath9k_is_chanctx_enabled()) {
1704 if (bss_conf->assoc)
1705 ath_chanctx_event(sc, vif,
1706 ATH_CHANCTX_EVENT_ASSOC);
1707 }
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001708 }
1709
Rajkumar Manoharan2e5ef452011-05-20 17:52:12 +05301710 if (changed & BSS_CHANGED_IBSS) {
Rajkumar Manoharan2e5ef452011-05-20 17:52:12 +05301711 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1712 common->curaid = bss_conf->aid;
1713 ath9k_hw_write_associd(sc->sc_ah);
Rajkumar Manoharan2e5ef452011-05-20 17:52:12 +05301714 }
1715
Sujith Manoharanef4ad632012-07-17 17:15:56 +05301716 if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
Rajkumar Manoharan9198cf42014-06-26 16:54:40 +05301717 (changed & BSS_CHANGED_BEACON_INT) ||
1718 (changed & BSS_CHANGED_BEACON_INFO)) {
Sujith Manoharan9bf30ff92014-09-05 08:03:11 +05301719 ath9k_beacon_config(sc, vif, changed);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301720 if (changed & BSS_CHANGED_BEACON_ENABLED)
1721 ath9k_calculate_summary_state(sc, avp->chanctx);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301722 }
Johannes Berg2d0ddec2009-04-23 16:13:26 +02001723
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05301724 if ((avp->chanctx == sc->cur_chan) &&
1725 (changed & BSS_CHANGED_ERP_SLOT)) {
Felix Fietkau0005baf2010-01-15 02:33:40 +01001726 if (bss_conf->use_short_slot)
1727 slottime = 9;
1728 else
1729 slottime = 20;
1730 if (vif->type == NL80211_IFTYPE_AP) {
1731 /*
1732 * Defer update, so that connected stations can adjust
1733 * their settings at the same time.
1734 * See beacon.c for more details
1735 */
1736 sc->beacon.slottime = slottime;
1737 sc->beacon.updateslot = UPDATE;
1738 } else {
1739 ah->slottime = slottime;
1740 ath9k_hw_init_global_settings(ah);
1741 }
1742 }
1743
Sujith Manoharanc7dd40c2014-08-22 20:39:30 +05301744 if (changed & BSS_CHANGED_P2P_PS)
1745 ath9k_p2p_bss_info_changed(sc, vif);
Felix Fietkaud463af42014-04-06 00:37:03 +02001746
Sujith Manoharanda0d45f2012-07-17 17:16:29 +05301747 if (changed & CHECK_ANI)
1748 ath_check_ani(sc);
1749
Sujith141b38b2009-02-04 08:10:07 +05301750 mutex_unlock(&sc->mutex);
Felix Fietkau96f372c2011-04-07 19:07:17 +02001751 ath9k_ps_restore(sc);
Sujith Manoharanda0d45f2012-07-17 17:16:29 +05301752
1753#undef CHECK_ANI
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001754}
1755
Eliad Peller37a41b42011-09-21 14:06:11 +03001756static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001757{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001758 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001759 u64 tsf;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001760
Sujith141b38b2009-02-04 08:10:07 +05301761 mutex_lock(&sc->mutex);
Sujith Manoharan9abbfb22010-12-10 11:27:06 +05301762 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05301763 tsf = ath9k_hw_gettsf64(sc->sc_ah);
Sujith Manoharan9abbfb22010-12-10 11:27:06 +05301764 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05301765 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001766
1767 return tsf;
1768}
1769
Eliad Peller37a41b42011-09-21 14:06:11 +03001770static void ath9k_set_tsf(struct ieee80211_hw *hw,
1771 struct ieee80211_vif *vif,
1772 u64 tsf)
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01001773{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001774 struct ath_softc *sc = hw->priv;
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01001775
Sujith141b38b2009-02-04 08:10:07 +05301776 mutex_lock(&sc->mutex);
Sujith Manoharan9abbfb22010-12-10 11:27:06 +05301777 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05301778 ath9k_hw_settsf64(sc->sc_ah, tsf);
Sujith Manoharan9abbfb22010-12-10 11:27:06 +05301779 ath9k_ps_restore(sc);
Sujith141b38b2009-02-04 08:10:07 +05301780 mutex_unlock(&sc->mutex);
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01001781}
1782
Eliad Peller37a41b42011-09-21 14:06:11 +03001783static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001784{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001785 struct ath_softc *sc = hw->priv;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001786
Sujith141b38b2009-02-04 08:10:07 +05301787 mutex_lock(&sc->mutex);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07001788
1789 ath9k_ps_wakeup(sc);
Sujith141b38b2009-02-04 08:10:07 +05301790 ath9k_hw_reset_tsf(sc->sc_ah);
Luis R. Rodriguez21526d52009-09-09 20:05:39 -07001791 ath9k_ps_restore(sc);
1792
Sujith141b38b2009-02-04 08:10:07 +05301793 mutex_unlock(&sc->mutex);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001794}
1795
1796static int ath9k_ampdu_action(struct ieee80211_hw *hw,
Johannes Bergc951ad32009-11-16 12:00:38 +01001797 struct ieee80211_vif *vif,
Sujith141b38b2009-02-04 08:10:07 +05301798 enum ieee80211_ampdu_mlme_action action,
1799 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001800 u16 tid, u16 *ssn, u8 buf_size)
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001801{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001802 struct ath_softc *sc = hw->priv;
Felix Fietkau16e23422013-05-17 12:58:24 +02001803 bool flush = false;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001804 int ret = 0;
1805
Sujith Manoharan7ca7c772013-05-08 05:03:32 +05301806 mutex_lock(&sc->mutex);
Johannes Berg85ad1812010-06-10 10:21:49 +02001807
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001808 switch (action) {
1809 case IEEE80211_AMPDU_RX_START:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001810 break;
1811 case IEEE80211_AMPDU_RX_STOP:
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001812 break;
1813 case IEEE80211_AMPDU_TX_START:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001814 ath9k_ps_wakeup(sc);
Felix Fietkau231c3a12010-09-20 19:35:28 +02001815 ret = ath_tx_aggr_start(sc, sta, tid, ssn);
1816 if (!ret)
1817 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001818 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001819 break;
Johannes Berg18b559d2012-07-18 13:51:25 +02001820 case IEEE80211_AMPDU_TX_STOP_FLUSH:
1821 case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
Felix Fietkau16e23422013-05-17 12:58:24 +02001822 flush = true;
1823 case IEEE80211_AMPDU_TX_STOP_CONT:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001824 ath9k_ps_wakeup(sc);
Sujithf83da962009-07-23 15:32:37 +05301825 ath_tx_aggr_stop(sc, sta, tid);
Felix Fietkau08c96ab2013-05-18 21:28:15 +02001826 if (!flush)
Felix Fietkau16e23422013-05-17 12:58:24 +02001827 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001828 ath9k_ps_restore(sc);
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001829 break;
Johannes Bergb1720232009-03-23 17:28:39 +01001830 case IEEE80211_AMPDU_TX_OPERATIONAL:
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001831 ath9k_ps_wakeup(sc);
Sujith8469cde2008-10-29 10:19:28 +05301832 ath_tx_aggr_resume(sc, sta, tid);
Luis R. Rodriguez8b685ba2009-12-23 20:03:29 -05001833 ath9k_ps_restore(sc);
Sujith8469cde2008-10-29 10:19:28 +05301834 break;
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001835 default:
Joe Perches38002762010-12-02 19:12:36 -08001836 ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001837 }
1838
Sujith Manoharan7ca7c772013-05-08 05:03:32 +05301839 mutex_unlock(&sc->mutex);
Johannes Berg85ad1812010-06-10 10:21:49 +02001840
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07001841 return ret;
1842}
1843
Benoit Papillault62dad5b2010-04-28 00:08:24 +02001844static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
1845 struct survey_info *survey)
1846{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001847 struct ath_softc *sc = hw->priv;
Felix Fietkau34300982010-10-10 18:21:52 +02001848 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau39162db2010-09-29 19:12:06 +02001849 struct ieee80211_supported_band *sband;
Felix Fietkau34300982010-10-10 18:21:52 +02001850 struct ieee80211_channel *chan;
Felix Fietkau34300982010-10-10 18:21:52 +02001851 int pos;
1852
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001853 if (config_enabled(CONFIG_ATH9K_TX99))
1854 return -EOPNOTSUPP;
1855
Sujith Manoharanb7cc9b92013-12-26 08:14:40 +05301856 spin_lock_bh(&common->cc_lock);
Felix Fietkau34300982010-10-10 18:21:52 +02001857 if (idx == 0)
1858 ath_update_survey_stats(sc);
Benoit Papillault62dad5b2010-04-28 00:08:24 +02001859
Felix Fietkau39162db2010-09-29 19:12:06 +02001860 sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ];
1861 if (sband && idx >= sband->n_channels) {
1862 idx -= sband->n_channels;
1863 sband = NULL;
1864 }
Benoit Papillault62dad5b2010-04-28 00:08:24 +02001865
Felix Fietkau39162db2010-09-29 19:12:06 +02001866 if (!sband)
1867 sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
1868
Felix Fietkau34300982010-10-10 18:21:52 +02001869 if (!sband || idx >= sband->n_channels) {
Sujith Manoharanb7cc9b92013-12-26 08:14:40 +05301870 spin_unlock_bh(&common->cc_lock);
Felix Fietkau34300982010-10-10 18:21:52 +02001871 return -ENOENT;
Felix Fietkau4f1a5a42010-09-29 17:15:28 +02001872 }
Benoit Papillault62dad5b2010-04-28 00:08:24 +02001873
Felix Fietkau34300982010-10-10 18:21:52 +02001874 chan = &sband->channels[idx];
1875 pos = chan->hw_value;
1876 memcpy(survey, &sc->survey[pos], sizeof(*survey));
1877 survey->channel = chan;
Sujith Manoharanb7cc9b92013-12-26 08:14:40 +05301878 spin_unlock_bh(&common->cc_lock);
Felix Fietkau34300982010-10-10 18:21:52 +02001879
Benoit Papillault62dad5b2010-04-28 00:08:24 +02001880 return 0;
1881}
1882
Lorenzo Bianconi24a19362014-09-16 02:13:15 +02001883static void ath9k_enable_dynack(struct ath_softc *sc)
1884{
1885#ifdef CONFIG_ATH9K_DYNACK
1886 u32 rfilt;
1887 struct ath_hw *ah = sc->sc_ah;
1888
1889 ath_dynack_reset(ah);
1890
1891 ah->dynack.enabled = true;
1892 rfilt = ath_calcrxfilter(sc);
1893 ath9k_hw_setrxfilter(ah, rfilt);
1894#endif
1895}
1896
Lorenzo Bianconia4bcaf52014-09-04 23:57:41 +02001897static void ath9k_set_coverage_class(struct ieee80211_hw *hw,
1898 s16 coverage_class)
Felix Fietkaue239d852010-01-15 02:34:58 +01001899{
Felix Fietkau9ac586152011-01-24 19:23:18 +01001900 struct ath_softc *sc = hw->priv;
Felix Fietkaue239d852010-01-15 02:34:58 +01001901 struct ath_hw *ah = sc->sc_ah;
1902
Luis R. Rodriguez89f927a2013-10-14 17:42:11 -07001903 if (config_enabled(CONFIG_ATH9K_TX99))
1904 return;
1905
Felix Fietkaue239d852010-01-15 02:34:58 +01001906 mutex_lock(&sc->mutex);
Mohammed Shafi Shajakhan8b2a38272011-08-24 21:38:07 +05301907
Lorenzo Bianconi24a19362014-09-16 02:13:15 +02001908 if (coverage_class >= 0) {
1909 ah->coverage_class = coverage_class;
1910 if (ah->dynack.enabled) {
1911 u32 rfilt;
1912
1913 ah->dynack.enabled = false;
1914 rfilt = ath_calcrxfilter(sc);
1915 ath9k_hw_setrxfilter(ah, rfilt);
1916 }
1917 ath9k_ps_wakeup(sc);
1918 ath9k_hw_init_global_settings(ah);
1919 ath9k_ps_restore(sc);
1920 } else if (!ah->dynack.enabled) {
1921 ath9k_enable_dynack(sc);
1922 }
Mohammed Shafi Shajakhan8b2a38272011-08-24 21:38:07 +05301923
Felix Fietkaue239d852010-01-15 02:34:58 +01001924 mutex_unlock(&sc->mutex);
1925}
1926
Felix Fietkau10e23182013-11-11 22:23:35 +01001927static bool ath9k_has_tx_pending(struct ath_softc *sc)
1928{
Geert Uytterhoevenf7838072014-01-26 11:53:21 +01001929 int i, npend = 0;
Felix Fietkau10e23182013-11-11 22:23:35 +01001930
1931 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
1932 if (!ATH_TXQ_SETUP(sc, i))
1933 continue;
1934
1935 if (!sc->tx.txq[i].axq_depth)
1936 continue;
1937
1938 npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]);
1939 if (npend)
1940 break;
1941 }
1942
1943 return !!npend;
1944}
1945
Emmanuel Grumbach77be2c52014-03-27 11:30:29 +02001946static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1947 u32 queues, bool drop)
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08001948{
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08001949 struct ath_softc *sc = hw->priv;
Felix Fietkaubff11762014-06-11 16:17:52 +05301950
1951 mutex_lock(&sc->mutex);
1952 __ath9k_flush(hw, queues, drop);
1953 mutex_unlock(&sc->mutex);
1954}
1955
1956void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
1957{
1958 struct ath_softc *sc = hw->priv;
Mohammed Shafi Shajakhan99aa55b2011-05-06 20:43:11 +05301959 struct ath_hw *ah = sc->sc_ah;
1960 struct ath_common *common = ath9k_hw_common(ah);
Felix Fietkau10e23182013-11-11 22:23:35 +01001961 int timeout = HZ / 5; /* 200 ms */
Rajkumar Manoharan2f6fc352011-04-28 15:31:57 +05301962 bool drain_txq;
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05301963 int i;
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08001964
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08001965 cancel_delayed_work_sync(&sc->tx_complete_work);
1966
Mohammed Shafi Shajakhan6a6b3f32011-09-09 10:41:08 +05301967 if (ah->ah_flags & AH_UNPLUGGED) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001968 ath_dbg(common, ANY, "Device has been unplugged!\n");
Mohammed Shafi Shajakhan6a6b3f32011-09-09 10:41:08 +05301969 return;
1970 }
1971
Oleksij Rempeleefa01d2014-02-27 11:40:46 +01001972 if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001973 ath_dbg(common, ANY, "Device not present\n");
Mohammed Shafi Shajakhan99aa55b2011-05-06 20:43:11 +05301974 return;
1975 }
1976
Felix Fietkau10e23182013-11-11 22:23:35 +01001977 if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc),
1978 timeout) > 0)
1979 drop = false;
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08001980
Felix Fietkau9df0d6a2011-11-16 13:08:42 +01001981 if (drop) {
1982 ath9k_ps_wakeup(sc);
1983 spin_lock_bh(&sc->sc_pcu_lock);
Felix Fietkau13815592013-01-20 18:51:53 +01001984 drain_txq = ath_drain_all_txq(sc);
Felix Fietkau9df0d6a2011-11-16 13:08:42 +01001985 spin_unlock_bh(&sc->sc_pcu_lock);
Felix Fietkau9adcf442011-09-03 01:40:26 +02001986
Felix Fietkau9df0d6a2011-11-16 13:08:42 +01001987 if (!drain_txq)
Felix Fietkau13815592013-01-20 18:51:53 +01001988 ath_reset(sc);
Felix Fietkau9adcf442011-09-03 01:40:26 +02001989
Felix Fietkau9df0d6a2011-11-16 13:08:42 +01001990 ath9k_ps_restore(sc);
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05301991 for (i = 0; i < IEEE80211_NUM_ACS; i++) {
1992 ieee80211_wake_queue(sc->hw,
1993 sc->cur_chan->hw_queue_base + i);
1994 }
Felix Fietkau9df0d6a2011-11-16 13:08:42 +01001995 }
Senthil Balasubramaniand78f4b32011-03-23 23:07:22 +05301996
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08001997 ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08001998}
1999
Vivek Natarajan15b91e82011-04-06 11:41:11 +05302000static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
2001{
2002 struct ath_softc *sc = hw->priv;
2003 int i;
2004
2005 for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
2006 if (!ATH_TXQ_SETUP(sc, i))
2007 continue;
2008
2009 if (ath9k_has_pending_frames(sc, &sc->tx.txq[i]))
2010 return true;
2011 }
2012 return false;
2013}
2014
Mohammed Shafi Shajakhan5595f112011-05-19 18:08:57 +05302015static int ath9k_tx_last_beacon(struct ieee80211_hw *hw)
Felix Fietkauba4903f2011-05-17 21:09:54 +02002016{
2017 struct ath_softc *sc = hw->priv;
2018 struct ath_hw *ah = sc->sc_ah;
2019 struct ieee80211_vif *vif;
2020 struct ath_vif *avp;
2021 struct ath_buf *bf;
2022 struct ath_tx_status ts;
Felix Fietkau4286df62012-02-27 19:58:40 +01002023 bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
Felix Fietkauba4903f2011-05-17 21:09:54 +02002024 int status;
2025
2026 vif = sc->beacon.bslot[0];
2027 if (!vif)
2028 return 0;
2029
Sujith Manoharanaa45fe92012-07-17 17:16:03 +05302030 if (!vif->bss_conf.enable_beacon)
Felix Fietkauba4903f2011-05-17 21:09:54 +02002031 return 0;
2032
Sujith Manoharanaa45fe92012-07-17 17:16:03 +05302033 avp = (void *)vif->drv_priv;
2034
Felix Fietkau4286df62012-02-27 19:58:40 +01002035 if (!sc->beacon.tx_processed && !edma) {
Felix Fietkauba4903f2011-05-17 21:09:54 +02002036 tasklet_disable(&sc->bcon_tasklet);
2037
2038 bf = avp->av_bcbuf;
2039 if (!bf || !bf->bf_mpdu)
2040 goto skip;
2041
2042 status = ath9k_hw_txprocdesc(ah, bf->bf_desc, &ts);
2043 if (status == -EINPROGRESS)
2044 goto skip;
2045
2046 sc->beacon.tx_processed = true;
2047 sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
2048
2049skip:
2050 tasklet_enable(&sc->bcon_tasklet);
2051 }
2052
2053 return sc->beacon.tx_last;
2054}
2055
Mohammed Shafi Shajakhan52c94f42011-08-20 17:21:42 +05302056static int ath9k_get_stats(struct ieee80211_hw *hw,
2057 struct ieee80211_low_level_stats *stats)
2058{
2059 struct ath_softc *sc = hw->priv;
2060 struct ath_hw *ah = sc->sc_ah;
2061 struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats;
2062
2063 stats->dot11ACKFailureCount = mib_stats->ackrcv_bad;
2064 stats->dot11RTSFailureCount = mib_stats->rts_bad;
2065 stats->dot11FCSErrorCount = mib_stats->fcs_bad;
2066 stats->dot11RTSSuccessCount = mib_stats->rts_good;
2067 return 0;
2068}
2069
Felix Fietkau43c35282011-09-03 01:40:27 +02002070static u32 fill_chainmask(u32 cap, u32 new)
2071{
2072 u32 filled = 0;
2073 int i;
2074
2075 for (i = 0; cap && new; i++, cap >>= 1) {
2076 if (!(cap & BIT(0)))
2077 continue;
2078
2079 if (new & BIT(0))
2080 filled |= BIT(i);
2081
2082 new >>= 1;
2083 }
2084
2085 return filled;
2086}
2087
Felix Fietkau5d9c7e32012-07-15 19:53:30 +02002088static bool validate_antenna_mask(struct ath_hw *ah, u32 val)
2089{
Felix Fietkaufea92cb2013-01-20 21:55:22 +01002090 if (AR_SREV_9300_20_OR_LATER(ah))
2091 return true;
2092
Felix Fietkau5d9c7e32012-07-15 19:53:30 +02002093 switch (val & 0x7) {
2094 case 0x1:
2095 case 0x3:
2096 case 0x7:
2097 return true;
2098 case 0x2:
2099 return (ah->caps.rx_chainmask == 1);
2100 default:
2101 return false;
2102 }
2103}
2104
Felix Fietkau43c35282011-09-03 01:40:27 +02002105static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
2106{
2107 struct ath_softc *sc = hw->priv;
2108 struct ath_hw *ah = sc->sc_ah;
2109
Felix Fietkau5d9c7e32012-07-15 19:53:30 +02002110 if (ah->caps.rx_chainmask != 1)
2111 rx_ant |= tx_ant;
2112
2113 if (!validate_antenna_mask(ah, rx_ant) || !tx_ant)
Felix Fietkau43c35282011-09-03 01:40:27 +02002114 return -EINVAL;
2115
2116 sc->ant_rx = rx_ant;
2117 sc->ant_tx = tx_ant;
2118
2119 if (ah->caps.rx_chainmask == 1)
2120 return 0;
2121
2122 /* AR9100 runs into calibration issues if not all rx chains are enabled */
2123 if (AR_SREV_9100(ah))
2124 ah->rxchainmask = 0x7;
2125 else
2126 ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant);
2127
2128 ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant);
Oleksij Rempelb57ba3b2014-02-25 14:48:55 +01002129 ath9k_cmn_reload_chainmask(ah);
Felix Fietkau43c35282011-09-03 01:40:27 +02002130
2131 return 0;
2132}
2133
2134static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
2135{
2136 struct ath_softc *sc = hw->priv;
2137
2138 *tx_ant = sc->ant_tx;
2139 *rx_ant = sc->ant_rx;
2140 return 0;
2141}
2142
Simon Wunderliche93d0832013-01-08 14:48:58 +01002143static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
2144{
2145 struct ath_softc *sc = hw->priv;
Oleksij Rempeleefa01d2014-02-27 11:40:46 +01002146 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2147 set_bit(ATH_OP_SCANNING, &common->op_flags);
Simon Wunderliche93d0832013-01-08 14:48:58 +01002148}
2149
2150static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
2151{
2152 struct ath_softc *sc = hw->priv;
Oleksij Rempeleefa01d2014-02-27 11:40:46 +01002153 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2154 clear_bit(ATH_OP_SCANNING, &common->op_flags);
Simon Wunderliche93d0832013-01-08 14:48:58 +01002155}
Mohammed Shafi Shajakhanb11e6402012-07-10 14:56:52 +05302156
Sujith Manoharan499afac2014-08-22 20:39:31 +05302157#ifdef CONFIG_ATH9K_CHANNEL_CONTEXT
2158
Felix Fietkau78b21942014-06-11 16:17:55 +05302159static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
John W. Linville855df362014-06-25 15:15:14 -04002160 struct ieee80211_scan_request *hw_req)
Felix Fietkau78b21942014-06-11 16:17:55 +05302161{
John W. Linville855df362014-06-25 15:15:14 -04002162 struct cfg80211_scan_request *req = &hw_req->req;
Felix Fietkau78b21942014-06-11 16:17:55 +05302163 struct ath_softc *sc = hw->priv;
2164 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2165 int ret = 0;
2166
2167 mutex_lock(&sc->mutex);
2168
2169 if (WARN_ON(sc->offchannel.scan_req)) {
2170 ret = -EBUSY;
2171 goto out;
2172 }
2173
2174 ath9k_ps_wakeup(sc);
2175 set_bit(ATH_OP_SCANNING, &common->op_flags);
2176 sc->offchannel.scan_vif = vif;
2177 sc->offchannel.scan_req = req;
2178 sc->offchannel.scan_idx = 0;
Felix Fietkau78b21942014-06-11 16:17:55 +05302179
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302180 ath_dbg(common, CHAN_CTX, "HW scan request received on vif: %pM\n",
2181 vif->addr);
2182
2183 if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) {
2184 ath_dbg(common, CHAN_CTX, "Starting HW scan\n");
Felix Fietkau405393c2014-06-11 16:17:56 +05302185 ath_offchannel_next(sc);
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302186 }
Felix Fietkau78b21942014-06-11 16:17:55 +05302187
2188out:
2189 mutex_unlock(&sc->mutex);
2190
2191 return ret;
2192}
2193
2194static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
2195 struct ieee80211_vif *vif)
2196{
2197 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302198 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2199
2200 ath_dbg(common, CHAN_CTX, "Cancel HW scan on vif: %pM\n", vif->addr);
Felix Fietkau78b21942014-06-11 16:17:55 +05302201
2202 mutex_lock(&sc->mutex);
2203 del_timer_sync(&sc->offchannel.timer);
2204 ath_scan_complete(sc, true);
2205 mutex_unlock(&sc->mutex);
2206}
2207
Felix Fietkau405393c2014-06-11 16:17:56 +05302208static int ath9k_remain_on_channel(struct ieee80211_hw *hw,
2209 struct ieee80211_vif *vif,
2210 struct ieee80211_channel *chan, int duration,
2211 enum ieee80211_roc_type type)
2212{
2213 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302214 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau405393c2014-06-11 16:17:56 +05302215 int ret = 0;
2216
2217 mutex_lock(&sc->mutex);
2218
2219 if (WARN_ON(sc->offchannel.roc_vif)) {
2220 ret = -EBUSY;
2221 goto out;
2222 }
2223
2224 ath9k_ps_wakeup(sc);
2225 sc->offchannel.roc_vif = vif;
2226 sc->offchannel.roc_chan = chan;
2227 sc->offchannel.roc_duration = duration;
2228
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302229 ath_dbg(common, CHAN_CTX,
2230 "RoC request on vif: %pM, type: %d duration: %d\n",
2231 vif->addr, type, duration);
2232
2233 if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE) {
2234 ath_dbg(common, CHAN_CTX, "Starting RoC period\n");
Felix Fietkau405393c2014-06-11 16:17:56 +05302235 ath_offchannel_next(sc);
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302236 }
Felix Fietkau405393c2014-06-11 16:17:56 +05302237
2238out:
2239 mutex_unlock(&sc->mutex);
2240
2241 return ret;
2242}
2243
2244static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw)
2245{
2246 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302247 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau405393c2014-06-11 16:17:56 +05302248
2249 mutex_lock(&sc->mutex);
2250
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302251 ath_dbg(common, CHAN_CTX, "Cancel RoC\n");
Felix Fietkau405393c2014-06-11 16:17:56 +05302252 del_timer_sync(&sc->offchannel.timer);
2253
2254 if (sc->offchannel.roc_vif) {
2255 if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
2256 ath_roc_complete(sc, true);
2257 }
2258
2259 mutex_unlock(&sc->mutex);
2260
2261 return 0;
2262}
2263
Felix Fietkau39305632014-06-11 16:17:57 +05302264static int ath9k_add_chanctx(struct ieee80211_hw *hw,
2265 struct ieee80211_chanctx_conf *conf)
2266{
2267 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302268 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau39305632014-06-11 16:17:57 +05302269 struct ath_chanctx *ctx, **ptr;
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05302270 int pos;
Felix Fietkau39305632014-06-11 16:17:57 +05302271
2272 mutex_lock(&sc->mutex);
Rajkumar Manoharanc4dc0d02014-06-11 16:17:58 +05302273
2274 ath_for_each_chanctx(sc, ctx) {
2275 if (ctx->assigned)
2276 continue;
2277
2278 ptr = (void *) conf->drv_priv;
2279 *ptr = ctx;
2280 ctx->assigned = true;
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05302281 pos = ctx - &sc->chanctx[0];
2282 ctx->hw_queue_base = pos * IEEE80211_NUM_ACS;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302283
2284 ath_dbg(common, CHAN_CTX,
2285 "Add channel context: %d MHz\n",
2286 conf->def.chan->center_freq);
2287
Rajkumar Manoharanc4dc0d02014-06-11 16:17:58 +05302288 ath_chanctx_set_channel(sc, ctx, &conf->def);
Sujith Manoharan4c7e9ae2014-08-24 21:16:13 +05302289 ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ASSIGN);
2290
Felix Fietkau39305632014-06-11 16:17:57 +05302291 mutex_unlock(&sc->mutex);
Rajkumar Manoharanc4dc0d02014-06-11 16:17:58 +05302292 return 0;
Felix Fietkau39305632014-06-11 16:17:57 +05302293 }
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302294
Felix Fietkau39305632014-06-11 16:17:57 +05302295 mutex_unlock(&sc->mutex);
Rajkumar Manoharanc4dc0d02014-06-11 16:17:58 +05302296 return -ENOSPC;
Felix Fietkau39305632014-06-11 16:17:57 +05302297}
2298
2299
2300static void ath9k_remove_chanctx(struct ieee80211_hw *hw,
2301 struct ieee80211_chanctx_conf *conf)
2302{
2303 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302304 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau39305632014-06-11 16:17:57 +05302305 struct ath_chanctx *ctx = ath_chanctx_get(conf);
2306
2307 mutex_lock(&sc->mutex);
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302308
2309 ath_dbg(common, CHAN_CTX,
2310 "Remove channel context: %d MHz\n",
2311 conf->def.chan->center_freq);
2312
Felix Fietkau39305632014-06-11 16:17:57 +05302313 ctx->assigned = false;
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05302314 ctx->hw_queue_base = -1;
Felix Fietkau73fa2f22014-06-11 16:18:10 +05302315 ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN);
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302316
Felix Fietkau39305632014-06-11 16:17:57 +05302317 mutex_unlock(&sc->mutex);
2318}
2319
2320static void ath9k_change_chanctx(struct ieee80211_hw *hw,
2321 struct ieee80211_chanctx_conf *conf,
2322 u32 changed)
2323{
2324 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302325 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau39305632014-06-11 16:17:57 +05302326 struct ath_chanctx *ctx = ath_chanctx_get(conf);
2327
2328 mutex_lock(&sc->mutex);
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302329 ath_dbg(common, CHAN_CTX,
2330 "Change channel context: %d MHz\n",
2331 conf->def.chan->center_freq);
Felix Fietkau39305632014-06-11 16:17:57 +05302332 ath_chanctx_set_channel(sc, ctx, &conf->def);
2333 mutex_unlock(&sc->mutex);
2334}
2335
2336static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw,
2337 struct ieee80211_vif *vif,
2338 struct ieee80211_chanctx_conf *conf)
2339{
2340 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302341 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau39305632014-06-11 16:17:57 +05302342 struct ath_vif *avp = (void *)vif->drv_priv;
2343 struct ath_chanctx *ctx = ath_chanctx_get(conf);
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05302344 int i;
Felix Fietkau39305632014-06-11 16:17:57 +05302345
2346 mutex_lock(&sc->mutex);
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302347
2348 ath_dbg(common, CHAN_CTX,
2349 "Assign VIF (addr: %pM, type: %d, p2p: %d) to channel context: %d MHz\n",
2350 vif->addr, vif->type, vif->p2p,
2351 conf->def.chan->center_freq);
2352
Felix Fietkau39305632014-06-11 16:17:57 +05302353 avp->chanctx = ctx;
2354 list_add_tail(&avp->list, &ctx->vifs);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05302355 ath9k_calculate_summary_state(sc, ctx);
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05302356 for (i = 0; i < IEEE80211_NUM_ACS; i++)
2357 vif->hw_queue[i] = ctx->hw_queue_base + i;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302358
Felix Fietkau39305632014-06-11 16:17:57 +05302359 mutex_unlock(&sc->mutex);
2360
2361 return 0;
2362}
2363
2364static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
2365 struct ieee80211_vif *vif,
2366 struct ieee80211_chanctx_conf *conf)
2367{
2368 struct ath_softc *sc = hw->priv;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302369 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Felix Fietkau39305632014-06-11 16:17:57 +05302370 struct ath_vif *avp = (void *)vif->drv_priv;
2371 struct ath_chanctx *ctx = ath_chanctx_get(conf);
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05302372 int ac;
Felix Fietkau39305632014-06-11 16:17:57 +05302373
2374 mutex_lock(&sc->mutex);
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302375
2376 ath_dbg(common, CHAN_CTX,
2377 "Remove VIF (addr: %pM, type: %d, p2p: %d) from channel context: %d MHz\n",
2378 vif->addr, vif->type, vif->p2p,
2379 conf->def.chan->center_freq);
2380
Felix Fietkau39305632014-06-11 16:17:57 +05302381 avp->chanctx = NULL;
2382 list_del(&avp->list);
Rajkumar Manoharan9a9c4fb2014-06-11 16:18:03 +05302383 ath9k_calculate_summary_state(sc, ctx);
Rajkumar Manoharan3ad9c382014-06-11 16:18:15 +05302384 for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
2385 vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302386
Felix Fietkau39305632014-06-11 16:17:57 +05302387 mutex_unlock(&sc->mutex);
2388}
2389
Sujith Manoharane20a8542014-08-23 13:29:09 +05302390static void ath9k_mgd_prepare_tx(struct ieee80211_hw *hw,
2391 struct ieee80211_vif *vif)
2392{
2393 struct ath_softc *sc = hw->priv;
2394 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
2395 struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
2396 bool changed = false;
2397
2398 if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
2399 return;
2400
2401 if (!avp->chanctx)
2402 return;
2403
2404 mutex_lock(&sc->mutex);
2405
2406 spin_lock_bh(&sc->chan_lock);
2407 if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
2408 sc->next_chan = avp->chanctx;
2409 changed = true;
2410 }
Sujith Manoharan878066e2014-08-27 12:07:24 +05302411 ath_dbg(common, CHAN_CTX,
2412 "%s: Set chanctx state to FORCE_ACTIVE, changed: %d\n",
2413 __func__, changed);
Sujith Manoharane20a8542014-08-23 13:29:09 +05302414 sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
2415 spin_unlock_bh(&sc->chan_lock);
2416
2417 if (changed)
2418 ath_chanctx_set_next(sc, true);
2419
2420 mutex_unlock(&sc->mutex);
2421}
2422
Felix Fietkau78b21942014-06-11 16:17:55 +05302423void ath9k_fill_chanctx_ops(void)
2424{
Sujith Manoharan499afac2014-08-22 20:39:31 +05302425 if (!ath9k_is_chanctx_enabled())
Felix Fietkau78b21942014-06-11 16:17:55 +05302426 return;
2427
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302428 ath9k_ops.hw_scan = ath9k_hw_scan;
2429 ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
2430 ath9k_ops.remain_on_channel = ath9k_remain_on_channel;
Felix Fietkau405393c2014-06-11 16:17:56 +05302431 ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel;
Sujith Manoharanbc81d432014-08-22 20:39:27 +05302432 ath9k_ops.add_chanctx = ath9k_add_chanctx;
2433 ath9k_ops.remove_chanctx = ath9k_remove_chanctx;
2434 ath9k_ops.change_chanctx = ath9k_change_chanctx;
2435 ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx;
2436 ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx;
Sujith Manoharane20a8542014-08-23 13:29:09 +05302437 ath9k_ops.mgd_prepare_tx = ath9k_mgd_prepare_tx;
Felix Fietkau78b21942014-06-11 16:17:55 +05302438}
2439
Sujith Manoharan499afac2014-08-22 20:39:31 +05302440#endif
2441
Gabor Juhos6baff7f2009-01-14 20:17:06 +01002442struct ieee80211_ops ath9k_ops = {
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002443 .tx = ath9k_tx,
2444 .start = ath9k_start,
2445 .stop = ath9k_stop,
2446 .add_interface = ath9k_add_interface,
Rajkumar Manoharan6b3b9912010-12-08 19:38:55 +05302447 .change_interface = ath9k_change_interface,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002448 .remove_interface = ath9k_remove_interface,
2449 .config = ath9k_config,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002450 .configure_filter = ath9k_configure_filter,
Johannes Berg4ca77862010-02-19 19:06:56 +01002451 .sta_add = ath9k_sta_add,
2452 .sta_remove = ath9k_sta_remove,
Felix Fietkau55195412011-04-17 23:28:09 +02002453 .sta_notify = ath9k_sta_notify,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002454 .conf_tx = ath9k_conf_tx,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002455 .bss_info_changed = ath9k_bss_info_changed,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002456 .set_key = ath9k_set_key,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002457 .get_tsf = ath9k_get_tsf,
Alina Friedrichsen3b5d6652009-01-24 07:09:59 +01002458 .set_tsf = ath9k_set_tsf,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002459 .reset_tsf = ath9k_reset_tsf,
Johannes Berg4233df62008-10-13 13:35:05 +02002460 .ampdu_action = ath9k_ampdu_action,
Benoit Papillault62dad5b2010-04-28 00:08:24 +02002461 .get_survey = ath9k_get_survey,
Johannes Berg3b319aa2009-06-13 14:50:26 +05302462 .rfkill_poll = ath9k_rfkill_poll_state,
Felix Fietkaue239d852010-01-15 02:34:58 +01002463 .set_coverage_class = ath9k_set_coverage_class,
Vasanthakumar Thiagarajan69081622011-02-19 01:13:42 -08002464 .flush = ath9k_flush,
Vivek Natarajan15b91e82011-04-06 11:41:11 +05302465 .tx_frames_pending = ath9k_tx_frames_pending,
Mohammed Shafi Shajakhan52c94f42011-08-20 17:21:42 +05302466 .tx_last_beacon = ath9k_tx_last_beacon,
Felix Fietkau86a22ac2013-06-07 18:12:01 +02002467 .release_buffered_frames = ath9k_release_buffered_frames,
Mohammed Shafi Shajakhan52c94f42011-08-20 17:21:42 +05302468 .get_stats = ath9k_get_stats,
Felix Fietkau43c35282011-09-03 01:40:27 +02002469 .set_antenna = ath9k_set_antenna,
2470 .get_antenna = ath9k_get_antenna,
Ben Greearb90bd9d2012-05-15 15:33:25 -07002471
Sujith Manoharane60001e2013-10-28 12:22:04 +05302472#ifdef CONFIG_ATH9K_WOW
Mohammed Shafi Shajakhanb11e6402012-07-10 14:56:52 +05302473 .suspend = ath9k_suspend,
2474 .resume = ath9k_resume,
2475 .set_wakeup = ath9k_set_wakeup,
2476#endif
2477
Ben Greearb90bd9d2012-05-15 15:33:25 -07002478#ifdef CONFIG_ATH9K_DEBUGFS
2479 .get_et_sset_count = ath9k_get_et_sset_count,
Sujith Manoharana145daf2012-11-28 15:08:54 +05302480 .get_et_stats = ath9k_get_et_stats,
2481 .get_et_strings = ath9k_get_et_strings,
2482#endif
2483
Sujith Manoharan1cdbaf02014-01-13 07:29:27 +05302484#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_STATION_STATISTICS)
Sujith Manoharana145daf2012-11-28 15:08:54 +05302485 .sta_add_debugfs = ath9k_sta_add_debugfs,
Ben Greearb90bd9d2012-05-15 15:33:25 -07002486#endif
Simon Wunderliche93d0832013-01-08 14:48:58 +01002487 .sw_scan_start = ath9k_sw_scan_start,
2488 .sw_scan_complete = ath9k_sw_scan_complete,
Luis R. Rodriguezf078f202008-08-04 00:16:41 -07002489};