| @@ -5,7 +5,7 @@ · 4 unmodified lines |
| 5 | * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> | 5 | * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> |
| 6 | * Copyright 2013-2014 Intel Mobile Communications GmbH | 6 | * Copyright 2013-2014 Intel Mobile Communications GmbH |
| 7 | * Copyright 2016 Intel Deutschland GmbH | 7 | * Copyright 2016 Intel Deutschland GmbH |
| 8 | * Copyright (C) 2018-2019 Intel Corporation | 8 | * Copyright (C) 2018-2020 Intel Corporation |
| 9 | */ | 9 | */ |
| 10 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 11 | #include <linux/slab.h> | 11 | #include <linux/slab.h> |
| @@ -14,6 +14,8 @@ · 2 unmodified lines |
| 14 | #include <linux/wireless.h> | 14 | #include <linux/wireless.h> |
| 15 | #include <linux/nl80211.h> | 15 | #include <linux/nl80211.h> |
| 16 | #include <linux/etherdevice.h> | 16 | #include <linux/etherdevice.h> |
| | 17 | #include <linux/crc32.h> |
| | 18 | #include <linux/bitfield.h> |
| 17 | #include <net/arp.h> | 19 | #include <net/arp.h> |
| 18 | #include <net/cfg80211.h> | 20 | #include <net/cfg80211.h> |
| 19 | #include <net/cfg80211-wext.h> | 21 | #include <net/cfg80211-wext.h> |
| @@ -74,6 +76,43 @@ MODULE_PARM_DESC(bss_entries_limit, · 54 unmodified lines |
| 74 | | 76 | |
| 75 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) | 77 | #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ) |
| 76 | | 78 | |
| | 79 | /** |
| | 80 | * struct cfg80211_colocated_ap - colocated AP information |
| | 81 | * |
| | 82 | * @list: linked list to all colocated aPS |
| | 83 | * @bssid: BSSID of the reported AP |
| | 84 | * @ssid: SSID of the reported AP |
| | 85 | * @ssid_len: length of the ssid |
| | 86 | * @center_freq: frequency the reported AP is on |
| | 87 | * @unsolicited_probe: the reported AP is part of an ESS, where all the APs |
| | 88 | * that operate in the same channel as the reported AP and that might be |
| | 89 | * detected by a STA receiving this frame, are transmitting unsolicited |
| | 90 | * Probe Response frames every 20 TUs |
| | 91 | * @oct_recommended: OCT is recommended to exchange MMPDUs with the reported AP |
| | 92 | * @same_ssid: the reported AP has the same SSID as the reporting AP |
| | 93 | * @multi_bss: the reported AP is part of a multiple BSSID set |
| | 94 | * @transmitted_bssid: the reported AP is the transmitting BSSID |
| | 95 | * @colocated_ess: all the APs that share the same ESS as the reported AP are |
| | 96 | * colocated and can be discovered via legacy bands. |
| | 97 | * @short_ssid_valid: short_ssid is valid and can be used |
| | 98 | * @short_ssid: the short SSID for this SSID |
| | 99 | */ |
| | 100 | struct cfg80211_colocated_ap { |
| | 101 | struct list_head list; |
| | 102 | u8 bssid[ETH_ALEN]; |
| | 103 | u8 ssid[IEEE80211_MAX_SSID_LEN]; |
| | 104 | size_t ssid_len; |
| | 105 | u32 short_ssid; |
| | 106 | u32 center_freq; |
| | 107 | u8 unsolicited_probe:1, |
| | 108 | oct_recommended:1, |
| | 109 | same_ssid:1, |
| | 110 | multi_bss:1, |
| | 111 | transmitted_bssid:1, |
| | 112 | colocated_ess:1, |
| | 113 | short_ssid_valid:1; |
| | 114 | }; |
| | 115 | |
| 77 | static void bss_free(struct cfg80211_internal_bss *bss) | 116 | static void bss_free(struct cfg80211_internal_bss *bss) |
| 78 | { | 117 | { |
| 79 | struct cfg80211_bss_ies *ies; | 118 | struct cfg80211_bss_ies *ies; |
| @@ -448,10 +487,433 @@ static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev) · 368 unmodified lines |
| 448 | return ret; | 487 | return ret; |
| 449 | } | 488 | } |
| 450 | | 489 | |
| | 490 | static u8 cfg80211_parse_bss_param(u8 data, |
| | 491 | struct cfg80211_colocated_ap *coloc_ap) |
| | 492 | { |
| | 493 | coloc_ap->oct_recommended = |
| | 494 | u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED); |
| | 495 | coloc_ap->same_ssid = |
| | 496 | u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_SAME_SSID); |
| | 497 | coloc_ap->multi_bss = |
| | 498 | u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_MULTI_BSSID); |
| | 499 | coloc_ap->transmitted_bssid = |
| | 500 | u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_TRANSMITTED_BSSID); |
| | 501 | coloc_ap->unsolicited_probe = |
| | 502 | u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE); |
| | 503 | coloc_ap->colocated_ess = |
| | 504 | u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS); |
| | 505 | |
| | 506 | return u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_AP); |
| | 507 | } |
| | 508 | |
| | 509 | static int cfg80211_calc_short_ssid(const struct cfg80211_bss_ies *ies, |
| | 510 | const struct element **elem, u32 *s_ssid) |
| | 511 | { |
| | 512 | |
| | 513 | *elem = cfg80211_find_elem(WLAN_EID_SSID, ies->data, ies->len); |
| | 514 | if (!*elem || (*elem)->datalen > IEEE80211_MAX_SSID_LEN) |
| | 515 | return -EINVAL; |
| | 516 | |
| | 517 | *s_ssid = ~crc32_le(~0, (*elem)->data, (*elem)->datalen); |
| | 518 | return 0; |
| | 519 | } |
| | 520 | |
| | 521 | static void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list) |
| | 522 | { |
| | 523 | struct cfg80211_colocated_ap *ap, *tmp_ap; |
| | 524 | |
| | 525 | list_for_each_entry_safe(ap, tmp_ap, coloc_ap_list, list) { |
| | 526 | list_del(&ap->list); |
| | 527 | kfree(ap); |
| | 528 | } |
| | 529 | } |
| | 530 | |
| | 531 | static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry, |
| | 532 | const u8 *pos, u8 length, |
| | 533 | const struct element *ssid_elem, |
| | 534 | int s_ssid_tmp) |
| | 535 | { |
| | 536 | /* skip the TBTT offset */ |
| | 537 | pos++; |
| | 538 | |
| | 539 | memcpy(entry->bssid, pos, ETH_ALEN); |
| | 540 | pos += ETH_ALEN; |
| | 541 | |
| | 542 | if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM) { |
| | 543 | memcpy(&entry->short_ssid, pos, |
| | 544 | sizeof(entry->short_ssid)); |
| | 545 | entry->short_ssid_valid = true; |
| | 546 | pos += 4; |
| | 547 | } |
| | 548 | |
| | 549 | /* skip non colocated APs */ |
| | 550 | if (!cfg80211_parse_bss_param(*pos, entry)) |
| | 551 | return -EINVAL; |
| | 552 | pos++; |
| | 553 | |
| | 554 | if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM) { |
| | 555 | /* |
| | 556 | * no information about the short ssid. Consider the entry valid |
| | 557 | * for now. It would later be dropped in case there are explicit |
| | 558 | * SSIDs that need to be matched |
| | 559 | */ |
| | 560 | if (!entry->same_ssid) |
| | 561 | return 0; |
| | 562 | } |
| | 563 | |
| | 564 | if (entry->same_ssid) { |
| | 565 | entry->short_ssid = s_ssid_tmp; |
| | 566 | entry->short_ssid_valid = true; |
| | 567 | |
| | 568 | /* |
| | 569 | * This is safe because we validate datalen in |
| | 570 | * cfg80211_parse_colocated_ap(), before calling this |
| | 571 | * function. |
| | 572 | */ |
| | 573 | memcpy(&entry->ssid, &ssid_elem->data, |
| | 574 | ssid_elem->datalen); |
| | 575 | entry->ssid_len = ssid_elem->datalen; |
| | 576 | } |
| | 577 | return 0; |
| | 578 | } |
| | 579 | |
| | 580 | static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies, |
| | 581 | struct list_head *list) |
| | 582 | { |
| | 583 | struct ieee80211_neighbor_ap_info *ap_info; |
| | 584 | const struct element *elem, *ssid_elem; |
| | 585 | const u8 *pos, *end; |
| | 586 | u32 s_ssid_tmp; |
| | 587 | int n_coloc = 0, ret; |
| | 588 | LIST_HEAD(ap_list); |
| | 589 | |
| | 590 | elem = cfg80211_find_elem(WLAN_EID_REDUCED_NEIGHBOR_REPORT, ies->data, |
| | 591 | ies->len); |
| | 592 | if (!elem || elem->datalen > IEEE80211_MAX_SSID_LEN) |
| | 593 | return 0; |
| | 594 | |
| | 595 | pos = elem->data; |
| | 596 | end = pos + elem->datalen; |
| | 597 | |
| | 598 | ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp); |
| | 599 | if (ret) |
| | 600 | return ret; |
| | 601 | |
| | 602 | /* RNR IE may contain more than one NEIGHBOR_AP_INFO */ |
| | 603 | while (pos + sizeof(*ap_info) <= end) { |
| | 604 | enum nl80211_band band; |
| | 605 | int freq; |
| | 606 | u8 length, i, count; |
| | 607 | |
| | 608 | ap_info = (void *)pos; |
| | 609 | count = u8_get_bits(ap_info->tbtt_info_hdr, |
| | 610 | IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1; |
| | 611 | length = ap_info->tbtt_info_len; |
| | 612 | |
| | 613 | pos += sizeof(*ap_info); |
| | 614 | |
| | 615 | if (!ieee80211_operating_class_to_band(ap_info->op_class, |
| | 616 | &band)) |
| | 617 | break; |
| | 618 | |
| | 619 | freq = ieee80211_channel_to_frequency(ap_info->channel, band); |
| | 620 | |
| | 621 | if (end - pos < count * ap_info->tbtt_info_len) |
| | 622 | break; |
| | 623 | |
| | 624 | /* |
| | 625 | * TBTT info must include bss param + BSSID + |
| | 626 | * (short SSID or same_ssid bit to be set). |
| | 627 | * ignore other options, and move to the |
| | 628 | * next AP info |
| | 629 | */ |
| | 630 | if (band != NL80211_BAND_6GHZ || |
| | 631 | (length != IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM && |
| | 632 | length < IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM)) { |
| | 633 | pos += count * ap_info->tbtt_info_len; |
| | 634 | continue; |
| | 635 | } |
| | 636 | |
| | 637 | for (i = 0; i < count; i++) { |
| | 638 | struct cfg80211_colocated_ap *entry; |
| | 639 | |
| | 640 | entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN, |
| | 641 | GFP_ATOMIC); |
| | 642 | |
| | 643 | if (!entry) |
| | 644 | break; |
| | 645 | |
| | 646 | entry->center_freq = freq; |
| | 647 | |
| | 648 | if (!cfg80211_parse_ap_info(entry, pos, length, |
| | 649 | ssid_elem, s_ssid_tmp)) { |
| | 650 | n_coloc++; |
| | 651 | list_add_tail(&entry->list, &ap_list); |
| | 652 | } else { |
| | 653 | kfree(entry); |
| | 654 | } |
| | 655 | |
| | 656 | pos += ap_info->tbtt_info_len; |
| | 657 | } |
| | 658 | } |
| | 659 | |
| | 660 | if (pos != end) { |
| | 661 | cfg80211_free_coloc_ap_list(&ap_list); |
| | 662 | return 0; |
| | 663 | } |
| | 664 | |
| | 665 | list_splice_tail(&ap_list, list); |
| | 666 | return n_coloc; |
| | 667 | } |
| | 668 | |
| | 669 | static void cfg80211_scan_req_add_chan(struct cfg80211_scan_request *request, |
| | 670 | struct ieee80211_channel *chan, |
| | 671 | bool add_to_6ghz) |
| | 672 | { |
| | 673 | int i; |
| | 674 | u32 n_channels = request->n_channels; |
| | 675 | struct cfg80211_scan_6ghz_params *params = |
| | 676 | &request->scan_6ghz_params[request->n_6ghz_params]; |
| | 677 | |
| | 678 | for (i = 0; i < n_channels; i++) { |
| | 679 | if (request->channels[i] == chan) { |
| | 680 | if (add_to_6ghz) |
| | 681 | params->channel_idx = i; |
| | 682 | return; |
| | 683 | } |
| | 684 | } |
| | 685 | |
| | 686 | request->channels[n_channels] = chan; |
| | 687 | if (add_to_6ghz) |
| | 688 | request->scan_6ghz_params[request->n_6ghz_params].channel_idx = |
| | 689 | n_channels; |
| | 690 | |
| | 691 | request->n_channels++; |
| | 692 | } |
| | 693 | |
| | 694 | static bool cfg80211_find_ssid_match(struct cfg80211_colocated_ap *ap, |
| | 695 | struct cfg80211_scan_request *request) |
| | 696 | { |
| | 697 | u8 i; |
| | 698 | u32 s_ssid; |
| | 699 | |
| | 700 | for (i = 0; i < request->n_ssids; i++) { |
| | 701 | /* wildcard ssid in the scan request */ |
| | 702 | if (!request->ssids[i].ssid_len) |
| | 703 | return true; |
| | 704 | |
| | 705 | if (ap->ssid_len && |
| | 706 | ap->ssid_len == request->ssids[i].ssid_len) { |
| | 707 | if (!memcmp(request->ssids[i].ssid, ap->ssid, |
| | 708 | ap->ssid_len)) |
| | 709 | return true; |
| | 710 | } else if (ap->short_ssid_valid) { |
| | 711 | s_ssid = ~crc32_le(~0, request->ssids[i].ssid, |
| | 712 | request->ssids[i].ssid_len); |
| | 713 | |
| | 714 | if (ap->short_ssid == s_ssid) |
| | 715 | return true; |
| | 716 | } |
| | 717 | } |
| | 718 | |
| | 719 | return false; |
| | 720 | } |
| | 721 | |
| | 722 | static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev) |
| | 723 | { |
| | 724 | u8 i; |
| | 725 | struct cfg80211_colocated_ap *ap; |
| | 726 | int n_channels, count = 0, err; |
| | 727 | struct cfg80211_scan_request *request, *rdev_req = rdev->scan_req; |
| | 728 | LIST_HEAD(coloc_ap_list); |
| | 729 | bool need_scan_psc; |
| | 730 | const struct ieee80211_sband_iftype_data *iftd; |
| | 731 | |
| | 732 | rdev_req->scan_6ghz = true; |
| | 733 | |
| | 734 | if (!rdev->wiphy.bands[NL80211_BAND_6GHZ]) |
| | 735 | return -EOPNOTSUPP; |
| | 736 | |
| | 737 | iftd = ieee80211_get_sband_iftype_data(rdev->wiphy.bands[NL80211_BAND_6GHZ], |
| | 738 | rdev_req->wdev->iftype); |
| | 739 | if (!iftd || !iftd->he_cap.has_he) |
| | 740 | return -EOPNOTSUPP; |
| | 741 | |
| | 742 | n_channels = rdev->wiphy.bands[NL80211_BAND_6GHZ]->n_channels; |
| | 743 | |
| | 744 | if (rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) { |
| | 745 | struct cfg80211_internal_bss *intbss; |
| | 746 | |
| | 747 | spin_lock_bh(&rdev->bss_lock); |
| | 748 | list_for_each_entry(intbss, &rdev->bss_list, list) { |
| | 749 | struct cfg80211_bss *res = &intbss->pub; |
| | 750 | const struct cfg80211_bss_ies *ies; |
| | 751 | |
| | 752 | ies = rcu_access_pointer(res->ies); |
| | 753 | count += cfg80211_parse_colocated_ap(ies, |
| | 754 | &coloc_ap_list); |
| | 755 | } |
| | 756 | spin_unlock_bh(&rdev->bss_lock); |
| | 757 | } |
| | 758 | |
| | 759 | request = kzalloc(struct_size(request, channels, n_channels) + |
| | 760 | sizeof(*request->scan_6ghz_params) * count, |
| | 761 | GFP_KERNEL); |
| | 762 | if (!request) { |
| | 763 | cfg80211_free_coloc_ap_list(&coloc_ap_list); |
| | 764 | return -ENOMEM; |
| | 765 | } |
| | 766 | |
| | 767 | *request = *rdev_req; |
| | 768 | request->n_channels = 0; |
| | 769 | request->scan_6ghz_params = |
| | 770 | (void *)&request->channels[n_channels]; |
| | 771 | |
| | 772 | /* |
| | 773 | * PSC channels should not be scanned if all the reported co-located APs |
| | 774 | * are indicating that all APs in the same ESS are co-located |
| | 775 | */ |
| | 776 | if (count) { |
| | 777 | need_scan_psc = false; |
| | 778 | |
| | 779 | list_for_each_entry(ap, &coloc_ap_list, list) { |
| | 780 | if (!ap->colocated_ess) { |
| | 781 | need_scan_psc = true; |
| | 782 | break; |
| | 783 | } |
| | 784 | } |
| | 785 | } else { |
| | 786 | need_scan_psc = true; |
| | 787 | } |
| | 788 | |
| | 789 | /* |
| | 790 | * add to the scan request the channels that need to be scanned |
| | 791 | * regardless of the collocated APs (PSC channels or all channels |
| | 792 | * in case that NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set) |
| | 793 | */ |
| | 794 | for (i = 0; i < rdev_req->n_channels; i++) { |
| | 795 | if (rdev_req->channels[i]->band == NL80211_BAND_6GHZ && |
| | 796 | ((need_scan_psc && |
| | 797 | cfg80211_channel_is_psc(rdev_req->channels[i])) || |
| | 798 | !(rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ))) { |
| | 799 | cfg80211_scan_req_add_chan(request, |
| | 800 | rdev_req->channels[i], |
| | 801 | false); |
| | 802 | } |
| | 803 | } |
| | 804 | |
| | 805 | if (!(rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ)) |
| | 806 | goto skip; |
| | 807 | |
| | 808 | list_for_each_entry(ap, &coloc_ap_list, list) { |
| | 809 | bool found = false; |
| | 810 | struct cfg80211_scan_6ghz_params *scan_6ghz_params = |
| | 811 | &request->scan_6ghz_params[request->n_6ghz_params]; |
| | 812 | struct ieee80211_channel *chan = |
| | 813 | ieee80211_get_channel(&rdev->wiphy, ap->center_freq); |
| | 814 | |
| | 815 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) |
| | 816 | continue; |
| | 817 | |
| | 818 | for (i = 0; i < rdev_req->n_channels; i++) { |
| | 819 | if (rdev_req->channels[i] == chan) |
| | 820 | found = true; |
| | 821 | } |
| | 822 | |
| | 823 | if (!found) |
| | 824 | continue; |
| | 825 | |
| | 826 | if (request->n_ssids > 0 && |
| | 827 | !cfg80211_find_ssid_match(ap, request)) |
| | 828 | continue; |
| | 829 | |
| | 830 | cfg80211_scan_req_add_chan(request, chan, true); |
| | 831 | memcpy(scan_6ghz_params->bssid, ap->bssid, ETH_ALEN); |
| | 832 | scan_6ghz_params->short_ssid = ap->short_ssid; |
| | 833 | scan_6ghz_params->short_ssid_valid = ap->short_ssid_valid; |
| | 834 | scan_6ghz_params->unsolicited_probe = ap->unsolicited_probe; |
| | 835 | |
| | 836 | /* |
| | 837 | * If a PSC channel is added to the scan and 'need_scan_psc' is |
| | 838 | * set to false, then all the APs that the scan logic is |
| | 839 | * interested with on the channel are collocated and thus there |
| | 840 | * is no need to perform the initial PSC channel listen. |
| | 841 | */ |
| | 842 | if (cfg80211_channel_is_psc(chan) && !need_scan_psc) |
| | 843 | scan_6ghz_params->psc_no_listen = true; |
| | 844 | |
| | 845 | request->n_6ghz_params++; |
| | 846 | } |
| | 847 | |
| | 848 | skip: |
| | 849 | cfg80211_free_coloc_ap_list(&coloc_ap_list); |
| | 850 | |
| | 851 | if (request->n_channels) { |
| | 852 | struct cfg80211_scan_request *old = rdev->int_scan_req; |
| | 853 | |
| | 854 | rdev->int_scan_req = request; |
| | 855 | |
| | 856 | /* |
| | 857 | * If this scan follows a previous scan, save the scan start |
| | 858 | * info from the first part of the scan |
| | 859 | */ |
| | 860 | if (old) |
| | 861 | rdev->int_scan_req->info = old->info; |
| | 862 | |
| | 863 | err = rdev_scan(rdev, request); |
| | 864 | if (err) { |
| | 865 | rdev->int_scan_req = old; |
| | 866 | kfree(request); |
| | 867 | } else { |
| | 868 | kfree(old); |
| | 869 | } |
| | 870 | |
| | 871 | return err; |
| | 872 | } |
| | 873 | |
| | 874 | kfree(request); |
| | 875 | return -EINVAL; |
| | 876 | } |
| | 877 | |
| | 878 | int cfg80211_scan(struct cfg80211_registered_device *rdev) |
| | 879 | { |
| | 880 | struct cfg80211_scan_request *request; |
| | 881 | struct cfg80211_scan_request *rdev_req = rdev->scan_req; |
| | 882 | u32 n_channels = 0, idx, i; |
| | 883 | |
| | 884 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ)) |
| | 885 | return rdev_scan(rdev, rdev_req); |
| | 886 | |
| | 887 | for (i = 0; i < rdev_req->n_channels; i++) { |
| | 888 | if (rdev_req->channels[i]->band != NL80211_BAND_6GHZ) |
| | 889 | n_channels++; |
| | 890 | } |
| | 891 | |
| | 892 | if (!n_channels) |
| | 893 | return cfg80211_scan_6ghz(rdev); |
| | 894 | |
| | 895 | request = kzalloc(struct_size(request, channels, n_channels), |
| | 896 | GFP_KERNEL); |
| | 897 | if (!request) |
| | 898 | return -ENOMEM; |
| | 899 | |
| | 900 | *request = *rdev_req; |
| | 901 | request->n_channels = n_channels; |
| | 902 | |
| | 903 | for (i = idx = 0; i < rdev_req->n_channels; i++) { |
| | 904 | if (rdev_req->channels[i]->band != NL80211_BAND_6GHZ) |
| | 905 | request->channels[idx++] = rdev_req->channels[i]; |
| | 906 | } |
| | 907 | |
| | 908 | rdev_req->scan_6ghz = false; |
| | 909 | rdev->int_scan_req = request; |
| | 910 | return rdev_scan(rdev, request); |
| | 911 | } |
| | 912 | |
| 451 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, | 913 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, |
| 452 | bool send_message) | 914 | bool send_message) |
| 453 | { | 915 | { |
| 454 | struct cfg80211_scan_request *request; | 916 | struct cfg80211_scan_request *request, *rdev_req; |
| 455 | struct wireless_dev *wdev; | 917 | struct wireless_dev *wdev; |
| 456 | struct sk_buff *msg; | 918 | struct sk_buff *msg; |
| 457 | #ifdef CONFIG_CFG80211_WEXT | 919 | #ifdef CONFIG_CFG80211_WEXT |
| @@ -466,11 +928,18 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, · 8 unmodified lines |
| 466 | return; | 928 | return; |
| 467 | } | 929 | } |
| 468 | | 930 | |
| 469 | request = rdev->scan_req; | 931 | rdev_req = rdev->scan_req; |
| 470 | if (!request) | 932 | if (!rdev_req) |
| 471 | return; | 933 | return; |
| 472 | | 934 | |
| 473 | wdev = request->wdev; | 935 | wdev = rdev_req->wdev; |
| | 936 | request = rdev->int_scan_req ? rdev->int_scan_req : rdev_req; |
| | 937 | |
| | 938 | if (wdev_running(wdev) && |
| | 939 | (rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ) && |
| | 940 | !rdev_req->scan_6ghz && !request->info.aborted && |
| | 941 | !cfg80211_scan_6ghz(rdev)) |
| | 942 | return; |
| 474 | | 943 | |
| 475 | /* | 944 | /* |
| 476 | * This must be before sending the other events! | 945 | * This must be before sending the other events! |
| @@ -501,8 +970,11 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, · 24 unmodified lines |
| 501 | if (wdev->netdev) | 970 | if (wdev->netdev) |
| 502 | dev_put(wdev->netdev); | 971 | dev_put(wdev->netdev); |
| 503 | | 972 | |
| | 973 | kfree(rdev->int_scan_req); |
| | 974 | rdev->int_scan_req = NULL; |
| | 975 | |
| | 976 | kfree(rdev->scan_req); |
| 504 | rdev->scan_req = NULL; | 977 | rdev->scan_req = NULL; |
| 505 | kfree(request); | | |
| 506 | | 978 | |
| 507 | if (!send_message) | 979 | if (!send_message) |
| 508 | rdev->scan_msg = msg; | 980 | rdev->scan_msg = msg; |
| @@ -525,10 +997,25 @@ void __cfg80211_scan_done(struct work_struct *wk) · 16 unmodified lines |
| 525 | void cfg80211_scan_done(struct cfg80211_scan_request *request, | 997 | void cfg80211_scan_done(struct cfg80211_scan_request *request, |
| 526 | struct cfg80211_scan_info *info) | 998 | struct cfg80211_scan_info *info) |
| 527 | { | 999 | { |
| | 1000 | struct cfg80211_scan_info old_info = request->info; |
| | 1001 | |
| 528 | trace_cfg80211_scan_done(request, info); | 1002 | trace_cfg80211_scan_done(request, info); |
| 529 | WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req); | 1003 | WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req && |
| | 1004 | request != wiphy_to_rdev(request->wiphy)->int_scan_req); |
| 530 | | 1005 | |
| 531 | request->info = *info; | 1006 | request->info = *info; |
| | 1007 | |
| | 1008 | /* |
| | 1009 | * In case the scan is split, the scan_start_tsf and tsf_bssid should |
| | 1010 | * be of the first part. In such a case old_info.scan_start_tsf should |
| | 1011 | * be non zero. |
| | 1012 | */ |
| | 1013 | if (request->scan_6ghz && old_info.scan_start_tsf) { |
| | 1014 | request->info.scan_start_tsf = old_info.scan_start_tsf; |
| | 1015 | memcpy(request->info.tsf_bssid, old_info.tsf_bssid, |
| | 1016 | sizeof(request->info.tsf_bssid)); |
| | 1017 | } |
| | 1018 | |
| 532 | request->notified = true; | 1019 | request->notified = true; |
| 533 | queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk); | 1020 | queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk); |
| 534 | } | 1021 | } |