diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 179 |
1 files changed, 109 insertions, 70 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 872d7b6ef6b3..fb274db77e3c 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -242,20 +242,19 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
242 | local->hw_scan_req->n_channels = n_chans; | 242 | local->hw_scan_req->n_channels = n_chans; |
243 | 243 | ||
244 | ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, | 244 | ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie, |
245 | req->ie, req->ie_len, band); | 245 | req->ie, req->ie_len, band, (u32) -1, |
246 | 0); | ||
246 | local->hw_scan_req->ie_len = ielen; | 247 | local->hw_scan_req->ie_len = ielen; |
247 | 248 | ||
248 | return true; | 249 | return true; |
249 | } | 250 | } |
250 | 251 | ||
251 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 252 | static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, |
253 | bool was_hw_scan) | ||
252 | { | 254 | { |
253 | struct ieee80211_local *local = hw_to_local(hw); | 255 | struct ieee80211_local *local = hw_to_local(hw); |
254 | bool was_hw_scan; | ||
255 | |||
256 | trace_api_scan_completed(local, aborted); | ||
257 | 256 | ||
258 | mutex_lock(&local->scan_mtx); | 257 | lockdep_assert_held(&local->mtx); |
259 | 258 | ||
260 | /* | 259 | /* |
261 | * It's ok to abort a not-yet-running scan (that | 260 | * It's ok to abort a not-yet-running scan (that |
@@ -266,17 +265,13 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
266 | if (WARN_ON(!local->scanning && !aborted)) | 265 | if (WARN_ON(!local->scanning && !aborted)) |
267 | aborted = true; | 266 | aborted = true; |
268 | 267 | ||
269 | if (WARN_ON(!local->scan_req)) { | 268 | if (WARN_ON(!local->scan_req)) |
270 | mutex_unlock(&local->scan_mtx); | 269 | return false; |
271 | return; | ||
272 | } | ||
273 | 270 | ||
274 | was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | ||
275 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | 271 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { |
276 | ieee80211_queue_delayed_work(&local->hw, | 272 | int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req); |
277 | &local->scan_work, 0); | 273 | if (rc == 0) |
278 | mutex_unlock(&local->scan_mtx); | 274 | return false; |
279 | return; | ||
280 | } | 275 | } |
281 | 276 | ||
282 | kfree(local->hw_scan_req); | 277 | kfree(local->hw_scan_req); |
@@ -290,26 +285,42 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
290 | local->scanning = 0; | 285 | local->scanning = 0; |
291 | local->scan_channel = NULL; | 286 | local->scan_channel = NULL; |
292 | 287 | ||
293 | /* we only have to protect scan_req and hw/sw scan */ | 288 | return true; |
294 | mutex_unlock(&local->scan_mtx); | 289 | } |
295 | |||
296 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
297 | if (was_hw_scan) | ||
298 | goto done; | ||
299 | |||
300 | ieee80211_configure_filter(local); | ||
301 | 290 | ||
302 | drv_sw_scan_complete(local); | 291 | static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, |
292 | bool was_hw_scan) | ||
293 | { | ||
294 | struct ieee80211_local *local = hw_to_local(hw); | ||
303 | 295 | ||
304 | ieee80211_offchannel_return(local, true); | 296 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
297 | if (!was_hw_scan) { | ||
298 | ieee80211_configure_filter(local); | ||
299 | drv_sw_scan_complete(local); | ||
300 | ieee80211_offchannel_return(local, true); | ||
301 | } | ||
305 | 302 | ||
306 | done: | 303 | mutex_lock(&local->mtx); |
307 | ieee80211_recalc_idle(local); | 304 | ieee80211_recalc_idle(local); |
305 | mutex_unlock(&local->mtx); | ||
306 | |||
308 | ieee80211_mlme_notify_scan_completed(local); | 307 | ieee80211_mlme_notify_scan_completed(local); |
309 | ieee80211_ibss_notify_scan_completed(local); | 308 | ieee80211_ibss_notify_scan_completed(local); |
310 | ieee80211_mesh_notify_scan_completed(local); | 309 | ieee80211_mesh_notify_scan_completed(local); |
311 | ieee80211_queue_work(&local->hw, &local->work_work); | 310 | ieee80211_queue_work(&local->hw, &local->work_work); |
312 | } | 311 | } |
312 | |||
313 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | ||
314 | { | ||
315 | struct ieee80211_local *local = hw_to_local(hw); | ||
316 | |||
317 | trace_api_scan_completed(local, aborted); | ||
318 | |||
319 | set_bit(SCAN_COMPLETED, &local->scanning); | ||
320 | if (aborted) | ||
321 | set_bit(SCAN_ABORTED, &local->scanning); | ||
322 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0); | ||
323 | } | ||
313 | EXPORT_SYMBOL(ieee80211_scan_completed); | 324 | EXPORT_SYMBOL(ieee80211_scan_completed); |
314 | 325 | ||
315 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) | 326 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) |
@@ -353,6 +364,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
353 | struct ieee80211_local *local = sdata->local; | 364 | struct ieee80211_local *local = sdata->local; |
354 | int rc; | 365 | int rc; |
355 | 366 | ||
367 | lockdep_assert_held(&local->mtx); | ||
368 | |||
356 | if (local->scan_req) | 369 | if (local->scan_req) |
357 | return -EBUSY; | 370 | return -EBUSY; |
358 | 371 | ||
@@ -434,8 +447,8 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan) | |||
434 | return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME; | 447 | return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME; |
435 | } | 448 | } |
436 | 449 | ||
437 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, | 450 | static void ieee80211_scan_state_decision(struct ieee80211_local *local, |
438 | unsigned long *next_delay) | 451 | unsigned long *next_delay) |
439 | { | 452 | { |
440 | bool associated = false; | 453 | bool associated = false; |
441 | bool tx_empty = true; | 454 | bool tx_empty = true; |
@@ -445,12 +458,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
445 | struct ieee80211_sub_if_data *sdata; | 458 | struct ieee80211_sub_if_data *sdata; |
446 | struct ieee80211_channel *next_chan; | 459 | struct ieee80211_channel *next_chan; |
447 | 460 | ||
448 | /* if no more bands/channels left, complete scan and advance to the idle state */ | ||
449 | if (local->scan_channel_idx >= local->scan_req->n_channels) { | ||
450 | ieee80211_scan_completed(&local->hw, false); | ||
451 | return 1; | ||
452 | } | ||
453 | |||
454 | /* | 461 | /* |
455 | * check if at least one STA interface is associated, | 462 | * check if at least one STA interface is associated, |
456 | * check if at least one STA interface has pending tx frames | 463 | * check if at least one STA interface has pending tx frames |
@@ -522,7 +529,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
522 | } | 529 | } |
523 | 530 | ||
524 | *next_delay = 0; | 531 | *next_delay = 0; |
525 | return 0; | ||
526 | } | 532 | } |
527 | 533 | ||
528 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | 534 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, |
@@ -638,21 +644,18 @@ void ieee80211_scan_work(struct work_struct *work) | |||
638 | container_of(work, struct ieee80211_local, scan_work.work); | 644 | container_of(work, struct ieee80211_local, scan_work.work); |
639 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | 645 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; |
640 | unsigned long next_delay = 0; | 646 | unsigned long next_delay = 0; |
647 | bool aborted, hw_scan, finish; | ||
641 | 648 | ||
642 | mutex_lock(&local->scan_mtx); | 649 | mutex_lock(&local->mtx); |
643 | if (!sdata || !local->scan_req) { | ||
644 | mutex_unlock(&local->scan_mtx); | ||
645 | return; | ||
646 | } | ||
647 | 650 | ||
648 | if (local->hw_scan_req) { | 651 | if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { |
649 | int rc = drv_hw_scan(local, sdata, local->hw_scan_req); | 652 | aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); |
650 | mutex_unlock(&local->scan_mtx); | 653 | goto out_complete; |
651 | if (rc) | ||
652 | ieee80211_scan_completed(&local->hw, true); | ||
653 | return; | ||
654 | } | 654 | } |
655 | 655 | ||
656 | if (!sdata || !local->scan_req) | ||
657 | goto out; | ||
658 | |||
656 | if (local->scan_req && !local->scanning) { | 659 | if (local->scan_req && !local->scanning) { |
657 | struct cfg80211_scan_request *req = local->scan_req; | 660 | struct cfg80211_scan_request *req = local->scan_req; |
658 | int rc; | 661 | int rc; |
@@ -661,21 +664,21 @@ void ieee80211_scan_work(struct work_struct *work) | |||
661 | local->scan_sdata = NULL; | 664 | local->scan_sdata = NULL; |
662 | 665 | ||
663 | rc = __ieee80211_start_scan(sdata, req); | 666 | rc = __ieee80211_start_scan(sdata, req); |
664 | mutex_unlock(&local->scan_mtx); | 667 | if (rc) { |
665 | 668 | /* need to complete scan in cfg80211 */ | |
666 | if (rc) | 669 | local->scan_req = req; |
667 | ieee80211_scan_completed(&local->hw, true); | 670 | aborted = true; |
668 | return; | 671 | goto out_complete; |
672 | } else | ||
673 | goto out; | ||
669 | } | 674 | } |
670 | 675 | ||
671 | mutex_unlock(&local->scan_mtx); | ||
672 | |||
673 | /* | 676 | /* |
674 | * Avoid re-scheduling when the sdata is going away. | 677 | * Avoid re-scheduling when the sdata is going away. |
675 | */ | 678 | */ |
676 | if (!ieee80211_sdata_running(sdata)) { | 679 | if (!ieee80211_sdata_running(sdata)) { |
677 | ieee80211_scan_completed(&local->hw, true); | 680 | aborted = true; |
678 | return; | 681 | goto out_complete; |
679 | } | 682 | } |
680 | 683 | ||
681 | /* | 684 | /* |
@@ -685,8 +688,12 @@ void ieee80211_scan_work(struct work_struct *work) | |||
685 | do { | 688 | do { |
686 | switch (local->next_scan_state) { | 689 | switch (local->next_scan_state) { |
687 | case SCAN_DECISION: | 690 | case SCAN_DECISION: |
688 | if (ieee80211_scan_state_decision(local, &next_delay)) | 691 | /* if no more bands/channels left, complete scan */ |
689 | return; | 692 | if (local->scan_channel_idx >= local->scan_req->n_channels) { |
693 | aborted = false; | ||
694 | goto out_complete; | ||
695 | } | ||
696 | ieee80211_scan_state_decision(local, &next_delay); | ||
690 | break; | 697 | break; |
691 | case SCAN_SET_CHANNEL: | 698 | case SCAN_SET_CHANNEL: |
692 | ieee80211_scan_state_set_channel(local, &next_delay); | 699 | ieee80211_scan_state_set_channel(local, &next_delay); |
@@ -704,6 +711,19 @@ void ieee80211_scan_work(struct work_struct *work) | |||
704 | } while (next_delay == 0); | 711 | } while (next_delay == 0); |
705 | 712 | ||
706 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); | 713 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); |
714 | mutex_unlock(&local->mtx); | ||
715 | return; | ||
716 | |||
717 | out_complete: | ||
718 | hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | ||
719 | finish = __ieee80211_scan_completed(&local->hw, aborted, hw_scan); | ||
720 | mutex_unlock(&local->mtx); | ||
721 | if (finish) | ||
722 | __ieee80211_scan_completed_finish(&local->hw, hw_scan); | ||
723 | return; | ||
724 | |||
725 | out: | ||
726 | mutex_unlock(&local->mtx); | ||
707 | } | 727 | } |
708 | 728 | ||
709 | int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, | 729 | int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, |
@@ -711,9 +731,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, | |||
711 | { | 731 | { |
712 | int res; | 732 | int res; |
713 | 733 | ||
714 | mutex_lock(&sdata->local->scan_mtx); | 734 | mutex_lock(&sdata->local->mtx); |
715 | res = __ieee80211_start_scan(sdata, req); | 735 | res = __ieee80211_start_scan(sdata, req); |
716 | mutex_unlock(&sdata->local->scan_mtx); | 736 | mutex_unlock(&sdata->local->mtx); |
717 | 737 | ||
718 | return res; | 738 | return res; |
719 | } | 739 | } |
@@ -726,7 +746,7 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, | |||
726 | int ret = -EBUSY; | 746 | int ret = -EBUSY; |
727 | enum ieee80211_band band; | 747 | enum ieee80211_band band; |
728 | 748 | ||
729 | mutex_lock(&local->scan_mtx); | 749 | mutex_lock(&local->mtx); |
730 | 750 | ||
731 | /* busy scanning */ | 751 | /* busy scanning */ |
732 | if (local->scan_req) | 752 | if (local->scan_req) |
@@ -761,25 +781,44 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, | |||
761 | 781 | ||
762 | ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req); | 782 | ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req); |
763 | unlock: | 783 | unlock: |
764 | mutex_unlock(&local->scan_mtx); | 784 | mutex_unlock(&local->mtx); |
765 | return ret; | 785 | return ret; |
766 | } | 786 | } |
767 | 787 | ||
788 | /* | ||
789 | * Only call this function when a scan can't be queued -- under RTNL. | ||
790 | */ | ||
768 | void ieee80211_scan_cancel(struct ieee80211_local *local) | 791 | void ieee80211_scan_cancel(struct ieee80211_local *local) |
769 | { | 792 | { |
770 | bool abortscan; | 793 | bool abortscan; |
771 | 794 | bool finish = false; | |
772 | cancel_delayed_work_sync(&local->scan_work); | ||
773 | 795 | ||
774 | /* | 796 | /* |
775 | * Only call this function when a scan can't be | 797 | * We are only canceling software scan, or deferred scan that was not |
776 | * queued -- mostly at suspend under RTNL. | 798 | * yet really started (see __ieee80211_start_scan ). |
799 | * | ||
800 | * Regarding hardware scan: | ||
801 | * - we can not call __ieee80211_scan_completed() as when | ||
802 | * SCAN_HW_SCANNING bit is set this function change | ||
803 | * local->hw_scan_req to operate on 5G band, what race with | ||
804 | * driver which can use local->hw_scan_req | ||
805 | * | ||
806 | * - we can not cancel scan_work since driver can schedule it | ||
807 | * by ieee80211_scan_completed(..., true) to finish scan | ||
808 | * | ||
809 | * Hence low lever driver is responsible for canceling HW scan. | ||
777 | */ | 810 | */ |
778 | mutex_lock(&local->scan_mtx); | ||
779 | abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) || | ||
780 | (!local->scanning && local->scan_req); | ||
781 | mutex_unlock(&local->scan_mtx); | ||
782 | 811 | ||
812 | mutex_lock(&local->mtx); | ||
813 | abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); | ||
783 | if (abortscan) | 814 | if (abortscan) |
784 | ieee80211_scan_completed(&local->hw, true); | 815 | finish = __ieee80211_scan_completed(&local->hw, true, false); |
816 | mutex_unlock(&local->mtx); | ||
817 | |||
818 | if (abortscan) { | ||
819 | /* The scan is canceled, but stop work from being pending */ | ||
820 | cancel_delayed_work_sync(&local->scan_work); | ||
821 | } | ||
822 | if (finish) | ||
823 | __ieee80211_scan_completed_finish(&local->hw, false); | ||
785 | } | 824 | } |