| @@ -5,7 +5,7 @@ · 4 unmodified lines |
| 5 | 5 | * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> |
| 6 | 6 | * Copyright 2013-2014 Intel Mobile Communications GmbH |
| 7 | 7 | * Copyright 2016 Intel Deutschland GmbH |
| 8 | | − * Copyright (C) 2018-2019 Intel Corporation |
| 8 | + * Copyright (C) 2018-2020 Intel Corporation |
| 9 | 9 | */ |
| 10 | 10 | #include <linux/kernel.h> |
| 11 | 11 | #include <linux/slab.h> |
| @@ -14,6 +14,8 @@ · 2 unmodified lines |
| 14 | 14 | #include <linux/wireless.h> |
| 15 | 15 | #include <linux/nl80211.h> |
| 16 | 16 | #include <linux/etherdevice.h> |
| 17 | +#include <linux/crc32.h> |
| 18 | +#include <linux/bitfield.h> |
| 17 | 19 | #include <net/arp.h> |
| 18 | 20 | #include <net/cfg80211.h> |
| 19 | 21 | #include <net/cfg80211-wext.h> |
| @@ -74,6 +76,43 @@ MODULE_PARM_DESC(bss_entries_limit, · 54 unmodified lines |
| 74 | 76 | |
| 75 | 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 | 116 | static void bss_free(struct cfg80211_internal_bss *bss) |
| 78 | 117 | { |
| 79 | 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 | 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 | 913 | void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, |
| 452 | 914 | bool send_message) |
| 453 | 915 | { |
| 454 | | − struct cfg80211_scan_request *request; |
| 916 | + struct cfg80211_scan_request *request, *rdev_req; |
| 455 | 917 | struct wireless_dev *wdev; |
| 456 | 918 | struct sk_buff *msg; |
| 457 | 919 | #ifdef CONFIG_CFG80211_WEXT |
| @@ -466,11 +928,18 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, · 8 unmodified lines |
| 466 | 928 | return; |
| 467 | 929 | } |
| 468 | 930 | |
| 469 | | − request = rdev->scan_req; |
| 470 | | − if (!request) |
| 931 | + rdev_req = rdev->scan_req; |
| 932 | + if (!rdev_req) |
| 471 | 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 | 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 | 970 | if (wdev->netdev) |
| 502 | 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 | 977 | rdev->scan_req = NULL; |
| 505 | | − kfree(request); |
| 506 | 978 | |
| 507 | 979 | if (!send_message) |
| 508 | 980 | rdev->scan_msg = msg; |
| @@ -525,10 +997,25 @@ void __cfg80211_scan_done(struct work_struct *wk) · 16 unmodified lines |
| 525 | 997 | void cfg80211_scan_done(struct cfg80211_scan_request *request, |
| 526 | 998 | struct cfg80211_scan_info *info) |
| 527 | 999 | { |
| 1000 | + struct cfg80211_scan_info old_info = request->info; |
| 1001 | + |
| 528 | 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 | 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 | 1019 | request->notified = true; |
| 533 | 1020 | queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk); |
| 534 | 1021 | } |