]>
Commit | Line | Data |
---|---|---|
8ca21f01 JM |
1 | /* |
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 | ||
17 | #include "ath9k.h" | |
18 | ||
19 | struct ath9k_vif_iter_data { | |
20 | int count; | |
21 | u8 *addr; | |
22 | }; | |
23 | ||
24 | static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | |
25 | { | |
26 | struct ath9k_vif_iter_data *iter_data = data; | |
27 | u8 *nbuf; | |
28 | ||
29 | nbuf = krealloc(iter_data->addr, (iter_data->count + 1) * ETH_ALEN, | |
30 | GFP_ATOMIC); | |
31 | if (nbuf == NULL) | |
32 | return; | |
33 | ||
34 | memcpy(nbuf + iter_data->count * ETH_ALEN, mac, ETH_ALEN); | |
35 | iter_data->addr = nbuf; | |
36 | iter_data->count++; | |
37 | } | |
38 | ||
39 | void ath9k_set_bssid_mask(struct ieee80211_hw *hw) | |
40 | { | |
bce048d7 JM |
41 | struct ath_wiphy *aphy = hw->priv; |
42 | struct ath_softc *sc = aphy->sc; | |
8ca21f01 JM |
43 | struct ath9k_vif_iter_data iter_data; |
44 | int i, j; | |
45 | u8 mask[ETH_ALEN]; | |
46 | ||
47 | /* | |
48 | * Add primary MAC address even if it is not in active use since it | |
49 | * will be configured to the hardware as the starting point and the | |
50 | * BSSID mask will need to be changed if another address is active. | |
51 | */ | |
52 | iter_data.addr = kmalloc(ETH_ALEN, GFP_ATOMIC); | |
53 | if (iter_data.addr) { | |
54 | memcpy(iter_data.addr, sc->sc_ah->macaddr, ETH_ALEN); | |
55 | iter_data.count = 1; | |
56 | } else | |
57 | iter_data.count = 0; | |
58 | ||
59 | /* Get list of all active MAC addresses */ | |
c52f33d0 JM |
60 | spin_lock_bh(&sc->wiphy_lock); |
61 | ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter, | |
8ca21f01 | 62 | &iter_data); |
c52f33d0 JM |
63 | for (i = 0; i < sc->num_sec_wiphy; i++) { |
64 | if (sc->sec_wiphy[i] == NULL) | |
65 | continue; | |
66 | ieee80211_iterate_active_interfaces_atomic( | |
67 | sc->sec_wiphy[i]->hw, ath9k_vif_iter, &iter_data); | |
68 | } | |
69 | spin_unlock_bh(&sc->wiphy_lock); | |
8ca21f01 JM |
70 | |
71 | /* Generate an address mask to cover all active addresses */ | |
72 | memset(mask, 0, ETH_ALEN); | |
73 | for (i = 0; i < iter_data.count; i++) { | |
74 | u8 *a1 = iter_data.addr + i * ETH_ALEN; | |
75 | for (j = i + 1; j < iter_data.count; j++) { | |
76 | u8 *a2 = iter_data.addr + j * ETH_ALEN; | |
77 | mask[0] |= a1[0] ^ a2[0]; | |
78 | mask[1] |= a1[1] ^ a2[1]; | |
79 | mask[2] |= a1[2] ^ a2[2]; | |
80 | mask[3] |= a1[3] ^ a2[3]; | |
81 | mask[4] |= a1[4] ^ a2[4]; | |
82 | mask[5] |= a1[5] ^ a2[5]; | |
83 | } | |
84 | } | |
85 | ||
86 | kfree(iter_data.addr); | |
87 | ||
88 | /* Invert the mask and configure hardware */ | |
89 | sc->bssidmask[0] = ~mask[0]; | |
90 | sc->bssidmask[1] = ~mask[1]; | |
91 | sc->bssidmask[2] = ~mask[2]; | |
92 | sc->bssidmask[3] = ~mask[3]; | |
93 | sc->bssidmask[4] = ~mask[4]; | |
94 | sc->bssidmask[5] = ~mask[5]; | |
95 | ||
96 | ath9k_hw_setbssidmask(sc); | |
97 | } | |
c52f33d0 JM |
98 | |
99 | int ath9k_wiphy_add(struct ath_softc *sc) | |
100 | { | |
101 | int i, error; | |
102 | struct ath_wiphy *aphy; | |
103 | struct ieee80211_hw *hw; | |
104 | u8 addr[ETH_ALEN]; | |
105 | ||
106 | hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy), &ath9k_ops); | |
107 | if (hw == NULL) | |
108 | return -ENOMEM; | |
109 | ||
110 | spin_lock_bh(&sc->wiphy_lock); | |
111 | for (i = 0; i < sc->num_sec_wiphy; i++) { | |
112 | if (sc->sec_wiphy[i] == NULL) | |
113 | break; | |
114 | } | |
115 | ||
116 | if (i == sc->num_sec_wiphy) { | |
117 | /* No empty slot available; increase array length */ | |
118 | struct ath_wiphy **n; | |
119 | n = krealloc(sc->sec_wiphy, | |
120 | (sc->num_sec_wiphy + 1) * | |
121 | sizeof(struct ath_wiphy *), | |
122 | GFP_ATOMIC); | |
123 | if (n == NULL) { | |
124 | spin_unlock_bh(&sc->wiphy_lock); | |
125 | ieee80211_free_hw(hw); | |
126 | return -ENOMEM; | |
127 | } | |
128 | n[i] = NULL; | |
129 | sc->sec_wiphy = n; | |
130 | sc->num_sec_wiphy++; | |
131 | } | |
132 | ||
133 | SET_IEEE80211_DEV(hw, sc->dev); | |
134 | ||
135 | aphy = hw->priv; | |
136 | aphy->sc = sc; | |
137 | aphy->hw = hw; | |
138 | sc->sec_wiphy[i] = aphy; | |
139 | spin_unlock_bh(&sc->wiphy_lock); | |
140 | ||
141 | memcpy(addr, sc->sc_ah->macaddr, ETH_ALEN); | |
142 | addr[0] |= 0x02; /* Locally managed address */ | |
143 | /* | |
144 | * XOR virtual wiphy index into the least significant bits to generate | |
145 | * a different MAC address for each virtual wiphy. | |
146 | */ | |
147 | addr[5] ^= i & 0xff; | |
148 | addr[4] ^= (i & 0xff00) >> 8; | |
149 | addr[3] ^= (i & 0xff0000) >> 16; | |
150 | ||
151 | SET_IEEE80211_PERM_ADDR(hw, addr); | |
152 | ||
153 | ath_set_hw_capab(sc, hw); | |
154 | ||
155 | error = ieee80211_register_hw(hw); | |
156 | ||
157 | return error; | |
158 | } | |
159 | ||
160 | int ath9k_wiphy_del(struct ath_wiphy *aphy) | |
161 | { | |
162 | struct ath_softc *sc = aphy->sc; | |
163 | int i; | |
164 | ||
165 | spin_lock_bh(&sc->wiphy_lock); | |
166 | for (i = 0; i < sc->num_sec_wiphy; i++) { | |
167 | if (aphy == sc->sec_wiphy[i]) { | |
168 | sc->sec_wiphy[i] = NULL; | |
169 | spin_unlock_bh(&sc->wiphy_lock); | |
170 | ieee80211_unregister_hw(aphy->hw); | |
171 | ieee80211_free_hw(aphy->hw); | |
172 | return 0; | |
173 | } | |
174 | } | |
175 | spin_unlock_bh(&sc->wiphy_lock); | |
176 | return -ENOENT; | |
177 | } | |
f0ed85c6 JM |
178 | |
179 | static int ath9k_send_nullfunc(struct ath_wiphy *aphy, | |
180 | struct ieee80211_vif *vif, const u8 *bssid, | |
181 | int ps) | |
182 | { | |
183 | struct ath_softc *sc = aphy->sc; | |
184 | struct ath_tx_control txctl; | |
185 | struct sk_buff *skb; | |
186 | struct ieee80211_hdr *hdr; | |
187 | __le16 fc; | |
188 | struct ieee80211_tx_info *info; | |
189 | ||
190 | skb = dev_alloc_skb(24); | |
191 | if (skb == NULL) | |
192 | return -ENOMEM; | |
193 | hdr = (struct ieee80211_hdr *) skb_put(skb, 24); | |
194 | memset(hdr, 0, 24); | |
195 | fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | | |
196 | IEEE80211_FCTL_TODS); | |
197 | if (ps) | |
198 | fc |= cpu_to_le16(IEEE80211_FCTL_PM); | |
199 | hdr->frame_control = fc; | |
200 | memcpy(hdr->addr1, bssid, ETH_ALEN); | |
201 | memcpy(hdr->addr2, aphy->hw->wiphy->perm_addr, ETH_ALEN); | |
202 | memcpy(hdr->addr3, bssid, ETH_ALEN); | |
203 | ||
204 | info = IEEE80211_SKB_CB(skb); | |
205 | memset(info, 0, sizeof(*info)); | |
206 | info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS; | |
207 | info->control.vif = vif; | |
208 | info->control.rates[0].idx = 0; | |
209 | info->control.rates[0].count = 4; | |
210 | info->control.rates[1].idx = -1; | |
211 | ||
212 | memset(&txctl, 0, sizeof(struct ath_tx_control)); | |
213 | txctl.txq = &sc->tx.txq[sc->tx.hwq_map[ATH9K_WME_AC_VO]]; | |
214 | txctl.frame_type = ps ? ATH9K_INT_PAUSE : ATH9K_INT_UNPAUSE; | |
215 | ||
216 | if (ath_tx_start(aphy->hw, skb, &txctl) != 0) | |
217 | goto exit; | |
218 | ||
219 | return 0; | |
220 | exit: | |
221 | dev_kfree_skb_any(skb); | |
222 | return -1; | |
223 | } | |
224 | ||
0e2dedf9 JM |
225 | static bool __ath9k_wiphy_pausing(struct ath_softc *sc) |
226 | { | |
227 | int i; | |
228 | if (sc->pri_wiphy->state == ATH_WIPHY_PAUSING) | |
229 | return true; | |
230 | for (i = 0; i < sc->num_sec_wiphy; i++) { | |
231 | if (sc->sec_wiphy[i] && | |
232 | sc->sec_wiphy[i]->state == ATH_WIPHY_PAUSING) | |
233 | return true; | |
234 | } | |
235 | return false; | |
236 | } | |
237 | ||
238 | static bool ath9k_wiphy_pausing(struct ath_softc *sc) | |
239 | { | |
240 | bool ret; | |
241 | spin_lock_bh(&sc->wiphy_lock); | |
242 | ret = __ath9k_wiphy_pausing(sc); | |
243 | spin_unlock_bh(&sc->wiphy_lock); | |
244 | return ret; | |
245 | } | |
246 | ||
247 | static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy); | |
248 | ||
249 | /* caller must hold wiphy_lock */ | |
250 | static void __ath9k_wiphy_unpause_ch(struct ath_wiphy *aphy) | |
251 | { | |
252 | if (aphy == NULL) | |
253 | return; | |
254 | if (aphy->chan_idx != aphy->sc->chan_idx) | |
255 | return; /* wiphy not on the selected channel */ | |
256 | __ath9k_wiphy_unpause(aphy); | |
257 | } | |
258 | ||
259 | static void ath9k_wiphy_unpause_channel(struct ath_softc *sc) | |
260 | { | |
261 | int i; | |
262 | spin_lock_bh(&sc->wiphy_lock); | |
263 | __ath9k_wiphy_unpause_ch(sc->pri_wiphy); | |
264 | for (i = 0; i < sc->num_sec_wiphy; i++) | |
265 | __ath9k_wiphy_unpause_ch(sc->sec_wiphy[i]); | |
266 | spin_unlock_bh(&sc->wiphy_lock); | |
267 | } | |
268 | ||
269 | void ath9k_wiphy_chan_work(struct work_struct *work) | |
270 | { | |
271 | struct ath_softc *sc = container_of(work, struct ath_softc, chan_work); | |
272 | struct ath_wiphy *aphy = sc->next_wiphy; | |
273 | ||
274 | if (aphy == NULL) | |
275 | return; | |
276 | ||
277 | /* | |
278 | * All pending interfaces paused; ready to change | |
279 | * channels. | |
280 | */ | |
281 | ||
282 | /* Change channels */ | |
283 | mutex_lock(&sc->mutex); | |
284 | /* XXX: remove me eventually */ | |
285 | ath9k_update_ichannel(sc, aphy->hw, | |
286 | &sc->sc_ah->channels[sc->chan_idx]); | |
287 | ath_update_chainmask(sc, sc->chan_is_ht); | |
288 | if (ath_set_channel(sc, aphy->hw, | |
289 | &sc->sc_ah->channels[sc->chan_idx]) < 0) { | |
290 | printk(KERN_DEBUG "ath9k: Failed to set channel for new " | |
291 | "virtual wiphy\n"); | |
292 | mutex_unlock(&sc->mutex); | |
293 | return; | |
294 | } | |
295 | mutex_unlock(&sc->mutex); | |
296 | ||
297 | ath9k_wiphy_unpause_channel(sc); | |
298 | } | |
299 | ||
f0ed85c6 JM |
300 | /* |
301 | * ath9k version of ieee80211_tx_status() for TX frames that are generated | |
302 | * internally in the driver. | |
303 | */ | |
304 | void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |
305 | { | |
306 | struct ath_wiphy *aphy = hw->priv; | |
307 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | |
308 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | |
309 | struct ath_tx_info_priv *tx_info_priv = ATH_TX_INFO_PRIV(tx_info); | |
310 | ||
311 | if (tx_info_priv && tx_info_priv->frame_type == ATH9K_INT_PAUSE && | |
312 | aphy->state == ATH_WIPHY_PAUSING) { | |
313 | if (!(info->flags & IEEE80211_TX_STAT_ACK)) { | |
314 | printk(KERN_DEBUG "ath9k: %s: no ACK for pause " | |
315 | "frame\n", wiphy_name(hw->wiphy)); | |
316 | /* | |
317 | * The AP did not reply; ignore this to allow us to | |
318 | * continue. | |
319 | */ | |
320 | } | |
321 | aphy->state = ATH_WIPHY_PAUSED; | |
0e2dedf9 JM |
322 | if (!ath9k_wiphy_pausing(aphy->sc)) { |
323 | /* | |
324 | * Drop from tasklet to work to allow mutex for channel | |
325 | * change. | |
326 | */ | |
327 | queue_work(aphy->sc->hw->workqueue, | |
328 | &aphy->sc->chan_work); | |
329 | } | |
f0ed85c6 JM |
330 | } |
331 | ||
332 | kfree(tx_info_priv); | |
333 | tx_info->rate_driver_data[0] = NULL; | |
334 | ||
335 | dev_kfree_skb(skb); | |
336 | } | |
337 | ||
0e2dedf9 JM |
338 | static void ath9k_mark_paused(struct ath_wiphy *aphy) |
339 | { | |
340 | struct ath_softc *sc = aphy->sc; | |
341 | aphy->state = ATH_WIPHY_PAUSED; | |
342 | if (!__ath9k_wiphy_pausing(sc)) | |
343 | queue_work(sc->hw->workqueue, &sc->chan_work); | |
344 | } | |
345 | ||
f0ed85c6 JM |
346 | static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) |
347 | { | |
348 | struct ath_wiphy *aphy = data; | |
349 | struct ath_vif *avp = (void *) vif->drv_priv; | |
350 | ||
351 | switch (vif->type) { | |
352 | case NL80211_IFTYPE_STATION: | |
353 | if (!vif->bss_conf.assoc) { | |
0e2dedf9 | 354 | ath9k_mark_paused(aphy); |
f0ed85c6 JM |
355 | break; |
356 | } | |
357 | /* TODO: could avoid this if already in PS mode */ | |
0e2dedf9 JM |
358 | if (ath9k_send_nullfunc(aphy, vif, avp->bssid, 1)) { |
359 | printk(KERN_DEBUG "%s: failed to send PS nullfunc\n", | |
360 | __func__); | |
361 | ath9k_mark_paused(aphy); | |
362 | } | |
f0ed85c6 JM |
363 | break; |
364 | case NL80211_IFTYPE_AP: | |
365 | /* Beacon transmission is paused by aphy->state change */ | |
0e2dedf9 | 366 | ath9k_mark_paused(aphy); |
f0ed85c6 JM |
367 | break; |
368 | default: | |
369 | break; | |
370 | } | |
371 | } | |
372 | ||
373 | /* caller must hold wiphy_lock */ | |
374 | static int __ath9k_wiphy_pause(struct ath_wiphy *aphy) | |
375 | { | |
376 | ieee80211_stop_queues(aphy->hw); | |
377 | aphy->state = ATH_WIPHY_PAUSING; | |
378 | /* | |
379 | * TODO: handle PAUSING->PAUSED for the case where there are multiple | |
380 | * active vifs (now we do it on the first vif getting ready; should be | |
381 | * on the last) | |
382 | */ | |
383 | ieee80211_iterate_active_interfaces_atomic(aphy->hw, ath9k_pause_iter, | |
384 | aphy); | |
385 | return 0; | |
386 | } | |
387 | ||
388 | int ath9k_wiphy_pause(struct ath_wiphy *aphy) | |
389 | { | |
390 | int ret; | |
391 | spin_lock_bh(&aphy->sc->wiphy_lock); | |
392 | ret = __ath9k_wiphy_pause(aphy); | |
393 | spin_unlock_bh(&aphy->sc->wiphy_lock); | |
394 | return ret; | |
395 | } | |
396 | ||
397 | static void ath9k_unpause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | |
398 | { | |
399 | struct ath_wiphy *aphy = data; | |
400 | struct ath_vif *avp = (void *) vif->drv_priv; | |
401 | ||
402 | switch (vif->type) { | |
403 | case NL80211_IFTYPE_STATION: | |
404 | if (!vif->bss_conf.assoc) | |
405 | break; | |
406 | ath9k_send_nullfunc(aphy, vif, avp->bssid, 0); | |
407 | break; | |
408 | case NL80211_IFTYPE_AP: | |
409 | /* Beacon transmission is re-enabled by aphy->state change */ | |
410 | break; | |
411 | default: | |
412 | break; | |
413 | } | |
414 | } | |
415 | ||
416 | /* caller must hold wiphy_lock */ | |
417 | static int __ath9k_wiphy_unpause(struct ath_wiphy *aphy) | |
418 | { | |
419 | ieee80211_iterate_active_interfaces_atomic(aphy->hw, | |
420 | ath9k_unpause_iter, aphy); | |
421 | aphy->state = ATH_WIPHY_ACTIVE; | |
422 | ieee80211_wake_queues(aphy->hw); | |
423 | return 0; | |
424 | } | |
425 | ||
426 | int ath9k_wiphy_unpause(struct ath_wiphy *aphy) | |
427 | { | |
428 | int ret; | |
429 | spin_lock_bh(&aphy->sc->wiphy_lock); | |
430 | ret = __ath9k_wiphy_unpause(aphy); | |
431 | spin_unlock_bh(&aphy->sc->wiphy_lock); | |
432 | return ret; | |
433 | } | |
0e2dedf9 | 434 | |
7ec3e514 JM |
435 | static void __ath9k_wiphy_mark_all_paused(struct ath_softc *sc) |
436 | { | |
437 | int i; | |
438 | if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) | |
439 | sc->pri_wiphy->state = ATH_WIPHY_PAUSED; | |
440 | for (i = 0; i < sc->num_sec_wiphy; i++) { | |
441 | if (sc->sec_wiphy[i] && | |
442 | sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) | |
443 | sc->sec_wiphy[i]->state = ATH_WIPHY_PAUSED; | |
444 | } | |
445 | } | |
446 | ||
0e2dedf9 JM |
447 | /* caller must hold wiphy_lock */ |
448 | static void __ath9k_wiphy_pause_all(struct ath_softc *sc) | |
449 | { | |
450 | int i; | |
451 | if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) | |
452 | __ath9k_wiphy_pause(sc->pri_wiphy); | |
453 | for (i = 0; i < sc->num_sec_wiphy; i++) { | |
454 | if (sc->sec_wiphy[i] && | |
455 | sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE) | |
456 | __ath9k_wiphy_pause(sc->sec_wiphy[i]); | |
457 | } | |
458 | } | |
459 | ||
460 | int ath9k_wiphy_select(struct ath_wiphy *aphy) | |
461 | { | |
462 | struct ath_softc *sc = aphy->sc; | |
463 | bool now; | |
464 | ||
465 | spin_lock_bh(&sc->wiphy_lock); | |
466 | if (__ath9k_wiphy_pausing(sc)) { | |
7ec3e514 JM |
467 | if (sc->wiphy_select_failures == 0) |
468 | sc->wiphy_select_first_fail = jiffies; | |
469 | sc->wiphy_select_failures++; | |
470 | if (time_after(jiffies, sc->wiphy_select_first_fail + HZ / 2)) | |
471 | { | |
472 | printk(KERN_DEBUG "ath9k: Previous wiphy select timed " | |
473 | "out; disable/enable hw to recover\n"); | |
474 | __ath9k_wiphy_mark_all_paused(sc); | |
475 | /* | |
476 | * TODO: this workaround to fix hardware is unlikely to | |
477 | * be specific to virtual wiphy changes. It can happen | |
478 | * on normal channel change, too, and as such, this | |
479 | * should really be made more generic. For example, | |
480 | * tricker radio disable/enable on GTT interrupt burst | |
481 | * (say, 10 GTT interrupts received without any TX | |
482 | * frame being completed) | |
483 | */ | |
484 | spin_unlock_bh(&sc->wiphy_lock); | |
485 | ath_radio_disable(sc); | |
486 | ath_radio_enable(sc); | |
487 | queue_work(aphy->sc->hw->workqueue, | |
488 | &aphy->sc->chan_work); | |
489 | return -EBUSY; /* previous select still in progress */ | |
490 | } | |
0e2dedf9 JM |
491 | spin_unlock_bh(&sc->wiphy_lock); |
492 | return -EBUSY; /* previous select still in progress */ | |
493 | } | |
7ec3e514 | 494 | sc->wiphy_select_failures = 0; |
0e2dedf9 JM |
495 | |
496 | /* Store the new channel */ | |
497 | sc->chan_idx = aphy->chan_idx; | |
498 | sc->chan_is_ht = aphy->chan_is_ht; | |
499 | sc->next_wiphy = aphy; | |
500 | ||
501 | __ath9k_wiphy_pause_all(sc); | |
502 | now = !__ath9k_wiphy_pausing(aphy->sc); | |
503 | spin_unlock_bh(&sc->wiphy_lock); | |
504 | ||
505 | if (now) { | |
506 | /* Ready to request channel change immediately */ | |
507 | queue_work(aphy->sc->hw->workqueue, &aphy->sc->chan_work); | |
508 | } | |
509 | ||
510 | /* | |
511 | * wiphys will be unpaused in ath9k_tx_status() once channel has been | |
512 | * changed if any wiphy needs time to become paused. | |
513 | */ | |
514 | ||
515 | return 0; | |
516 | } | |
9580a222 JM |
517 | |
518 | bool ath9k_wiphy_started(struct ath_softc *sc) | |
519 | { | |
520 | int i; | |
521 | spin_lock_bh(&sc->wiphy_lock); | |
522 | if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) { | |
523 | spin_unlock_bh(&sc->wiphy_lock); | |
524 | return true; | |
525 | } | |
526 | for (i = 0; i < sc->num_sec_wiphy; i++) { | |
527 | if (sc->sec_wiphy[i] && | |
528 | sc->sec_wiphy[i]->state != ATH_WIPHY_INACTIVE) { | |
529 | spin_unlock_bh(&sc->wiphy_lock); | |
530 | return true; | |
531 | } | |
532 | } | |
533 | spin_unlock_bh(&sc->wiphy_lock); | |
534 | return false; | |
535 | } | |
18eb62f8 JM |
536 | |
537 | static void ath9k_wiphy_pause_chan(struct ath_wiphy *aphy, | |
538 | struct ath_wiphy *selected) | |
539 | { | |
540 | if (aphy->chan_idx == selected->chan_idx) | |
541 | return; | |
542 | aphy->state = ATH_WIPHY_PAUSED; | |
543 | ieee80211_stop_queues(aphy->hw); | |
544 | } | |
545 | ||
546 | void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, | |
547 | struct ath_wiphy *selected) | |
548 | { | |
549 | int i; | |
550 | spin_lock_bh(&sc->wiphy_lock); | |
551 | if (sc->pri_wiphy->state == ATH_WIPHY_ACTIVE) | |
552 | ath9k_wiphy_pause_chan(sc->pri_wiphy, selected); | |
553 | for (i = 0; i < sc->num_sec_wiphy; i++) { | |
554 | if (sc->sec_wiphy[i] && | |
555 | sc->sec_wiphy[i]->state == ATH_WIPHY_ACTIVE) | |
556 | ath9k_wiphy_pause_chan(sc->sec_wiphy[i], selected); | |
557 | } | |
558 | spin_unlock_bh(&sc->wiphy_lock); | |
559 | } |