diff mbox series

[v2] nl80211/cfg80211: support 6 GHz scanning

Message ID 20200528171713.19bf0d7ce186.I9d94ae093e08fb15b6c8f8fb7406b316778c6a5f@changeid (mailing list archive)
State Superseded
Delegated to: Johannes Berg
Headers show
Series [v2] nl80211/cfg80211: support 6 GHz scanning | expand

Commit Message

Johannes Berg May 28, 2020, 3:17 p.m. UTC
From: Tova Mussai <tova.mussai@intel.com>

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.

Signed-off-by: Tova Mussai <tova.mussai@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/net/cfg80211.h       |  29 ++-
 include/uapi/linux/nl80211.h |   3 +
 net/wireless/core.c          |   4 +-
 net/wireless/core.h          |   5 +-
 net/wireless/nl80211.c       |  11 +-
 net/wireless/scan.c          | 482 ++++++++++++++++++++++++++++++++++-
 6 files changed, 520 insertions(+), 14 deletions(-)

Comments

kernel test robot May 28, 2020, 7:02 p.m. UTC | #1
Hi Johannes,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on mac80211-next/master]
[also build test ERROR on next-20200528]
[cannot apply to mac80211/master v5.7-rc7]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Johannes-Berg/nl80211-cfg80211-support-6-GHz-scanning/20200528-232159
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next.git master
config: x86_64-allyesconfig (attached as .config)
compiler: clang version 11.0.0 (https://github.com/llvm/llvm-project 2d068e534f1671459e1b135852c1b3c10502e929)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>, old ones prefixed by <<):

>> net/wireless/scan.c:493:21: error: use of undeclared identifier 'IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED'
u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED);
^
>> net/wireless/scan.c:495:21: error: use of undeclared identifier 'IEEE80211_RNR_TBTT_PARAMS_SAME_SSID'
u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_SAME_SSID);
^
>> net/wireless/scan.c:497:21: error: use of undeclared identifier 'IEEE80211_RNR_TBTT_PARAMS_MULTI_BSSID'
u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_MULTI_BSSID);
^
>> net/wireless/scan.c:499:21: error: use of undeclared identifier 'IEEE80211_RNR_TBTT_PARAMS_TRANSMITTED_BSSID'
u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_TRANSMITTED_BSSID);
^
>> net/wireless/scan.c:501:21: error: use of undeclared identifier 'IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE'
u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE);
^
>> net/wireless/scan.c:503:21: error: use of undeclared identifier 'IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS'
u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS);
^
>> net/wireless/scan.c:505:27: error: use of undeclared identifier 'IEEE80211_RNR_TBTT_PARAMS_COLOC_AP'
return u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_AP);
^
>> net/wireless/scan.c:541:16: error: use of undeclared identifier 'IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM'
if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM) {
^
>> net/wireless/scan.c:553:16: error: use of undeclared identifier 'IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM'
if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM) {
^
>> net/wireless/scan.c:589:28: error: use of undeclared identifier 'WLAN_EID_REDUCED_NEIGHBOR_REPORT'; did you mean 'WLAN_EID_NEIGHBOR_REPORT'?
elem = cfg80211_find_elem(WLAN_EID_REDUCED_NEIGHBOR_REPORT, ies->data,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
WLAN_EID_NEIGHBOR_REPORT
include/linux/ieee80211.h:2615:2: note: 'WLAN_EID_NEIGHBOR_REPORT' declared here
WLAN_EID_NEIGHBOR_REPORT = 52,
^
>> net/wireless/scan.c:602:21: error: invalid application of 'sizeof' to an incomplete type 'struct ieee80211_neighbor_ap_info'
while (pos + sizeof(*ap_info) <= end) {
^~~~~~~~~~
net/wireless/scan.c:582:9: note: forward declaration of 'struct ieee80211_neighbor_ap_info'
struct ieee80211_neighbor_ap_info *ap_info;
^
>> net/wireless/scan.c:608:30: error: incomplete definition of type 'struct ieee80211_neighbor_ap_info'
count = u8_get_bits(ap_info->tbtt_info_hdr,
~~~~~~~^
net/wireless/scan.c:582:9: note: forward declaration of 'struct ieee80211_neighbor_ap_info'
struct ieee80211_neighbor_ap_info *ap_info;
^
>> net/wireless/scan.c:609:9: error: use of undeclared identifier 'IEEE80211_AP_INFO_TBTT_HDR_COUNT'
IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1;
^
net/wireless/scan.c:610:19: error: incomplete definition of type 'struct ieee80211_neighbor_ap_info'
length = ap_info->tbtt_info_len;
~~~~~~~^
net/wireless/scan.c:582:9: note: forward declaration of 'struct ieee80211_neighbor_ap_info'
struct ieee80211_neighbor_ap_info *ap_info;
^
net/wireless/scan.c:612:16: error: invalid application of 'sizeof' to an incomplete type 'struct ieee80211_neighbor_ap_info'
pos += sizeof(*ap_info);
^~~~~~~~~~
net/wireless/scan.c:582:9: note: forward declaration of 'struct ieee80211_neighbor_ap_info'
struct ieee80211_neighbor_ap_info *ap_info;
^
net/wireless/scan.c:614:49: error: incomplete definition of type 'struct ieee80211_neighbor_ap_info'
if (!ieee80211_operating_class_to_band(ap_info->op_class,
~~~~~~~^
net/wireless/scan.c:582:9: note: forward declaration of 'struct ieee80211_neighbor_ap_info'
struct ieee80211_neighbor_ap_info *ap_info;
^
net/wireless/scan.c:618:48: error: incomplete definition of type 'struct ieee80211_neighbor_ap_info'
freq = ieee80211_channel_to_frequency(ap_info->channel, band);
~~~~~~~^
net/wireless/scan.c:582:9: note: forward declaration of 'struct ieee80211_neighbor_ap_info'
struct ieee80211_neighbor_ap_info *ap_info;
^
net/wireless/scan.c:620:34: error: incomplete definition of type 'struct ieee80211_neighbor_ap_info'
if (end - pos < count * ap_info->tbtt_info_len)
~~~~~~~^
net/wireless/scan.c:582:9: note: forward declaration of 'struct ieee80211_neighbor_ap_info'
struct ieee80211_neighbor_ap_info *ap_info;
^
net/wireless/scan.c:630:18: error: use of undeclared identifier 'IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM'
(length != IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM &&
^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.

vim +/IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED +493 net/wireless/scan.c

   488	
   489	static u8 cfg80211_parse_bss_param(u8 data,
   490					   struct cfg80211_colocated_ap *coloc_ap)
   491	{
   492		coloc_ap->oct_recommended =
 > 493			u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED);
   494		coloc_ap->same_ssid =
 > 495			u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_SAME_SSID);
   496		coloc_ap->multi_bss =
 > 497			u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_MULTI_BSSID);
   498		coloc_ap->transmitted_bssid =
 > 499			u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_TRANSMITTED_BSSID);
   500		coloc_ap->unsolicited_probe =
 > 501			u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE);
   502		coloc_ap->colocated_ess =
 > 503			u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS);
   504	
 > 505		return u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_AP);
   506	}
   507	
   508	static int cfg80211_calc_short_ssid(const struct cfg80211_bss_ies *ies,
   509					    const struct element **elem, u32 *s_ssid)
   510	{
   511	
   512		*elem = cfg80211_find_elem(WLAN_EID_SSID, ies->data, ies->len);
   513		if (!*elem || (*elem)->datalen > IEEE80211_MAX_SSID_LEN)
   514			return -EINVAL;
   515	
   516		*s_ssid = ~crc32_le(~0, (*elem)->data, (*elem)->datalen);
   517		return 0;
   518	}
   519	
   520	static void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list)
   521	{
   522		struct cfg80211_colocated_ap *ap, *tmp_ap;
   523	
   524		list_for_each_entry_safe(ap, tmp_ap, coloc_ap_list, list) {
   525			list_del(&ap->list);
   526			kfree(ap);
   527		}
   528	}
   529	
   530	static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
   531					  const u8 *pos, u8 length,
   532					  const struct element *ssid_elem,
   533					  int s_ssid_tmp)
   534	{
   535		/* skip the TBTT offset */
   536		pos++;
   537	
   538		memcpy(entry->bssid, pos, ETH_ALEN);
   539		pos += ETH_ALEN;
   540	
 > 541		if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM) {
   542			memcpy(&entry->short_ssid, pos,
   543			       sizeof(entry->short_ssid));
   544			entry->short_ssid_valid = true;
   545			pos += 4;
   546		}
   547	
   548		/* skip non colocated APs */
   549		if (!cfg80211_parse_bss_param(*pos, entry))
   550			return -EINVAL;
   551		pos++;
   552	
 > 553		if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM) {
   554			/*
   555			 * no information about the short ssid. Consider the entry valid
   556			 * for now. It would later be dropped in case there are explicit
   557			 * SSIDs that need to be matched
   558			 */
   559			if (!entry->same_ssid)
   560				return 0;
   561		}
   562	
   563		if (entry->same_ssid) {
   564			entry->short_ssid = s_ssid_tmp;
   565			entry->short_ssid_valid = true;
   566	
   567			/*
   568			 * This is safe because we validate datalen in
   569			 * cfg80211_parse_colocated_ap(), before calling this
   570			 * function.
   571			 */
   572			memcpy(&entry->ssid, &ssid_elem->data,
   573			       ssid_elem->datalen);
   574			entry->ssid_len = ssid_elem->datalen;
   575		}
   576		return 0;
   577	}
   578	
   579	static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
   580					       struct list_head *list)
   581	{
 > 582		struct ieee80211_neighbor_ap_info *ap_info;
   583		const struct element *elem, *ssid_elem;
   584		const u8 *pos, *end;
   585		u32 s_ssid_tmp;
   586		int n_coloc = 0, ret;
   587		LIST_HEAD(ap_list);
   588	
 > 589		elem = cfg80211_find_elem(WLAN_EID_REDUCED_NEIGHBOR_REPORT, ies->data,
   590					  ies->len);
   591		if (!elem)
   592			return 0;
   593	
   594		pos = elem->data;
   595		end = pos + elem->datalen;
   596	
   597		ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp);
   598		if (ret)
   599			return ret;
   600	
   601		/* RNR IE may contain more than one NEIGHBOR_AP_INFO */
 > 602		while (pos + sizeof(*ap_info) <= end) {
   603			enum nl80211_band band;
   604			int freq;
   605			u8 length, i, count;
   606	
   607			ap_info = (void *)pos;
 > 608			count = u8_get_bits(ap_info->tbtt_info_hdr,
 > 609					    IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1;
   610			length = ap_info->tbtt_info_len;
   611	
   612			pos += sizeof(*ap_info);
   613	
   614			if (!ieee80211_operating_class_to_band(ap_info->op_class,
   615							       &band))
   616				break;
   617	
   618			freq = ieee80211_channel_to_frequency(ap_info->channel, band);
   619	
   620			if (end - pos < count * ap_info->tbtt_info_len)
   621				break;
   622	
   623			/*
   624			 * TBTT info must include bss param + BSSID +
   625			 * (short SSID or same_ssid bit to be set).
   626			 * ignore other options, and move to the
   627			 * next AP info
   628			 */
   629			if (band != NL80211_BAND_6GHZ ||
   630			    (length != IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM &&
   631			     length < IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM)) {
   632				pos += count * ap_info->tbtt_info_len;
   633				continue;
   634			}
   635	
   636			for (i = 0; i < count; i++) {
   637				struct cfg80211_colocated_ap *entry;
   638	
   639				entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN,
   640						GFP_ATOMIC);
   641	
   642				if (!entry)
   643					break;
   644	
   645				entry->center_freq = freq;
   646	
   647				if (!cfg80211_parse_ap_info(entry, pos, length, elem,
   648							    s_ssid_tmp)){
   649					n_coloc++;
   650					list_add_tail(&entry->list, &ap_list);
   651				} else {
   652					kfree(entry);
   653				}
   654	
   655				pos += ap_info->tbtt_info_len;
   656			}
   657		}
   658	
   659		if (pos != end) {
   660			cfg80211_free_coloc_ap_list(&ap_list);
   661			return 0;
   662		}
   663	
   664		list_splice_tail(&ap_list, list);
   665		return n_coloc;
   666	}
   667	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff mbox series

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 95b55eea2afb..c09d782618bb 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -2011,6 +2011,24 @@  struct cfg80211_scan_info {
 	bool aborted;
 };
 
+/**
+ * struct cfg80211_scan_6ghz_params - relevant for 6 GHz only
+ *
+ * @short_bssid: short ssid to scan for
+ * @bssid: bssid to scan for
+ * @channel_idx: idx of the channel in the channel array in the scan request
+ *	 which the above info relvant to
+ * @unsolicited_probe: the AP transmits unsolicited probe response every 20 TU
+ * @short_ssid_valid: short_ssid is valid and can be used
+ */
+struct cfg80211_scan_6ghz_params {
+	u32 short_ssid;
+	u32 channel_idx;
+	u8 bssid[ETH_ALEN];
+	bool unsolicited_probe;
+	bool short_ssid_valid;
+};
+
 /**
  * struct cfg80211_scan_request - scan request description
  *
@@ -2038,6 +2056,10 @@  struct cfg80211_scan_info {
  * @mac_addr_mask: MAC address mask used with randomisation, bits that
  *	are 0 in the mask should be randomised, bits that are 1 should
  *	be taken from the @mac_addr
+ * @scan_6ghz: relevant for split scan request only,
+ *	true if this is the second scan request
+ * @n_6ghz_params: number of 6 GHz params
+ * @scan_6ghz_params: 6 GHz params
  * @bssid: BSSID to scan for (most commonly, the wildcard BSSID)
  */
 struct cfg80211_scan_request {
@@ -2065,6 +2087,9 @@  struct cfg80211_scan_request {
 	struct cfg80211_scan_info info;
 	bool notified;
 	bool no_cck;
+	bool scan_6ghz;
+	u32 n_6ghz_params;
+	struct cfg80211_scan_6ghz_params *scan_6ghz_params;
 
 	/* keep last */
 	struct ieee80211_channel *channels[];
@@ -4128,6 +4153,8 @@  struct cfg80211_ops {
 /**
  * enum wiphy_flags - wiphy capability flags
  *
+ * @WIPHY_FLAG_SPLIT_SCAN_6GHZ: if set to true, the scan request will be split
+ *	 into two, first for legacy bands and second for UHB.
  * @WIPHY_FLAG_NETNS_OK: if not set, do not allow changing the netns of this
  *	wiphy at all
  * @WIPHY_FLAG_PS_ON_BY_DEFAULT: if set to true, powersave will be enabled
@@ -4170,7 +4197,7 @@  struct cfg80211_ops {
 enum wiphy_flags {
 	/* use hole at 0 */
 	/* use hole at 1 */
-	/* use hole at 2 */
+	WIPHY_FLAG_SPLIT_SCAN_6GHZ		= BIT(2),
 	WIPHY_FLAG_NETNS_OK			= BIT(3),
 	WIPHY_FLAG_PS_ON_BY_DEFAULT		= BIT(4),
 	WIPHY_FLAG_4ADDR_AP			= BIT(5),
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 5b350d032fa3..65de604afad0 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -5938,6 +5938,8 @@  enum nl80211_timeout_reason {
  * @NL80211_SCAN_FLAG_FREQ_KHZ: report scan results with
  *	%NL80211_ATTR_SCAN_FREQ_KHZ. This also means
  *	%NL80211_ATTR_SCAN_FREQUENCIES will not be included.
+ * @NL80211_SCAN_FLAG_COLOCATED_6GHZ: scan for colocated APs reported by
+ *	2.4/5 GHz APs
  */
 enum nl80211_scan_flags {
 	NL80211_SCAN_FLAG_LOW_PRIORITY				= 1<<0,
@@ -5954,6 +5956,7 @@  enum nl80211_scan_flags {
 	NL80211_SCAN_FLAG_RANDOM_SN				= 1<<11,
 	NL80211_SCAN_FLAG_MIN_PREQ_CONTENT			= 1<<12,
 	NL80211_SCAN_FLAG_FREQ_KHZ				= 1<<13,
+	NL80211_SCAN_FLAG_COLOCATED_6GHZ			= 1<<14,
 };
 
 /**
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 5b6714460490..0085e80282f5 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -1335,7 +1335,9 @@  static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
 	case NETDEV_DOWN:
 		cfg80211_update_iface_num(rdev, wdev->iftype, -1);
 		if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
-			if (WARN_ON(!rdev->scan_req->notified))
+			if (WARN_ON(!rdev->scan_req->notified &&
+				    (!rdev->int_scan_req ||
+				     !rdev->int_scan_req->notified)))
 				rdev->scan_req->info.aborted = true;
 			___cfg80211_scan_done(rdev, false);
 		}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index e0e5b3ee9699..24a58f56072c 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -3,7 +3,7 @@ 
  * Wireless configuration interface internals.
  *
  * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2018-2019 Intel Corporation
+ * Copyright (C) 2018-2020 Intel Corporation
  */
 #ifndef __NET_WIRELESS_CORE_H
 #define __NET_WIRELESS_CORE_H
@@ -72,6 +72,7 @@  struct cfg80211_registered_device {
 	u32 bss_generation;
 	u32 bss_entries;
 	struct cfg80211_scan_request *scan_req; /* protected by RTNL */
+	struct cfg80211_scan_request *int_scan_req;
 	struct sk_buff *scan_msg;
 	struct list_head sched_scan_req_list;
 	time64_t suspend_at;
@@ -455,6 +456,8 @@  void cfg80211_process_wdev_events(struct wireless_dev *wdev);
 bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
 				u32 center_freq_khz, u32 bw_khz);
 
+int cfg80211_scan(struct cfg80211_registered_device *rdev);
+
 extern struct work_struct cfg80211_disconnect_work;
 
 /**
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 3a24e6add13e..4be9ac52421d 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -7993,7 +7993,7 @@  static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 	request->scan_start = jiffies;
 
 	rdev->scan_req = request;
-	err = rdev_scan(rdev, request);
+	err = cfg80211_scan(rdev);
 
 	if (!err) {
 		nl80211_send_scan_start(rdev, wdev);
@@ -15288,6 +15288,7 @@  static int nl80211_add_scan_req(struct sk_buff *msg,
 	struct cfg80211_scan_request *req = rdev->scan_req;
 	struct nlattr *nest;
 	int i;
+	struct cfg80211_scan_info *info;
 
 	if (WARN_ON(!req))
 		return 0;
@@ -15331,11 +15332,13 @@  static int nl80211_add_scan_req(struct sk_buff *msg,
 	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags))
 		goto nla_put_failure;
 
-	if (req->info.scan_start_tsf &&
+	info = rdev->int_scan_req ? &rdev->int_scan_req->info :
+		&rdev->scan_req->info;
+	if (info->scan_start_tsf &&
 	    (nla_put_u64_64bit(msg, NL80211_ATTR_SCAN_START_TIME_TSF,
-			       req->info.scan_start_tsf, NL80211_BSS_PAD) ||
+			       info->scan_start_tsf, NL80211_BSS_PAD) ||
 	     nla_put(msg, NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, ETH_ALEN,
-		     req->info.tsf_bssid)))
+		     info->tsf_bssid)))
 		goto nla_put_failure;
 
 	return 0;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 74ea4cfb39fb..684d1132bc12 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -5,7 +5,7 @@ 
  * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2016	Intel Deutschland GmbH
- * Copyright (C) 2018-2019 Intel Corporation
+ * Copyright (C) 2018-2020 Intel Corporation
  */
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -14,6 +14,8 @@ 
 #include <linux/wireless.h>
 #include <linux/nl80211.h>
 #include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/bitfield.h>
 #include <net/arp.h>
 #include <net/cfg80211.h>
 #include <net/cfg80211-wext.h>
@@ -74,6 +76,42 @@  MODULE_PARM_DESC(bss_entries_limit,
 
 #define IEEE80211_SCAN_RESULT_EXPIRE	(30 * HZ)
 
+/**
+ * struct cfg80211_colocated_ap - colocated AP information
+ *
+ * @list: linked list to all colocated aPS
+ * @bssid: BSSID of the reported AP
+ * @ssid: SSID of the reported AP
+ * @ssid_len: length of the ssid
+ * @center_freq: frequency the reported AP is on
+ * @unsolicited_probe: the reported AP is part of an ESS, where all the APs
+ *	that operate in the same channel as the reported AP and that might be
+ *	detected by a STA receiving this frame, are transmitting unsolicited
+ *	Probe Response frames every 20 TUs
+ * @oct_recommended: OCT is recommended to exchange MMPDUs with the reported AP
+ * @same_ssid: the reported AP has the same SSID as the reporting AP
+ * @multi_bss: the reported AP is part of a multiple BSSID set
+ * @transmitted_bssid: the reported AP is the transmitting BSSID
+ * @colocated_ess: all the APs that share the same ESS as the reported AP are
+ *	colocated and can be discovered via legacy bands.
+ * @short_ssid_valid: short_ssid is valid and can be used
+ */
+struct cfg80211_colocated_ap {
+	struct list_head list;
+	u8 bssid[ETH_ALEN];
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	size_t ssid_len;
+	u32 short_ssid;
+	u32 center_freq;
+	u8 unsolicited_probe:1,
+	   oct_recommended:1,
+	   same_ssid:1,
+	   multi_bss:1,
+	   transmitted_bssid:1,
+	   colocated_ess:1,
+	   short_ssid_valid:1;
+};
+
 static void bss_free(struct cfg80211_internal_bss *bss)
 {
 	struct cfg80211_bss_ies *ies;
@@ -448,10 +486,415 @@  static bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev)
 	return ret;
 }
 
+static u8 cfg80211_parse_bss_param(u8 data,
+				   struct cfg80211_colocated_ap *coloc_ap)
+{
+	coloc_ap->oct_recommended =
+		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED);
+	coloc_ap->same_ssid =
+		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_SAME_SSID);
+	coloc_ap->multi_bss =
+		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_MULTI_BSSID);
+	coloc_ap->transmitted_bssid =
+		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_TRANSMITTED_BSSID);
+	coloc_ap->unsolicited_probe =
+		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE);
+	coloc_ap->colocated_ess =
+		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS);
+
+	return u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_AP);
+}
+
+static int cfg80211_calc_short_ssid(const struct cfg80211_bss_ies *ies,
+				    const struct element **elem, u32 *s_ssid)
+{
+
+	*elem = cfg80211_find_elem(WLAN_EID_SSID, ies->data, ies->len);
+	if (!*elem || (*elem)->datalen > IEEE80211_MAX_SSID_LEN)
+		return -EINVAL;
+
+	*s_ssid = ~crc32_le(~0, (*elem)->data, (*elem)->datalen);
+	return 0;
+}
+
+static void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list)
+{
+	struct cfg80211_colocated_ap *ap, *tmp_ap;
+
+	list_for_each_entry_safe(ap, tmp_ap, coloc_ap_list, list) {
+		list_del(&ap->list);
+		kfree(ap);
+	}
+}
+
+static int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
+				  const u8 *pos, u8 length,
+				  const struct element *ssid_elem,
+				  int s_ssid_tmp)
+{
+	/* skip the TBTT offset */
+	pos++;
+
+	memcpy(entry->bssid, pos, ETH_ALEN);
+	pos += ETH_ALEN;
+
+	if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM) {
+		memcpy(&entry->short_ssid, pos,
+		       sizeof(entry->short_ssid));
+		entry->short_ssid_valid = true;
+		pos += 4;
+	}
+
+	/* skip non colocated APs */
+	if (!cfg80211_parse_bss_param(*pos, entry))
+		return -EINVAL;
+	pos++;
+
+	if (length == IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM) {
+		/*
+		 * no information about the short ssid. Consider the entry valid
+		 * for now. It would later be dropped in case there are explicit
+		 * SSIDs that need to be matched
+		 */
+		if (!entry->same_ssid)
+			return 0;
+	}
+
+	if (entry->same_ssid) {
+		entry->short_ssid = s_ssid_tmp;
+		entry->short_ssid_valid = true;
+
+		/*
+		 * This is safe because we validate datalen in
+		 * cfg80211_parse_colocated_ap(), before calling this
+		 * function.
+		 */
+		memcpy(&entry->ssid, &ssid_elem->data,
+		       ssid_elem->datalen);
+		entry->ssid_len = ssid_elem->datalen;
+	}
+	return 0;
+}
+
+static int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
+				       struct list_head *list)
+{
+	struct ieee80211_neighbor_ap_info *ap_info;
+	const struct element *elem, *ssid_elem;
+	const u8 *pos, *end;
+	u32 s_ssid_tmp;
+	int n_coloc = 0, ret;
+	LIST_HEAD(ap_list);
+
+	elem = cfg80211_find_elem(WLAN_EID_REDUCED_NEIGHBOR_REPORT, ies->data,
+				  ies->len);
+	if (!elem)
+		return 0;
+
+	pos = elem->data;
+	end = pos + elem->datalen;
+
+	ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp);
+	if (ret)
+		return ret;
+
+	/* RNR IE may contain more than one NEIGHBOR_AP_INFO */
+	while (pos + sizeof(*ap_info) <= end) {
+		enum nl80211_band band;
+		int freq;
+		u8 length, i, count;
+
+		ap_info = (void *)pos;
+		count = u8_get_bits(ap_info->tbtt_info_hdr,
+				    IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1;
+		length = ap_info->tbtt_info_len;
+
+		pos += sizeof(*ap_info);
+
+		if (!ieee80211_operating_class_to_band(ap_info->op_class,
+						       &band))
+			break;
+
+		freq = ieee80211_channel_to_frequency(ap_info->channel, band);
+
+		if (end - pos < count * ap_info->tbtt_info_len)
+			break;
+
+		/*
+		 * TBTT info must include bss param + BSSID +
+		 * (short SSID or same_ssid bit to be set).
+		 * ignore other options, and move to the
+		 * next AP info
+		 */
+		if (band != NL80211_BAND_6GHZ ||
+		    (length != IEEE80211_TBTT_INFO_OFFSET_BSSID_BSS_PARAM &&
+		     length < IEEE80211_TBTT_INFO_OFFSET_BSSID_SSSID_BSS_PARAM)) {
+			pos += count * ap_info->tbtt_info_len;
+			continue;
+		}
+
+		for (i = 0; i < count; i++) {
+			struct cfg80211_colocated_ap *entry;
+
+			entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN,
+					GFP_ATOMIC);
+
+			if (!entry)
+				break;
+
+			entry->center_freq = freq;
+
+			if (!cfg80211_parse_ap_info(entry, pos, length, elem,
+						    s_ssid_tmp)){
+				n_coloc++;
+				list_add_tail(&entry->list, &ap_list);
+			} else {
+				kfree(entry);
+			}
+
+			pos += ap_info->tbtt_info_len;
+		}
+	}
+
+	if (pos != end) {
+		cfg80211_free_coloc_ap_list(&ap_list);
+		return 0;
+	}
+
+	list_splice_tail(&ap_list, list);
+	return n_coloc;
+}
+
+static  void cfg80211_scan_req_add_chan(struct cfg80211_scan_request *request,
+					struct ieee80211_channel *chan,
+					bool add_to_6ghz)
+{
+	int i;
+	u32 n_channels = request->n_channels;
+	struct cfg80211_scan_6ghz_params *params =
+		&request->scan_6ghz_params[request->n_6ghz_params];
+
+	for (i = 0; i < n_channels; i++) {
+		if (request->channels[i] == chan) {
+			if (add_to_6ghz)
+				params->channel_idx = i;
+			return;
+		}
+	}
+
+	request->channels[n_channels] = chan;
+	if (add_to_6ghz)
+		request->scan_6ghz_params[request->n_6ghz_params].channel_idx =
+			n_channels;
+
+	request->n_channels++;
+}
+
+static bool cfg80211_find_ssid_match(struct cfg80211_colocated_ap *ap,
+				     struct cfg80211_scan_request *request)
+{
+	u8 i;
+	u32 s_ssid;
+
+	for (i = 0; i < request->n_ssids; i++) {
+		/* wildcard ssid in the scan request */
+		if (!request->ssids[i].ssid_len)
+			return true;
+
+		if (ap->ssid_len &&
+		    ap->ssid_len == request->ssids[i].ssid_len) {
+			if (!memcmp(request->ssids[i].ssid, ap->ssid,
+				    ap->ssid_len))
+				return true;
+		} else if (ap->short_ssid_valid) {
+			s_ssid = ~crc32_le(~0, request->ssids[i].ssid,
+					   request->ssids[i].ssid_len);
+
+			if (ap->short_ssid == s_ssid)
+				return true;
+		}
+	}
+
+	return false;
+}
+
+static int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev)
+{
+	u8 i;
+	struct cfg80211_colocated_ap *ap;
+	int n_channels, count = 0, err;
+	struct cfg80211_scan_request *request, *rdev_req = rdev->scan_req;
+	LIST_HEAD(coloc_ap_list);
+	bool need_scan_psc;
+
+	rdev_req->scan_6ghz = true;
+
+	if (!rdev->wiphy.bands[NL80211_BAND_6GHZ])
+		return -EOPNOTSUPP;
+
+	n_channels = rdev->wiphy.bands[NL80211_BAND_6GHZ]->n_channels;
+
+	if (rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) {
+		struct cfg80211_internal_bss *intbss;
+
+		spin_lock_bh(&rdev->bss_lock);
+		list_for_each_entry(intbss, &rdev->bss_list, list) {
+			struct cfg80211_bss *res = &intbss->pub;
+
+			count += cfg80211_parse_colocated_ap(res->ies,
+							     &coloc_ap_list);
+		}
+		spin_unlock_bh(&rdev->bss_lock);
+	}
+
+	request = kzalloc(struct_size(request, channels, n_channels) +
+			  sizeof(*request->scan_6ghz_params) * count,
+			  GFP_KERNEL);
+	if (!request) {
+		cfg80211_free_coloc_ap_list(&coloc_ap_list);
+		return -ENOMEM;
+	}
+
+	*request = *rdev_req;
+	request->n_channels = 0;
+	request->scan_6ghz_params =
+		(void *)&request->channels[n_channels];
+
+	/*
+	 * PSC channels should not be scanned if all the reported co-located APs
+	 * are indicating that all APs in the same ESS are co-located
+	 */
+	if (count) {
+		need_scan_psc = false;
+
+		list_for_each_entry(ap, &coloc_ap_list, list) {
+			if (!ap->colocated_ess) {
+				need_scan_psc = true;
+				break;
+			}
+		}
+	} else {
+		need_scan_psc = true;
+	}
+
+	/*
+	 * add to the scan request the channels that need to be scanned
+	 * regardless of the collocated APs (PSC channels or all channels
+	 * in case that NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set)
+	 */
+	for (i = 0; i < rdev_req->n_channels; i++) {
+		if (rdev_req->channels[i]->band == NL80211_BAND_6GHZ &&
+		    ((need_scan_psc &&
+		      cfg80211_channel_is_psc(rdev_req->channels[i])) ||
+		     !(rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ))) {
+			cfg80211_scan_req_add_chan(request,
+						   rdev_req->channels[i],
+						   false);
+		}
+	}
+
+	if (!(rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ))
+		goto skip;
+
+	list_for_each_entry(ap, &coloc_ap_list, list) {
+		bool found = false;
+		struct cfg80211_scan_6ghz_params *scan_6ghz_params =
+			&request->scan_6ghz_params[request->n_6ghz_params];
+		struct ieee80211_channel *chan =
+			ieee80211_get_channel(&rdev->wiphy, ap->center_freq);
+
+		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+			continue;
+
+		for (i = 0; i < rdev_req->n_channels; i++) {
+			if (rdev_req->channels[i] == chan)
+				found = true;
+		}
+
+		if (!found)
+			continue;
+
+		if (request->n_ssids > 0 &&
+		    !cfg80211_find_ssid_match(ap, request))
+			continue;
+
+		cfg80211_scan_req_add_chan(request, chan, true);
+		memcpy(scan_6ghz_params->bssid, ap->bssid, ETH_ALEN);
+		scan_6ghz_params->short_ssid = ap->short_ssid;
+		scan_6ghz_params->short_ssid_valid = ap->short_ssid_valid;
+		scan_6ghz_params->unsolicited_probe = ap->unsolicited_probe;
+		request->n_6ghz_params++;
+	}
+
+skip:
+	cfg80211_free_coloc_ap_list(&coloc_ap_list);
+
+	if (request->n_channels) {
+		struct cfg80211_scan_request *old = rdev->int_scan_req;
+
+		rdev->int_scan_req = request;
+
+		/*
+		 * If this scan follows a previous scan, save the scan start
+		 * info from the first part of the scan
+		 */
+		if (old)
+			rdev->int_scan_req->info = old->info;
+
+		err = rdev_scan(rdev, request);
+		if (err) {
+			rdev->int_scan_req = old;
+			kfree(request);
+		} else {
+			kfree(old);
+		}
+
+		return err;
+	}
+
+	kfree(request);
+	return -EINVAL;
+}
+
+int cfg80211_scan(struct cfg80211_registered_device *rdev)
+{
+	struct cfg80211_scan_request *request;
+	struct cfg80211_scan_request *rdev_req = rdev->scan_req;
+	u32 n_channels = 0, idx, i;
+
+	if (!(rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ))
+		return rdev_scan(rdev, rdev_req);
+
+	for (i = 0; i < rdev_req->n_channels; i++) {
+		if (rdev_req->channels[i]->band != NL80211_BAND_6GHZ)
+			n_channels++;
+	}
+
+	if (!n_channels)
+		return cfg80211_scan_6ghz(rdev);
+
+	request = kzalloc(struct_size(request, channels, n_channels),
+			  GFP_KERNEL);
+	if (!request)
+		return -ENOMEM;
+
+	*request = *rdev_req;
+	request->n_channels = n_channels;
+
+	for (i = idx = 0; i < rdev_req->n_channels; i++) {
+		if (rdev_req->channels[i]->band != NL80211_BAND_6GHZ)
+			request->channels[idx++] = rdev_req->channels[i];
+	}
+
+	rdev_req->scan_6ghz = false;
+	rdev->int_scan_req = request;
+	return rdev_scan(rdev, request);
+}
+
 void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
 			   bool send_message)
 {
-	struct cfg80211_scan_request *request;
+	struct cfg80211_scan_request *request, *rdev_req;
 	struct wireless_dev *wdev;
 	struct sk_buff *msg;
 #ifdef CONFIG_CFG80211_WEXT
@@ -466,11 +909,18 @@  void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
 		return;
 	}
 
-	request = rdev->scan_req;
-	if (!request)
+	rdev_req = rdev->scan_req;
+	if (!rdev_req)
 		return;
 
-	wdev = request->wdev;
+	wdev = rdev_req->wdev;
+	request = rdev->int_scan_req ? rdev->int_scan_req : rdev_req;
+
+	if (wdev_running(wdev) &&
+	    (rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ) &&
+	    !rdev_req->scan_6ghz && !request->info.aborted &&
+	    !cfg80211_scan_6ghz(rdev))
+		return;
 
 	/*
 	 * This must be before sending the other events!
@@ -501,8 +951,11 @@  void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
 	if (wdev->netdev)
 		dev_put(wdev->netdev);
 
+	kfree(rdev->int_scan_req);
+	rdev->int_scan_req = NULL;
+
+	kfree(rdev->scan_req);
 	rdev->scan_req = NULL;
-	kfree(request);
 
 	if (!send_message)
 		rdev->scan_msg = msg;
@@ -525,10 +978,25 @@  void __cfg80211_scan_done(struct work_struct *wk)
 void cfg80211_scan_done(struct cfg80211_scan_request *request,
 			struct cfg80211_scan_info *info)
 {
+	struct cfg80211_scan_info old_info = request->info;
+
 	trace_cfg80211_scan_done(request, info);
-	WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req);
+	WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req &&
+		request != wiphy_to_rdev(request->wiphy)->int_scan_req);
 
 	request->info = *info;
+
+	/*
+	 * In case the scan is split, the scan_start_tsf and tsf_bssid should
+	 * be of the first part. In such a case old_info.scan_start_tsf should
+	 * be non zero.
+	 */
+	if (request->scan_6ghz && old_info.scan_start_tsf) {
+		request->info.scan_start_tsf = old_info.scan_start_tsf;
+		memcpy(request->info.tsf_bssid, old_info.tsf_bssid,
+		       sizeof(request->info.tsf_bssid));
+	}
+
 	request->notified = true;
 	queue_work(cfg80211_wq, &wiphy_to_rdev(request->wiphy)->scan_done_wk);
 }