blob: ec7cf5ee56bc3ac9a850f7ee56fab95251b88d6d [file] [log] [blame]
Jouni Malinen8ca21f02009-03-03 19:23:27 +02001/*
2 * Copyright (c) 2008-2009 Atheros Communications Inc.
3 *
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
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090017#include <linux/slab.h>
18
Jouni Malinen8ca21f02009-03-03 19:23:27 +020019#include "ath9k.h"
20
21struct ath9k_vif_iter_data {
Felix Fietkau31a01642010-09-14 18:37:19 +020022 const u8 *hw_macaddr;
23 u8 mask[ETH_ALEN];
Jouni Malinen8ca21f02009-03-03 19:23:27 +020024};
25
26static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
27{
28 struct ath9k_vif_iter_data *iter_data = data;
Felix Fietkau31a01642010-09-14 18:37:19 +020029 int i;
Jouni Malinen8ca21f02009-03-03 19:23:27 +020030
Felix Fietkau31a01642010-09-14 18:37:19 +020031 for (i = 0; i < ETH_ALEN; i++)
32 iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
Jouni Malinen8ca21f02009-03-03 19:23:27 +020033}
34
Felix Fietkau31a01642010-09-14 18:37:19 +020035void ath9k_set_bssid_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
Jouni Malinen8ca21f02009-03-03 19:23:27 +020036{
Jouni Malinenbce048d2009-03-03 19:23:28 +020037 struct ath_wiphy *aphy = hw->priv;
38 struct ath_softc *sc = aphy->sc;
Luis R. Rodriguez15107182009-09-10 09:22:37 -070039 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Jouni Malinen8ca21f02009-03-03 19:23:27 +020040 struct ath9k_vif_iter_data iter_data;
Felix Fietkau31a01642010-09-14 18:37:19 +020041 int i;
Jouni Malinen8ca21f02009-03-03 19:23:27 +020042
43 /*
Felix Fietkau31a01642010-09-14 18:37:19 +020044 * Use the hardware MAC address as reference, the hardware uses it
45 * together with the BSSID mask when matching addresses.
Jouni Malinen8ca21f02009-03-03 19:23:27 +020046 */
Felix Fietkau31a01642010-09-14 18:37:19 +020047 iter_data.hw_macaddr = common->macaddr;
48 memset(&iter_data.mask, 0xff, ETH_ALEN);
49
50 if (vif)
51 ath9k_vif_iter(&iter_data, vif->addr, vif);
Jouni Malinen8ca21f02009-03-03 19:23:27 +020052
53 /* Get list of all active MAC addresses */
Jouni Malinenc52f33d2009-03-03 19:23:29 +020054 spin_lock_bh(&sc->wiphy_lock);
55 ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter,
Jouni Malinen8ca21f02009-03-03 19:23:27 +020056 &iter_data);
Jouni Malinenc52f33d2009-03-03 19:23:29 +020057 for (i = 0; i < sc->num_sec_wiphy; i++) {
58 if (sc->sec_wiphy[i] == NULL)
59 continue;
60 ieee80211_iterate_active_interfaces_atomic(
61 sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data);
62 }
63 spin_unlock_bh(&sc->wiphy_lock);
Jouni Malinen8ca21f02009-03-03 19:23:27 +020064
Felix Fietkau31a01642010-09-14 18:37:19 +020065 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
Luis R. Rodriguez13b81552009-09-10 17:52:45 -070066 ath_hw_setbssidmask(common);
Jouni Malinen8ca21f02009-03-03 19:23:27 +020067}
Jouni Malinenc52f33d2009-03-03 19:23:29 +020068
69int ath9k_wiphy_add(struct ath_softc *sc)
70{
71 int i, error;
72 struct ath_wiphy *aphy;
Luis R. Rodriguez15107182009-09-10 09:22:37 -070073 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Jouni Malinenc52f33d2009-03-03 19:23:29 +020074 struct ieee80211_hw *hw;
75 u8 addr[ETH_ALEN];
76
77 hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy), &ath9k_ops);
78 if (hw == NULL)
79 return -ENOMEM;
80
81 spin_lock_bh(&sc->wiphy_lock);
82 for (i = 0; i < sc->num_sec_wiphy; i++) {
83 if (sc->sec_wiphy[i] == NULL)
84 break;
85 }
86
87 if (i == sc->num_sec_wiphy) {
88 /* No empty slot available; increase array length */
89 struct ath_wiphy **n;
90 n = krealloc(sc->sec_wiphy,
91 (sc->num_sec_wiphy + 1) *
92 sizeof(struct ath_wiphy *),
93 GFP_ATOMIC);
94 if (n == NULL) {
95 spin_unlock_bh(&sc->wiphy_lock);
96 ieee80211_free_hw(hw);
97 return -ENOMEM;
98 }
99 n[i] = NULL;
100 sc->sec_wiphy = n;
101 sc->num_sec_wiphy++;
102 }
103
104 SET_IEEE80211_DEV(hw, sc->dev);
105
106 aphy = hw->priv;
107 aphy->sc = sc;
108 aphy->hw = hw;
109 sc->sec_wiphy[i] = aphy;
110 spin_unlock_bh(&sc->wiphy_lock);
111
Luis R. Rodriguez15107182009-09-10 09:22:37 -0700112 memcpy(addr, common->macaddr, ETH_ALEN);
Jouni Malinenc52f33d2009-03-03 19:23:29 +0200113 addr[0] |= 0x02; /* Locally managed address */
114 /*
115 * XOR virtual wiphy index into the least significant bits to generate
116 * a different MAC address for each virtual wiphy.
117 */
118 addr[5] ^= i & 0xff;
119 addr[4] ^= (i & 0xff00) >> 8;
120 addr[3] ^= (i & 0xff0000) >> 16;
121
122 SET_IEEE80211_PERM_ADDR(hw, addr);
123
Sujith285f2dd2010-01-08 10:36:07 +0530124 ath9k_set_hw_capab(sc, hw);
Jouni Malinenc52f33d2009-03-03 19:23:29 +0200125
126 error = ieee80211_register_hw(hw);
127
Jouni Malinenf98c3bd2009-03-03 19:23:39 +0200128 if (error == 0) {
129 /* Make sure wiphy scheduler is started (if enabled) */
130 ath9k_wiphy_set_scheduler(sc, sc->wiphy_scheduler_int);
131 }
132
Jouni Malinenc52f33d2009-03-03 19:23:29 +0200133 return error;
134}
135
136int ath9k_wiphy_del(struct ath_wiphy *aphy)
137{
138 struct ath_softc *sc = aphy->sc;
139 int i;
140
141 spin_lock_bh(&sc->wiphy_lock);
142 for (i = 0; i < sc->num_sec_wiphy; i++) {
143 if (aphy == sc->sec_wiphy[i]) {
144 sc->sec_wiphy[i] = NULL;
145 spin_unlock_bh(&sc->wiphy_lock);
146 ieee80211_unregister_hw(aphy->hw);
147 ieee80211_free_hw(aphy->hw);
148 return 0;
149 }
150 }
151 spin_unlock_bh(&sc->wiphy_lock);
152 return -ENOENT;
153}
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200154
155static int ath9k_send_nullfunc(struct ath_wiphy *aphy,
156 struct ieee80211_vif *vif, const u8 *bssid,
157 int ps)
158{
159 struct ath_softc *sc = aphy->sc;
160 struct ath_tx_control txctl;
161 struct sk_buff *skb;
162 struct ieee80211_hdr *hdr;
163 __le16 fc;
164 struct ieee80211_tx_info *info;
165
166 skb = dev_alloc_skb(24);
167 if (skb == NULL)
168 return -ENOMEM;
169 hdr = (struct ieee80211_hdr *) skb_put(skb, 24);
170 memset(hdr, 0, 24);
171 fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
172 IEEE80211_FCTL_TODS);
173 if (ps)
174 fc |= cpu_to_le16(IEEE80211_FCTL_PM);
175 hdr->frame_control = fc;
176 memcpy(hdr->addr1, bssid, ETH_ALEN);
177 memcpy(hdr->addr2, aphy->hw->wiphy->perm_addr, ETH_ALEN);
178 memcpy(hdr->addr3, bssid, ETH_ALEN);
179
180 info = IEEE80211_SKB_CB(skb);
181 memset(info, 0, sizeof(*info));
182 info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS;
183 info->control.vif = vif;
184 info->control.rates[0].idx = 0;
185 info->control.rates[0].count = 4;
186 info->control.rates[1].idx = -1;
187
188 memset(&txctl, 0, sizeof(struct ath_tx_control));
Felix Fietkau1d2231e2010-06-12 00:33:51 -0400189 txctl.txq = &sc->tx.txq[sc->tx.hwq_map[WME_AC_VO]];
Pavel Roskinc81494d2010-03-31 18:05:25 -0400190 txctl.frame_type = ps ? ATH9K_IFT_PAUSE : ATH9K_IFT_UNPAUSE;
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200191
192 if (ath_tx_start(aphy->hw, skb, &txctl) != 0)
193 goto exit;
194
195 return 0;
196exit:
197 dev_kfree_skb_any(skb);
198 return -1;
199}
200
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200201static bool __ath9k_wiphy_pausing(struct ath_softc *sc)
202{
203 int i;
204 if (sc->pri_wiphy->state == ATH_WIPHY_PAUSING)
205 return true;
206 for (i = 0; i < sc->num_sec_wiphy; i++) {
207 if (sc->sec_wiphy[i] &&
208 sc->sec_wiphy[i]->state == ATH_WIPHY_PAUSING)
209 return true;
210 }
211 return false;
212}
213
214static bool ath9k_wiphy_pausing(struct ath_softc *sc)
215{
216 bool ret;
217 spin_lock_bh(&sc->wiphy_lock);
218 ret = __ath9k_wiphy_pausing(sc);
219 spin_unlock_bh(&sc->wiphy_lock);
220 return ret;
221}
222
Jouni Malinen8089cc42009-03-03 19:23:38 +0200223static bool __ath9k_wiphy_scanning(struct ath_softc *sc)
224{
225 int i;
226 if (sc->pri_wiphy->state == ATH_WIPHY_SCAN)
227 return true;
228 for (i = 0; i < sc->num_sec_wiphy; i++) {
229 if (sc->sec_wiphy[i] &&
230 sc->sec_wiphy[i]->state == ATH_WIPHY_SCAN)
231 return true;
232 }
233 return false;
234}
235
236bool ath9k_wiphy_scanning(struct ath_softc *sc)
237{
238 bool ret;
239 spin_lock_bh(&sc->wiphy_lock);
240 ret = __ath9k_wiphy_scanning(sc);
241 spin_unlock_bh(&sc->wiphy_lock);
242 return ret;
243}
244
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200245static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy);
246
247/* caller must hold wiphy_lock */
248static void __ath9k_wiphy_unpause_ch(struct ath_wiphy *aphy)
249{
250 if (aphy == NULL)
251 return;
252 if (aphy->chan_idx != aphy->sc->chan_idx)
253 return; /* wiphy not on the selected channel */
254 __ath9k_wiphy_unpause(aphy);
255}
256
257static void ath9k_wiphy_unpause_channel(struct ath_softc *sc)
258{
259 int i;
260 spin_lock_bh(&sc->wiphy_lock);
261 __ath9k_wiphy_unpause_ch(sc->pri_wiphy);
262 for (i = 0; i < sc->num_sec_wiphy; i++)
263 __ath9k_wiphy_unpause_ch(sc->sec_wiphy[i]);
264 spin_unlock_bh(&sc->wiphy_lock);
265}
266
267void ath9k_wiphy_chan_work(struct work_struct *work)
268{
269 struct ath_softc *sc = container_of(work, struct ath_softc, chan_work);
Luis R. Rodriguez1bdf6c32009-10-28 13:39:40 -0700270 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200271 struct ath_wiphy *aphy = sc->next_wiphy;
272
273 if (aphy == NULL)
274 return;
275
276 /*
277 * All pending interfaces paused; ready to change
278 * channels.
279 */
280
281 /* Change channels */
282 mutex_lock(&sc->mutex);
283 /* XXX: remove me eventually */
284 ath9k_update_ichannel(sc, aphy->hw,
285 &sc->sc_ah->channels[sc->chan_idx]);
Luis R. Rodriguez1bdf6c32009-10-28 13:39:40 -0700286
287 /* sync hw configuration for hw code */
288 common->hw = aphy->hw;
289
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200290 ath_update_chainmask(sc, sc->chan_is_ht);
291 if (ath_set_channel(sc, aphy->hw,
292 &sc->sc_ah->channels[sc->chan_idx]) < 0) {
293 printk(KERN_DEBUG "ath9k: Failed to set channel for new "
294 "virtual wiphy\n");
295 mutex_unlock(&sc->mutex);
296 return;
297 }
298 mutex_unlock(&sc->mutex);
299
300 ath9k_wiphy_unpause_channel(sc);
301}
302
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200303/*
304 * ath9k version of ieee80211_tx_status() for TX frames that are generated
305 * internally in the driver.
306 */
307void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
308{
309 struct ath_wiphy *aphy = hw->priv;
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200310 struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200311
Felix Fietkau827e69b2009-11-15 23:09:25 +0100312 if ((tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_PAUSE) &&
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200313 aphy->state == ATH_WIPHY_PAUSING) {
Felix Fietkau827e69b2009-11-15 23:09:25 +0100314 if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) {
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200315 printk(KERN_DEBUG "ath9k: %s: no ACK for pause "
316 "frame\n", wiphy_name(hw->wiphy));
317 /*
318 * The AP did not reply; ignore this to allow us to
319 * continue.
320 */
321 }
322 aphy->state = ATH_WIPHY_PAUSED;
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200323 if (!ath9k_wiphy_pausing(aphy->sc)) {
324 /*
325 * Drop from tasklet to work to allow mutex for channel
326 * change.
327 */
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -0400328 ieee80211_queue_work(aphy->sc->hw,
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200329 &aphy->sc->chan_work);
330 }
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200331 }
332
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200333 dev_kfree_skb(skb);
334}
335
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200336static void ath9k_mark_paused(struct ath_wiphy *aphy)
337{
338 struct ath_softc *sc = aphy->sc;
339 aphy->state = ATH_WIPHY_PAUSED;
340 if (!__ath9k_wiphy_pausing(sc))
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -0400341 ieee80211_queue_work(sc->hw, &sc->chan_work);
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200342}
343
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200344static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
345{
346 struct ath_wiphy *aphy = data;
347 struct ath_vif *avp = (void *) vif->drv_priv;
348
349 switch (vif->type) {
350 case NL80211_IFTYPE_STATION:
351 if (!vif->bss_conf.assoc) {
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200352 ath9k_mark_paused(aphy);
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200353 break;
354 }
355 /* TODO: could avoid this if already in PS mode */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200356 if (ath9k_send_nullfunc(aphy, vif, avp->bssid, 1)) {
357 printk(KERN_DEBUG "%s: failed to send PS nullfunc\n",
358 __func__);
359 ath9k_mark_paused(aphy);
360 }
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200361 break;
362 case NL80211_IFTYPE_AP:
363 /* Beacon transmission is paused by aphy->state change */
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200364 ath9k_mark_paused(aphy);
Jouni Malinenf0ed85c2009-03-03 19:23:31 +0200365 break;
366 default:
367 break;
368 }
369}
370
371/* caller must hold wiphy_lock */
372static int __ath9k_wiphy_pause(struct ath_wiphy *aphy)
373{
374 ieee80211_stop_queues(aphy->hw);
375 aphy->state = ATH_WIPHY_PAUSING;
376 /*
377 * TODO: handle PAUSING->PAUSED for the case where there are multiple
378 * active vifs (now we do it on the first vif getting ready; should be
379 * on the last)
380 */
381 ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter,
382 aphy);
383 return 0;
384}
385
386int ath9k_wiphy_pause(struct ath_wiphy *aphy)
387{
388 int ret;
389 spin_lock_bh(&aphy->sc->wiphy_lock);
390 ret = __ath9k_wiphy_pause(aphy);
391 spin_unlock_bh(&aphy->sc->wiphy_lock);
392 return ret;
393}
394
395static void ath9k_unpause_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
396{
397 struct ath_wiphy *aphy = data;
398 struct ath_vif *avp = (void *) vif->drv_priv;
399
400 switch (vif->type) {
401 case NL80211_IFTYPE_STATION:
402 if (!vif->bss_conf.assoc)
403 break;
404 ath9k_send_nullfunc(aphy, vif, avp->bssid, 0);
405 break;
406 case NL80211_IFTYPE_AP:
407 /* Beacon transmission is re-enabled by aphy->state change */
408 break;
409 default:
410 break;
411 }
412}
413
414/* caller must hold wiphy_lock */
415static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy)
416{
417 ieee80211_iterate_active_interfaces_atomic(aphy->hw,
418 ath9k_unpause_iter, aphy);
419 aphy->state = ATH_WIPHY_ACTIVE;
420 ieee80211_wake_queues(aphy->hw);
421 return 0;
422}
423
424int ath9k_wiphy_unpause(struct ath_wiphy *aphy)
425{
426 int ret;
427 spin_lock_bh(&aphy->sc->wiphy_lock);
428 ret = __ath9k_wiphy_unpause(aphy);
429 spin_unlock_bh(&aphy->sc->wiphy_lock);
430 return ret;
431}
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200432
Jouni Malinen7ec3e512009-03-03 19:23:37 +0200433static void __ath9k_wiphy_mark_all_paused(struct ath_softc *sc)
434{
435 int i;
436 if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE)
437 sc->pri_wiphy->state = ATH_WIPHY_PAUSED;
438 for (i = 0; i < sc->num_sec_wiphy; i++) {
439 if (sc->sec_wiphy[i] &&
440 sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE)
441 sc->sec_wiphy[i]->state = ATH_WIPHY_PAUSED;
442 }
443}
444
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200445/* caller must hold wiphy_lock */
446static void __ath9k_wiphy_pause_all(struct ath_softc *sc)
447{
448 int i;
449 if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE)
450 __ath9k_wiphy_pause(sc->pri_wiphy);
451 for (i = 0; i < sc->num_sec_wiphy; i++) {
452 if (sc->sec_wiphy[i] &&
453 sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE)
454 __ath9k_wiphy_pause(sc->sec_wiphy[i]);
455 }
456}
457
458int ath9k_wiphy_select(struct ath_wiphy *aphy)
459{
460 struct ath_softc *sc = aphy->sc;
461 bool now;
462
463 spin_lock_bh(&sc->wiphy_lock);
Jouni Malinen8089cc42009-03-03 19:23:38 +0200464 if (__ath9k_wiphy_scanning(sc)) {
465 /*
466 * For now, we are using mac80211 sw scan and it expects to
467 * have full control over channel changes, so avoid wiphy
468 * scheduling during a scan. This could be optimized if the
469 * scanning control were moved into the driver.
470 */
471 spin_unlock_bh(&sc->wiphy_lock);
472 return -EBUSY;
473 }
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200474 if (__ath9k_wiphy_pausing(sc)) {
Jouni Malinen7ec3e512009-03-03 19:23:37 +0200475 if (sc->wiphy_select_failures == 0)
476 sc->wiphy_select_first_fail = jiffies;
477 sc->wiphy_select_failures++;
478 if (time_after(jiffies, sc->wiphy_select_first_fail + HZ / 2))
479 {
480 printk(KERN_DEBUG "ath9k: Previous wiphy select timed "
481 "out; disable/enable hw to recover\n");
482 __ath9k_wiphy_mark_all_paused(sc);
483 /*
484 * TODO: this workaround to fix hardware is unlikely to
485 * be specific to virtual wiphy changes. It can happen
486 * on normal channel change, too, and as such, this
487 * should really be made more generic. For example,
488 * tricker radio disable/enable on GTT interrupt burst
489 * (say, 10 GTT interrupts received without any TX
490 * frame being completed)
491 */
492 spin_unlock_bh(&sc->wiphy_lock);
Luis R. Rodriguez68a89112009-11-02 14:35:42 -0800493 ath_radio_disable(sc, aphy->hw);
494 ath_radio_enable(sc, aphy->hw);
495 /* Only the primary wiphy hw is used for queuing work */
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -0400496 ieee80211_queue_work(aphy->sc->hw,
Jouni Malinen7ec3e512009-03-03 19:23:37 +0200497 &aphy->sc->chan_work);
498 return -EBUSY; /* previous select still in progress */
499 }
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200500 spin_unlock_bh(&sc->wiphy_lock);
501 return -EBUSY; /* previous select still in progress */
502 }
Jouni Malinen7ec3e512009-03-03 19:23:37 +0200503 sc->wiphy_select_failures = 0;
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200504
505 /* Store the new channel */
506 sc->chan_idx = aphy->chan_idx;
507 sc->chan_is_ht = aphy->chan_is_ht;
508 sc->next_wiphy = aphy;
509
510 __ath9k_wiphy_pause_all(sc);
511 now = !__ath9k_wiphy_pausing(aphy->sc);
512 spin_unlock_bh(&sc->wiphy_lock);
513
514 if (now) {
515 /* Ready to request channel change immediately */
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -0400516 ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work);
Jouni Malinen0e2dedf2009-03-03 19:23:32 +0200517 }
518
519 /*
520 * wiphys will be unpaused in ath9k_tx_status() once channel has been
521 * changed if any wiphy needs time to become paused.
522 */
523
524 return 0;
525}
Jouni Malinen9580a222009-03-03 19:23:33 +0200526
527bool ath9k_wiphy_started(struct ath_softc *sc)
528{
529 int i;
530 spin_lock_bh(&sc->wiphy_lock);
531 if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) {
532 spin_unlock_bh(&sc->wiphy_lock);
533 return true;
534 }
535 for (i = 0; i < sc->num_sec_wiphy; i++) {
536 if (sc->sec_wiphy[i] &&
537 sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) {
538 spin_unlock_bh(&sc->wiphy_lock);
539 return true;
540 }
541 }
542 spin_unlock_bh(&sc->wiphy_lock);
543 return false;
544}
Jouni Malinen18eb62f2009-03-03 19:23:35 +0200545
546static void ath9k_wiphy_pause_chan(struct ath_wiphy *aphy,
547 struct ath_wiphy *selected)
548{
Jouni Malinen8089cc42009-03-03 19:23:38 +0200549 if (selected->state == ATH_WIPHY_SCAN) {
550 if (aphy == selected)
551 return;
552 /*
553 * Pause all other wiphys for the duration of the scan even if
554 * they are on the current channel now.
555 */
556 } else if (aphy->chan_idx == selected->chan_idx)
Jouni Malinen18eb62f2009-03-03 19:23:35 +0200557 return;
558 aphy->state = ATH_WIPHY_PAUSED;
559 ieee80211_stop_queues(aphy->hw);
560}
561
562void ath9k_wiphy_pause_all_forced(struct ath_softc *sc,
563 struct ath_wiphy *selected)
564{
565 int i;
566 spin_lock_bh(&sc->wiphy_lock);
567 if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE)
568 ath9k_wiphy_pause_chan(sc->pri_wiphy, selected);
569 for (i = 0; i < sc->num_sec_wiphy; i++) {
570 if (sc->sec_wiphy[i] &&
571 sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE)
572 ath9k_wiphy_pause_chan(sc->sec_wiphy[i], selected);
573 }
574 spin_unlock_bh(&sc->wiphy_lock);
575}
Jouni Malinenf98c3bd2009-03-03 19:23:39 +0200576
577void ath9k_wiphy_work(struct work_struct *work)
578{
579 struct ath_softc *sc = container_of(work, struct ath_softc,
580 wiphy_work.work);
581 struct ath_wiphy *aphy = NULL;
582 bool first = true;
583
584 spin_lock_bh(&sc->wiphy_lock);
585
586 if (sc->wiphy_scheduler_int == 0) {
587 /* wiphy scheduler is disabled */
588 spin_unlock_bh(&sc->wiphy_lock);
589 return;
590 }
591
592try_again:
593 sc->wiphy_scheduler_index++;
594 while (sc->wiphy_scheduler_index <= sc->num_sec_wiphy) {
595 aphy = sc->sec_wiphy[sc->wiphy_scheduler_index - 1];
596 if (aphy && aphy->state != ATH_WIPHY_INACTIVE)
597 break;
598
599 sc->wiphy_scheduler_index++;
600 aphy = NULL;
601 }
602 if (aphy == NULL) {
603 sc->wiphy_scheduler_index = 0;
604 if (sc->pri_wiphy->state == ATH_WIPHY_INACTIVE) {
605 if (first) {
606 first = false;
607 goto try_again;
608 }
609 /* No wiphy is ready to be scheduled */
610 } else
611 aphy = sc->pri_wiphy;
612 }
613
614 spin_unlock_bh(&sc->wiphy_lock);
615
616 if (aphy &&
617 aphy->state != ATH_WIPHY_ACTIVE && aphy->state != ATH_WIPHY_SCAN &&
618 ath9k_wiphy_select(aphy)) {
619 printk(KERN_DEBUG "ath9k: Failed to schedule virtual wiphy "
620 "change\n");
621 }
622
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -0400623 ieee80211_queue_delayed_work(sc->hw,
624 &sc->wiphy_work,
625 sc->wiphy_scheduler_int);
Jouni Malinenf98c3bd2009-03-03 19:23:39 +0200626}
627
628void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int)
629{
630 cancel_delayed_work_sync(&sc->wiphy_work);
631 sc->wiphy_scheduler_int = msecs_to_jiffies(msec_int);
632 if (sc->wiphy_scheduler_int)
Luis R. Rodriguez42935ec2009-07-29 20:08:07 -0400633 ieee80211_queue_delayed_work(sc->hw, &sc->wiphy_work,
634 sc->wiphy_scheduler_int);
Jouni Malinenf98c3bd2009-03-03 19:23:39 +0200635}
Luis R. Rodriguez64839172009-07-14 20:22:53 -0400636
637/* caller must hold wiphy_lock */
638bool ath9k_all_wiphys_idle(struct ath_softc *sc)
639{
640 unsigned int i;
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -0700641 if (!sc->pri_wiphy->idle)
Luis R. Rodriguez64839172009-07-14 20:22:53 -0400642 return false;
Luis R. Rodriguez64839172009-07-14 20:22:53 -0400643 for (i = 0; i < sc->num_sec_wiphy; i++) {
644 struct ath_wiphy *aphy = sc->sec_wiphy[i];
645 if (!aphy)
646 continue;
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -0700647 if (!aphy->idle)
Luis R. Rodriguez64839172009-07-14 20:22:53 -0400648 return false;
649 }
650 return true;
651}
Luis R. Rodriguez194b7c12009-10-29 10:41:15 -0700652
653/* caller must hold wiphy_lock */
654void ath9k_set_wiphy_idle(struct ath_wiphy *aphy, bool idle)
655{
656 struct ath_softc *sc = aphy->sc;
657
658 aphy->idle = idle;
659 ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_CONFIG,
660 "Marking %s as %s\n",
661 wiphy_name(aphy->hw->wiphy),
662 idle ? "idle" : "not-idle");
663}
Luis R. Rodriguezf52de032009-11-02 17:09:12 -0800664/* Only bother starting a queue on an active virtual wiphy */
Vasanthakumar Thiagarajan68e8f2f2010-07-22 02:24:11 -0700665bool ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue)
Luis R. Rodriguezf52de032009-11-02 17:09:12 -0800666{
667 struct ieee80211_hw *hw = sc->pri_wiphy->hw;
668 unsigned int i;
Vasanthakumar Thiagarajan68e8f2f2010-07-22 02:24:11 -0700669 bool txq_started = false;
Luis R. Rodriguezf52de032009-11-02 17:09:12 -0800670
671 spin_lock_bh(&sc->wiphy_lock);
672
673 /* Start the primary wiphy */
674 if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) {
675 ieee80211_wake_queue(hw, skb_queue);
Vasanthakumar Thiagarajan68e8f2f2010-07-22 02:24:11 -0700676 txq_started = true;
Luis R. Rodriguezf52de032009-11-02 17:09:12 -0800677 goto unlock;
678 }
679
680 /* Now start the secondary wiphy queues */
681 for (i = 0; i < sc->num_sec_wiphy; i++) {
682 struct ath_wiphy *aphy = sc->sec_wiphy[i];
683 if (!aphy)
684 continue;
685 if (aphy->state != ATH_WIPHY_ACTIVE)
686 continue;
687
688 hw = aphy->hw;
689 ieee80211_wake_queue(hw, skb_queue);
Vasanthakumar Thiagarajan68e8f2f2010-07-22 02:24:11 -0700690 txq_started = true;
Luis R. Rodriguezf52de032009-11-02 17:09:12 -0800691 break;
692 }
693
694unlock:
695 spin_unlock_bh(&sc->wiphy_lock);
Vasanthakumar Thiagarajan68e8f2f2010-07-22 02:24:11 -0700696 return txq_started;
Luis R. Rodriguezf52de032009-11-02 17:09:12 -0800697}
698
699/* Go ahead and propagate information to all virtual wiphys, it won't hurt */
700void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue)
701{
702 struct ieee80211_hw *hw = sc->pri_wiphy->hw;
703 unsigned int i;
704
705 spin_lock_bh(&sc->wiphy_lock);
706
707 /* Stop the primary wiphy */
708 ieee80211_stop_queue(hw, skb_queue);
709
710 /* Now stop the secondary wiphy queues */
711 for (i = 0; i < sc->num_sec_wiphy; i++) {
712 struct ath_wiphy *aphy = sc->sec_wiphy[i];
713 if (!aphy)
714 continue;
715 hw = aphy->hw;
716 ieee80211_stop_queue(hw, skb_queue);
717 }
718 spin_unlock_bh(&sc->wiphy_lock);
719}