diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 129 |
1 files changed, 84 insertions, 45 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 71e10cabf811..4cf387c944bf 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -12,8 +12,6 @@ | |||
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> | 15 | #include <linux/wireless.h> |
18 | #include <linux/if_arp.h> | 16 | #include <linux/if_arp.h> |
19 | #include <linux/rtnetlink.h> | 17 | #include <linux/rtnetlink.h> |
@@ -189,6 +187,39 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
189 | return RX_QUEUED; | 187 | return RX_QUEUED; |
190 | } | 188 | } |
191 | 189 | ||
190 | /* return false if no more work */ | ||
191 | static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | ||
192 | { | ||
193 | struct cfg80211_scan_request *req = local->scan_req; | ||
194 | enum ieee80211_band band; | ||
195 | int i, ielen, n_chans; | ||
196 | |||
197 | do { | ||
198 | if (local->hw_scan_band == IEEE80211_NUM_BANDS) | ||
199 | return false; | ||
200 | |||
201 | band = local->hw_scan_band; | ||
202 | n_chans = 0; | ||
203 | for (i = 0; i < req->n_channels; i++) { | ||
204 | if (req->channels[i]->band == band) { | ||
205 | local->hw_scan_req->channels[n_chans] = | ||
206 | req->channels[i]; | ||
207 | n_chans++; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | local->hw_scan_band++; | ||
212 | } while (!n_chans); | ||
213 | |||
214 | local->hw_scan_req->n_channels = n_chans; | ||
215 | |||
216 | ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, | ||
217 | req->ie, req->ie_len, band); | ||
218 | local->hw_scan_req->ie_len = ielen; | ||
219 | |||
220 | return true; | ||
221 | } | ||
222 | |||
192 | /* | 223 | /* |
193 | * inform AP that we will go to sleep so that it will buffer the frames | 224 | * inform AP that we will go to sleep so that it will buffer the frames |
194 | * while we scan | 225 | * while we scan |
@@ -249,13 +280,6 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata) | |||
249 | } | 280 | } |
250 | } | 281 | } |
251 | 282 | ||
252 | static void ieee80211_restore_scan_ies(struct ieee80211_local *local) | ||
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 | } | ||
258 | |||
259 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 283 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) |
260 | { | 284 | { |
261 | struct ieee80211_local *local = hw_to_local(hw); | 285 | struct ieee80211_local *local = hw_to_local(hw); |
@@ -264,25 +288,36 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
264 | 288 | ||
265 | mutex_lock(&local->scan_mtx); | 289 | mutex_lock(&local->scan_mtx); |
266 | 290 | ||
267 | if (WARN_ON(!local->scanning)) { | 291 | /* |
292 | * It's ok to abort a not-yet-running scan (that | ||
293 | * we have one at all will be verified by checking | ||
294 | * local->scan_req next), but not to complete it | ||
295 | * successfully. | ||
296 | */ | ||
297 | if (WARN_ON(!local->scanning && !aborted)) | ||
298 | aborted = true; | ||
299 | |||
300 | if (WARN_ON(!local->scan_req)) { | ||
268 | mutex_unlock(&local->scan_mtx); | 301 | mutex_unlock(&local->scan_mtx); |
269 | return; | 302 | return; |
270 | } | 303 | } |
271 | 304 | ||
272 | if (WARN_ON(!local->scan_req)) { | 305 | was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); |
306 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | ||
307 | ieee80211_queue_delayed_work(&local->hw, | ||
308 | &local->scan_work, 0); | ||
273 | mutex_unlock(&local->scan_mtx); | 309 | mutex_unlock(&local->scan_mtx); |
274 | return; | 310 | return; |
275 | } | 311 | } |
276 | 312 | ||
277 | if (test_bit(SCAN_HW_SCANNING, &local->scanning)) | 313 | kfree(local->hw_scan_req); |
278 | ieee80211_restore_scan_ies(local); | 314 | local->hw_scan_req = NULL; |
279 | 315 | ||
280 | if (local->scan_req != local->int_scan_req) | 316 | if (local->scan_req != local->int_scan_req) |
281 | cfg80211_scan_done(local->scan_req, aborted); | 317 | cfg80211_scan_done(local->scan_req, aborted); |
282 | local->scan_req = NULL; | 318 | local->scan_req = NULL; |
283 | local->scan_sdata = NULL; | 319 | local->scan_sdata = NULL; |
284 | 320 | ||
285 | was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | ||
286 | local->scanning = 0; | 321 | local->scanning = 0; |
287 | local->scan_channel = NULL; | 322 | local->scan_channel = NULL; |
288 | 323 | ||
@@ -306,10 +341,10 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
306 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 341 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
307 | if (sdata->u.mgd.associated) { | 342 | if (sdata->u.mgd.associated) { |
308 | ieee80211_scan_ps_disable(sdata); | 343 | ieee80211_scan_ps_disable(sdata); |
309 | netif_tx_wake_all_queues(sdata->dev); | 344 | netif_wake_queue(sdata->dev); |
310 | } | 345 | } |
311 | } else | 346 | } else |
312 | netif_tx_wake_all_queues(sdata->dev); | 347 | netif_wake_queue(sdata->dev); |
313 | 348 | ||
314 | /* re-enable beaconing */ | 349 | /* re-enable beaconing */ |
315 | if (sdata->vif.type == NL80211_IFTYPE_AP || | 350 | if (sdata->vif.type == NL80211_IFTYPE_AP || |
@@ -364,7 +399,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
364 | * are handled in the scan state machine | 399 | * are handled in the scan state machine |
365 | */ | 400 | */ |
366 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | 401 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
367 | netif_tx_stop_all_queues(sdata->dev); | 402 | netif_stop_queue(sdata->dev); |
368 | } | 403 | } |
369 | mutex_unlock(&local->iflist_mtx); | 404 | mutex_unlock(&local->iflist_mtx); |
370 | 405 | ||
@@ -394,19 +429,23 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
394 | 429 | ||
395 | if (local->ops->hw_scan) { | 430 | if (local->ops->hw_scan) { |
396 | u8 *ies; | 431 | u8 *ies; |
397 | int ielen; | ||
398 | 432 | ||
399 | ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN + | 433 | local->hw_scan_req = kmalloc( |
400 | local->scan_ies_len + req->ie_len, GFP_KERNEL); | 434 | sizeof(*local->hw_scan_req) + |
401 | if (!ies) | 435 | req->n_channels * sizeof(req->channels[0]) + |
436 | 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + | ||
437 | req->ie_len, GFP_KERNEL); | ||
438 | if (!local->hw_scan_req) | ||
402 | return -ENOMEM; | 439 | return -ENOMEM; |
403 | 440 | ||
404 | ielen = ieee80211_build_preq_ies(local, ies, | 441 | local->hw_scan_req->ssids = req->ssids; |
405 | req->ie, req->ie_len); | 442 | local->hw_scan_req->n_ssids = req->n_ssids; |
406 | local->orig_ies = req->ie; | 443 | ies = (u8 *)local->hw_scan_req + |
407 | local->orig_ies_len = req->ie_len; | 444 | sizeof(*local->hw_scan_req) + |
408 | req->ie = ies; | 445 | req->n_channels * sizeof(req->channels[0]); |
409 | req->ie_len = ielen; | 446 | local->hw_scan_req->ie = ies; |
447 | |||
448 | local->hw_scan_band = 0; | ||
410 | } | 449 | } |
411 | 450 | ||
412 | local->scan_req = req; | 451 | local->scan_req = req; |
@@ -438,16 +477,17 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
438 | ieee80211_recalc_idle(local); | 477 | ieee80211_recalc_idle(local); |
439 | mutex_unlock(&local->scan_mtx); | 478 | mutex_unlock(&local->scan_mtx); |
440 | 479 | ||
441 | if (local->ops->hw_scan) | 480 | if (local->ops->hw_scan) { |
442 | rc = drv_hw_scan(local, local->scan_req); | 481 | WARN_ON(!ieee80211_prep_hw_scan(local)); |
443 | else | 482 | rc = drv_hw_scan(local, local->hw_scan_req); |
483 | } else | ||
444 | rc = ieee80211_start_sw_scan(local); | 484 | rc = ieee80211_start_sw_scan(local); |
445 | 485 | ||
446 | mutex_lock(&local->scan_mtx); | 486 | mutex_lock(&local->scan_mtx); |
447 | 487 | ||
448 | if (rc) { | 488 | if (rc) { |
449 | if (local->ops->hw_scan) | 489 | kfree(local->hw_scan_req); |
450 | ieee80211_restore_scan_ies(local); | 490 | local->hw_scan_req = NULL; |
451 | local->scanning = 0; | 491 | local->scanning = 0; |
452 | 492 | ||
453 | ieee80211_recalc_idle(local); | 493 | ieee80211_recalc_idle(local); |
@@ -523,7 +563,7 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca | |||
523 | continue; | 563 | continue; |
524 | 564 | ||
525 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 565 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
526 | netif_tx_stop_all_queues(sdata->dev); | 566 | netif_stop_queue(sdata->dev); |
527 | if (sdata->u.mgd.associated) | 567 | if (sdata->u.mgd.associated) |
528 | ieee80211_scan_ps_enable(sdata); | 568 | ieee80211_scan_ps_enable(sdata); |
529 | } | 569 | } |
@@ -558,7 +598,7 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca | |||
558 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 598 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
559 | if (sdata->u.mgd.associated) | 599 | if (sdata->u.mgd.associated) |
560 | ieee80211_scan_ps_disable(sdata); | 600 | ieee80211_scan_ps_disable(sdata); |
561 | netif_tx_wake_all_queues(sdata->dev); | 601 | netif_wake_queue(sdata->dev); |
562 | } | 602 | } |
563 | } | 603 | } |
564 | mutex_unlock(&local->iflist_mtx); | 604 | mutex_unlock(&local->iflist_mtx); |
@@ -574,23 +614,14 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, | |||
574 | { | 614 | { |
575 | int skip; | 615 | int skip; |
576 | struct ieee80211_channel *chan; | 616 | struct ieee80211_channel *chan; |
577 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | ||
578 | 617 | ||
579 | skip = 0; | 618 | skip = 0; |
580 | chan = local->scan_req->channels[local->scan_channel_idx]; | 619 | chan = local->scan_req->channels[local->scan_channel_idx]; |
581 | 620 | ||
582 | if (chan->flags & IEEE80211_CHAN_DISABLED || | 621 | local->scan_channel = chan; |
583 | (sdata->vif.type == NL80211_IFTYPE_ADHOC && | 622 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) |
584 | chan->flags & IEEE80211_CHAN_NO_IBSS)) | ||
585 | skip = 1; | 623 | skip = 1; |
586 | 624 | ||
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 */ | 625 | /* advance state machine to next channel/band */ |
595 | local->scan_channel_idx++; | 626 | local->scan_channel_idx++; |
596 | 627 | ||
@@ -656,6 +687,14 @@ void ieee80211_scan_work(struct work_struct *work) | |||
656 | return; | 687 | return; |
657 | } | 688 | } |
658 | 689 | ||
690 | if (local->hw_scan_req) { | ||
691 | int rc = drv_hw_scan(local, local->hw_scan_req); | ||
692 | mutex_unlock(&local->scan_mtx); | ||
693 | if (rc) | ||
694 | ieee80211_scan_completed(&local->hw, true); | ||
695 | return; | ||
696 | } | ||
697 | |||
659 | if (local->scan_req && !local->scanning) { | 698 | if (local->scan_req && !local->scanning) { |
660 | struct cfg80211_scan_request *req = local->scan_req; | 699 | struct cfg80211_scan_request *req = local->scan_req; |
661 | int rc; | 700 | int rc; |