diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 349 |
1 files changed, 147 insertions, 202 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 71e10cabf811..85507bd9e341 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -12,11 +12,9 @@ | |||
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | /* TODO: figure out how to avoid that the "current BSS" expires */ | ||
16 | |||
17 | #include <linux/wireless.h> | ||
18 | #include <linux/if_arp.h> | 15 | #include <linux/if_arp.h> |
19 | #include <linux/rtnetlink.h> | 16 | #include <linux/rtnetlink.h> |
17 | #include <linux/slab.h> | ||
20 | #include <net/mac80211.h> | 18 | #include <net/mac80211.h> |
21 | 19 | ||
22 | #include "ieee80211_i.h" | 20 | #include "ieee80211_i.h" |
@@ -31,16 +29,19 @@ struct ieee80211_bss * | |||
31 | ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, | 29 | ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, |
32 | u8 *ssid, u8 ssid_len) | 30 | u8 *ssid, u8 ssid_len) |
33 | { | 31 | { |
34 | return (void *)cfg80211_get_bss(local->hw.wiphy, | 32 | struct cfg80211_bss *cbss; |
35 | ieee80211_get_channel(local->hw.wiphy, | 33 | |
36 | freq), | 34 | cbss = cfg80211_get_bss(local->hw.wiphy, |
37 | bssid, ssid, ssid_len, | 35 | ieee80211_get_channel(local->hw.wiphy, freq), |
38 | 0, 0); | 36 | bssid, ssid, ssid_len, 0, 0); |
37 | if (!cbss) | ||
38 | return NULL; | ||
39 | return (void *)cbss->priv; | ||
39 | } | 40 | } |
40 | 41 | ||
41 | static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) | 42 | static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) |
42 | { | 43 | { |
43 | struct ieee80211_bss *bss = (void *)cbss; | 44 | struct ieee80211_bss *bss = (void *)cbss->priv; |
44 | 45 | ||
45 | kfree(bss_mesh_id(bss)); | 46 | kfree(bss_mesh_id(bss)); |
46 | kfree(bss_mesh_cfg(bss)); | 47 | kfree(bss_mesh_cfg(bss)); |
@@ -49,7 +50,26 @@ static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) | |||
49 | void ieee80211_rx_bss_put(struct ieee80211_local *local, | 50 | void ieee80211_rx_bss_put(struct ieee80211_local *local, |
50 | struct ieee80211_bss *bss) | 51 | struct ieee80211_bss *bss) |
51 | { | 52 | { |
52 | cfg80211_put_bss((struct cfg80211_bss *)bss); | 53 | if (!bss) |
54 | return; | ||
55 | cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv)); | ||
56 | } | ||
57 | |||
58 | static bool is_uapsd_supported(struct ieee802_11_elems *elems) | ||
59 | { | ||
60 | u8 qos_info; | ||
61 | |||
62 | if (elems->wmm_info && elems->wmm_info_len == 7 | ||
63 | && elems->wmm_info[5] == 1) | ||
64 | qos_info = elems->wmm_info[6]; | ||
65 | else if (elems->wmm_param && elems->wmm_param_len == 24 | ||
66 | && elems->wmm_param[5] == 1) | ||
67 | qos_info = elems->wmm_param[6]; | ||
68 | else | ||
69 | /* no valid wmm information or parameter element found */ | ||
70 | return false; | ||
71 | |||
72 | return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD; | ||
53 | } | 73 | } |
54 | 74 | ||
55 | struct ieee80211_bss * | 75 | struct ieee80211_bss * |
@@ -61,6 +81,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
61 | struct ieee80211_channel *channel, | 81 | struct ieee80211_channel *channel, |
62 | bool beacon) | 82 | bool beacon) |
63 | { | 83 | { |
84 | struct cfg80211_bss *cbss; | ||
64 | struct ieee80211_bss *bss; | 85 | struct ieee80211_bss *bss; |
65 | int clen; | 86 | int clen; |
66 | s32 signal = 0; | 87 | s32 signal = 0; |
@@ -70,13 +91,14 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
70 | else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) | 91 | else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) |
71 | signal = (rx_status->signal * 100) / local->hw.max_signal; | 92 | signal = (rx_status->signal * 100) / local->hw.max_signal; |
72 | 93 | ||
73 | bss = (void *)cfg80211_inform_bss_frame(local->hw.wiphy, channel, | 94 | cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel, |
74 | mgmt, len, signal, GFP_ATOMIC); | 95 | mgmt, len, signal, GFP_ATOMIC); |
75 | 96 | ||
76 | if (!bss) | 97 | if (!cbss) |
77 | return NULL; | 98 | return NULL; |
78 | 99 | ||
79 | bss->cbss.free_priv = ieee80211_rx_bss_free; | 100 | cbss->free_priv = ieee80211_rx_bss_free; |
101 | bss = (void *)cbss->priv; | ||
80 | 102 | ||
81 | /* save the ERP value so that it is available at association time */ | 103 | /* save the ERP value so that it is available at association time */ |
82 | if (elems->erp_info && elems->erp_info_len >= 1) { | 104 | if (elems->erp_info && elems->erp_info_len >= 1) { |
@@ -90,10 +112,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
90 | bss->dtim_period = tim_ie->dtim_period; | 112 | bss->dtim_period = tim_ie->dtim_period; |
91 | } | 113 | } |
92 | 114 | ||
93 | /* set default value for buggy AP/no TIM element */ | ||
94 | if (bss->dtim_period == 0) | ||
95 | bss->dtim_period = 1; | ||
96 | |||
97 | bss->supp_rates_len = 0; | 115 | bss->supp_rates_len = 0; |
98 | if (elems->supp_rates) { | 116 | if (elems->supp_rates) { |
99 | clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; | 117 | clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; |
@@ -113,6 +131,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
113 | } | 131 | } |
114 | 132 | ||
115 | bss->wmm_used = elems->wmm_param || elems->wmm_info; | 133 | bss->wmm_used = elems->wmm_param || elems->wmm_info; |
134 | bss->uapsd_supported = is_uapsd_supported(elems); | ||
116 | 135 | ||
117 | if (!beacon) | 136 | if (!beacon) |
118 | bss->last_probe_resp = jiffies; | 137 | bss->last_probe_resp = jiffies; |
@@ -149,7 +168,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
149 | presp = ieee80211_is_probe_resp(fc); | 168 | presp = ieee80211_is_probe_resp(fc); |
150 | if (presp) { | 169 | if (presp) { |
151 | /* ignore ProbeResp to foreign address */ | 170 | /* ignore ProbeResp to foreign address */ |
152 | if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) | 171 | if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) |
153 | return RX_DROP_MONITOR; | 172 | return RX_DROP_MONITOR; |
154 | 173 | ||
155 | presp = true; | 174 | presp = true; |
@@ -189,100 +208,76 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
189 | return RX_QUEUED; | 208 | return RX_QUEUED; |
190 | } | 209 | } |
191 | 210 | ||
192 | /* | 211 | /* return false if no more work */ |
193 | * inform AP that we will go to sleep so that it will buffer the frames | 212 | static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) |
194 | * while we scan | ||
195 | */ | ||
196 | static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata) | ||
197 | { | 213 | { |
198 | struct ieee80211_local *local = sdata->local; | 214 | struct cfg80211_scan_request *req = local->scan_req; |
199 | bool ps = false; | 215 | enum ieee80211_band band; |
216 | int i, ielen, n_chans; | ||
200 | 217 | ||
201 | /* FIXME: what to do when local->pspolling is true? */ | 218 | do { |
219 | if (local->hw_scan_band == IEEE80211_NUM_BANDS) | ||
220 | return false; | ||
221 | |||
222 | band = local->hw_scan_band; | ||
223 | n_chans = 0; | ||
224 | for (i = 0; i < req->n_channels; i++) { | ||
225 | if (req->channels[i]->band == band) { | ||
226 | local->hw_scan_req->channels[n_chans] = | ||
227 | req->channels[i]; | ||
228 | n_chans++; | ||
229 | } | ||
230 | } | ||
202 | 231 | ||
203 | del_timer_sync(&local->dynamic_ps_timer); | 232 | local->hw_scan_band++; |
204 | cancel_work_sync(&local->dynamic_ps_enable_work); | 233 | } while (!n_chans); |
205 | 234 | ||
206 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { | 235 | local->hw_scan_req->n_channels = n_chans; |
207 | ps = true; | ||
208 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; | ||
209 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
210 | } | ||
211 | 236 | ||
212 | if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) | 237 | ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, |
213 | /* | 238 | req->ie, req->ie_len, band); |
214 | * If power save was enabled, no need to send a nullfunc | 239 | local->hw_scan_req->ie_len = ielen; |
215 | * frame because AP knows that we are sleeping. But if the | ||
216 | * hardware is creating the nullfunc frame for power save | ||
217 | * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not | ||
218 | * enabled) and power save was enabled, the firmware just | ||
219 | * sent a null frame with power save disabled. So we need | ||
220 | * to send a new nullfunc frame to inform the AP that we | ||
221 | * are again sleeping. | ||
222 | */ | ||
223 | ieee80211_send_nullfunc(local, sdata, 1); | ||
224 | } | ||
225 | |||
226 | /* inform AP that we are awake again, unless power save is enabled */ | ||
227 | static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata) | ||
228 | { | ||
229 | struct ieee80211_local *local = sdata->local; | ||
230 | |||
231 | if (!local->ps_sdata) | ||
232 | ieee80211_send_nullfunc(local, sdata, 0); | ||
233 | else { | ||
234 | /* | ||
235 | * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware | ||
236 | * will send a nullfunc frame with the powersave bit set | ||
237 | * even though the AP already knows that we are sleeping. | ||
238 | * This could be avoided by sending a null frame with power | ||
239 | * save bit disabled before enabling the power save, but | ||
240 | * this doesn't gain anything. | ||
241 | * | ||
242 | * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need | ||
243 | * to send a nullfunc frame because AP already knows that | ||
244 | * we are sleeping, let's just enable power save mode in | ||
245 | * hardware. | ||
246 | */ | ||
247 | local->hw.conf.flags |= IEEE80211_CONF_PS; | ||
248 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
249 | } | ||
250 | } | ||
251 | 240 | ||
252 | static void ieee80211_restore_scan_ies(struct ieee80211_local *local) | 241 | return true; |
253 | { | ||
254 | kfree(local->scan_req->ie); | ||
255 | local->scan_req->ie = local->orig_ies; | ||
256 | local->scan_req->ie_len = local->orig_ies_len; | ||
257 | } | 242 | } |
258 | 243 | ||
259 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 244 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) |
260 | { | 245 | { |
261 | struct ieee80211_local *local = hw_to_local(hw); | 246 | struct ieee80211_local *local = hw_to_local(hw); |
262 | struct ieee80211_sub_if_data *sdata; | ||
263 | bool was_hw_scan; | 247 | bool was_hw_scan; |
264 | 248 | ||
265 | mutex_lock(&local->scan_mtx); | 249 | mutex_lock(&local->scan_mtx); |
266 | 250 | ||
267 | if (WARN_ON(!local->scanning)) { | 251 | /* |
252 | * It's ok to abort a not-yet-running scan (that | ||
253 | * we have one at all will be verified by checking | ||
254 | * local->scan_req next), but not to complete it | ||
255 | * successfully. | ||
256 | */ | ||
257 | if (WARN_ON(!local->scanning && !aborted)) | ||
258 | aborted = true; | ||
259 | |||
260 | if (WARN_ON(!local->scan_req)) { | ||
268 | mutex_unlock(&local->scan_mtx); | 261 | mutex_unlock(&local->scan_mtx); |
269 | return; | 262 | return; |
270 | } | 263 | } |
271 | 264 | ||
272 | if (WARN_ON(!local->scan_req)) { | 265 | was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); |
266 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | ||
267 | ieee80211_queue_delayed_work(&local->hw, | ||
268 | &local->scan_work, 0); | ||
273 | mutex_unlock(&local->scan_mtx); | 269 | mutex_unlock(&local->scan_mtx); |
274 | return; | 270 | return; |
275 | } | 271 | } |
276 | 272 | ||
277 | if (test_bit(SCAN_HW_SCANNING, &local->scanning)) | 273 | kfree(local->hw_scan_req); |
278 | ieee80211_restore_scan_ies(local); | 274 | local->hw_scan_req = NULL; |
279 | 275 | ||
280 | if (local->scan_req != local->int_scan_req) | 276 | if (local->scan_req != local->int_scan_req) |
281 | cfg80211_scan_done(local->scan_req, aborted); | 277 | cfg80211_scan_done(local->scan_req, aborted); |
282 | local->scan_req = NULL; | 278 | local->scan_req = NULL; |
283 | local->scan_sdata = NULL; | 279 | local->scan_sdata = NULL; |
284 | 280 | ||
285 | was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | ||
286 | local->scanning = 0; | 281 | local->scanning = 0; |
287 | local->scan_channel = NULL; | 282 | local->scan_channel = NULL; |
288 | 283 | ||
@@ -297,41 +292,19 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
297 | 292 | ||
298 | drv_sw_scan_complete(local); | 293 | drv_sw_scan_complete(local); |
299 | 294 | ||
300 | mutex_lock(&local->iflist_mtx); | 295 | ieee80211_offchannel_return(local, true); |
301 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
302 | if (!netif_running(sdata->dev)) | ||
303 | continue; | ||
304 | |||
305 | /* Tell AP we're back */ | ||
306 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
307 | if (sdata->u.mgd.associated) { | ||
308 | ieee80211_scan_ps_disable(sdata); | ||
309 | netif_tx_wake_all_queues(sdata->dev); | ||
310 | } | ||
311 | } else | ||
312 | netif_tx_wake_all_queues(sdata->dev); | ||
313 | |||
314 | /* re-enable beaconing */ | ||
315 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
316 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
317 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
318 | ieee80211_bss_info_change_notify( | ||
319 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
320 | } | ||
321 | mutex_unlock(&local->iflist_mtx); | ||
322 | 296 | ||
323 | done: | 297 | done: |
324 | ieee80211_recalc_idle(local); | 298 | ieee80211_recalc_idle(local); |
325 | ieee80211_mlme_notify_scan_completed(local); | 299 | ieee80211_mlme_notify_scan_completed(local); |
326 | ieee80211_ibss_notify_scan_completed(local); | 300 | ieee80211_ibss_notify_scan_completed(local); |
327 | ieee80211_mesh_notify_scan_completed(local); | 301 | ieee80211_mesh_notify_scan_completed(local); |
302 | ieee80211_queue_work(&local->hw, &local->work_work); | ||
328 | } | 303 | } |
329 | EXPORT_SYMBOL(ieee80211_scan_completed); | 304 | EXPORT_SYMBOL(ieee80211_scan_completed); |
330 | 305 | ||
331 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) | 306 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) |
332 | { | 307 | { |
333 | struct ieee80211_sub_if_data *sdata; | ||
334 | |||
335 | /* | 308 | /* |
336 | * Hardware/driver doesn't support hw_scan, so use software | 309 | * Hardware/driver doesn't support hw_scan, so use software |
337 | * scanning instead. First send a nullfunc frame with power save | 310 | * scanning instead. First send a nullfunc frame with power save |
@@ -347,33 +320,15 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
347 | */ | 320 | */ |
348 | drv_sw_scan_start(local); | 321 | drv_sw_scan_start(local); |
349 | 322 | ||
350 | mutex_lock(&local->iflist_mtx); | 323 | ieee80211_offchannel_stop_beaconing(local); |
351 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
352 | if (!netif_running(sdata->dev)) | ||
353 | continue; | ||
354 | |||
355 | /* disable beaconing */ | ||
356 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
357 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
358 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
359 | ieee80211_bss_info_change_notify( | ||
360 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
361 | |||
362 | /* | ||
363 | * only handle non-STA interfaces here, STA interfaces | ||
364 | * are handled in the scan state machine | ||
365 | */ | ||
366 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
367 | netif_tx_stop_all_queues(sdata->dev); | ||
368 | } | ||
369 | mutex_unlock(&local->iflist_mtx); | ||
370 | 324 | ||
371 | local->next_scan_state = SCAN_DECISION; | 325 | local->next_scan_state = SCAN_DECISION; |
372 | local->scan_channel_idx = 0; | 326 | local->scan_channel_idx = 0; |
373 | 327 | ||
328 | drv_flush(local, false); | ||
329 | |||
374 | ieee80211_configure_filter(local); | 330 | ieee80211_configure_filter(local); |
375 | 331 | ||
376 | /* TODO: start scan as soon as all nullfunc frames are ACKed */ | ||
377 | ieee80211_queue_delayed_work(&local->hw, | 332 | ieee80211_queue_delayed_work(&local->hw, |
378 | &local->scan_work, | 333 | &local->scan_work, |
379 | IEEE80211_CHANNEL_TIME); | 334 | IEEE80211_CHANNEL_TIME); |
@@ -386,68 +341,80 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
386 | struct cfg80211_scan_request *req) | 341 | struct cfg80211_scan_request *req) |
387 | { | 342 | { |
388 | struct ieee80211_local *local = sdata->local; | 343 | struct ieee80211_local *local = sdata->local; |
389 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
390 | int rc; | 344 | int rc; |
391 | 345 | ||
392 | if (local->scan_req) | 346 | if (local->scan_req) |
393 | return -EBUSY; | 347 | return -EBUSY; |
394 | 348 | ||
349 | if (!list_empty(&local->work_list)) { | ||
350 | /* wait for the work to finish/time out */ | ||
351 | local->scan_req = req; | ||
352 | local->scan_sdata = sdata; | ||
353 | return 0; | ||
354 | } | ||
355 | |||
395 | if (local->ops->hw_scan) { | 356 | if (local->ops->hw_scan) { |
396 | u8 *ies; | 357 | u8 *ies; |
397 | int ielen; | ||
398 | 358 | ||
399 | ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN + | 359 | local->hw_scan_req = kmalloc( |
400 | local->scan_ies_len + req->ie_len, GFP_KERNEL); | 360 | sizeof(*local->hw_scan_req) + |
401 | if (!ies) | 361 | req->n_channels * sizeof(req->channels[0]) + |
362 | 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + | ||
363 | req->ie_len, GFP_KERNEL); | ||
364 | if (!local->hw_scan_req) | ||
402 | return -ENOMEM; | 365 | return -ENOMEM; |
403 | 366 | ||
404 | ielen = ieee80211_build_preq_ies(local, ies, | 367 | local->hw_scan_req->ssids = req->ssids; |
405 | req->ie, req->ie_len); | 368 | local->hw_scan_req->n_ssids = req->n_ssids; |
406 | local->orig_ies = req->ie; | 369 | ies = (u8 *)local->hw_scan_req + |
407 | local->orig_ies_len = req->ie_len; | 370 | sizeof(*local->hw_scan_req) + |
408 | req->ie = ies; | 371 | req->n_channels * sizeof(req->channels[0]); |
409 | req->ie_len = ielen; | 372 | local->hw_scan_req->ie = ies; |
373 | |||
374 | local->hw_scan_band = 0; | ||
375 | |||
376 | /* | ||
377 | * After allocating local->hw_scan_req, we must | ||
378 | * go through until ieee80211_prep_hw_scan(), so | ||
379 | * anything that might be changed here and leave | ||
380 | * this function early must not go after this | ||
381 | * allocation. | ||
382 | */ | ||
410 | } | 383 | } |
411 | 384 | ||
412 | local->scan_req = req; | 385 | local->scan_req = req; |
413 | local->scan_sdata = sdata; | 386 | local->scan_sdata = sdata; |
414 | 387 | ||
415 | if (req != local->int_scan_req && | ||
416 | sdata->vif.type == NL80211_IFTYPE_STATION && | ||
417 | !list_empty(&ifmgd->work_list)) { | ||
418 | /* actually wait for the work it's doing to finish/time out */ | ||
419 | set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request); | ||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | if (local->ops->hw_scan) | 388 | if (local->ops->hw_scan) |
424 | __set_bit(SCAN_HW_SCANNING, &local->scanning); | 389 | __set_bit(SCAN_HW_SCANNING, &local->scanning); |
425 | else | 390 | else |
426 | __set_bit(SCAN_SW_SCANNING, &local->scanning); | 391 | __set_bit(SCAN_SW_SCANNING, &local->scanning); |
392 | |||
427 | /* | 393 | /* |
428 | * Kicking off the scan need not be protected, | 394 | * Kicking off the scan need not be protected, |
429 | * only the scan variable stuff, since now | 395 | * only the scan variable stuff, since now |
430 | * local->scan_req is assigned and other callers | 396 | * local->scan_req is assigned and other callers |
431 | * will abort their scan attempts. | 397 | * will abort their scan attempts. |
432 | * | 398 | * |
433 | * This avoids getting a scan_mtx -> iflist_mtx | 399 | * This avoids too many locking dependencies |
434 | * dependency, so that the scan completed calls | 400 | * so that the scan completed calls have more |
435 | * have more locking freedom. | 401 | * locking freedom. |
436 | */ | 402 | */ |
437 | 403 | ||
438 | ieee80211_recalc_idle(local); | 404 | ieee80211_recalc_idle(local); |
439 | mutex_unlock(&local->scan_mtx); | 405 | mutex_unlock(&local->scan_mtx); |
440 | 406 | ||
441 | if (local->ops->hw_scan) | 407 | if (local->ops->hw_scan) { |
442 | rc = drv_hw_scan(local, local->scan_req); | 408 | WARN_ON(!ieee80211_prep_hw_scan(local)); |
443 | else | 409 | rc = drv_hw_scan(local, local->hw_scan_req); |
410 | } else | ||
444 | rc = ieee80211_start_sw_scan(local); | 411 | rc = ieee80211_start_sw_scan(local); |
445 | 412 | ||
446 | mutex_lock(&local->scan_mtx); | 413 | mutex_lock(&local->scan_mtx); |
447 | 414 | ||
448 | if (rc) { | 415 | if (rc) { |
449 | if (local->ops->hw_scan) | 416 | kfree(local->hw_scan_req); |
450 | ieee80211_restore_scan_ies(local); | 417 | local->hw_scan_req = NULL; |
451 | local->scanning = 0; | 418 | local->scanning = 0; |
452 | 419 | ||
453 | ieee80211_recalc_idle(local); | 420 | ieee80211_recalc_idle(local); |
@@ -474,7 +441,7 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
474 | /* check if at least one STA interface is associated */ | 441 | /* check if at least one STA interface is associated */ |
475 | mutex_lock(&local->iflist_mtx); | 442 | mutex_lock(&local->iflist_mtx); |
476 | list_for_each_entry(sdata, &local->interfaces, list) { | 443 | list_for_each_entry(sdata, &local->interfaces, list) { |
477 | if (!netif_running(sdata->dev)) | 444 | if (!ieee80211_sdata_running(sdata)) |
478 | continue; | 445 | continue; |
479 | 446 | ||
480 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 447 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
@@ -512,56 +479,35 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
512 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | 479 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, |
513 | unsigned long *next_delay) | 480 | unsigned long *next_delay) |
514 | { | 481 | { |
515 | struct ieee80211_sub_if_data *sdata; | 482 | ieee80211_offchannel_stop_station(local); |
483 | |||
484 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); | ||
516 | 485 | ||
517 | /* | 486 | /* |
518 | * notify the AP about us leaving the channel and stop all STA interfaces | 487 | * What if the nullfunc frames didn't arrive? |
519 | */ | 488 | */ |
520 | mutex_lock(&local->iflist_mtx); | 489 | drv_flush(local, false); |
521 | list_for_each_entry(sdata, &local->interfaces, list) { | 490 | if (local->ops->flush) |
522 | if (!netif_running(sdata->dev)) | 491 | *next_delay = 0; |
523 | continue; | 492 | else |
524 | 493 | *next_delay = HZ / 10; | |
525 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
526 | netif_tx_stop_all_queues(sdata->dev); | ||
527 | if (sdata->u.mgd.associated) | ||
528 | ieee80211_scan_ps_enable(sdata); | ||
529 | } | ||
530 | } | ||
531 | mutex_unlock(&local->iflist_mtx); | ||
532 | |||
533 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); | ||
534 | 494 | ||
535 | /* advance to the next channel to be scanned */ | 495 | /* advance to the next channel to be scanned */ |
536 | *next_delay = HZ / 10; | ||
537 | local->next_scan_state = SCAN_SET_CHANNEL; | 496 | local->next_scan_state = SCAN_SET_CHANNEL; |
538 | } | 497 | } |
539 | 498 | ||
540 | static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, | 499 | static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, |
541 | unsigned long *next_delay) | 500 | unsigned long *next_delay) |
542 | { | 501 | { |
543 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | ||
544 | |||
545 | /* switch back to the operating channel */ | 502 | /* switch back to the operating channel */ |
546 | local->scan_channel = NULL; | 503 | local->scan_channel = NULL; |
547 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 504 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
548 | 505 | ||
549 | /* | 506 | /* |
550 | * notify the AP about us being back and restart all STA interfaces | 507 | * Only re-enable station mode interface now; beaconing will be |
508 | * re-enabled once the full scan has been completed. | ||
551 | */ | 509 | */ |
552 | mutex_lock(&local->iflist_mtx); | 510 | ieee80211_offchannel_return(local, false); |
553 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
554 | if (!netif_running(sdata->dev)) | ||
555 | continue; | ||
556 | |||
557 | /* Tell AP we're back */ | ||
558 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
559 | if (sdata->u.mgd.associated) | ||
560 | ieee80211_scan_ps_disable(sdata); | ||
561 | netif_tx_wake_all_queues(sdata->dev); | ||
562 | } | ||
563 | } | ||
564 | mutex_unlock(&local->iflist_mtx); | ||
565 | 511 | ||
566 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); | 512 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); |
567 | 513 | ||
@@ -574,23 +520,14 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, | |||
574 | { | 520 | { |
575 | int skip; | 521 | int skip; |
576 | struct ieee80211_channel *chan; | 522 | struct ieee80211_channel *chan; |
577 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | ||
578 | 523 | ||
579 | skip = 0; | 524 | skip = 0; |
580 | chan = local->scan_req->channels[local->scan_channel_idx]; | 525 | chan = local->scan_req->channels[local->scan_channel_idx]; |
581 | 526 | ||
582 | if (chan->flags & IEEE80211_CHAN_DISABLED || | 527 | local->scan_channel = chan; |
583 | (sdata->vif.type == NL80211_IFTYPE_ADHOC && | 528 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) |
584 | chan->flags & IEEE80211_CHAN_NO_IBSS)) | ||
585 | skip = 1; | 529 | skip = 1; |
586 | 530 | ||
587 | if (!skip) { | ||
588 | local->scan_channel = chan; | ||
589 | if (ieee80211_hw_config(local, | ||
590 | IEEE80211_CONF_CHANGE_CHANNEL)) | ||
591 | skip = 1; | ||
592 | } | ||
593 | |||
594 | /* advance state machine to next channel/band */ | 531 | /* advance state machine to next channel/band */ |
595 | local->scan_channel_idx++; | 532 | local->scan_channel_idx++; |
596 | 533 | ||
@@ -656,6 +593,14 @@ void ieee80211_scan_work(struct work_struct *work) | |||
656 | return; | 593 | return; |
657 | } | 594 | } |
658 | 595 | ||
596 | if (local->hw_scan_req) { | ||
597 | int rc = drv_hw_scan(local, local->hw_scan_req); | ||
598 | mutex_unlock(&local->scan_mtx); | ||
599 | if (rc) | ||
600 | ieee80211_scan_completed(&local->hw, true); | ||
601 | return; | ||
602 | } | ||
603 | |||
659 | if (local->scan_req && !local->scanning) { | 604 | if (local->scan_req && !local->scanning) { |
660 | struct cfg80211_scan_request *req = local->scan_req; | 605 | struct cfg80211_scan_request *req = local->scan_req; |
661 | int rc; | 606 | int rc; |
@@ -676,7 +621,7 @@ void ieee80211_scan_work(struct work_struct *work) | |||
676 | /* | 621 | /* |
677 | * Avoid re-scheduling when the sdata is going away. | 622 | * Avoid re-scheduling when the sdata is going away. |
678 | */ | 623 | */ |
679 | if (!netif_running(sdata->dev)) { | 624 | if (!ieee80211_sdata_running(sdata)) { |
680 | ieee80211_scan_completed(&local->hw, true); | 625 | ieee80211_scan_completed(&local->hw, true); |
681 | return; | 626 | return; |
682 | } | 627 | } |