History

nl80211/cfg80211: support 6 GHz scanning

Support 6 GHz scanning, by * a new scan flag to scan for colocated BSSes advertised by (and found) APs on 2.4 & 5 GHz * doing the necessary reduced neighbor report parsing for this, to find them * adding the ability to split the scan request in case the device by itself cannot support this. Also add some necessary bits in mac80211 to not break with these changes.

Tova Mussai committed 6 years ago

Commit c8cb5b81 parent dba04917 files · +552 −17Browse files

Unified · Split

7 files
  1. include/net/cfg80211.h +31 −1
  2. include/uapi/linux/nl80211.h +3 −0
  3. net/mac80211/scan.c +7 −2
  4. net/wireless/core.c +6 −2
  5. net/wireless/core.h +4 −1
  6. net/wireless/nl80211.c +7 −4
  7. net/wireless/scan.c +494 −7
include/net/cfg80211.h+31−1
@@ -2095,6 +2095,27 @@ struct cfg80211_scan_info { · 2094 unmodified lines
20952095 bool aborted;
20962096 };
20972097
2098+/**
2099+ * struct cfg80211_scan_6ghz_params - relevant for 6 GHz only
2100+ *
2101+ * @short_bssid: short ssid to scan for
2102+ * @bssid: bssid to scan for
2103+ * @channel_idx: idx of the channel in the channel array in the scan request
2104+ * which the above info relvant to
2105+ * @unsolicited_probe: the AP transmits unsolicited probe response every 20 TU
2106+ * @short_ssid_valid: short_ssid is valid and can be used
2107+ * @psc_no_listen: when set, and the channel is a PSC channel, no need to wait
2108+ * 20 TUs before starting to send probe requests.
2109+ */
2110+struct cfg80211_scan_6ghz_params {
2111+ u32 short_ssid;
2112+ u32 channel_idx;
2113+ u8 bssid[ETH_ALEN];
2114+ bool unsolicited_probe;
2115+ bool short_ssid_valid;
2116+ bool psc_no_listen;
2117+};
2118+
20982119 /**
20992120 * struct cfg80211_scan_request - scan request description
21002121 *
@@ -2122,6 +2143,10 @@ struct cfg80211_scan_info { · 21 unmodified lines
21222143 * @mac_addr_mask: MAC address mask used with randomisation, bits that
21232144 * are 0 in the mask should be randomised, bits that are 1 should
21242145 * be taken from the @mac_addr
2146+ * @scan_6ghz: relevant for split scan request only,
2147+ * true if this is the second scan request
2148+ * @n_6ghz_params: number of 6 GHz params
2149+ * @scan_6ghz_params: 6 GHz params
21252150 * @bssid: BSSID to scan for (most commonly, the wildcard BSSID)
21262151 */
21272152 struct cfg80211_scan_request {
@@ -2149,6 +2174,9 @@ struct cfg80211_scan_request { · 21 unmodified lines
21492174 struct cfg80211_scan_info info;
21502175 bool notified;
21512176 bool no_cck;
2177+ bool scan_6ghz;
2178+ u32 n_6ghz_params;
2179+ struct cfg80211_scan_6ghz_params *scan_6ghz_params;
21522180
21532181 /* keep last */
21542182 struct ieee80211_channel *channels[];
@@ -4217,6 +4245,8 @@ struct cfg80211_ops { · 2062 unmodified lines
42174245 /**
42184246 * enum wiphy_flags - wiphy capability flags
42194247 *
4248+ * @WIPHY_FLAG_SPLIT_SCAN_6GHZ: if set to true, the scan request will be split
4249+ * into two, first for legacy bands and second for UHB.
42204250 * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this
42214251 * wiphy at all
42224252 * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled
@@ -4260,7 +4290,7 @@ struct cfg80211_ops { · 37 unmodified lines
42604290 enum wiphy_flags {
42614291 WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK = BIT(0),
42624292 /* use hole at 1 */
4263− /* use hole at 2 */
4293+ WIPHY_FLAG_SPLIT_SCAN_6GHZ = BIT(2),
42644294 WIPHY_FLAG_NETNS_OK = BIT(3),
42654295 WIPHY_FLAG_PS_ON_BY_DEFAULT = BIT(4),
42664296 WIPHY_FLAG_4ADDR_AP = BIT(5),
include/uapi/linux/nl80211.h+3−0
@@ -6059,6 +6059,8 @@ enum nl80211_timeout_reason { · 6058 unmodified lines
60596059 * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
60606060 * %NL80211_ATTR_SCAN_FREQ_KHZ. This also means
60616061 * %NL80211_ATTR_SCAN_FREQUENCIES will not be included.
6062+ * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for colocated APs reported by
6063+ * 2.4/5 GHz APs
60626064 */
60636065 enum nl80211_scan_flags {
60646066 NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
@@ -6075,6 +6077,7 @@ enum nl80211_scan_flags { · 10 unmodified lines
60756077 NL80211_SCAN_FLAG_RANDOM_SN = 1<<11,
60766078 NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 1<<12,
60776079 NL80211_SCAN_FLAG_FREQ_KHZ = 1<<13,
6080+ NL80211_SCAN_FLAG_COLOCATED_6GHZ = 1<<14,
60786081 };
60796082
60806083 /**
net/mac80211/scan.c+7−2
@@ -9,7 +9,7 @@ · 8 unmodified lines
99 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
1010 * Copyright 2013-2015 Intel Mobile Communications GmbH
1111 * Copyright 2016-2017 Intel Deutschland GmbH
12− * Copyright (C) 2018-2019 Intel Corporation
12+ * Copyright (C) 2018-2020 Intel Corporation
1313 */
1414
1515 #include <linux/if_arp.h>
@@ -712,6 +712,10 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, · 696 unmodified lines
712712 req->duration_mandatory;
713713
714714 local->hw_scan_band = 0;
715+ local->hw_scan_req->req.n_6ghz_params = req->n_6ghz_params;
716+ local->hw_scan_req->req.scan_6ghz_params =
717+ req->scan_6ghz_params;
718+ local->hw_scan_req->req.scan_6ghz = req->scan_6ghz;
715719
716720 /*
717721 * After allocating local->hw_scan_req, we must
@@ -1124,7 +1128,8 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, · 406 unmodified lines
11241128 int max_n;
11251129
11261130 for (band = 0; band < NUM_NL80211_BANDS; band++) {
1127− if (!local->hw.wiphy->bands[band])
1131+ if (!local->hw.wiphy->bands[band] ||
1132+ band == NL80211_BAND_6GHZ)
11281133 continue;
11291134
11301135 max_n = local->hw.wiphy->bands[band]->n_channels;
net/wireless/core.c+6−2
@@ -236,7 +236,9 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, · 235 unmodified lines
236236 rdev->opencount--;
237237
238238 if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
239− if (WARN_ON(!rdev->scan_req->notified))
239+ if (WARN_ON(!rdev->scan_req->notified &&
240+ (!rdev->int_scan_req ||
241+ !rdev->int_scan_req->notified)))
240242 rdev->scan_req->info.aborted = true;
241243 ___cfg80211_scan_done(rdev, false);
242244 }
@@ -1336,7 +1338,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, · 1093 unmodified lines
13361338 case NETDEV_DOWN:
13371339 cfg80211_update_iface_num(rdev, wdev->iftype, -1);
13381340 if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
1339− if (WARN_ON(!rdev->scan_req->notified))
1341+ if (WARN_ON(!rdev->scan_req->notified &&
1342+ (!rdev->int_scan_req ||
1343+ !rdev->int_scan_req->notified)))
13401344 rdev->scan_req->info.aborted = true;
13411345 ___cfg80211_scan_done(rdev, false);
13421346 }
net/wireless/core.h+4−1
@@ -3,7 +3,7 @@ · 2 unmodified lines
33 * Wireless configuration interface internals.
44 *
55 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
6− * Copyright (C) 2018-2019 Intel Corporation
6+ * Copyright (C) 2018-2020 Intel Corporation
77 */
88 #ifndef __NET_WIRELESS_CORE_H
99 #define __NET_WIRELESS_CORE_H
@@ -72,6 +72,7 @@ struct cfg80211_registered_device { · 62 unmodified lines
7272 u32 bss_generation;
7373 u32 bss_entries;
7474 struct cfg80211_scan_request *scan_req; /* protected by RTNL */
75+ struct cfg80211_scan_request *int_scan_req;
7576 struct sk_buff *scan_msg;
7677 struct list_head sched_scan_req_list;
7778 time64_t suspend_at;
@@ -457,6 +458,8 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev); · 379 unmodified lines
457458 bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
458459 u32 center_freq_khz, u32 bw_khz);
459460
461+int cfg80211_scan(struct cfg80211_registered_device *rdev);
462+
460463 extern struct work_struct cfg80211_disconnect_work;
461464
462465 /**
net/wireless/nl80211.c+7−4
@@ -8236,7 +8236,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) · 8235 unmodified lines
82368236 request->scan_start = jiffies;
82378237
82388238 rdev->scan_req = request;
8239− err = rdev_scan(rdev, request);
8239+ err = cfg80211_scan(rdev);
82408240
82418241 if (err)
82428242 goto out_free;
@@ -15518,6 +15518,7 @@ static int nl80211_add_scan_req(struct sk_buff *msg, · 7275 unmodified lines
1551815518 struct cfg80211_scan_request *req = rdev->scan_req;
1551915519 struct nlattr *nest;
1552015520 int i;
15521+ struct cfg80211_scan_info *info;
1552115522
1552215523 if (WARN_ON(!req))
1552315524 return 0;
@@ -15561,11 +15562,13 @@ static int nl80211_add_scan_req(struct sk_buff *msg, · 37 unmodified lines
1556115562 nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags))
1556215563 goto nla_put_failure;
1556315564
15564− if (req->info.scan_start_tsf &&
15565+ info = rdev->int_scan_req ? &rdev->int_scan_req->info :
15566+ &rdev->scan_req->info;
15567+ if (info->scan_start_tsf &&
1556515568 (nla_put_u64_64bit(msg, NL80211_ATTR_SCAN_START_TIME_TSF,
15566− req->info.scan_start_tsf, NL80211_BSS_PAD) ||
15569+ info->scan_start_tsf, NL80211_BSS_PAD) ||
1556715570 nla_put(msg, NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, ETH_ALEN,
15568− req->info.tsf_bssid)))
15571+ info->tsf_bssid)))
1556915572 goto nla_put_failure;
1557015573
1557115574 return 0;
net/wireless/scan.c+494−7
@@ -5,7 +5,7 @@ · 4 unmodified lines
55 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
66 * Copyright 2013-2014 Intel Mobile Communications GmbH
77 * Copyright 2016 Intel Deutschland GmbH
8− * Copyright (C) 2018-2019 Intel Corporation
8+ * Copyright (C) 2018-2020 Intel Corporation
99 */
1010 #include <linux/kernel.h>
1111 #include <linux/slab.h>
@@ -14,6 +14,8 @@ · 2 unmodified lines
1414 #include <linux/wireless.h>
1515 #include <linux/nl80211.h>
1616 #include <linux/etherdevice.h>
17+#include <linux/crc32.h>
18+#include <linux/bitfield.h>
1719 #include <net/arp.h>
1820 #include <net/cfg80211.h>
1921 #include <net/cfg80211-wext.h>
@@ -74,6 +76,43 @@ MODULE_PARM_DESC(bss_entries_limit, · 54 unmodified lines
7476
7577 #define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
7678
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+
77116 static void bss_free(struct cfg80211_internal_bss *bss)
78117 {
79118 struct cfg80211_bss_ies *ies;
@@ -448,10 +487,433 @@ static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev) · 368 unmodified lines
448487 return ret;
449488 }
450489
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+
451913 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
452914 bool send_message)
453915 {
454− struct cfg80211_scan_request *request;
916+ struct cfg80211_scan_request *request, *rdev_req;
455917 struct wireless_dev *wdev;
456918 struct sk_buff *msg;
457919 #ifdef CONFIG_CFG80211_WEXT
@@ -466,11 +928,18 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, · 8 unmodified lines
466928 return;
467929 }
468930
469− request = rdev->scan_req;
470− if (!request)
931+ rdev_req = rdev->scan_req;
932+ if (!rdev_req)
471933 return;
472934
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;
474943
475944 /*
476945 * This must be before sending the other events!
@@ -501,8 +970,11 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, · 24 unmodified lines
501970 if (wdev->netdev)
502971 dev_put(wdev->netdev);
503972
973+ kfree(rdev->int_scan_req);
974+ rdev->int_scan_req = NULL;
975+
976+ kfree(rdev->scan_req);
504977 rdev->scan_req = NULL;
505− kfree(request);
506978
507979 if (!send_message)
508980 rdev->scan_msg = msg;
@@ -525,10 +997,25 @@ void __cfg80211_scan_done(struct work_struct *wk) · 16 unmodified lines
525997 void cfg80211_scan_done(struct cfg80211_scan_request *request,
526998 struct cfg80211_scan_info *info)
527999 {
1000+ struct cfg80211_scan_info old_info = request->info;
1001+
5281002 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);
5301005
5311006 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+
5321019 request->notified = true;
5331020 queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
5341021 }