162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * cfg80211 scan result handling
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
662306a36Sopenharmony_ci * Copyright 2013-2014  Intel Mobile Communications GmbH
762306a36Sopenharmony_ci * Copyright 2016	Intel Deutschland GmbH
862306a36Sopenharmony_ci * Copyright (C) 2018-2023 Intel Corporation
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/netdevice.h>
1462306a36Sopenharmony_ci#include <linux/wireless.h>
1562306a36Sopenharmony_ci#include <linux/nl80211.h>
1662306a36Sopenharmony_ci#include <linux/etherdevice.h>
1762306a36Sopenharmony_ci#include <linux/crc32.h>
1862306a36Sopenharmony_ci#include <linux/bitfield.h>
1962306a36Sopenharmony_ci#include <net/arp.h>
2062306a36Sopenharmony_ci#include <net/cfg80211.h>
2162306a36Sopenharmony_ci#include <net/cfg80211-wext.h>
2262306a36Sopenharmony_ci#include <net/iw_handler.h>
2362306a36Sopenharmony_ci#include "core.h"
2462306a36Sopenharmony_ci#include "nl80211.h"
2562306a36Sopenharmony_ci#include "wext-compat.h"
2662306a36Sopenharmony_ci#include "rdev-ops.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/**
2962306a36Sopenharmony_ci * DOC: BSS tree/list structure
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * At the top level, the BSS list is kept in both a list in each
3262306a36Sopenharmony_ci * registered device (@bss_list) as well as an RB-tree for faster
3362306a36Sopenharmony_ci * lookup. In the RB-tree, entries can be looked up using their
3462306a36Sopenharmony_ci * channel, MESHID, MESHCONF (for MBSSes) or channel, BSSID, SSID
3562306a36Sopenharmony_ci * for other BSSes.
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * Due to the possibility of hidden SSIDs, there's a second level
3862306a36Sopenharmony_ci * structure, the "hidden_list" and "hidden_beacon_bss" pointer.
3962306a36Sopenharmony_ci * The hidden_list connects all BSSes belonging to a single AP
4062306a36Sopenharmony_ci * that has a hidden SSID, and connects beacon and probe response
4162306a36Sopenharmony_ci * entries. For a probe response entry for a hidden SSID, the
4262306a36Sopenharmony_ci * hidden_beacon_bss pointer points to the BSS struct holding the
4362306a36Sopenharmony_ci * beacon's information.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Reference counting is done for all these references except for
4662306a36Sopenharmony_ci * the hidden_list, so that a beacon BSS struct that is otherwise
4762306a36Sopenharmony_ci * not referenced has one reference for being on the bss_list and
4862306a36Sopenharmony_ci * one for each probe response entry that points to it using the
4962306a36Sopenharmony_ci * hidden_beacon_bss pointer. When a BSS struct that has such a
5062306a36Sopenharmony_ci * pointer is get/put, the refcount update is also propagated to
5162306a36Sopenharmony_ci * the referenced struct, this ensure that it cannot get removed
5262306a36Sopenharmony_ci * while somebody is using the probe response version.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Note that the hidden_beacon_bss pointer never changes, due to
5562306a36Sopenharmony_ci * the reference counting. Therefore, no locking is needed for
5662306a36Sopenharmony_ci * it.
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci * Also note that the hidden_beacon_bss pointer is only relevant
5962306a36Sopenharmony_ci * if the driver uses something other than the IEs, e.g. private
6062306a36Sopenharmony_ci * data stored in the BSS struct, since the beacon IEs are
6162306a36Sopenharmony_ci * also linked into the probe response struct.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci * Limit the number of BSS entries stored in mac80211. Each one is
6662306a36Sopenharmony_ci * a bit over 4k at most, so this limits to roughly 4-5M of memory.
6762306a36Sopenharmony_ci * If somebody wants to really attack this though, they'd likely
6862306a36Sopenharmony_ci * use small beacons, and only one type of frame, limiting each of
6962306a36Sopenharmony_ci * the entries to a much smaller size (in order to generate more
7062306a36Sopenharmony_ci * entries in total, so overhead is bigger.)
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic int bss_entries_limit = 1000;
7362306a36Sopenharmony_cimodule_param(bss_entries_limit, int, 0644);
7462306a36Sopenharmony_ciMODULE_PARM_DESC(bss_entries_limit,
7562306a36Sopenharmony_ci                 "limit to number of scan BSS entries (per wiphy, default 1000)");
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define IEEE80211_SCAN_RESULT_EXPIRE	(30 * HZ)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/**
8062306a36Sopenharmony_ci * struct cfg80211_colocated_ap - colocated AP information
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * @list: linked list to all colocated aPS
8362306a36Sopenharmony_ci * @bssid: BSSID of the reported AP
8462306a36Sopenharmony_ci * @ssid: SSID of the reported AP
8562306a36Sopenharmony_ci * @ssid_len: length of the ssid
8662306a36Sopenharmony_ci * @center_freq: frequency the reported AP is on
8762306a36Sopenharmony_ci * @unsolicited_probe: the reported AP is part of an ESS, where all the APs
8862306a36Sopenharmony_ci *	that operate in the same channel as the reported AP and that might be
8962306a36Sopenharmony_ci *	detected by a STA receiving this frame, are transmitting unsolicited
9062306a36Sopenharmony_ci *	Probe Response frames every 20 TUs
9162306a36Sopenharmony_ci * @oct_recommended: OCT is recommended to exchange MMPDUs with the reported AP
9262306a36Sopenharmony_ci * @same_ssid: the reported AP has the same SSID as the reporting AP
9362306a36Sopenharmony_ci * @multi_bss: the reported AP is part of a multiple BSSID set
9462306a36Sopenharmony_ci * @transmitted_bssid: the reported AP is the transmitting BSSID
9562306a36Sopenharmony_ci * @colocated_ess: all the APs that share the same ESS as the reported AP are
9662306a36Sopenharmony_ci *	colocated and can be discovered via legacy bands.
9762306a36Sopenharmony_ci * @short_ssid_valid: short_ssid is valid and can be used
9862306a36Sopenharmony_ci * @short_ssid: the short SSID for this SSID
9962306a36Sopenharmony_ci * @psd_20: The 20MHz PSD EIRP of the primary 20MHz channel for the reported AP
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_cistruct cfg80211_colocated_ap {
10262306a36Sopenharmony_ci	struct list_head list;
10362306a36Sopenharmony_ci	u8 bssid[ETH_ALEN];
10462306a36Sopenharmony_ci	u8 ssid[IEEE80211_MAX_SSID_LEN];
10562306a36Sopenharmony_ci	size_t ssid_len;
10662306a36Sopenharmony_ci	u32 short_ssid;
10762306a36Sopenharmony_ci	u32 center_freq;
10862306a36Sopenharmony_ci	u8 unsolicited_probe:1,
10962306a36Sopenharmony_ci	   oct_recommended:1,
11062306a36Sopenharmony_ci	   same_ssid:1,
11162306a36Sopenharmony_ci	   multi_bss:1,
11262306a36Sopenharmony_ci	   transmitted_bssid:1,
11362306a36Sopenharmony_ci	   colocated_ess:1,
11462306a36Sopenharmony_ci	   short_ssid_valid:1;
11562306a36Sopenharmony_ci	s8 psd_20;
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void bss_free(struct cfg80211_internal_bss *bss)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct cfg80211_bss_ies *ies;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (WARN_ON(atomic_read(&bss->hold)))
12362306a36Sopenharmony_ci		return;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
12662306a36Sopenharmony_ci	if (ies && !bss->pub.hidden_beacon_bss)
12762306a36Sopenharmony_ci		kfree_rcu(ies, rcu_head);
12862306a36Sopenharmony_ci	ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
12962306a36Sopenharmony_ci	if (ies)
13062306a36Sopenharmony_ci		kfree_rcu(ies, rcu_head);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/*
13362306a36Sopenharmony_ci	 * This happens when the module is removed, it doesn't
13462306a36Sopenharmony_ci	 * really matter any more save for completeness
13562306a36Sopenharmony_ci	 */
13662306a36Sopenharmony_ci	if (!list_empty(&bss->hidden_list))
13762306a36Sopenharmony_ci		list_del(&bss->hidden_list);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	kfree(bss);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic inline void bss_ref_get(struct cfg80211_registered_device *rdev,
14362306a36Sopenharmony_ci			       struct cfg80211_internal_bss *bss)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	lockdep_assert_held(&rdev->bss_lock);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	bss->refcount++;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (bss->pub.hidden_beacon_bss)
15062306a36Sopenharmony_ci		bss_from_pub(bss->pub.hidden_beacon_bss)->refcount++;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (bss->pub.transmitted_bss)
15362306a36Sopenharmony_ci		bss_from_pub(bss->pub.transmitted_bss)->refcount++;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic inline void bss_ref_put(struct cfg80211_registered_device *rdev,
15762306a36Sopenharmony_ci			       struct cfg80211_internal_bss *bss)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	lockdep_assert_held(&rdev->bss_lock);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (bss->pub.hidden_beacon_bss) {
16262306a36Sopenharmony_ci		struct cfg80211_internal_bss *hbss;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci		hbss = bss_from_pub(bss->pub.hidden_beacon_bss);
16562306a36Sopenharmony_ci		hbss->refcount--;
16662306a36Sopenharmony_ci		if (hbss->refcount == 0)
16762306a36Sopenharmony_ci			bss_free(hbss);
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (bss->pub.transmitted_bss) {
17162306a36Sopenharmony_ci		struct cfg80211_internal_bss *tbss;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		tbss = bss_from_pub(bss->pub.transmitted_bss);
17462306a36Sopenharmony_ci		tbss->refcount--;
17562306a36Sopenharmony_ci		if (tbss->refcount == 0)
17662306a36Sopenharmony_ci			bss_free(tbss);
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	bss->refcount--;
18062306a36Sopenharmony_ci	if (bss->refcount == 0)
18162306a36Sopenharmony_ci		bss_free(bss);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic bool __cfg80211_unlink_bss(struct cfg80211_registered_device *rdev,
18562306a36Sopenharmony_ci				  struct cfg80211_internal_bss *bss)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	lockdep_assert_held(&rdev->bss_lock);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (!list_empty(&bss->hidden_list)) {
19062306a36Sopenharmony_ci		/*
19162306a36Sopenharmony_ci		 * don't remove the beacon entry if it has
19262306a36Sopenharmony_ci		 * probe responses associated with it
19362306a36Sopenharmony_ci		 */
19462306a36Sopenharmony_ci		if (!bss->pub.hidden_beacon_bss)
19562306a36Sopenharmony_ci			return false;
19662306a36Sopenharmony_ci		/*
19762306a36Sopenharmony_ci		 * if it's a probe response entry break its
19862306a36Sopenharmony_ci		 * link to the other entries in the group
19962306a36Sopenharmony_ci		 */
20062306a36Sopenharmony_ci		list_del_init(&bss->hidden_list);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	list_del_init(&bss->list);
20462306a36Sopenharmony_ci	list_del_init(&bss->pub.nontrans_list);
20562306a36Sopenharmony_ci	rb_erase(&bss->rbn, &rdev->bss_tree);
20662306a36Sopenharmony_ci	rdev->bss_entries--;
20762306a36Sopenharmony_ci	WARN_ONCE((rdev->bss_entries == 0) ^ list_empty(&rdev->bss_list),
20862306a36Sopenharmony_ci		  "rdev bss entries[%d]/list[empty:%d] corruption\n",
20962306a36Sopenharmony_ci		  rdev->bss_entries, list_empty(&rdev->bss_list));
21062306a36Sopenharmony_ci	bss_ref_put(rdev, bss);
21162306a36Sopenharmony_ci	return true;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cibool cfg80211_is_element_inherited(const struct element *elem,
21562306a36Sopenharmony_ci				   const struct element *non_inherit_elem)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	u8 id_len, ext_id_len, i, loop_len, id;
21862306a36Sopenharmony_ci	const u8 *list;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (elem->id == WLAN_EID_MULTIPLE_BSSID)
22162306a36Sopenharmony_ci		return false;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (elem->id == WLAN_EID_EXTENSION && elem->datalen > 1 &&
22462306a36Sopenharmony_ci	    elem->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK)
22562306a36Sopenharmony_ci		return false;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!non_inherit_elem || non_inherit_elem->datalen < 2)
22862306a36Sopenharmony_ci		return true;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/*
23162306a36Sopenharmony_ci	 * non inheritance element format is:
23262306a36Sopenharmony_ci	 * ext ID (56) | IDs list len | list | extension IDs list len | list
23362306a36Sopenharmony_ci	 * Both lists are optional. Both lengths are mandatory.
23462306a36Sopenharmony_ci	 * This means valid length is:
23562306a36Sopenharmony_ci	 * elem_len = 1 (extension ID) + 2 (list len fields) + list lengths
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	id_len = non_inherit_elem->data[1];
23862306a36Sopenharmony_ci	if (non_inherit_elem->datalen < 3 + id_len)
23962306a36Sopenharmony_ci		return true;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ext_id_len = non_inherit_elem->data[2 + id_len];
24262306a36Sopenharmony_ci	if (non_inherit_elem->datalen < 3 + id_len + ext_id_len)
24362306a36Sopenharmony_ci		return true;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (elem->id == WLAN_EID_EXTENSION) {
24662306a36Sopenharmony_ci		if (!ext_id_len)
24762306a36Sopenharmony_ci			return true;
24862306a36Sopenharmony_ci		loop_len = ext_id_len;
24962306a36Sopenharmony_ci		list = &non_inherit_elem->data[3 + id_len];
25062306a36Sopenharmony_ci		id = elem->data[0];
25162306a36Sopenharmony_ci	} else {
25262306a36Sopenharmony_ci		if (!id_len)
25362306a36Sopenharmony_ci			return true;
25462306a36Sopenharmony_ci		loop_len = id_len;
25562306a36Sopenharmony_ci		list = &non_inherit_elem->data[2];
25662306a36Sopenharmony_ci		id = elem->id;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	for (i = 0; i < loop_len; i++) {
26062306a36Sopenharmony_ci		if (list[i] == id)
26162306a36Sopenharmony_ci			return false;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return true;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_is_element_inherited);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic size_t cfg80211_copy_elem_with_frags(const struct element *elem,
26962306a36Sopenharmony_ci					    const u8 *ie, size_t ie_len,
27062306a36Sopenharmony_ci					    u8 **pos, u8 *buf, size_t buf_len)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	if (WARN_ON((u8 *)elem < ie || elem->data > ie + ie_len ||
27362306a36Sopenharmony_ci		    elem->data + elem->datalen > ie + ie_len))
27462306a36Sopenharmony_ci		return 0;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if (elem->datalen + 2 > buf + buf_len - *pos)
27762306a36Sopenharmony_ci		return 0;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	memcpy(*pos, elem, elem->datalen + 2);
28062306a36Sopenharmony_ci	*pos += elem->datalen + 2;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Finish if it is not fragmented  */
28362306a36Sopenharmony_ci	if (elem->datalen != 255)
28462306a36Sopenharmony_ci		return *pos - buf;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	ie_len = ie + ie_len - elem->data - elem->datalen;
28762306a36Sopenharmony_ci	ie = (const u8 *)elem->data + elem->datalen;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	for_each_element(elem, ie, ie_len) {
29062306a36Sopenharmony_ci		if (elem->id != WLAN_EID_FRAGMENT)
29162306a36Sopenharmony_ci			break;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		if (elem->datalen + 2 > buf + buf_len - *pos)
29462306a36Sopenharmony_ci			return 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		memcpy(*pos, elem, elem->datalen + 2);
29762306a36Sopenharmony_ci		*pos += elem->datalen + 2;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		if (elem->datalen != 255)
30062306a36Sopenharmony_ci			break;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return *pos - buf;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
30762306a36Sopenharmony_ci				  const u8 *subie, size_t subie_len,
30862306a36Sopenharmony_ci				  u8 *new_ie, size_t new_ie_len)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	const struct element *non_inherit_elem, *parent, *sub;
31162306a36Sopenharmony_ci	u8 *pos = new_ie;
31262306a36Sopenharmony_ci	u8 id, ext_id;
31362306a36Sopenharmony_ci	unsigned int match_len;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	non_inherit_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
31662306a36Sopenharmony_ci						  subie, subie_len);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/* We copy the elements one by one from the parent to the generated
31962306a36Sopenharmony_ci	 * elements.
32062306a36Sopenharmony_ci	 * If they are not inherited (included in subie or in the non
32162306a36Sopenharmony_ci	 * inheritance element), then we copy all occurrences the first time
32262306a36Sopenharmony_ci	 * we see this element type.
32362306a36Sopenharmony_ci	 */
32462306a36Sopenharmony_ci	for_each_element(parent, ie, ielen) {
32562306a36Sopenharmony_ci		if (parent->id == WLAN_EID_FRAGMENT)
32662306a36Sopenharmony_ci			continue;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		if (parent->id == WLAN_EID_EXTENSION) {
32962306a36Sopenharmony_ci			if (parent->datalen < 1)
33062306a36Sopenharmony_ci				continue;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci			id = WLAN_EID_EXTENSION;
33362306a36Sopenharmony_ci			ext_id = parent->data[0];
33462306a36Sopenharmony_ci			match_len = 1;
33562306a36Sopenharmony_ci		} else {
33662306a36Sopenharmony_ci			id = parent->id;
33762306a36Sopenharmony_ci			match_len = 0;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		/* Find first occurrence in subie */
34162306a36Sopenharmony_ci		sub = cfg80211_find_elem_match(id, subie, subie_len,
34262306a36Sopenharmony_ci					       &ext_id, match_len, 0);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		/* Copy from parent if not in subie and inherited */
34562306a36Sopenharmony_ci		if (!sub &&
34662306a36Sopenharmony_ci		    cfg80211_is_element_inherited(parent, non_inherit_elem)) {
34762306a36Sopenharmony_ci			if (!cfg80211_copy_elem_with_frags(parent,
34862306a36Sopenharmony_ci							   ie, ielen,
34962306a36Sopenharmony_ci							   &pos, new_ie,
35062306a36Sopenharmony_ci							   new_ie_len))
35162306a36Sopenharmony_ci				return 0;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci			continue;
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/* Already copied if an earlier element had the same type */
35762306a36Sopenharmony_ci		if (cfg80211_find_elem_match(id, ie, (u8 *)parent - ie,
35862306a36Sopenharmony_ci					     &ext_id, match_len, 0))
35962306a36Sopenharmony_ci			continue;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		/* Not inheriting, copy all similar elements from subie */
36262306a36Sopenharmony_ci		while (sub) {
36362306a36Sopenharmony_ci			if (!cfg80211_copy_elem_with_frags(sub,
36462306a36Sopenharmony_ci							   subie, subie_len,
36562306a36Sopenharmony_ci							   &pos, new_ie,
36662306a36Sopenharmony_ci							   new_ie_len))
36762306a36Sopenharmony_ci				return 0;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci			sub = cfg80211_find_elem_match(id,
37062306a36Sopenharmony_ci						       sub->data + sub->datalen,
37162306a36Sopenharmony_ci						       subie_len + subie -
37262306a36Sopenharmony_ci						       (sub->data +
37362306a36Sopenharmony_ci							sub->datalen),
37462306a36Sopenharmony_ci						       &ext_id, match_len, 0);
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* The above misses elements that are included in subie but not in the
37962306a36Sopenharmony_ci	 * parent, so do a pass over subie and append those.
38062306a36Sopenharmony_ci	 * Skip the non-tx BSSID caps and non-inheritance element.
38162306a36Sopenharmony_ci	 */
38262306a36Sopenharmony_ci	for_each_element(sub, subie, subie_len) {
38362306a36Sopenharmony_ci		if (sub->id == WLAN_EID_NON_TX_BSSID_CAP)
38462306a36Sopenharmony_ci			continue;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (sub->id == WLAN_EID_FRAGMENT)
38762306a36Sopenharmony_ci			continue;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		if (sub->id == WLAN_EID_EXTENSION) {
39062306a36Sopenharmony_ci			if (sub->datalen < 1)
39162306a36Sopenharmony_ci				continue;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci			id = WLAN_EID_EXTENSION;
39462306a36Sopenharmony_ci			ext_id = sub->data[0];
39562306a36Sopenharmony_ci			match_len = 1;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci			if (ext_id == WLAN_EID_EXT_NON_INHERITANCE)
39862306a36Sopenharmony_ci				continue;
39962306a36Sopenharmony_ci		} else {
40062306a36Sopenharmony_ci			id = sub->id;
40162306a36Sopenharmony_ci			match_len = 0;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		/* Processed if one was included in the parent */
40562306a36Sopenharmony_ci		if (cfg80211_find_elem_match(id, ie, ielen,
40662306a36Sopenharmony_ci					     &ext_id, match_len, 0))
40762306a36Sopenharmony_ci			continue;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		if (!cfg80211_copy_elem_with_frags(sub, subie, subie_len,
41062306a36Sopenharmony_ci						   &pos, new_ie, new_ie_len))
41162306a36Sopenharmony_ci			return 0;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	return pos - new_ie;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
41862306a36Sopenharmony_ci		   const u8 *ssid, size_t ssid_len)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	const struct cfg80211_bss_ies *ies;
42162306a36Sopenharmony_ci	const struct element *ssid_elem;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (bssid && !ether_addr_equal(a->bssid, bssid))
42462306a36Sopenharmony_ci		return false;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (!ssid)
42762306a36Sopenharmony_ci		return true;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	ies = rcu_access_pointer(a->ies);
43062306a36Sopenharmony_ci	if (!ies)
43162306a36Sopenharmony_ci		return false;
43262306a36Sopenharmony_ci	ssid_elem = cfg80211_find_elem(WLAN_EID_SSID, ies->data, ies->len);
43362306a36Sopenharmony_ci	if (!ssid_elem)
43462306a36Sopenharmony_ci		return false;
43562306a36Sopenharmony_ci	if (ssid_elem->datalen != ssid_len)
43662306a36Sopenharmony_ci		return false;
43762306a36Sopenharmony_ci	return memcmp(ssid_elem->data, ssid, ssid_len) == 0;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic int
44162306a36Sopenharmony_cicfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss,
44262306a36Sopenharmony_ci			   struct cfg80211_bss *nontrans_bss)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	const struct element *ssid_elem;
44562306a36Sopenharmony_ci	struct cfg80211_bss *bss = NULL;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	rcu_read_lock();
44862306a36Sopenharmony_ci	ssid_elem = ieee80211_bss_get_elem(nontrans_bss, WLAN_EID_SSID);
44962306a36Sopenharmony_ci	if (!ssid_elem) {
45062306a36Sopenharmony_ci		rcu_read_unlock();
45162306a36Sopenharmony_ci		return -EINVAL;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/* check if nontrans_bss is in the list */
45562306a36Sopenharmony_ci	list_for_each_entry(bss, &trans_bss->nontrans_list, nontrans_list) {
45662306a36Sopenharmony_ci		if (is_bss(bss, nontrans_bss->bssid, ssid_elem->data,
45762306a36Sopenharmony_ci			   ssid_elem->datalen)) {
45862306a36Sopenharmony_ci			rcu_read_unlock();
45962306a36Sopenharmony_ci			return 0;
46062306a36Sopenharmony_ci		}
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	rcu_read_unlock();
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/*
46662306a36Sopenharmony_ci	 * This is a bit weird - it's not on the list, but already on another
46762306a36Sopenharmony_ci	 * one! The only way that could happen is if there's some BSSID/SSID
46862306a36Sopenharmony_ci	 * shared by multiple APs in their multi-BSSID profiles, potentially
46962306a36Sopenharmony_ci	 * with hidden SSID mixed in ... ignore it.
47062306a36Sopenharmony_ci	 */
47162306a36Sopenharmony_ci	if (!list_empty(&nontrans_bss->nontrans_list))
47262306a36Sopenharmony_ci		return -EINVAL;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* add to the list */
47562306a36Sopenharmony_ci	list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
47662306a36Sopenharmony_ci	return 0;
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic void __cfg80211_bss_expire(struct cfg80211_registered_device *rdev,
48062306a36Sopenharmony_ci				  unsigned long expire_time)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss, *tmp;
48362306a36Sopenharmony_ci	bool expired = false;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	lockdep_assert_held(&rdev->bss_lock);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	list_for_each_entry_safe(bss, tmp, &rdev->bss_list, list) {
48862306a36Sopenharmony_ci		if (atomic_read(&bss->hold))
48962306a36Sopenharmony_ci			continue;
49062306a36Sopenharmony_ci		if (!time_after(expire_time, bss->ts))
49162306a36Sopenharmony_ci			continue;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		if (__cfg80211_unlink_bss(rdev, bss))
49462306a36Sopenharmony_ci			expired = true;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (expired)
49862306a36Sopenharmony_ci		rdev->bss_generation++;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic bool cfg80211_bss_expire_oldest(struct cfg80211_registered_device *rdev)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss, *oldest = NULL;
50462306a36Sopenharmony_ci	bool ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	lockdep_assert_held(&rdev->bss_lock);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	list_for_each_entry(bss, &rdev->bss_list, list) {
50962306a36Sopenharmony_ci		if (atomic_read(&bss->hold))
51062306a36Sopenharmony_ci			continue;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci		if (!list_empty(&bss->hidden_list) &&
51362306a36Sopenharmony_ci		    !bss->pub.hidden_beacon_bss)
51462306a36Sopenharmony_ci			continue;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		if (oldest && time_before(oldest->ts, bss->ts))
51762306a36Sopenharmony_ci			continue;
51862306a36Sopenharmony_ci		oldest = bss;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (WARN_ON(!oldest))
52262306a36Sopenharmony_ci		return false;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	/*
52562306a36Sopenharmony_ci	 * The callers make sure to increase rdev->bss_generation if anything
52662306a36Sopenharmony_ci	 * gets removed (and a new entry added), so there's no need to also do
52762306a36Sopenharmony_ci	 * it here.
52862306a36Sopenharmony_ci	 */
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	ret = __cfg80211_unlink_bss(rdev, oldest);
53162306a36Sopenharmony_ci	WARN_ON(!ret);
53262306a36Sopenharmony_ci	return ret;
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic u8 cfg80211_parse_bss_param(u8 data,
53662306a36Sopenharmony_ci				   struct cfg80211_colocated_ap *coloc_ap)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	coloc_ap->oct_recommended =
53962306a36Sopenharmony_ci		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_OCT_RECOMMENDED);
54062306a36Sopenharmony_ci	coloc_ap->same_ssid =
54162306a36Sopenharmony_ci		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_SAME_SSID);
54262306a36Sopenharmony_ci	coloc_ap->multi_bss =
54362306a36Sopenharmony_ci		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_MULTI_BSSID);
54462306a36Sopenharmony_ci	coloc_ap->transmitted_bssid =
54562306a36Sopenharmony_ci		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_TRANSMITTED_BSSID);
54662306a36Sopenharmony_ci	coloc_ap->unsolicited_probe =
54762306a36Sopenharmony_ci		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE);
54862306a36Sopenharmony_ci	coloc_ap->colocated_ess =
54962306a36Sopenharmony_ci		u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return u8_get_bits(data, IEEE80211_RNR_TBTT_PARAMS_COLOC_AP);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic int cfg80211_calc_short_ssid(const struct cfg80211_bss_ies *ies,
55562306a36Sopenharmony_ci				    const struct element **elem, u32 *s_ssid)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	*elem = cfg80211_find_elem(WLAN_EID_SSID, ies->data, ies->len);
55962306a36Sopenharmony_ci	if (!*elem || (*elem)->datalen > IEEE80211_MAX_SSID_LEN)
56062306a36Sopenharmony_ci		return -EINVAL;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	*s_ssid = ~crc32_le(~0, (*elem)->data, (*elem)->datalen);
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct cfg80211_colocated_ap *ap, *tmp_ap;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	list_for_each_entry_safe(ap, tmp_ap, coloc_ap_list, list) {
57162306a36Sopenharmony_ci		list_del(&ap->list);
57262306a36Sopenharmony_ci		kfree(ap);
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic int cfg80211_parse_ap_info(struct cfg80211_colocated_ap *entry,
57762306a36Sopenharmony_ci				  const u8 *pos, u8 length,
57862306a36Sopenharmony_ci				  const struct element *ssid_elem,
57962306a36Sopenharmony_ci				  u32 s_ssid_tmp)
58062306a36Sopenharmony_ci{
58162306a36Sopenharmony_ci	u8 bss_params;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	entry->psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* The length is already verified by the caller to contain bss_params */
58662306a36Sopenharmony_ci	if (length > sizeof(struct ieee80211_tbtt_info_7_8_9)) {
58762306a36Sopenharmony_ci		struct ieee80211_tbtt_info_ge_11 *tbtt_info = (void *)pos;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		memcpy(entry->bssid, tbtt_info->bssid, ETH_ALEN);
59062306a36Sopenharmony_ci		entry->short_ssid = le32_to_cpu(tbtt_info->short_ssid);
59162306a36Sopenharmony_ci		entry->short_ssid_valid = true;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		bss_params = tbtt_info->bss_params;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		/* Ignore disabled links */
59662306a36Sopenharmony_ci		if (length >= offsetofend(typeof(*tbtt_info), mld_params)) {
59762306a36Sopenharmony_ci			if (le16_get_bits(tbtt_info->mld_params.params,
59862306a36Sopenharmony_ci					  IEEE80211_RNR_MLD_PARAMS_DISABLED_LINK))
59962306a36Sopenharmony_ci				return -EINVAL;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		if (length >= offsetofend(struct ieee80211_tbtt_info_ge_11,
60362306a36Sopenharmony_ci					  psd_20))
60462306a36Sopenharmony_ci			entry->psd_20 = tbtt_info->psd_20;
60562306a36Sopenharmony_ci	} else {
60662306a36Sopenharmony_ci		struct ieee80211_tbtt_info_7_8_9 *tbtt_info = (void *)pos;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci		memcpy(entry->bssid, tbtt_info->bssid, ETH_ALEN);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		bss_params = tbtt_info->bss_params;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		if (length == offsetofend(struct ieee80211_tbtt_info_7_8_9,
61362306a36Sopenharmony_ci					  psd_20))
61462306a36Sopenharmony_ci			entry->psd_20 = tbtt_info->psd_20;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	/* ignore entries with invalid BSSID */
61862306a36Sopenharmony_ci	if (!is_valid_ether_addr(entry->bssid))
61962306a36Sopenharmony_ci		return -EINVAL;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* skip non colocated APs */
62262306a36Sopenharmony_ci	if (!cfg80211_parse_bss_param(bss_params, entry))
62362306a36Sopenharmony_ci		return -EINVAL;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	/* no information about the short ssid. Consider the entry valid
62662306a36Sopenharmony_ci	 * for now. It would later be dropped in case there are explicit
62762306a36Sopenharmony_ci	 * SSIDs that need to be matched
62862306a36Sopenharmony_ci	 */
62962306a36Sopenharmony_ci	if (!entry->same_ssid && !entry->short_ssid_valid)
63062306a36Sopenharmony_ci		return 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (entry->same_ssid) {
63362306a36Sopenharmony_ci		entry->short_ssid = s_ssid_tmp;
63462306a36Sopenharmony_ci		entry->short_ssid_valid = true;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		/*
63762306a36Sopenharmony_ci		 * This is safe because we validate datalen in
63862306a36Sopenharmony_ci		 * cfg80211_parse_colocated_ap(), before calling this
63962306a36Sopenharmony_ci		 * function.
64062306a36Sopenharmony_ci		 */
64162306a36Sopenharmony_ci		memcpy(&entry->ssid, &ssid_elem->data, ssid_elem->datalen);
64262306a36Sopenharmony_ci		entry->ssid_len = ssid_elem->datalen;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	return 0;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
64962306a36Sopenharmony_ci				       struct list_head *list)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	struct ieee80211_neighbor_ap_info *ap_info;
65262306a36Sopenharmony_ci	const struct element *elem, *ssid_elem;
65362306a36Sopenharmony_ci	const u8 *pos, *end;
65462306a36Sopenharmony_ci	u32 s_ssid_tmp;
65562306a36Sopenharmony_ci	int n_coloc = 0, ret;
65662306a36Sopenharmony_ci	LIST_HEAD(ap_list);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	ret = cfg80211_calc_short_ssid(ies, &ssid_elem, &s_ssid_tmp);
65962306a36Sopenharmony_ci	if (ret)
66062306a36Sopenharmony_ci		return 0;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
66362306a36Sopenharmony_ci			    ies->data, ies->len) {
66462306a36Sopenharmony_ci		pos = elem->data;
66562306a36Sopenharmony_ci		end = elem->data + elem->datalen;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		/* RNR IE may contain more than one NEIGHBOR_AP_INFO */
66862306a36Sopenharmony_ci		while (pos + sizeof(*ap_info) <= end) {
66962306a36Sopenharmony_ci			enum nl80211_band band;
67062306a36Sopenharmony_ci			int freq;
67162306a36Sopenharmony_ci			u8 length, i, count;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci			ap_info = (void *)pos;
67462306a36Sopenharmony_ci			count = u8_get_bits(ap_info->tbtt_info_hdr,
67562306a36Sopenharmony_ci					    IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1;
67662306a36Sopenharmony_ci			length = ap_info->tbtt_info_len;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci			pos += sizeof(*ap_info);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci			if (!ieee80211_operating_class_to_band(ap_info->op_class,
68162306a36Sopenharmony_ci							       &band))
68262306a36Sopenharmony_ci				break;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci			freq = ieee80211_channel_to_frequency(ap_info->channel,
68562306a36Sopenharmony_ci							      band);
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci			if (end - pos < count * length)
68862306a36Sopenharmony_ci				break;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci			if (u8_get_bits(ap_info->tbtt_info_hdr,
69162306a36Sopenharmony_ci					IEEE80211_AP_INFO_TBTT_HDR_TYPE) !=
69262306a36Sopenharmony_ci			    IEEE80211_TBTT_INFO_TYPE_TBTT) {
69362306a36Sopenharmony_ci				pos += count * length;
69462306a36Sopenharmony_ci				continue;
69562306a36Sopenharmony_ci			}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci			/* TBTT info must include bss param + BSSID +
69862306a36Sopenharmony_ci			 * (short SSID or same_ssid bit to be set).
69962306a36Sopenharmony_ci			 * ignore other options, and move to the
70062306a36Sopenharmony_ci			 * next AP info
70162306a36Sopenharmony_ci			 */
70262306a36Sopenharmony_ci			if (band != NL80211_BAND_6GHZ ||
70362306a36Sopenharmony_ci			    !(length == offsetofend(struct ieee80211_tbtt_info_7_8_9,
70462306a36Sopenharmony_ci						    bss_params) ||
70562306a36Sopenharmony_ci			      length == sizeof(struct ieee80211_tbtt_info_7_8_9) ||
70662306a36Sopenharmony_ci			      length >= offsetofend(struct ieee80211_tbtt_info_ge_11,
70762306a36Sopenharmony_ci						    bss_params))) {
70862306a36Sopenharmony_ci				pos += count * length;
70962306a36Sopenharmony_ci				continue;
71062306a36Sopenharmony_ci			}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci			for (i = 0; i < count; i++) {
71362306a36Sopenharmony_ci				struct cfg80211_colocated_ap *entry;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci				entry = kzalloc(sizeof(*entry) + IEEE80211_MAX_SSID_LEN,
71662306a36Sopenharmony_ci						GFP_ATOMIC);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci				if (!entry)
71962306a36Sopenharmony_ci					goto error;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci				entry->center_freq = freq;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci				if (!cfg80211_parse_ap_info(entry, pos, length,
72462306a36Sopenharmony_ci							    ssid_elem,
72562306a36Sopenharmony_ci							    s_ssid_tmp)) {
72662306a36Sopenharmony_ci					n_coloc++;
72762306a36Sopenharmony_ci					list_add_tail(&entry->list, &ap_list);
72862306a36Sopenharmony_ci				} else {
72962306a36Sopenharmony_ci					kfree(entry);
73062306a36Sopenharmony_ci				}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci				pos += length;
73362306a36Sopenharmony_ci			}
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_cierror:
73762306a36Sopenharmony_ci		if (pos != end) {
73862306a36Sopenharmony_ci			cfg80211_free_coloc_ap_list(&ap_list);
73962306a36Sopenharmony_ci			return 0;
74062306a36Sopenharmony_ci		}
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	list_splice_tail(&ap_list, list);
74462306a36Sopenharmony_ci	return n_coloc;
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic  void cfg80211_scan_req_add_chan(struct cfg80211_scan_request *request,
74862306a36Sopenharmony_ci					struct ieee80211_channel *chan,
74962306a36Sopenharmony_ci					bool add_to_6ghz)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	int i;
75262306a36Sopenharmony_ci	u32 n_channels = request->n_channels;
75362306a36Sopenharmony_ci	struct cfg80211_scan_6ghz_params *params =
75462306a36Sopenharmony_ci		&request->scan_6ghz_params[request->n_6ghz_params];
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	for (i = 0; i < n_channels; i++) {
75762306a36Sopenharmony_ci		if (request->channels[i] == chan) {
75862306a36Sopenharmony_ci			if (add_to_6ghz)
75962306a36Sopenharmony_ci				params->channel_idx = i;
76062306a36Sopenharmony_ci			return;
76162306a36Sopenharmony_ci		}
76262306a36Sopenharmony_ci	}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	request->channels[n_channels] = chan;
76562306a36Sopenharmony_ci	if (add_to_6ghz)
76662306a36Sopenharmony_ci		request->scan_6ghz_params[request->n_6ghz_params].channel_idx =
76762306a36Sopenharmony_ci			n_channels;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	request->n_channels++;
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic bool cfg80211_find_ssid_match(struct cfg80211_colocated_ap *ap,
77362306a36Sopenharmony_ci				     struct cfg80211_scan_request *request)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	int i;
77662306a36Sopenharmony_ci	u32 s_ssid;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	for (i = 0; i < request->n_ssids; i++) {
77962306a36Sopenharmony_ci		/* wildcard ssid in the scan request */
78062306a36Sopenharmony_ci		if (!request->ssids[i].ssid_len) {
78162306a36Sopenharmony_ci			if (ap->multi_bss && !ap->transmitted_bssid)
78262306a36Sopenharmony_ci				continue;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci			return true;
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		if (ap->ssid_len &&
78862306a36Sopenharmony_ci		    ap->ssid_len == request->ssids[i].ssid_len) {
78962306a36Sopenharmony_ci			if (!memcmp(request->ssids[i].ssid, ap->ssid,
79062306a36Sopenharmony_ci				    ap->ssid_len))
79162306a36Sopenharmony_ci				return true;
79262306a36Sopenharmony_ci		} else if (ap->short_ssid_valid) {
79362306a36Sopenharmony_ci			s_ssid = ~crc32_le(~0, request->ssids[i].ssid,
79462306a36Sopenharmony_ci					   request->ssids[i].ssid_len);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci			if (ap->short_ssid == s_ssid)
79762306a36Sopenharmony_ci				return true;
79862306a36Sopenharmony_ci		}
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	return false;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int cfg80211_scan_6ghz(struct cfg80211_registered_device *rdev)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	u8 i;
80762306a36Sopenharmony_ci	struct cfg80211_colocated_ap *ap;
80862306a36Sopenharmony_ci	int n_channels, count = 0, err;
80962306a36Sopenharmony_ci	struct cfg80211_scan_request *request, *rdev_req = rdev->scan_req;
81062306a36Sopenharmony_ci	LIST_HEAD(coloc_ap_list);
81162306a36Sopenharmony_ci	bool need_scan_psc = true;
81262306a36Sopenharmony_ci	const struct ieee80211_sband_iftype_data *iftd;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	rdev_req->scan_6ghz = true;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (!rdev->wiphy.bands[NL80211_BAND_6GHZ])
81762306a36Sopenharmony_ci		return -EOPNOTSUPP;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	iftd = ieee80211_get_sband_iftype_data(rdev->wiphy.bands[NL80211_BAND_6GHZ],
82062306a36Sopenharmony_ci					       rdev_req->wdev->iftype);
82162306a36Sopenharmony_ci	if (!iftd || !iftd->he_cap.has_he)
82262306a36Sopenharmony_ci		return -EOPNOTSUPP;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	n_channels = rdev->wiphy.bands[NL80211_BAND_6GHZ]->n_channels;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) {
82762306a36Sopenharmony_ci		struct cfg80211_internal_bss *intbss;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		spin_lock_bh(&rdev->bss_lock);
83062306a36Sopenharmony_ci		list_for_each_entry(intbss, &rdev->bss_list, list) {
83162306a36Sopenharmony_ci			struct cfg80211_bss *res = &intbss->pub;
83262306a36Sopenharmony_ci			const struct cfg80211_bss_ies *ies;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci			ies = rcu_access_pointer(res->ies);
83562306a36Sopenharmony_ci			count += cfg80211_parse_colocated_ap(ies,
83662306a36Sopenharmony_ci							     &coloc_ap_list);
83762306a36Sopenharmony_ci		}
83862306a36Sopenharmony_ci		spin_unlock_bh(&rdev->bss_lock);
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	request = kzalloc(struct_size(request, channels, n_channels) +
84262306a36Sopenharmony_ci			  sizeof(*request->scan_6ghz_params) * count +
84362306a36Sopenharmony_ci			  sizeof(*request->ssids) * rdev_req->n_ssids,
84462306a36Sopenharmony_ci			  GFP_KERNEL);
84562306a36Sopenharmony_ci	if (!request) {
84662306a36Sopenharmony_ci		cfg80211_free_coloc_ap_list(&coloc_ap_list);
84762306a36Sopenharmony_ci		return -ENOMEM;
84862306a36Sopenharmony_ci	}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	*request = *rdev_req;
85162306a36Sopenharmony_ci	request->n_channels = 0;
85262306a36Sopenharmony_ci	request->scan_6ghz_params =
85362306a36Sopenharmony_ci		(void *)&request->channels[n_channels];
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	/*
85662306a36Sopenharmony_ci	 * PSC channels should not be scanned in case of direct scan with 1 SSID
85762306a36Sopenharmony_ci	 * and at least one of the reported co-located APs with same SSID
85862306a36Sopenharmony_ci	 * indicating that all APs in the same ESS are co-located
85962306a36Sopenharmony_ci	 */
86062306a36Sopenharmony_ci	if (count && request->n_ssids == 1 && request->ssids[0].ssid_len) {
86162306a36Sopenharmony_ci		list_for_each_entry(ap, &coloc_ap_list, list) {
86262306a36Sopenharmony_ci			if (ap->colocated_ess &&
86362306a36Sopenharmony_ci			    cfg80211_find_ssid_match(ap, request)) {
86462306a36Sopenharmony_ci				need_scan_psc = false;
86562306a36Sopenharmony_ci				break;
86662306a36Sopenharmony_ci			}
86762306a36Sopenharmony_ci		}
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/*
87162306a36Sopenharmony_ci	 * add to the scan request the channels that need to be scanned
87262306a36Sopenharmony_ci	 * regardless of the collocated APs (PSC channels or all channels
87362306a36Sopenharmony_ci	 * in case that NL80211_SCAN_FLAG_COLOCATED_6GHZ is not set)
87462306a36Sopenharmony_ci	 */
87562306a36Sopenharmony_ci	for (i = 0; i < rdev_req->n_channels; i++) {
87662306a36Sopenharmony_ci		if (rdev_req->channels[i]->band == NL80211_BAND_6GHZ &&
87762306a36Sopenharmony_ci		    ((need_scan_psc &&
87862306a36Sopenharmony_ci		      cfg80211_channel_is_psc(rdev_req->channels[i])) ||
87962306a36Sopenharmony_ci		     !(rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ))) {
88062306a36Sopenharmony_ci			cfg80211_scan_req_add_chan(request,
88162306a36Sopenharmony_ci						   rdev_req->channels[i],
88262306a36Sopenharmony_ci						   false);
88362306a36Sopenharmony_ci		}
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (!(rdev_req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ))
88762306a36Sopenharmony_ci		goto skip;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	list_for_each_entry(ap, &coloc_ap_list, list) {
89062306a36Sopenharmony_ci		bool found = false;
89162306a36Sopenharmony_ci		struct cfg80211_scan_6ghz_params *scan_6ghz_params =
89262306a36Sopenharmony_ci			&request->scan_6ghz_params[request->n_6ghz_params];
89362306a36Sopenharmony_ci		struct ieee80211_channel *chan =
89462306a36Sopenharmony_ci			ieee80211_get_channel(&rdev->wiphy, ap->center_freq);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
89762306a36Sopenharmony_ci			continue;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci		for (i = 0; i < rdev_req->n_channels; i++) {
90062306a36Sopenharmony_ci			if (rdev_req->channels[i] == chan)
90162306a36Sopenharmony_ci				found = true;
90262306a36Sopenharmony_ci		}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci		if (!found)
90562306a36Sopenharmony_ci			continue;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci		if (request->n_ssids > 0 &&
90862306a36Sopenharmony_ci		    !cfg80211_find_ssid_match(ap, request))
90962306a36Sopenharmony_ci			continue;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		if (!is_broadcast_ether_addr(request->bssid) &&
91262306a36Sopenharmony_ci		    !ether_addr_equal(request->bssid, ap->bssid))
91362306a36Sopenharmony_ci			continue;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		if (!request->n_ssids && ap->multi_bss && !ap->transmitted_bssid)
91662306a36Sopenharmony_ci			continue;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		cfg80211_scan_req_add_chan(request, chan, true);
91962306a36Sopenharmony_ci		memcpy(scan_6ghz_params->bssid, ap->bssid, ETH_ALEN);
92062306a36Sopenharmony_ci		scan_6ghz_params->short_ssid = ap->short_ssid;
92162306a36Sopenharmony_ci		scan_6ghz_params->short_ssid_valid = ap->short_ssid_valid;
92262306a36Sopenharmony_ci		scan_6ghz_params->unsolicited_probe = ap->unsolicited_probe;
92362306a36Sopenharmony_ci		scan_6ghz_params->psd_20 = ap->psd_20;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		/*
92662306a36Sopenharmony_ci		 * If a PSC channel is added to the scan and 'need_scan_psc' is
92762306a36Sopenharmony_ci		 * set to false, then all the APs that the scan logic is
92862306a36Sopenharmony_ci		 * interested with on the channel are collocated and thus there
92962306a36Sopenharmony_ci		 * is no need to perform the initial PSC channel listen.
93062306a36Sopenharmony_ci		 */
93162306a36Sopenharmony_ci		if (cfg80211_channel_is_psc(chan) && !need_scan_psc)
93262306a36Sopenharmony_ci			scan_6ghz_params->psc_no_listen = true;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci		request->n_6ghz_params++;
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ciskip:
93862306a36Sopenharmony_ci	cfg80211_free_coloc_ap_list(&coloc_ap_list);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (request->n_channels) {
94162306a36Sopenharmony_ci		struct cfg80211_scan_request *old = rdev->int_scan_req;
94262306a36Sopenharmony_ci		rdev->int_scan_req = request;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		/*
94562306a36Sopenharmony_ci		 * Add the ssids from the parent scan request to the new scan
94662306a36Sopenharmony_ci		 * request, so the driver would be able to use them in its
94762306a36Sopenharmony_ci		 * probe requests to discover hidden APs on PSC channels.
94862306a36Sopenharmony_ci		 */
94962306a36Sopenharmony_ci		request->ssids = (void *)&request->channels[request->n_channels];
95062306a36Sopenharmony_ci		request->n_ssids = rdev_req->n_ssids;
95162306a36Sopenharmony_ci		memcpy(request->ssids, rdev_req->ssids, sizeof(*request->ssids) *
95262306a36Sopenharmony_ci		       request->n_ssids);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		/*
95562306a36Sopenharmony_ci		 * If this scan follows a previous scan, save the scan start
95662306a36Sopenharmony_ci		 * info from the first part of the scan
95762306a36Sopenharmony_ci		 */
95862306a36Sopenharmony_ci		if (old)
95962306a36Sopenharmony_ci			rdev->int_scan_req->info = old->info;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		err = rdev_scan(rdev, request);
96262306a36Sopenharmony_ci		if (err) {
96362306a36Sopenharmony_ci			rdev->int_scan_req = old;
96462306a36Sopenharmony_ci			kfree(request);
96562306a36Sopenharmony_ci		} else {
96662306a36Sopenharmony_ci			kfree(old);
96762306a36Sopenharmony_ci		}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		return err;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	kfree(request);
97362306a36Sopenharmony_ci	return -EINVAL;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ciint cfg80211_scan(struct cfg80211_registered_device *rdev)
97762306a36Sopenharmony_ci{
97862306a36Sopenharmony_ci	struct cfg80211_scan_request *request;
97962306a36Sopenharmony_ci	struct cfg80211_scan_request *rdev_req = rdev->scan_req;
98062306a36Sopenharmony_ci	u32 n_channels = 0, idx, i;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	if (!(rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ))
98362306a36Sopenharmony_ci		return rdev_scan(rdev, rdev_req);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	for (i = 0; i < rdev_req->n_channels; i++) {
98662306a36Sopenharmony_ci		if (rdev_req->channels[i]->band != NL80211_BAND_6GHZ)
98762306a36Sopenharmony_ci			n_channels++;
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (!n_channels)
99162306a36Sopenharmony_ci		return cfg80211_scan_6ghz(rdev);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	request = kzalloc(struct_size(request, channels, n_channels),
99462306a36Sopenharmony_ci			  GFP_KERNEL);
99562306a36Sopenharmony_ci	if (!request)
99662306a36Sopenharmony_ci		return -ENOMEM;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	*request = *rdev_req;
99962306a36Sopenharmony_ci	request->n_channels = n_channels;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	for (i = idx = 0; i < rdev_req->n_channels; i++) {
100262306a36Sopenharmony_ci		if (rdev_req->channels[i]->band != NL80211_BAND_6GHZ)
100362306a36Sopenharmony_ci			request->channels[idx++] = rdev_req->channels[i];
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	rdev_req->scan_6ghz = false;
100762306a36Sopenharmony_ci	rdev->int_scan_req = request;
100862306a36Sopenharmony_ci	return rdev_scan(rdev, request);
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_civoid ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
101262306a36Sopenharmony_ci			   bool send_message)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	struct cfg80211_scan_request *request, *rdev_req;
101562306a36Sopenharmony_ci	struct wireless_dev *wdev;
101662306a36Sopenharmony_ci	struct sk_buff *msg;
101762306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
101862306a36Sopenharmony_ci	union iwreq_data wrqu;
101962306a36Sopenharmony_ci#endif
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	lockdep_assert_held(&rdev->wiphy.mtx);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (rdev->scan_msg) {
102462306a36Sopenharmony_ci		nl80211_send_scan_msg(rdev, rdev->scan_msg);
102562306a36Sopenharmony_ci		rdev->scan_msg = NULL;
102662306a36Sopenharmony_ci		return;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	rdev_req = rdev->scan_req;
103062306a36Sopenharmony_ci	if (!rdev_req)
103162306a36Sopenharmony_ci		return;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	wdev = rdev_req->wdev;
103462306a36Sopenharmony_ci	request = rdev->int_scan_req ? rdev->int_scan_req : rdev_req;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	if (wdev_running(wdev) &&
103762306a36Sopenharmony_ci	    (rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ) &&
103862306a36Sopenharmony_ci	    !rdev_req->scan_6ghz && !request->info.aborted &&
103962306a36Sopenharmony_ci	    !cfg80211_scan_6ghz(rdev))
104062306a36Sopenharmony_ci		return;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/*
104362306a36Sopenharmony_ci	 * This must be before sending the other events!
104462306a36Sopenharmony_ci	 * Otherwise, wpa_supplicant gets completely confused with
104562306a36Sopenharmony_ci	 * wext events.
104662306a36Sopenharmony_ci	 */
104762306a36Sopenharmony_ci	if (wdev->netdev)
104862306a36Sopenharmony_ci		cfg80211_sme_scan_done(wdev->netdev);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (!request->info.aborted &&
105162306a36Sopenharmony_ci	    request->flags & NL80211_SCAN_FLAG_FLUSH) {
105262306a36Sopenharmony_ci		/* flush entries from previous scans */
105362306a36Sopenharmony_ci		spin_lock_bh(&rdev->bss_lock);
105462306a36Sopenharmony_ci		__cfg80211_bss_expire(rdev, request->scan_start);
105562306a36Sopenharmony_ci		spin_unlock_bh(&rdev->bss_lock);
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	msg = nl80211_build_scan_msg(rdev, wdev, request->info.aborted);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
106162306a36Sopenharmony_ci	if (wdev->netdev && !request->info.aborted) {
106262306a36Sopenharmony_ci		memset(&wrqu, 0, sizeof(wrqu));
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci		wireless_send_event(wdev->netdev, SIOCGIWSCAN, &wrqu, NULL);
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci#endif
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	dev_put(wdev->netdev);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	kfree(rdev->int_scan_req);
107162306a36Sopenharmony_ci	rdev->int_scan_req = NULL;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	kfree(rdev->scan_req);
107462306a36Sopenharmony_ci	rdev->scan_req = NULL;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (!send_message)
107762306a36Sopenharmony_ci		rdev->scan_msg = msg;
107862306a36Sopenharmony_ci	else
107962306a36Sopenharmony_ci		nl80211_send_scan_msg(rdev, msg);
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_civoid __cfg80211_scan_done(struct wiphy *wiphy, struct wiphy_work *wk)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	___cfg80211_scan_done(wiphy_to_rdev(wiphy), true);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_civoid cfg80211_scan_done(struct cfg80211_scan_request *request,
108862306a36Sopenharmony_ci			struct cfg80211_scan_info *info)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci	struct cfg80211_scan_info old_info = request->info;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	trace_cfg80211_scan_done(request, info);
109362306a36Sopenharmony_ci	WARN_ON(request != wiphy_to_rdev(request->wiphy)->scan_req &&
109462306a36Sopenharmony_ci		request != wiphy_to_rdev(request->wiphy)->int_scan_req);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	request->info = *info;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	/*
109962306a36Sopenharmony_ci	 * In case the scan is split, the scan_start_tsf and tsf_bssid should
110062306a36Sopenharmony_ci	 * be of the first part. In such a case old_info.scan_start_tsf should
110162306a36Sopenharmony_ci	 * be non zero.
110262306a36Sopenharmony_ci	 */
110362306a36Sopenharmony_ci	if (request->scan_6ghz && old_info.scan_start_tsf) {
110462306a36Sopenharmony_ci		request->info.scan_start_tsf = old_info.scan_start_tsf;
110562306a36Sopenharmony_ci		memcpy(request->info.tsf_bssid, old_info.tsf_bssid,
110662306a36Sopenharmony_ci		       sizeof(request->info.tsf_bssid));
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	request->notified = true;
111062306a36Sopenharmony_ci	wiphy_work_queue(request->wiphy,
111162306a36Sopenharmony_ci			 &wiphy_to_rdev(request->wiphy)->scan_done_wk);
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_scan_done);
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_civoid cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
111662306a36Sopenharmony_ci				 struct cfg80211_sched_scan_request *req)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	lockdep_assert_held(&rdev->wiphy.mtx);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	list_add_rcu(&req->list, &rdev->sched_scan_req_list);
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
112462306a36Sopenharmony_ci					struct cfg80211_sched_scan_request *req)
112562306a36Sopenharmony_ci{
112662306a36Sopenharmony_ci	lockdep_assert_held(&rdev->wiphy.mtx);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	list_del_rcu(&req->list);
112962306a36Sopenharmony_ci	kfree_rcu(req, rcu_head);
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic struct cfg80211_sched_scan_request *
113362306a36Sopenharmony_cicfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	struct cfg80211_sched_scan_request *pos;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	list_for_each_entry_rcu(pos, &rdev->sched_scan_req_list, list,
113862306a36Sopenharmony_ci				lockdep_is_held(&rdev->wiphy.mtx)) {
113962306a36Sopenharmony_ci		if (pos->reqid == reqid)
114062306a36Sopenharmony_ci			return pos;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci	return NULL;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci/*
114662306a36Sopenharmony_ci * Determines if a scheduled scan request can be handled. When a legacy
114762306a36Sopenharmony_ci * scheduled scan is running no other scheduled scan is allowed regardless
114862306a36Sopenharmony_ci * whether the request is for legacy or multi-support scan. When a multi-support
114962306a36Sopenharmony_ci * scheduled scan is running a request for legacy scan is not allowed. In this
115062306a36Sopenharmony_ci * case a request for multi-support scan can be handled if resources are
115162306a36Sopenharmony_ci * available, ie. struct wiphy::max_sched_scan_reqs limit is not yet reached.
115262306a36Sopenharmony_ci */
115362306a36Sopenharmony_ciint cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
115462306a36Sopenharmony_ci				     bool want_multi)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	struct cfg80211_sched_scan_request *pos;
115762306a36Sopenharmony_ci	int i = 0;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	list_for_each_entry(pos, &rdev->sched_scan_req_list, list) {
116062306a36Sopenharmony_ci		/* request id zero means legacy in progress */
116162306a36Sopenharmony_ci		if (!i && !pos->reqid)
116262306a36Sopenharmony_ci			return -EINPROGRESS;
116362306a36Sopenharmony_ci		i++;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	if (i) {
116762306a36Sopenharmony_ci		/* no legacy allowed when multi request(s) are active */
116862306a36Sopenharmony_ci		if (!want_multi)
116962306a36Sopenharmony_ci			return -EINPROGRESS;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		/* resource limit reached */
117262306a36Sopenharmony_ci		if (i == rdev->wiphy.max_sched_scan_reqs)
117362306a36Sopenharmony_ci			return -ENOSPC;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci	return 0;
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_civoid cfg80211_sched_scan_results_wk(struct work_struct *work)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev;
118162306a36Sopenharmony_ci	struct cfg80211_sched_scan_request *req, *tmp;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	rdev = container_of(work, struct cfg80211_registered_device,
118462306a36Sopenharmony_ci			   sched_scan_res_wk);
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	wiphy_lock(&rdev->wiphy);
118762306a36Sopenharmony_ci	list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
118862306a36Sopenharmony_ci		if (req->report_results) {
118962306a36Sopenharmony_ci			req->report_results = false;
119062306a36Sopenharmony_ci			if (req->flags & NL80211_SCAN_FLAG_FLUSH) {
119162306a36Sopenharmony_ci				/* flush entries from previous scans */
119262306a36Sopenharmony_ci				spin_lock_bh(&rdev->bss_lock);
119362306a36Sopenharmony_ci				__cfg80211_bss_expire(rdev, req->scan_start);
119462306a36Sopenharmony_ci				spin_unlock_bh(&rdev->bss_lock);
119562306a36Sopenharmony_ci				req->scan_start = jiffies;
119662306a36Sopenharmony_ci			}
119762306a36Sopenharmony_ci			nl80211_send_sched_scan(req,
119862306a36Sopenharmony_ci						NL80211_CMD_SCHED_SCAN_RESULTS);
119962306a36Sopenharmony_ci		}
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci	wiphy_unlock(&rdev->wiphy);
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_civoid cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
120762306a36Sopenharmony_ci	struct cfg80211_sched_scan_request *request;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	trace_cfg80211_sched_scan_results(wiphy, reqid);
121062306a36Sopenharmony_ci	/* ignore if we're not scanning */
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	rcu_read_lock();
121362306a36Sopenharmony_ci	request = cfg80211_find_sched_scan_req(rdev, reqid);
121462306a36Sopenharmony_ci	if (request) {
121562306a36Sopenharmony_ci		request->report_results = true;
121662306a36Sopenharmony_ci		queue_work(cfg80211_wq, &rdev->sched_scan_res_wk);
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci	rcu_read_unlock();
121962306a36Sopenharmony_ci}
122062306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_sched_scan_results);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_civoid cfg80211_sched_scan_stopped_locked(struct wiphy *wiphy, u64 reqid)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	lockdep_assert_held(&wiphy->mtx);
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	trace_cfg80211_sched_scan_stopped(wiphy, reqid);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	__cfg80211_stop_sched_scan(rdev, reqid, true);
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_sched_scan_stopped_locked);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_civoid cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	wiphy_lock(wiphy);
123762306a36Sopenharmony_ci	cfg80211_sched_scan_stopped_locked(wiphy, reqid);
123862306a36Sopenharmony_ci	wiphy_unlock(wiphy);
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_sched_scan_stopped);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ciint cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
124362306a36Sopenharmony_ci				 struct cfg80211_sched_scan_request *req,
124462306a36Sopenharmony_ci				 bool driver_initiated)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	lockdep_assert_held(&rdev->wiphy.mtx);
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (!driver_initiated) {
124962306a36Sopenharmony_ci		int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid);
125062306a36Sopenharmony_ci		if (err)
125162306a36Sopenharmony_ci			return err;
125262306a36Sopenharmony_ci	}
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	nl80211_send_sched_scan(req, NL80211_CMD_SCHED_SCAN_STOPPED);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	cfg80211_del_sched_scan_req(rdev, req);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return 0;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ciint __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
126262306a36Sopenharmony_ci			       u64 reqid, bool driver_initiated)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct cfg80211_sched_scan_request *sched_scan_req;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	lockdep_assert_held(&rdev->wiphy.mtx);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
126962306a36Sopenharmony_ci	if (!sched_scan_req)
127062306a36Sopenharmony_ci		return -ENOENT;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	return cfg80211_stop_sched_scan_req(rdev, sched_scan_req,
127362306a36Sopenharmony_ci					    driver_initiated);
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_civoid cfg80211_bss_age(struct cfg80211_registered_device *rdev,
127762306a36Sopenharmony_ci                      unsigned long age_secs)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss;
128062306a36Sopenharmony_ci	unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
128362306a36Sopenharmony_ci	list_for_each_entry(bss, &rdev->bss_list, list)
128462306a36Sopenharmony_ci		bss->ts -= age_jiffies;
128562306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_civoid cfg80211_bss_expire(struct cfg80211_registered_device *rdev)
128962306a36Sopenharmony_ci{
129062306a36Sopenharmony_ci	__cfg80211_bss_expire(rdev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
129162306a36Sopenharmony_ci}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_civoid cfg80211_bss_flush(struct wiphy *wiphy)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
129862306a36Sopenharmony_ci	__cfg80211_bss_expire(rdev, jiffies);
129962306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_bss_flush);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ciconst struct element *
130462306a36Sopenharmony_cicfg80211_find_elem_match(u8 eid, const u8 *ies, unsigned int len,
130562306a36Sopenharmony_ci			 const u8 *match, unsigned int match_len,
130662306a36Sopenharmony_ci			 unsigned int match_offset)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	const struct element *elem;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	for_each_element_id(elem, eid, ies, len) {
131162306a36Sopenharmony_ci		if (elem->datalen >= match_offset + match_len &&
131262306a36Sopenharmony_ci		    !memcmp(elem->data + match_offset, match, match_len))
131362306a36Sopenharmony_ci			return elem;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	return NULL;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_find_elem_match);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ciconst struct element *cfg80211_find_vendor_elem(unsigned int oui, int oui_type,
132162306a36Sopenharmony_ci						const u8 *ies,
132262306a36Sopenharmony_ci						unsigned int len)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	const struct element *elem;
132562306a36Sopenharmony_ci	u8 match[] = { oui >> 16, oui >> 8, oui, oui_type };
132662306a36Sopenharmony_ci	int match_len = (oui_type < 0) ? 3 : sizeof(match);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (WARN_ON(oui_type > 0xff))
132962306a36Sopenharmony_ci		return NULL;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	elem = cfg80211_find_elem_match(WLAN_EID_VENDOR_SPECIFIC, ies, len,
133262306a36Sopenharmony_ci					match, match_len, 0);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	if (!elem || elem->datalen < 4)
133562306a36Sopenharmony_ci		return NULL;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	return elem;
133862306a36Sopenharmony_ci}
133962306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_find_vendor_elem);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci/**
134262306a36Sopenharmony_ci * enum bss_compare_mode - BSS compare mode
134362306a36Sopenharmony_ci * @BSS_CMP_REGULAR: regular compare mode (for insertion and normal find)
134462306a36Sopenharmony_ci * @BSS_CMP_HIDE_ZLEN: find hidden SSID with zero-length mode
134562306a36Sopenharmony_ci * @BSS_CMP_HIDE_NUL: find hidden SSID with NUL-ed out mode
134662306a36Sopenharmony_ci */
134762306a36Sopenharmony_cienum bss_compare_mode {
134862306a36Sopenharmony_ci	BSS_CMP_REGULAR,
134962306a36Sopenharmony_ci	BSS_CMP_HIDE_ZLEN,
135062306a36Sopenharmony_ci	BSS_CMP_HIDE_NUL,
135162306a36Sopenharmony_ci};
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_cistatic int cmp_bss(struct cfg80211_bss *a,
135462306a36Sopenharmony_ci		   struct cfg80211_bss *b,
135562306a36Sopenharmony_ci		   enum bss_compare_mode mode)
135662306a36Sopenharmony_ci{
135762306a36Sopenharmony_ci	const struct cfg80211_bss_ies *a_ies, *b_ies;
135862306a36Sopenharmony_ci	const u8 *ie1 = NULL;
135962306a36Sopenharmony_ci	const u8 *ie2 = NULL;
136062306a36Sopenharmony_ci	int i, r;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	if (a->channel != b->channel)
136362306a36Sopenharmony_ci		return (b->channel->center_freq * 1000 + b->channel->freq_offset) -
136462306a36Sopenharmony_ci		       (a->channel->center_freq * 1000 + a->channel->freq_offset);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	a_ies = rcu_access_pointer(a->ies);
136762306a36Sopenharmony_ci	if (!a_ies)
136862306a36Sopenharmony_ci		return -1;
136962306a36Sopenharmony_ci	b_ies = rcu_access_pointer(b->ies);
137062306a36Sopenharmony_ci	if (!b_ies)
137162306a36Sopenharmony_ci		return 1;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (WLAN_CAPABILITY_IS_STA_BSS(a->capability))
137462306a36Sopenharmony_ci		ie1 = cfg80211_find_ie(WLAN_EID_MESH_ID,
137562306a36Sopenharmony_ci				       a_ies->data, a_ies->len);
137662306a36Sopenharmony_ci	if (WLAN_CAPABILITY_IS_STA_BSS(b->capability))
137762306a36Sopenharmony_ci		ie2 = cfg80211_find_ie(WLAN_EID_MESH_ID,
137862306a36Sopenharmony_ci				       b_ies->data, b_ies->len);
137962306a36Sopenharmony_ci	if (ie1 && ie2) {
138062306a36Sopenharmony_ci		int mesh_id_cmp;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci		if (ie1[1] == ie2[1])
138362306a36Sopenharmony_ci			mesh_id_cmp = memcmp(ie1 + 2, ie2 + 2, ie1[1]);
138462306a36Sopenharmony_ci		else
138562306a36Sopenharmony_ci			mesh_id_cmp = ie2[1] - ie1[1];
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci		ie1 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
138862306a36Sopenharmony_ci				       a_ies->data, a_ies->len);
138962306a36Sopenharmony_ci		ie2 = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
139062306a36Sopenharmony_ci				       b_ies->data, b_ies->len);
139162306a36Sopenharmony_ci		if (ie1 && ie2) {
139262306a36Sopenharmony_ci			if (mesh_id_cmp)
139362306a36Sopenharmony_ci				return mesh_id_cmp;
139462306a36Sopenharmony_ci			if (ie1[1] != ie2[1])
139562306a36Sopenharmony_ci				return ie2[1] - ie1[1];
139662306a36Sopenharmony_ci			return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
139762306a36Sopenharmony_ci		}
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	r = memcmp(a->bssid, b->bssid, sizeof(a->bssid));
140162306a36Sopenharmony_ci	if (r)
140262306a36Sopenharmony_ci		return r;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
140562306a36Sopenharmony_ci	ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	if (!ie1 && !ie2)
140862306a36Sopenharmony_ci		return 0;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	/*
141162306a36Sopenharmony_ci	 * Note that with "hide_ssid", the function returns a match if
141262306a36Sopenharmony_ci	 * the already-present BSS ("b") is a hidden SSID beacon for
141362306a36Sopenharmony_ci	 * the new BSS ("a").
141462306a36Sopenharmony_ci	 */
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	/* sort missing IE before (left of) present IE */
141762306a36Sopenharmony_ci	if (!ie1)
141862306a36Sopenharmony_ci		return -1;
141962306a36Sopenharmony_ci	if (!ie2)
142062306a36Sopenharmony_ci		return 1;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	switch (mode) {
142362306a36Sopenharmony_ci	case BSS_CMP_HIDE_ZLEN:
142462306a36Sopenharmony_ci		/*
142562306a36Sopenharmony_ci		 * In ZLEN mode we assume the BSS entry we're
142662306a36Sopenharmony_ci		 * looking for has a zero-length SSID. So if
142762306a36Sopenharmony_ci		 * the one we're looking at right now has that,
142862306a36Sopenharmony_ci		 * return 0. Otherwise, return the difference
142962306a36Sopenharmony_ci		 * in length, but since we're looking for the
143062306a36Sopenharmony_ci		 * 0-length it's really equivalent to returning
143162306a36Sopenharmony_ci		 * the length of the one we're looking at.
143262306a36Sopenharmony_ci		 *
143362306a36Sopenharmony_ci		 * No content comparison is needed as we assume
143462306a36Sopenharmony_ci		 * the content length is zero.
143562306a36Sopenharmony_ci		 */
143662306a36Sopenharmony_ci		return ie2[1];
143762306a36Sopenharmony_ci	case BSS_CMP_REGULAR:
143862306a36Sopenharmony_ci	default:
143962306a36Sopenharmony_ci		/* sort by length first, then by contents */
144062306a36Sopenharmony_ci		if (ie1[1] != ie2[1])
144162306a36Sopenharmony_ci			return ie2[1] - ie1[1];
144262306a36Sopenharmony_ci		return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
144362306a36Sopenharmony_ci	case BSS_CMP_HIDE_NUL:
144462306a36Sopenharmony_ci		if (ie1[1] != ie2[1])
144562306a36Sopenharmony_ci			return ie2[1] - ie1[1];
144662306a36Sopenharmony_ci		/* this is equivalent to memcmp(zeroes, ie2 + 2, len) */
144762306a36Sopenharmony_ci		for (i = 0; i < ie2[1]; i++)
144862306a36Sopenharmony_ci			if (ie2[i + 2])
144962306a36Sopenharmony_ci				return -1;
145062306a36Sopenharmony_ci		return 0;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_cistatic bool cfg80211_bss_type_match(u16 capability,
145562306a36Sopenharmony_ci				    enum nl80211_band band,
145662306a36Sopenharmony_ci				    enum ieee80211_bss_type bss_type)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	bool ret = true;
145962306a36Sopenharmony_ci	u16 mask, val;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	if (bss_type == IEEE80211_BSS_TYPE_ANY)
146262306a36Sopenharmony_ci		return ret;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	if (band == NL80211_BAND_60GHZ) {
146562306a36Sopenharmony_ci		mask = WLAN_CAPABILITY_DMG_TYPE_MASK;
146662306a36Sopenharmony_ci		switch (bss_type) {
146762306a36Sopenharmony_ci		case IEEE80211_BSS_TYPE_ESS:
146862306a36Sopenharmony_ci			val = WLAN_CAPABILITY_DMG_TYPE_AP;
146962306a36Sopenharmony_ci			break;
147062306a36Sopenharmony_ci		case IEEE80211_BSS_TYPE_PBSS:
147162306a36Sopenharmony_ci			val = WLAN_CAPABILITY_DMG_TYPE_PBSS;
147262306a36Sopenharmony_ci			break;
147362306a36Sopenharmony_ci		case IEEE80211_BSS_TYPE_IBSS:
147462306a36Sopenharmony_ci			val = WLAN_CAPABILITY_DMG_TYPE_IBSS;
147562306a36Sopenharmony_ci			break;
147662306a36Sopenharmony_ci		default:
147762306a36Sopenharmony_ci			return false;
147862306a36Sopenharmony_ci		}
147962306a36Sopenharmony_ci	} else {
148062306a36Sopenharmony_ci		mask = WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS;
148162306a36Sopenharmony_ci		switch (bss_type) {
148262306a36Sopenharmony_ci		case IEEE80211_BSS_TYPE_ESS:
148362306a36Sopenharmony_ci			val = WLAN_CAPABILITY_ESS;
148462306a36Sopenharmony_ci			break;
148562306a36Sopenharmony_ci		case IEEE80211_BSS_TYPE_IBSS:
148662306a36Sopenharmony_ci			val = WLAN_CAPABILITY_IBSS;
148762306a36Sopenharmony_ci			break;
148862306a36Sopenharmony_ci		case IEEE80211_BSS_TYPE_MBSS:
148962306a36Sopenharmony_ci			val = 0;
149062306a36Sopenharmony_ci			break;
149162306a36Sopenharmony_ci		default:
149262306a36Sopenharmony_ci			return false;
149362306a36Sopenharmony_ci		}
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	ret = ((capability & mask) == val);
149762306a36Sopenharmony_ci	return ret;
149862306a36Sopenharmony_ci}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci/* Returned bss is reference counted and must be cleaned up appropriately. */
150162306a36Sopenharmony_cistruct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
150262306a36Sopenharmony_ci				      struct ieee80211_channel *channel,
150362306a36Sopenharmony_ci				      const u8 *bssid,
150462306a36Sopenharmony_ci				      const u8 *ssid, size_t ssid_len,
150562306a36Sopenharmony_ci				      enum ieee80211_bss_type bss_type,
150662306a36Sopenharmony_ci				      enum ieee80211_privacy privacy)
150762306a36Sopenharmony_ci{
150862306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
150962306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss, *res = NULL;
151062306a36Sopenharmony_ci	unsigned long now = jiffies;
151162306a36Sopenharmony_ci	int bss_privacy;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, bss_type,
151462306a36Sopenharmony_ci			       privacy);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	list_for_each_entry(bss, &rdev->bss_list, list) {
151962306a36Sopenharmony_ci		if (!cfg80211_bss_type_match(bss->pub.capability,
152062306a36Sopenharmony_ci					     bss->pub.channel->band, bss_type))
152162306a36Sopenharmony_ci			continue;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci		bss_privacy = (bss->pub.capability & WLAN_CAPABILITY_PRIVACY);
152462306a36Sopenharmony_ci		if ((privacy == IEEE80211_PRIVACY_ON && !bss_privacy) ||
152562306a36Sopenharmony_ci		    (privacy == IEEE80211_PRIVACY_OFF && bss_privacy))
152662306a36Sopenharmony_ci			continue;
152762306a36Sopenharmony_ci		if (channel && bss->pub.channel != channel)
152862306a36Sopenharmony_ci			continue;
152962306a36Sopenharmony_ci		if (!is_valid_ether_addr(bss->pub.bssid))
153062306a36Sopenharmony_ci			continue;
153162306a36Sopenharmony_ci		/* Don't get expired BSS structs */
153262306a36Sopenharmony_ci		if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
153362306a36Sopenharmony_ci		    !atomic_read(&bss->hold))
153462306a36Sopenharmony_ci			continue;
153562306a36Sopenharmony_ci		if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
153662306a36Sopenharmony_ci			res = bss;
153762306a36Sopenharmony_ci			bss_ref_get(rdev, res);
153862306a36Sopenharmony_ci			break;
153962306a36Sopenharmony_ci		}
154062306a36Sopenharmony_ci	}
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
154362306a36Sopenharmony_ci	if (!res)
154462306a36Sopenharmony_ci		return NULL;
154562306a36Sopenharmony_ci	trace_cfg80211_return_bss(&res->pub);
154662306a36Sopenharmony_ci	return &res->pub;
154762306a36Sopenharmony_ci}
154862306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_get_bss);
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_cistatic void rb_insert_bss(struct cfg80211_registered_device *rdev,
155162306a36Sopenharmony_ci			  struct cfg80211_internal_bss *bss)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	struct rb_node **p = &rdev->bss_tree.rb_node;
155462306a36Sopenharmony_ci	struct rb_node *parent = NULL;
155562306a36Sopenharmony_ci	struct cfg80211_internal_bss *tbss;
155662306a36Sopenharmony_ci	int cmp;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	while (*p) {
155962306a36Sopenharmony_ci		parent = *p;
156062306a36Sopenharmony_ci		tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		cmp = cmp_bss(&bss->pub, &tbss->pub, BSS_CMP_REGULAR);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci		if (WARN_ON(!cmp)) {
156562306a36Sopenharmony_ci			/* will sort of leak this BSS */
156662306a36Sopenharmony_ci			return;
156762306a36Sopenharmony_ci		}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci		if (cmp < 0)
157062306a36Sopenharmony_ci			p = &(*p)->rb_left;
157162306a36Sopenharmony_ci		else
157262306a36Sopenharmony_ci			p = &(*p)->rb_right;
157362306a36Sopenharmony_ci	}
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	rb_link_node(&bss->rbn, parent, p);
157662306a36Sopenharmony_ci	rb_insert_color(&bss->rbn, &rdev->bss_tree);
157762306a36Sopenharmony_ci}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_cistatic struct cfg80211_internal_bss *
158062306a36Sopenharmony_cirb_find_bss(struct cfg80211_registered_device *rdev,
158162306a36Sopenharmony_ci	    struct cfg80211_internal_bss *res,
158262306a36Sopenharmony_ci	    enum bss_compare_mode mode)
158362306a36Sopenharmony_ci{
158462306a36Sopenharmony_ci	struct rb_node *n = rdev->bss_tree.rb_node;
158562306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss;
158662306a36Sopenharmony_ci	int r;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	while (n) {
158962306a36Sopenharmony_ci		bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
159062306a36Sopenharmony_ci		r = cmp_bss(&res->pub, &bss->pub, mode);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci		if (r == 0)
159362306a36Sopenharmony_ci			return bss;
159462306a36Sopenharmony_ci		else if (r < 0)
159562306a36Sopenharmony_ci			n = n->rb_left;
159662306a36Sopenharmony_ci		else
159762306a36Sopenharmony_ci			n = n->rb_right;
159862306a36Sopenharmony_ci	}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	return NULL;
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cistatic bool cfg80211_combine_bsses(struct cfg80211_registered_device *rdev,
160462306a36Sopenharmony_ci				   struct cfg80211_internal_bss *new)
160562306a36Sopenharmony_ci{
160662306a36Sopenharmony_ci	const struct cfg80211_bss_ies *ies;
160762306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss;
160862306a36Sopenharmony_ci	const u8 *ie;
160962306a36Sopenharmony_ci	int i, ssidlen;
161062306a36Sopenharmony_ci	u8 fold = 0;
161162306a36Sopenharmony_ci	u32 n_entries = 0;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	ies = rcu_access_pointer(new->pub.beacon_ies);
161462306a36Sopenharmony_ci	if (WARN_ON(!ies))
161562306a36Sopenharmony_ci		return false;
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
161862306a36Sopenharmony_ci	if (!ie) {
161962306a36Sopenharmony_ci		/* nothing to do */
162062306a36Sopenharmony_ci		return true;
162162306a36Sopenharmony_ci	}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	ssidlen = ie[1];
162462306a36Sopenharmony_ci	for (i = 0; i < ssidlen; i++)
162562306a36Sopenharmony_ci		fold |= ie[2 + i];
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	if (fold) {
162862306a36Sopenharmony_ci		/* not a hidden SSID */
162962306a36Sopenharmony_ci		return true;
163062306a36Sopenharmony_ci	}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	/* This is the bad part ... */
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	list_for_each_entry(bss, &rdev->bss_list, list) {
163562306a36Sopenharmony_ci		/*
163662306a36Sopenharmony_ci		 * we're iterating all the entries anyway, so take the
163762306a36Sopenharmony_ci		 * opportunity to validate the list length accounting
163862306a36Sopenharmony_ci		 */
163962306a36Sopenharmony_ci		n_entries++;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci		if (!ether_addr_equal(bss->pub.bssid, new->pub.bssid))
164262306a36Sopenharmony_ci			continue;
164362306a36Sopenharmony_ci		if (bss->pub.channel != new->pub.channel)
164462306a36Sopenharmony_ci			continue;
164562306a36Sopenharmony_ci		if (bss->pub.scan_width != new->pub.scan_width)
164662306a36Sopenharmony_ci			continue;
164762306a36Sopenharmony_ci		if (rcu_access_pointer(bss->pub.beacon_ies))
164862306a36Sopenharmony_ci			continue;
164962306a36Sopenharmony_ci		ies = rcu_access_pointer(bss->pub.ies);
165062306a36Sopenharmony_ci		if (!ies)
165162306a36Sopenharmony_ci			continue;
165262306a36Sopenharmony_ci		ie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
165362306a36Sopenharmony_ci		if (!ie)
165462306a36Sopenharmony_ci			continue;
165562306a36Sopenharmony_ci		if (ssidlen && ie[1] != ssidlen)
165662306a36Sopenharmony_ci			continue;
165762306a36Sopenharmony_ci		if (WARN_ON_ONCE(bss->pub.hidden_beacon_bss))
165862306a36Sopenharmony_ci			continue;
165962306a36Sopenharmony_ci		if (WARN_ON_ONCE(!list_empty(&bss->hidden_list)))
166062306a36Sopenharmony_ci			list_del(&bss->hidden_list);
166162306a36Sopenharmony_ci		/* combine them */
166262306a36Sopenharmony_ci		list_add(&bss->hidden_list, &new->hidden_list);
166362306a36Sopenharmony_ci		bss->pub.hidden_beacon_bss = &new->pub;
166462306a36Sopenharmony_ci		new->refcount += bss->refcount;
166562306a36Sopenharmony_ci		rcu_assign_pointer(bss->pub.beacon_ies,
166662306a36Sopenharmony_ci				   new->pub.beacon_ies);
166762306a36Sopenharmony_ci	}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	WARN_ONCE(n_entries != rdev->bss_entries,
167062306a36Sopenharmony_ci		  "rdev bss entries[%d]/list[len:%d] corruption\n",
167162306a36Sopenharmony_ci		  rdev->bss_entries, n_entries);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	return true;
167462306a36Sopenharmony_ci}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_cistatic void cfg80211_update_hidden_bsses(struct cfg80211_internal_bss *known,
167762306a36Sopenharmony_ci					 const struct cfg80211_bss_ies *new_ies,
167862306a36Sopenharmony_ci					 const struct cfg80211_bss_ies *old_ies)
167962306a36Sopenharmony_ci{
168062306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	/* Assign beacon IEs to all sub entries */
168362306a36Sopenharmony_ci	list_for_each_entry(bss, &known->hidden_list, hidden_list) {
168462306a36Sopenharmony_ci		const struct cfg80211_bss_ies *ies;
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci		ies = rcu_access_pointer(bss->pub.beacon_ies);
168762306a36Sopenharmony_ci		WARN_ON(ies != old_ies);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci		rcu_assign_pointer(bss->pub.beacon_ies, new_ies);
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_cistatic bool
169462306a36Sopenharmony_cicfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
169562306a36Sopenharmony_ci			  struct cfg80211_internal_bss *known,
169662306a36Sopenharmony_ci			  struct cfg80211_internal_bss *new,
169762306a36Sopenharmony_ci			  bool signal_valid)
169862306a36Sopenharmony_ci{
169962306a36Sopenharmony_ci	lockdep_assert_held(&rdev->bss_lock);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	/* Update IEs */
170262306a36Sopenharmony_ci	if (rcu_access_pointer(new->pub.proberesp_ies)) {
170362306a36Sopenharmony_ci		const struct cfg80211_bss_ies *old;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci		old = rcu_access_pointer(known->pub.proberesp_ies);
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci		rcu_assign_pointer(known->pub.proberesp_ies,
170862306a36Sopenharmony_ci				   new->pub.proberesp_ies);
170962306a36Sopenharmony_ci		/* Override possible earlier Beacon frame IEs */
171062306a36Sopenharmony_ci		rcu_assign_pointer(known->pub.ies,
171162306a36Sopenharmony_ci				   new->pub.proberesp_ies);
171262306a36Sopenharmony_ci		if (old)
171362306a36Sopenharmony_ci			kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
171462306a36Sopenharmony_ci	} else if (rcu_access_pointer(new->pub.beacon_ies)) {
171562306a36Sopenharmony_ci		const struct cfg80211_bss_ies *old;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci		if (known->pub.hidden_beacon_bss &&
171862306a36Sopenharmony_ci		    !list_empty(&known->hidden_list)) {
171962306a36Sopenharmony_ci			const struct cfg80211_bss_ies *f;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci			/* The known BSS struct is one of the probe
172262306a36Sopenharmony_ci			 * response members of a group, but we're
172362306a36Sopenharmony_ci			 * receiving a beacon (beacon_ies in the new
172462306a36Sopenharmony_ci			 * bss is used). This can only mean that the
172562306a36Sopenharmony_ci			 * AP changed its beacon from not having an
172662306a36Sopenharmony_ci			 * SSID to showing it, which is confusing so
172762306a36Sopenharmony_ci			 * drop this information.
172862306a36Sopenharmony_ci			 */
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci			f = rcu_access_pointer(new->pub.beacon_ies);
173162306a36Sopenharmony_ci			kfree_rcu((struct cfg80211_bss_ies *)f, rcu_head);
173262306a36Sopenharmony_ci			return false;
173362306a36Sopenharmony_ci		}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci		old = rcu_access_pointer(known->pub.beacon_ies);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci		rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci		/* Override IEs if they were from a beacon before */
174062306a36Sopenharmony_ci		if (old == rcu_access_pointer(known->pub.ies))
174162306a36Sopenharmony_ci			rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci		cfg80211_update_hidden_bsses(known,
174462306a36Sopenharmony_ci					     rcu_access_pointer(new->pub.beacon_ies),
174562306a36Sopenharmony_ci					     old);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci		if (old)
174862306a36Sopenharmony_ci			kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
174962306a36Sopenharmony_ci	}
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	known->pub.beacon_interval = new->pub.beacon_interval;
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci	/* don't update the signal if beacon was heard on
175462306a36Sopenharmony_ci	 * adjacent channel.
175562306a36Sopenharmony_ci	 */
175662306a36Sopenharmony_ci	if (signal_valid)
175762306a36Sopenharmony_ci		known->pub.signal = new->pub.signal;
175862306a36Sopenharmony_ci	known->pub.capability = new->pub.capability;
175962306a36Sopenharmony_ci	known->ts = new->ts;
176062306a36Sopenharmony_ci	known->ts_boottime = new->ts_boottime;
176162306a36Sopenharmony_ci	known->parent_tsf = new->parent_tsf;
176262306a36Sopenharmony_ci	known->pub.chains = new->pub.chains;
176362306a36Sopenharmony_ci	memcpy(known->pub.chain_signal, new->pub.chain_signal,
176462306a36Sopenharmony_ci	       IEEE80211_MAX_CHAINS);
176562306a36Sopenharmony_ci	ether_addr_copy(known->parent_bssid, new->parent_bssid);
176662306a36Sopenharmony_ci	known->pub.max_bssid_indicator = new->pub.max_bssid_indicator;
176762306a36Sopenharmony_ci	known->pub.bssid_index = new->pub.bssid_index;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	return true;
177062306a36Sopenharmony_ci}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci/* Returned bss is reference counted and must be cleaned up appropriately. */
177362306a36Sopenharmony_cistatic struct cfg80211_internal_bss *
177462306a36Sopenharmony_ci__cfg80211_bss_update(struct cfg80211_registered_device *rdev,
177562306a36Sopenharmony_ci		      struct cfg80211_internal_bss *tmp,
177662306a36Sopenharmony_ci		      bool signal_valid, unsigned long ts)
177762306a36Sopenharmony_ci{
177862306a36Sopenharmony_ci	struct cfg80211_internal_bss *found = NULL;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	if (WARN_ON(!tmp->pub.channel))
178162306a36Sopenharmony_ci		return NULL;
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	tmp->ts = ts;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
178662306a36Sopenharmony_ci		return NULL;
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	found = rb_find_bss(rdev, tmp, BSS_CMP_REGULAR);
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	if (found) {
179262306a36Sopenharmony_ci		if (!cfg80211_update_known_bss(rdev, found, tmp, signal_valid))
179362306a36Sopenharmony_ci			return NULL;
179462306a36Sopenharmony_ci	} else {
179562306a36Sopenharmony_ci		struct cfg80211_internal_bss *new;
179662306a36Sopenharmony_ci		struct cfg80211_internal_bss *hidden;
179762306a36Sopenharmony_ci		struct cfg80211_bss_ies *ies;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci		/*
180062306a36Sopenharmony_ci		 * create a copy -- the "res" variable that is passed in
180162306a36Sopenharmony_ci		 * is allocated on the stack since it's not needed in the
180262306a36Sopenharmony_ci		 * more common case of an update
180362306a36Sopenharmony_ci		 */
180462306a36Sopenharmony_ci		new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
180562306a36Sopenharmony_ci			      GFP_ATOMIC);
180662306a36Sopenharmony_ci		if (!new) {
180762306a36Sopenharmony_ci			ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
180862306a36Sopenharmony_ci			if (ies)
180962306a36Sopenharmony_ci				kfree_rcu(ies, rcu_head);
181062306a36Sopenharmony_ci			ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
181162306a36Sopenharmony_ci			if (ies)
181262306a36Sopenharmony_ci				kfree_rcu(ies, rcu_head);
181362306a36Sopenharmony_ci			return NULL;
181462306a36Sopenharmony_ci		}
181562306a36Sopenharmony_ci		memcpy(new, tmp, sizeof(*new));
181662306a36Sopenharmony_ci		new->refcount = 1;
181762306a36Sopenharmony_ci		INIT_LIST_HEAD(&new->hidden_list);
181862306a36Sopenharmony_ci		INIT_LIST_HEAD(&new->pub.nontrans_list);
181962306a36Sopenharmony_ci		/* we'll set this later if it was non-NULL */
182062306a36Sopenharmony_ci		new->pub.transmitted_bss = NULL;
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci		if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
182362306a36Sopenharmony_ci			hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN);
182462306a36Sopenharmony_ci			if (!hidden)
182562306a36Sopenharmony_ci				hidden = rb_find_bss(rdev, tmp,
182662306a36Sopenharmony_ci						     BSS_CMP_HIDE_NUL);
182762306a36Sopenharmony_ci			if (hidden) {
182862306a36Sopenharmony_ci				new->pub.hidden_beacon_bss = &hidden->pub;
182962306a36Sopenharmony_ci				list_add(&new->hidden_list,
183062306a36Sopenharmony_ci					 &hidden->hidden_list);
183162306a36Sopenharmony_ci				hidden->refcount++;
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci				ies = (void *)rcu_access_pointer(new->pub.beacon_ies);
183462306a36Sopenharmony_ci				rcu_assign_pointer(new->pub.beacon_ies,
183562306a36Sopenharmony_ci						   hidden->pub.beacon_ies);
183662306a36Sopenharmony_ci				if (ies)
183762306a36Sopenharmony_ci					kfree_rcu(ies, rcu_head);
183862306a36Sopenharmony_ci			}
183962306a36Sopenharmony_ci		} else {
184062306a36Sopenharmony_ci			/*
184162306a36Sopenharmony_ci			 * Ok so we found a beacon, and don't have an entry. If
184262306a36Sopenharmony_ci			 * it's a beacon with hidden SSID, we might be in for an
184362306a36Sopenharmony_ci			 * expensive search for any probe responses that should
184462306a36Sopenharmony_ci			 * be grouped with this beacon for updates ...
184562306a36Sopenharmony_ci			 */
184662306a36Sopenharmony_ci			if (!cfg80211_combine_bsses(rdev, new)) {
184762306a36Sopenharmony_ci				bss_ref_put(rdev, new);
184862306a36Sopenharmony_ci				return NULL;
184962306a36Sopenharmony_ci			}
185062306a36Sopenharmony_ci		}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci		if (rdev->bss_entries >= bss_entries_limit &&
185362306a36Sopenharmony_ci		    !cfg80211_bss_expire_oldest(rdev)) {
185462306a36Sopenharmony_ci			bss_ref_put(rdev, new);
185562306a36Sopenharmony_ci			return NULL;
185662306a36Sopenharmony_ci		}
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci		/* This must be before the call to bss_ref_get */
185962306a36Sopenharmony_ci		if (tmp->pub.transmitted_bss) {
186062306a36Sopenharmony_ci			new->pub.transmitted_bss = tmp->pub.transmitted_bss;
186162306a36Sopenharmony_ci			bss_ref_get(rdev, bss_from_pub(tmp->pub.transmitted_bss));
186262306a36Sopenharmony_ci		}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci		list_add_tail(&new->list, &rdev->bss_list);
186562306a36Sopenharmony_ci		rdev->bss_entries++;
186662306a36Sopenharmony_ci		rb_insert_bss(rdev, new);
186762306a36Sopenharmony_ci		found = new;
186862306a36Sopenharmony_ci	}
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci	rdev->bss_generation++;
187162306a36Sopenharmony_ci	bss_ref_get(rdev, found);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	return found;
187462306a36Sopenharmony_ci}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_cistruct cfg80211_internal_bss *
187762306a36Sopenharmony_cicfg80211_bss_update(struct cfg80211_registered_device *rdev,
187862306a36Sopenharmony_ci		    struct cfg80211_internal_bss *tmp,
187962306a36Sopenharmony_ci		    bool signal_valid, unsigned long ts)
188062306a36Sopenharmony_ci{
188162306a36Sopenharmony_ci	struct cfg80211_internal_bss *res;
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
188462306a36Sopenharmony_ci	res = __cfg80211_bss_update(rdev, tmp, signal_valid, ts);
188562306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	return res;
188862306a36Sopenharmony_ci}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ciint cfg80211_get_ies_channel_number(const u8 *ie, size_t ielen,
189162306a36Sopenharmony_ci				    enum nl80211_band band)
189262306a36Sopenharmony_ci{
189362306a36Sopenharmony_ci	const struct element *tmp;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	if (band == NL80211_BAND_6GHZ) {
189662306a36Sopenharmony_ci		struct ieee80211_he_operation *he_oper;
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci		tmp = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ie,
189962306a36Sopenharmony_ci					     ielen);
190062306a36Sopenharmony_ci		if (tmp && tmp->datalen >= sizeof(*he_oper) &&
190162306a36Sopenharmony_ci		    tmp->datalen >= ieee80211_he_oper_size(&tmp->data[1])) {
190262306a36Sopenharmony_ci			const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci			he_oper = (void *)&tmp->data[1];
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci			he_6ghz_oper = ieee80211_he_6ghz_oper(he_oper);
190762306a36Sopenharmony_ci			if (!he_6ghz_oper)
190862306a36Sopenharmony_ci				return -1;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci			return he_6ghz_oper->primary;
191162306a36Sopenharmony_ci		}
191262306a36Sopenharmony_ci	} else if (band == NL80211_BAND_S1GHZ) {
191362306a36Sopenharmony_ci		tmp = cfg80211_find_elem(WLAN_EID_S1G_OPERATION, ie, ielen);
191462306a36Sopenharmony_ci		if (tmp && tmp->datalen >= sizeof(struct ieee80211_s1g_oper_ie)) {
191562306a36Sopenharmony_ci			struct ieee80211_s1g_oper_ie *s1gop = (void *)tmp->data;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci			return s1gop->oper_ch;
191862306a36Sopenharmony_ci		}
191962306a36Sopenharmony_ci	} else {
192062306a36Sopenharmony_ci		tmp = cfg80211_find_elem(WLAN_EID_DS_PARAMS, ie, ielen);
192162306a36Sopenharmony_ci		if (tmp && tmp->datalen == 1)
192262306a36Sopenharmony_ci			return tmp->data[0];
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci		tmp = cfg80211_find_elem(WLAN_EID_HT_OPERATION, ie, ielen);
192562306a36Sopenharmony_ci		if (tmp &&
192662306a36Sopenharmony_ci		    tmp->datalen >= sizeof(struct ieee80211_ht_operation)) {
192762306a36Sopenharmony_ci			struct ieee80211_ht_operation *htop = (void *)tmp->data;
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci			return htop->primary_chan;
193062306a36Sopenharmony_ci		}
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	return -1;
193462306a36Sopenharmony_ci}
193562306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_get_ies_channel_number);
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci/*
193862306a36Sopenharmony_ci * Update RX channel information based on the available frame payload
193962306a36Sopenharmony_ci * information. This is mainly for the 2.4 GHz band where frames can be received
194062306a36Sopenharmony_ci * from neighboring channels and the Beacon frames use the DSSS Parameter Set
194162306a36Sopenharmony_ci * element to indicate the current (transmitting) channel, but this might also
194262306a36Sopenharmony_ci * be needed on other bands if RX frequency does not match with the actual
194362306a36Sopenharmony_ci * operating channel of a BSS, or if the AP reports a different primary channel.
194462306a36Sopenharmony_ci */
194562306a36Sopenharmony_cistatic struct ieee80211_channel *
194662306a36Sopenharmony_cicfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
194762306a36Sopenharmony_ci			 struct ieee80211_channel *channel,
194862306a36Sopenharmony_ci			 enum nl80211_bss_scan_width scan_width)
194962306a36Sopenharmony_ci{
195062306a36Sopenharmony_ci	u32 freq;
195162306a36Sopenharmony_ci	int channel_number;
195262306a36Sopenharmony_ci	struct ieee80211_channel *alt_channel;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	channel_number = cfg80211_get_ies_channel_number(ie, ielen,
195562306a36Sopenharmony_ci							 channel->band);
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	if (channel_number < 0) {
195862306a36Sopenharmony_ci		/* No channel information in frame payload */
195962306a36Sopenharmony_ci		return channel;
196062306a36Sopenharmony_ci	}
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	freq = ieee80211_channel_to_freq_khz(channel_number, channel->band);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	/*
196562306a36Sopenharmony_ci	 * Frame info (beacon/prob res) is the same as received channel,
196662306a36Sopenharmony_ci	 * no need for further processing.
196762306a36Sopenharmony_ci	 */
196862306a36Sopenharmony_ci	if (freq == ieee80211_channel_to_khz(channel))
196962306a36Sopenharmony_ci		return channel;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	alt_channel = ieee80211_get_channel_khz(wiphy, freq);
197262306a36Sopenharmony_ci	if (!alt_channel) {
197362306a36Sopenharmony_ci		if (channel->band == NL80211_BAND_2GHZ ||
197462306a36Sopenharmony_ci		    channel->band == NL80211_BAND_6GHZ) {
197562306a36Sopenharmony_ci			/*
197662306a36Sopenharmony_ci			 * Better not allow unexpected channels when that could
197762306a36Sopenharmony_ci			 * be going beyond the 1-11 range (e.g., discovering
197862306a36Sopenharmony_ci			 * BSS on channel 12 when radio is configured for
197962306a36Sopenharmony_ci			 * channel 11) or beyond the 6 GHz channel range.
198062306a36Sopenharmony_ci			 */
198162306a36Sopenharmony_ci			return NULL;
198262306a36Sopenharmony_ci		}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci		/* No match for the payload channel number - ignore it */
198562306a36Sopenharmony_ci		return channel;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	if (scan_width == NL80211_BSS_CHAN_WIDTH_10 ||
198962306a36Sopenharmony_ci	    scan_width == NL80211_BSS_CHAN_WIDTH_5) {
199062306a36Sopenharmony_ci		/*
199162306a36Sopenharmony_ci		 * Ignore channel number in 5 and 10 MHz channels where there
199262306a36Sopenharmony_ci		 * may not be an n:1 or 1:n mapping between frequencies and
199362306a36Sopenharmony_ci		 * channel numbers.
199462306a36Sopenharmony_ci		 */
199562306a36Sopenharmony_ci		return channel;
199662306a36Sopenharmony_ci	}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	/*
199962306a36Sopenharmony_ci	 * Use the channel determined through the payload channel number
200062306a36Sopenharmony_ci	 * instead of the RX channel reported by the driver.
200162306a36Sopenharmony_ci	 */
200262306a36Sopenharmony_ci	if (alt_channel->flags & IEEE80211_CHAN_DISABLED)
200362306a36Sopenharmony_ci		return NULL;
200462306a36Sopenharmony_ci	return alt_channel;
200562306a36Sopenharmony_ci}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_cistruct cfg80211_inform_single_bss_data {
200862306a36Sopenharmony_ci	struct cfg80211_inform_bss *drv_data;
200962306a36Sopenharmony_ci	enum cfg80211_bss_frame_type ftype;
201062306a36Sopenharmony_ci	struct ieee80211_channel *channel;
201162306a36Sopenharmony_ci	u8 bssid[ETH_ALEN];
201262306a36Sopenharmony_ci	u64 tsf;
201362306a36Sopenharmony_ci	u16 capability;
201462306a36Sopenharmony_ci	u16 beacon_interval;
201562306a36Sopenharmony_ci	const u8 *ie;
201662306a36Sopenharmony_ci	size_t ielen;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	enum {
201962306a36Sopenharmony_ci		BSS_SOURCE_DIRECT = 0,
202062306a36Sopenharmony_ci		BSS_SOURCE_MBSSID,
202162306a36Sopenharmony_ci		BSS_SOURCE_STA_PROFILE,
202262306a36Sopenharmony_ci	} bss_source;
202362306a36Sopenharmony_ci	/* Set if reporting bss_source != BSS_SOURCE_DIRECT */
202462306a36Sopenharmony_ci	struct cfg80211_bss *source_bss;
202562306a36Sopenharmony_ci	u8 max_bssid_indicator;
202662306a36Sopenharmony_ci	u8 bssid_index;
202762306a36Sopenharmony_ci};
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci/* Returned bss is reference counted and must be cleaned up appropriately. */
203062306a36Sopenharmony_cistatic struct cfg80211_bss *
203162306a36Sopenharmony_cicfg80211_inform_single_bss_data(struct wiphy *wiphy,
203262306a36Sopenharmony_ci				struct cfg80211_inform_single_bss_data *data,
203362306a36Sopenharmony_ci				gfp_t gfp)
203462306a36Sopenharmony_ci{
203562306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
203662306a36Sopenharmony_ci	struct cfg80211_inform_bss *drv_data = data->drv_data;
203762306a36Sopenharmony_ci	struct cfg80211_bss_ies *ies;
203862306a36Sopenharmony_ci	struct ieee80211_channel *channel;
203962306a36Sopenharmony_ci	struct cfg80211_internal_bss tmp = {}, *res;
204062306a36Sopenharmony_ci	int bss_type;
204162306a36Sopenharmony_ci	bool signal_valid;
204262306a36Sopenharmony_ci	unsigned long ts;
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	if (WARN_ON(!wiphy))
204562306a36Sopenharmony_ci		return NULL;
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
204862306a36Sopenharmony_ci		    (drv_data->signal < 0 || drv_data->signal > 100)))
204962306a36Sopenharmony_ci		return NULL;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (WARN_ON(data->bss_source != BSS_SOURCE_DIRECT && !data->source_bss))
205262306a36Sopenharmony_ci		return NULL;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	channel = data->channel;
205562306a36Sopenharmony_ci	if (!channel)
205662306a36Sopenharmony_ci		channel = cfg80211_get_bss_channel(wiphy, data->ie, data->ielen,
205762306a36Sopenharmony_ci						   drv_data->chan,
205862306a36Sopenharmony_ci						   drv_data->scan_width);
205962306a36Sopenharmony_ci	if (!channel)
206062306a36Sopenharmony_ci		return NULL;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	memcpy(tmp.pub.bssid, data->bssid, ETH_ALEN);
206362306a36Sopenharmony_ci	tmp.pub.channel = channel;
206462306a36Sopenharmony_ci	tmp.pub.scan_width = drv_data->scan_width;
206562306a36Sopenharmony_ci	if (data->bss_source != BSS_SOURCE_STA_PROFILE)
206662306a36Sopenharmony_ci		tmp.pub.signal = drv_data->signal;
206762306a36Sopenharmony_ci	else
206862306a36Sopenharmony_ci		tmp.pub.signal = 0;
206962306a36Sopenharmony_ci	tmp.pub.beacon_interval = data->beacon_interval;
207062306a36Sopenharmony_ci	tmp.pub.capability = data->capability;
207162306a36Sopenharmony_ci	tmp.ts_boottime = drv_data->boottime_ns;
207262306a36Sopenharmony_ci	tmp.parent_tsf = drv_data->parent_tsf;
207362306a36Sopenharmony_ci	ether_addr_copy(tmp.parent_bssid, drv_data->parent_bssid);
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	if (data->bss_source != BSS_SOURCE_DIRECT) {
207662306a36Sopenharmony_ci		tmp.pub.transmitted_bss = data->source_bss;
207762306a36Sopenharmony_ci		ts = bss_from_pub(data->source_bss)->ts;
207862306a36Sopenharmony_ci		tmp.pub.bssid_index = data->bssid_index;
207962306a36Sopenharmony_ci		tmp.pub.max_bssid_indicator = data->max_bssid_indicator;
208062306a36Sopenharmony_ci	} else {
208162306a36Sopenharmony_ci		ts = jiffies;
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci		if (channel->band == NL80211_BAND_60GHZ) {
208462306a36Sopenharmony_ci			bss_type = data->capability &
208562306a36Sopenharmony_ci				   WLAN_CAPABILITY_DMG_TYPE_MASK;
208662306a36Sopenharmony_ci			if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
208762306a36Sopenharmony_ci			    bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
208862306a36Sopenharmony_ci				regulatory_hint_found_beacon(wiphy, channel,
208962306a36Sopenharmony_ci							     gfp);
209062306a36Sopenharmony_ci		} else {
209162306a36Sopenharmony_ci			if (data->capability & WLAN_CAPABILITY_ESS)
209262306a36Sopenharmony_ci				regulatory_hint_found_beacon(wiphy, channel,
209362306a36Sopenharmony_ci							     gfp);
209462306a36Sopenharmony_ci		}
209562306a36Sopenharmony_ci	}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	/*
209862306a36Sopenharmony_ci	 * If we do not know here whether the IEs are from a Beacon or Probe
209962306a36Sopenharmony_ci	 * Response frame, we need to pick one of the options and only use it
210062306a36Sopenharmony_ci	 * with the driver that does not provide the full Beacon/Probe Response
210162306a36Sopenharmony_ci	 * frame. Use Beacon frame pointer to avoid indicating that this should
210262306a36Sopenharmony_ci	 * override the IEs pointer should we have received an earlier
210362306a36Sopenharmony_ci	 * indication of Probe Response data.
210462306a36Sopenharmony_ci	 */
210562306a36Sopenharmony_ci	ies = kzalloc(sizeof(*ies) + data->ielen, gfp);
210662306a36Sopenharmony_ci	if (!ies)
210762306a36Sopenharmony_ci		return NULL;
210862306a36Sopenharmony_ci	ies->len = data->ielen;
210962306a36Sopenharmony_ci	ies->tsf = data->tsf;
211062306a36Sopenharmony_ci	ies->from_beacon = false;
211162306a36Sopenharmony_ci	memcpy(ies->data, data->ie, data->ielen);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	switch (data->ftype) {
211462306a36Sopenharmony_ci	case CFG80211_BSS_FTYPE_BEACON:
211562306a36Sopenharmony_ci		ies->from_beacon = true;
211662306a36Sopenharmony_ci		fallthrough;
211762306a36Sopenharmony_ci	case CFG80211_BSS_FTYPE_UNKNOWN:
211862306a36Sopenharmony_ci		rcu_assign_pointer(tmp.pub.beacon_ies, ies);
211962306a36Sopenharmony_ci		break;
212062306a36Sopenharmony_ci	case CFG80211_BSS_FTYPE_PRESP:
212162306a36Sopenharmony_ci		rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
212262306a36Sopenharmony_ci		break;
212362306a36Sopenharmony_ci	}
212462306a36Sopenharmony_ci	rcu_assign_pointer(tmp.pub.ies, ies);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	signal_valid = drv_data->chan == channel;
212762306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
212862306a36Sopenharmony_ci	res = __cfg80211_bss_update(rdev, &tmp, signal_valid, ts);
212962306a36Sopenharmony_ci	if (!res)
213062306a36Sopenharmony_ci		goto drop;
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	rdev_inform_bss(rdev, &res->pub, ies, drv_data->drv_data);
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	if (data->bss_source == BSS_SOURCE_MBSSID) {
213562306a36Sopenharmony_ci		/* this is a nontransmitting bss, we need to add it to
213662306a36Sopenharmony_ci		 * transmitting bss' list if it is not there
213762306a36Sopenharmony_ci		 */
213862306a36Sopenharmony_ci		if (cfg80211_add_nontrans_list(data->source_bss, &res->pub)) {
213962306a36Sopenharmony_ci			if (__cfg80211_unlink_bss(rdev, res)) {
214062306a36Sopenharmony_ci				rdev->bss_generation++;
214162306a36Sopenharmony_ci				res = NULL;
214262306a36Sopenharmony_ci			}
214362306a36Sopenharmony_ci		}
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci		if (!res)
214662306a36Sopenharmony_ci			goto drop;
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	trace_cfg80211_return_bss(&res->pub);
215162306a36Sopenharmony_ci	/* __cfg80211_bss_update gives us a referenced result */
215262306a36Sopenharmony_ci	return &res->pub;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_cidrop:
215562306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
215662306a36Sopenharmony_ci	return NULL;
215762306a36Sopenharmony_ci}
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_cistatic const struct element
216062306a36Sopenharmony_ci*cfg80211_get_profile_continuation(const u8 *ie, size_t ielen,
216162306a36Sopenharmony_ci				   const struct element *mbssid_elem,
216262306a36Sopenharmony_ci				   const struct element *sub_elem)
216362306a36Sopenharmony_ci{
216462306a36Sopenharmony_ci	const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen;
216562306a36Sopenharmony_ci	const struct element *next_mbssid;
216662306a36Sopenharmony_ci	const struct element *next_sub;
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	next_mbssid = cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
216962306a36Sopenharmony_ci					 mbssid_end,
217062306a36Sopenharmony_ci					 ielen - (mbssid_end - ie));
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	/*
217362306a36Sopenharmony_ci	 * If it is not the last subelement in current MBSSID IE or there isn't
217462306a36Sopenharmony_ci	 * a next MBSSID IE - profile is complete.
217562306a36Sopenharmony_ci	*/
217662306a36Sopenharmony_ci	if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
217762306a36Sopenharmony_ci	    !next_mbssid)
217862306a36Sopenharmony_ci		return NULL;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	/* For any length error, just return NULL */
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	if (next_mbssid->datalen < 4)
218362306a36Sopenharmony_ci		return NULL;
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci	next_sub = (void *)&next_mbssid->data[1];
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	if (next_mbssid->data + next_mbssid->datalen <
218862306a36Sopenharmony_ci	    next_sub->data + next_sub->datalen)
218962306a36Sopenharmony_ci		return NULL;
219062306a36Sopenharmony_ci
219162306a36Sopenharmony_ci	if (next_sub->id != 0 || next_sub->datalen < 2)
219262306a36Sopenharmony_ci		return NULL;
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	/*
219562306a36Sopenharmony_ci	 * Check if the first element in the next sub element is a start
219662306a36Sopenharmony_ci	 * of a new profile
219762306a36Sopenharmony_ci	 */
219862306a36Sopenharmony_ci	return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
219962306a36Sopenharmony_ci	       NULL : next_mbssid;
220062306a36Sopenharmony_ci}
220162306a36Sopenharmony_ci
220262306a36Sopenharmony_cisize_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
220362306a36Sopenharmony_ci			      const struct element *mbssid_elem,
220462306a36Sopenharmony_ci			      const struct element *sub_elem,
220562306a36Sopenharmony_ci			      u8 *merged_ie, size_t max_copy_len)
220662306a36Sopenharmony_ci{
220762306a36Sopenharmony_ci	size_t copied_len = sub_elem->datalen;
220862306a36Sopenharmony_ci	const struct element *next_mbssid;
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	if (sub_elem->datalen > max_copy_len)
221162306a36Sopenharmony_ci		return 0;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	memcpy(merged_ie, sub_elem->data, sub_elem->datalen);
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	while ((next_mbssid = cfg80211_get_profile_continuation(ie, ielen,
221662306a36Sopenharmony_ci								mbssid_elem,
221762306a36Sopenharmony_ci								sub_elem))) {
221862306a36Sopenharmony_ci		const struct element *next_sub = (void *)&next_mbssid->data[1];
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci		if (copied_len + next_sub->datalen > max_copy_len)
222162306a36Sopenharmony_ci			break;
222262306a36Sopenharmony_ci		memcpy(merged_ie + copied_len, next_sub->data,
222362306a36Sopenharmony_ci		       next_sub->datalen);
222462306a36Sopenharmony_ci		copied_len += next_sub->datalen;
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	return copied_len;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_merge_profile);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_cistatic void
223262306a36Sopenharmony_cicfg80211_parse_mbssid_data(struct wiphy *wiphy,
223362306a36Sopenharmony_ci			   struct cfg80211_inform_single_bss_data *tx_data,
223462306a36Sopenharmony_ci			   struct cfg80211_bss *source_bss,
223562306a36Sopenharmony_ci			   gfp_t gfp)
223662306a36Sopenharmony_ci{
223762306a36Sopenharmony_ci	struct cfg80211_inform_single_bss_data data = {
223862306a36Sopenharmony_ci		.drv_data = tx_data->drv_data,
223962306a36Sopenharmony_ci		.ftype = tx_data->ftype,
224062306a36Sopenharmony_ci		.tsf = tx_data->tsf,
224162306a36Sopenharmony_ci		.beacon_interval = tx_data->beacon_interval,
224262306a36Sopenharmony_ci		.source_bss = source_bss,
224362306a36Sopenharmony_ci		.bss_source = BSS_SOURCE_MBSSID,
224462306a36Sopenharmony_ci	};
224562306a36Sopenharmony_ci	const u8 *mbssid_index_ie;
224662306a36Sopenharmony_ci	const struct element *elem, *sub;
224762306a36Sopenharmony_ci	u8 *new_ie, *profile;
224862306a36Sopenharmony_ci	u64 seen_indices = 0;
224962306a36Sopenharmony_ci	struct cfg80211_bss *bss;
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	if (!source_bss)
225262306a36Sopenharmony_ci		return;
225362306a36Sopenharmony_ci	if (!cfg80211_find_elem(WLAN_EID_MULTIPLE_BSSID,
225462306a36Sopenharmony_ci				tx_data->ie, tx_data->ielen))
225562306a36Sopenharmony_ci		return;
225662306a36Sopenharmony_ci	if (!wiphy->support_mbssid)
225762306a36Sopenharmony_ci		return;
225862306a36Sopenharmony_ci	if (wiphy->support_only_he_mbssid &&
225962306a36Sopenharmony_ci	    !cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
226062306a36Sopenharmony_ci				    tx_data->ie, tx_data->ielen))
226162306a36Sopenharmony_ci		return;
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci	new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
226462306a36Sopenharmony_ci	if (!new_ie)
226562306a36Sopenharmony_ci		return;
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	profile = kmalloc(tx_data->ielen, gfp);
226862306a36Sopenharmony_ci	if (!profile)
226962306a36Sopenharmony_ci		goto out;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID,
227262306a36Sopenharmony_ci			    tx_data->ie, tx_data->ielen) {
227362306a36Sopenharmony_ci		if (elem->datalen < 4)
227462306a36Sopenharmony_ci			continue;
227562306a36Sopenharmony_ci		if (elem->data[0] < 1 || (int)elem->data[0] > 8)
227662306a36Sopenharmony_ci			continue;
227762306a36Sopenharmony_ci		for_each_element(sub, elem->data + 1, elem->datalen - 1) {
227862306a36Sopenharmony_ci			u8 profile_len;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci			if (sub->id != 0 || sub->datalen < 4) {
228162306a36Sopenharmony_ci				/* not a valid BSS profile */
228262306a36Sopenharmony_ci				continue;
228362306a36Sopenharmony_ci			}
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci			if (sub->data[0] != WLAN_EID_NON_TX_BSSID_CAP ||
228662306a36Sopenharmony_ci			    sub->data[1] != 2) {
228762306a36Sopenharmony_ci				/* The first element within the Nontransmitted
228862306a36Sopenharmony_ci				 * BSSID Profile is not the Nontransmitted
228962306a36Sopenharmony_ci				 * BSSID Capability element.
229062306a36Sopenharmony_ci				 */
229162306a36Sopenharmony_ci				continue;
229262306a36Sopenharmony_ci			}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci			memset(profile, 0, tx_data->ielen);
229562306a36Sopenharmony_ci			profile_len = cfg80211_merge_profile(tx_data->ie,
229662306a36Sopenharmony_ci							     tx_data->ielen,
229762306a36Sopenharmony_ci							     elem,
229862306a36Sopenharmony_ci							     sub,
229962306a36Sopenharmony_ci							     profile,
230062306a36Sopenharmony_ci							     tx_data->ielen);
230162306a36Sopenharmony_ci
230262306a36Sopenharmony_ci			/* found a Nontransmitted BSSID Profile */
230362306a36Sopenharmony_ci			mbssid_index_ie = cfg80211_find_ie
230462306a36Sopenharmony_ci				(WLAN_EID_MULTI_BSSID_IDX,
230562306a36Sopenharmony_ci				 profile, profile_len);
230662306a36Sopenharmony_ci			if (!mbssid_index_ie || mbssid_index_ie[1] < 1 ||
230762306a36Sopenharmony_ci			    mbssid_index_ie[2] == 0 ||
230862306a36Sopenharmony_ci			    mbssid_index_ie[2] > 46) {
230962306a36Sopenharmony_ci				/* No valid Multiple BSSID-Index element */
231062306a36Sopenharmony_ci				continue;
231162306a36Sopenharmony_ci			}
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci			if (seen_indices & BIT_ULL(mbssid_index_ie[2]))
231462306a36Sopenharmony_ci				/* We don't support legacy split of a profile */
231562306a36Sopenharmony_ci				net_dbg_ratelimited("Partial info for BSSID index %d\n",
231662306a36Sopenharmony_ci						    mbssid_index_ie[2]);
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci			seen_indices |= BIT_ULL(mbssid_index_ie[2]);
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci			data.bssid_index = mbssid_index_ie[2];
232162306a36Sopenharmony_ci			data.max_bssid_indicator = elem->data[0];
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci			cfg80211_gen_new_bssid(tx_data->bssid,
232462306a36Sopenharmony_ci					       data.max_bssid_indicator,
232562306a36Sopenharmony_ci					       data.bssid_index,
232662306a36Sopenharmony_ci					       data.bssid);
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci			memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
232962306a36Sopenharmony_ci			data.ie = new_ie;
233062306a36Sopenharmony_ci			data.ielen = cfg80211_gen_new_ie(tx_data->ie,
233162306a36Sopenharmony_ci							 tx_data->ielen,
233262306a36Sopenharmony_ci							 profile,
233362306a36Sopenharmony_ci							 profile_len,
233462306a36Sopenharmony_ci							 new_ie,
233562306a36Sopenharmony_ci							 IEEE80211_MAX_DATA_LEN);
233662306a36Sopenharmony_ci			if (!data.ielen)
233762306a36Sopenharmony_ci				continue;
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci			data.capability = get_unaligned_le16(profile + 2);
234062306a36Sopenharmony_ci			bss = cfg80211_inform_single_bss_data(wiphy, &data, gfp);
234162306a36Sopenharmony_ci			if (!bss)
234262306a36Sopenharmony_ci				break;
234362306a36Sopenharmony_ci			cfg80211_put_bss(wiphy, bss);
234462306a36Sopenharmony_ci		}
234562306a36Sopenharmony_ci	}
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ciout:
234862306a36Sopenharmony_ci	kfree(new_ie);
234962306a36Sopenharmony_ci	kfree(profile);
235062306a36Sopenharmony_ci}
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_cissize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
235362306a36Sopenharmony_ci				    size_t ieslen, u8 *data, size_t data_len,
235462306a36Sopenharmony_ci				    u8 frag_id)
235562306a36Sopenharmony_ci{
235662306a36Sopenharmony_ci	const struct element *next;
235762306a36Sopenharmony_ci	ssize_t copied;
235862306a36Sopenharmony_ci	u8 elem_datalen;
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	if (!elem)
236162306a36Sopenharmony_ci		return -EINVAL;
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	/* elem might be invalid after the memmove */
236462306a36Sopenharmony_ci	next = (void *)(elem->data + elem->datalen);
236562306a36Sopenharmony_ci	elem_datalen = elem->datalen;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci	if (elem->id == WLAN_EID_EXTENSION) {
236862306a36Sopenharmony_ci		copied = elem->datalen - 1;
236962306a36Sopenharmony_ci		if (copied > data_len)
237062306a36Sopenharmony_ci			return -ENOSPC;
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci		memmove(data, elem->data + 1, copied);
237362306a36Sopenharmony_ci	} else {
237462306a36Sopenharmony_ci		copied = elem->datalen;
237562306a36Sopenharmony_ci		if (copied > data_len)
237662306a36Sopenharmony_ci			return -ENOSPC;
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci		memmove(data, elem->data, copied);
237962306a36Sopenharmony_ci	}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_ci	/* Fragmented elements must have 255 bytes */
238262306a36Sopenharmony_ci	if (elem_datalen < 255)
238362306a36Sopenharmony_ci		return copied;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	for (elem = next;
238662306a36Sopenharmony_ci	     elem->data < ies + ieslen &&
238762306a36Sopenharmony_ci		elem->data + elem->datalen <= ies + ieslen;
238862306a36Sopenharmony_ci	     elem = next) {
238962306a36Sopenharmony_ci		/* elem might be invalid after the memmove */
239062306a36Sopenharmony_ci		next = (void *)(elem->data + elem->datalen);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci		if (elem->id != frag_id)
239362306a36Sopenharmony_ci			break;
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci		elem_datalen = elem->datalen;
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci		if (copied + elem_datalen > data_len)
239862306a36Sopenharmony_ci			return -ENOSPC;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci		memmove(data + copied, elem->data, elem_datalen);
240162306a36Sopenharmony_ci		copied += elem_datalen;
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci		/* Only the last fragment may be short */
240462306a36Sopenharmony_ci		if (elem_datalen != 255)
240562306a36Sopenharmony_ci			break;
240662306a36Sopenharmony_ci	}
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	return copied;
240962306a36Sopenharmony_ci}
241062306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_defragment_element);
241162306a36Sopenharmony_ci
241262306a36Sopenharmony_cistruct cfg80211_mle {
241362306a36Sopenharmony_ci	struct ieee80211_multi_link_elem *mle;
241462306a36Sopenharmony_ci	struct ieee80211_mle_per_sta_profile
241562306a36Sopenharmony_ci		*sta_prof[IEEE80211_MLD_MAX_NUM_LINKS];
241662306a36Sopenharmony_ci	ssize_t sta_prof_len[IEEE80211_MLD_MAX_NUM_LINKS];
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	u8 data[];
241962306a36Sopenharmony_ci};
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_cistatic struct cfg80211_mle *
242262306a36Sopenharmony_cicfg80211_defrag_mle(const struct element *mle, const u8 *ie, size_t ielen,
242362306a36Sopenharmony_ci		    gfp_t gfp)
242462306a36Sopenharmony_ci{
242562306a36Sopenharmony_ci	const struct element *elem;
242662306a36Sopenharmony_ci	struct cfg80211_mle *res;
242762306a36Sopenharmony_ci	size_t buf_len;
242862306a36Sopenharmony_ci	ssize_t mle_len;
242962306a36Sopenharmony_ci	u8 common_size, idx;
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci	if (!mle || !ieee80211_mle_size_ok(mle->data + 1, mle->datalen - 1))
243262306a36Sopenharmony_ci		return NULL;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	/* Required length for first defragmentation */
243562306a36Sopenharmony_ci	buf_len = mle->datalen - 1;
243662306a36Sopenharmony_ci	for_each_element(elem, mle->data + mle->datalen,
243762306a36Sopenharmony_ci			 ielen - sizeof(*mle) + mle->datalen) {
243862306a36Sopenharmony_ci		if (elem->id != WLAN_EID_FRAGMENT)
243962306a36Sopenharmony_ci			break;
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci		buf_len += elem->datalen;
244262306a36Sopenharmony_ci	}
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	res = kzalloc(struct_size(res, data, buf_len), gfp);
244562306a36Sopenharmony_ci	if (!res)
244662306a36Sopenharmony_ci		return NULL;
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_ci	mle_len = cfg80211_defragment_element(mle, ie, ielen,
244962306a36Sopenharmony_ci					      res->data, buf_len,
245062306a36Sopenharmony_ci					      WLAN_EID_FRAGMENT);
245162306a36Sopenharmony_ci	if (mle_len < 0)
245262306a36Sopenharmony_ci		goto error;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	res->mle = (void *)res->data;
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	/* Find the sub-element area in the buffer */
245762306a36Sopenharmony_ci	common_size = ieee80211_mle_common_size((u8 *)res->mle);
245862306a36Sopenharmony_ci	ie = res->data + common_size;
245962306a36Sopenharmony_ci	ielen = mle_len - common_size;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci	idx = 0;
246262306a36Sopenharmony_ci	for_each_element_id(elem, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE,
246362306a36Sopenharmony_ci			    ie, ielen) {
246462306a36Sopenharmony_ci		res->sta_prof[idx] = (void *)elem->data;
246562306a36Sopenharmony_ci		res->sta_prof_len[idx] = elem->datalen;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci		idx++;
246862306a36Sopenharmony_ci		if (idx >= IEEE80211_MLD_MAX_NUM_LINKS)
246962306a36Sopenharmony_ci			break;
247062306a36Sopenharmony_ci	}
247162306a36Sopenharmony_ci	if (!for_each_element_completed(elem, ie, ielen))
247262306a36Sopenharmony_ci		goto error;
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	/* Defragment sta_info in-place */
247562306a36Sopenharmony_ci	for (idx = 0; idx < IEEE80211_MLD_MAX_NUM_LINKS && res->sta_prof[idx];
247662306a36Sopenharmony_ci	     idx++) {
247762306a36Sopenharmony_ci		if (res->sta_prof_len[idx] < 255)
247862306a36Sopenharmony_ci			continue;
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci		elem = (void *)res->sta_prof[idx] - 2;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci		if (idx + 1 < ARRAY_SIZE(res->sta_prof) &&
248362306a36Sopenharmony_ci		    res->sta_prof[idx + 1])
248462306a36Sopenharmony_ci			buf_len = (u8 *)res->sta_prof[idx + 1] -
248562306a36Sopenharmony_ci				  (u8 *)res->sta_prof[idx];
248662306a36Sopenharmony_ci		else
248762306a36Sopenharmony_ci			buf_len = ielen + ie - (u8 *)elem;
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci		res->sta_prof_len[idx] =
249062306a36Sopenharmony_ci			cfg80211_defragment_element(elem,
249162306a36Sopenharmony_ci						    (u8 *)elem, buf_len,
249262306a36Sopenharmony_ci						    (u8 *)res->sta_prof[idx],
249362306a36Sopenharmony_ci						    buf_len,
249462306a36Sopenharmony_ci						    IEEE80211_MLE_SUBELEM_FRAGMENT);
249562306a36Sopenharmony_ci		if (res->sta_prof_len[idx] < 0)
249662306a36Sopenharmony_ci			goto error;
249762306a36Sopenharmony_ci	}
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	return res;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_cierror:
250262306a36Sopenharmony_ci	kfree(res);
250362306a36Sopenharmony_ci	return NULL;
250462306a36Sopenharmony_ci}
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_cistatic bool
250762306a36Sopenharmony_cicfg80211_tbtt_info_for_mld_ap(const u8 *ie, size_t ielen, u8 mld_id, u8 link_id,
250862306a36Sopenharmony_ci			      const struct ieee80211_neighbor_ap_info **ap_info,
250962306a36Sopenharmony_ci			      const u8 **tbtt_info)
251062306a36Sopenharmony_ci{
251162306a36Sopenharmony_ci	const struct ieee80211_neighbor_ap_info *info;
251262306a36Sopenharmony_ci	const struct element *rnr;
251362306a36Sopenharmony_ci	const u8 *pos, *end;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	for_each_element_id(rnr, WLAN_EID_REDUCED_NEIGHBOR_REPORT, ie, ielen) {
251662306a36Sopenharmony_ci		pos = rnr->data;
251762306a36Sopenharmony_ci		end = rnr->data + rnr->datalen;
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci		/* RNR IE may contain more than one NEIGHBOR_AP_INFO */
252062306a36Sopenharmony_ci		while (sizeof(*info) <= end - pos) {
252162306a36Sopenharmony_ci			const struct ieee80211_rnr_mld_params *mld_params;
252262306a36Sopenharmony_ci			u16 params;
252362306a36Sopenharmony_ci			u8 length, i, count, mld_params_offset;
252462306a36Sopenharmony_ci			u8 type, lid;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci			info = (void *)pos;
252762306a36Sopenharmony_ci			count = u8_get_bits(info->tbtt_info_hdr,
252862306a36Sopenharmony_ci					    IEEE80211_AP_INFO_TBTT_HDR_COUNT) + 1;
252962306a36Sopenharmony_ci			length = info->tbtt_info_len;
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci			pos += sizeof(*info);
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci			if (count * length > end - pos)
253462306a36Sopenharmony_ci				return false;
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_ci			type = u8_get_bits(info->tbtt_info_hdr,
253762306a36Sopenharmony_ci					   IEEE80211_AP_INFO_TBTT_HDR_TYPE);
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci			/* Only accept full TBTT information. NSTR mobile APs
254062306a36Sopenharmony_ci			 * use the shortened version, but we ignore them here.
254162306a36Sopenharmony_ci			 */
254262306a36Sopenharmony_ci			if (type == IEEE80211_TBTT_INFO_TYPE_TBTT &&
254362306a36Sopenharmony_ci			    length >=
254462306a36Sopenharmony_ci			    offsetofend(struct ieee80211_tbtt_info_ge_11,
254562306a36Sopenharmony_ci					mld_params)) {
254662306a36Sopenharmony_ci				mld_params_offset =
254762306a36Sopenharmony_ci					offsetof(struct ieee80211_tbtt_info_ge_11, mld_params);
254862306a36Sopenharmony_ci			} else {
254962306a36Sopenharmony_ci				pos += count * length;
255062306a36Sopenharmony_ci				continue;
255162306a36Sopenharmony_ci			}
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci			for (i = 0; i < count; i++) {
255462306a36Sopenharmony_ci				mld_params = (void *)pos + mld_params_offset;
255562306a36Sopenharmony_ci				params = le16_to_cpu(mld_params->params);
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci				lid = u16_get_bits(params,
255862306a36Sopenharmony_ci						   IEEE80211_RNR_MLD_PARAMS_LINK_ID);
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci				if (mld_id == mld_params->mld_id &&
256162306a36Sopenharmony_ci				    link_id == lid) {
256262306a36Sopenharmony_ci					*ap_info = info;
256362306a36Sopenharmony_ci					*tbtt_info = pos;
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci					return true;
256662306a36Sopenharmony_ci				}
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci				pos += length;
256962306a36Sopenharmony_ci			}
257062306a36Sopenharmony_ci		}
257162306a36Sopenharmony_ci	}
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	return false;
257462306a36Sopenharmony_ci}
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_cistatic void
257762306a36Sopenharmony_cicfg80211_parse_ml_elem_sta_data(struct wiphy *wiphy,
257862306a36Sopenharmony_ci				struct cfg80211_inform_single_bss_data *tx_data,
257962306a36Sopenharmony_ci				struct cfg80211_bss *source_bss,
258062306a36Sopenharmony_ci				const struct element *elem,
258162306a36Sopenharmony_ci				gfp_t gfp)
258262306a36Sopenharmony_ci{
258362306a36Sopenharmony_ci	struct cfg80211_inform_single_bss_data data = {
258462306a36Sopenharmony_ci		.drv_data = tx_data->drv_data,
258562306a36Sopenharmony_ci		.ftype = tx_data->ftype,
258662306a36Sopenharmony_ci		.source_bss = source_bss,
258762306a36Sopenharmony_ci		.bss_source = BSS_SOURCE_STA_PROFILE,
258862306a36Sopenharmony_ci	};
258962306a36Sopenharmony_ci	struct ieee80211_multi_link_elem *ml_elem;
259062306a36Sopenharmony_ci	struct cfg80211_mle *mle;
259162306a36Sopenharmony_ci	u16 control;
259262306a36Sopenharmony_ci	u8 *new_ie;
259362306a36Sopenharmony_ci	struct cfg80211_bss *bss;
259462306a36Sopenharmony_ci	int mld_id;
259562306a36Sopenharmony_ci	u16 seen_links = 0;
259662306a36Sopenharmony_ci	const u8 *pos;
259762306a36Sopenharmony_ci	u8 i;
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	if (!ieee80211_mle_size_ok(elem->data + 1, elem->datalen - 1))
260062306a36Sopenharmony_ci		return;
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	ml_elem = (void *)elem->data + 1;
260362306a36Sopenharmony_ci	control = le16_to_cpu(ml_elem->control);
260462306a36Sopenharmony_ci	if (u16_get_bits(control, IEEE80211_ML_CONTROL_TYPE) !=
260562306a36Sopenharmony_ci	    IEEE80211_ML_CONTROL_TYPE_BASIC)
260662306a36Sopenharmony_ci		return;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	/* Must be present when transmitted by an AP (in a probe response) */
260962306a36Sopenharmony_ci	if (!(control & IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT) ||
261062306a36Sopenharmony_ci	    !(control & IEEE80211_MLC_BASIC_PRES_LINK_ID) ||
261162306a36Sopenharmony_ci	    !(control & IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP))
261262306a36Sopenharmony_ci		return;
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci	/* length + MLD MAC address + link ID info + BSS Params Change Count */
261562306a36Sopenharmony_ci	pos = ml_elem->variable + 1 + 6 + 1 + 1;
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	if (u16_get_bits(control, IEEE80211_MLC_BASIC_PRES_MED_SYNC_DELAY))
261862306a36Sopenharmony_ci		pos += 2;
261962306a36Sopenharmony_ci	if (u16_get_bits(control, IEEE80211_MLC_BASIC_PRES_EML_CAPA))
262062306a36Sopenharmony_ci		pos += 2;
262162306a36Sopenharmony_ci
262262306a36Sopenharmony_ci	/* MLD capabilities and operations */
262362306a36Sopenharmony_ci	pos += 2;
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci	/*
262662306a36Sopenharmony_ci	 * The MLD ID of the reporting AP is always zero. It is set if the AP
262762306a36Sopenharmony_ci	 * is part of an MBSSID set and will be non-zero for ML Elements
262862306a36Sopenharmony_ci	 * relating to a nontransmitted BSS (matching the Multi-BSSID Index,
262962306a36Sopenharmony_ci	 * Draft P802.11be_D3.2, 35.3.4.2)
263062306a36Sopenharmony_ci	 */
263162306a36Sopenharmony_ci	if (u16_get_bits(control, IEEE80211_MLC_BASIC_PRES_MLD_ID)) {
263262306a36Sopenharmony_ci		mld_id = *pos;
263362306a36Sopenharmony_ci		pos += 1;
263462306a36Sopenharmony_ci	} else {
263562306a36Sopenharmony_ci		mld_id = 0;
263662306a36Sopenharmony_ci	}
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	/* Extended MLD capabilities and operations */
263962306a36Sopenharmony_ci	pos += 2;
264062306a36Sopenharmony_ci
264162306a36Sopenharmony_ci	/* Fully defrag the ML element for sta information/profile iteration */
264262306a36Sopenharmony_ci	mle = cfg80211_defrag_mle(elem, tx_data->ie, tx_data->ielen, gfp);
264362306a36Sopenharmony_ci	if (!mle)
264462306a36Sopenharmony_ci		return;
264562306a36Sopenharmony_ci
264662306a36Sopenharmony_ci	new_ie = kmalloc(IEEE80211_MAX_DATA_LEN, gfp);
264762306a36Sopenharmony_ci	if (!new_ie)
264862306a36Sopenharmony_ci		goto out;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mle->sta_prof) && mle->sta_prof[i]; i++) {
265162306a36Sopenharmony_ci		const struct ieee80211_neighbor_ap_info *ap_info;
265262306a36Sopenharmony_ci		enum nl80211_band band;
265362306a36Sopenharmony_ci		u32 freq;
265462306a36Sopenharmony_ci		const u8 *profile;
265562306a36Sopenharmony_ci		const u8 *tbtt_info;
265662306a36Sopenharmony_ci		ssize_t profile_len;
265762306a36Sopenharmony_ci		u8 link_id;
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_ci		if (!ieee80211_mle_basic_sta_prof_size_ok((u8 *)mle->sta_prof[i],
266062306a36Sopenharmony_ci							  mle->sta_prof_len[i]))
266162306a36Sopenharmony_ci			continue;
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci		control = le16_to_cpu(mle->sta_prof[i]->control);
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci		if (!(control & IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE))
266662306a36Sopenharmony_ci			continue;
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci		link_id = u16_get_bits(control,
266962306a36Sopenharmony_ci				       IEEE80211_MLE_STA_CONTROL_LINK_ID);
267062306a36Sopenharmony_ci		if (seen_links & BIT(link_id))
267162306a36Sopenharmony_ci			break;
267262306a36Sopenharmony_ci		seen_links |= BIT(link_id);
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci		if (!(control & IEEE80211_MLE_STA_CONTROL_BEACON_INT_PRESENT) ||
267562306a36Sopenharmony_ci		    !(control & IEEE80211_MLE_STA_CONTROL_TSF_OFFS_PRESENT) ||
267662306a36Sopenharmony_ci		    !(control & IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT))
267762306a36Sopenharmony_ci			continue;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci		memcpy(data.bssid, mle->sta_prof[i]->variable, ETH_ALEN);
268062306a36Sopenharmony_ci		data.beacon_interval =
268162306a36Sopenharmony_ci			get_unaligned_le16(mle->sta_prof[i]->variable + 6);
268262306a36Sopenharmony_ci		data.tsf = tx_data->tsf +
268362306a36Sopenharmony_ci			   get_unaligned_le64(mle->sta_prof[i]->variable + 8);
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci		/* sta_info_len counts itself */
268662306a36Sopenharmony_ci		profile = mle->sta_prof[i]->variable +
268762306a36Sopenharmony_ci			  mle->sta_prof[i]->sta_info_len - 1;
268862306a36Sopenharmony_ci		profile_len = (u8 *)mle->sta_prof[i] + mle->sta_prof_len[i] -
268962306a36Sopenharmony_ci			      profile;
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci		if (profile_len < 2)
269262306a36Sopenharmony_ci			continue;
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci		data.capability = get_unaligned_le16(profile);
269562306a36Sopenharmony_ci		profile += 2;
269662306a36Sopenharmony_ci		profile_len -= 2;
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci		/* Find in RNR to look up channel information */
269962306a36Sopenharmony_ci		if (!cfg80211_tbtt_info_for_mld_ap(tx_data->ie, tx_data->ielen,
270062306a36Sopenharmony_ci						   mld_id, link_id,
270162306a36Sopenharmony_ci						   &ap_info, &tbtt_info))
270262306a36Sopenharmony_ci			continue;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci		/* We could sanity check the BSSID is included */
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci		if (!ieee80211_operating_class_to_band(ap_info->op_class,
270762306a36Sopenharmony_ci						       &band))
270862306a36Sopenharmony_ci			continue;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci		freq = ieee80211_channel_to_freq_khz(ap_info->channel, band);
271162306a36Sopenharmony_ci		data.channel = ieee80211_get_channel_khz(wiphy, freq);
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci		/* Generate new elements */
271462306a36Sopenharmony_ci		memset(new_ie, 0, IEEE80211_MAX_DATA_LEN);
271562306a36Sopenharmony_ci		data.ie = new_ie;
271662306a36Sopenharmony_ci		data.ielen = cfg80211_gen_new_ie(tx_data->ie, tx_data->ielen,
271762306a36Sopenharmony_ci						 profile, profile_len,
271862306a36Sopenharmony_ci						 new_ie,
271962306a36Sopenharmony_ci						 IEEE80211_MAX_DATA_LEN);
272062306a36Sopenharmony_ci		if (!data.ielen)
272162306a36Sopenharmony_ci			continue;
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci		bss = cfg80211_inform_single_bss_data(wiphy, &data, gfp);
272462306a36Sopenharmony_ci		if (!bss)
272562306a36Sopenharmony_ci			break;
272662306a36Sopenharmony_ci		cfg80211_put_bss(wiphy, bss);
272762306a36Sopenharmony_ci	}
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ciout:
273062306a36Sopenharmony_ci	kfree(new_ie);
273162306a36Sopenharmony_ci	kfree(mle);
273262306a36Sopenharmony_ci}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_cistatic void cfg80211_parse_ml_sta_data(struct wiphy *wiphy,
273562306a36Sopenharmony_ci				       struct cfg80211_inform_single_bss_data *tx_data,
273662306a36Sopenharmony_ci				       struct cfg80211_bss *source_bss,
273762306a36Sopenharmony_ci				       gfp_t gfp)
273862306a36Sopenharmony_ci{
273962306a36Sopenharmony_ci	const struct element *elem;
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	if (!source_bss)
274262306a36Sopenharmony_ci		return;
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	if (tx_data->ftype != CFG80211_BSS_FTYPE_PRESP)
274562306a36Sopenharmony_ci		return;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci	for_each_element_extid(elem, WLAN_EID_EXT_EHT_MULTI_LINK,
274862306a36Sopenharmony_ci			       tx_data->ie, tx_data->ielen)
274962306a36Sopenharmony_ci		cfg80211_parse_ml_elem_sta_data(wiphy, tx_data, source_bss,
275062306a36Sopenharmony_ci						elem, gfp);
275162306a36Sopenharmony_ci}
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_cistruct cfg80211_bss *
275462306a36Sopenharmony_cicfg80211_inform_bss_data(struct wiphy *wiphy,
275562306a36Sopenharmony_ci			 struct cfg80211_inform_bss *data,
275662306a36Sopenharmony_ci			 enum cfg80211_bss_frame_type ftype,
275762306a36Sopenharmony_ci			 const u8 *bssid, u64 tsf, u16 capability,
275862306a36Sopenharmony_ci			 u16 beacon_interval, const u8 *ie, size_t ielen,
275962306a36Sopenharmony_ci			 gfp_t gfp)
276062306a36Sopenharmony_ci{
276162306a36Sopenharmony_ci	struct cfg80211_inform_single_bss_data inform_data = {
276262306a36Sopenharmony_ci		.drv_data = data,
276362306a36Sopenharmony_ci		.ftype = ftype,
276462306a36Sopenharmony_ci		.tsf = tsf,
276562306a36Sopenharmony_ci		.capability = capability,
276662306a36Sopenharmony_ci		.beacon_interval = beacon_interval,
276762306a36Sopenharmony_ci		.ie = ie,
276862306a36Sopenharmony_ci		.ielen = ielen,
276962306a36Sopenharmony_ci	};
277062306a36Sopenharmony_ci	struct cfg80211_bss *res;
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	memcpy(inform_data.bssid, bssid, ETH_ALEN);
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	res = cfg80211_inform_single_bss_data(wiphy, &inform_data, gfp);
277562306a36Sopenharmony_ci	if (!res)
277662306a36Sopenharmony_ci		return NULL;
277762306a36Sopenharmony_ci
277862306a36Sopenharmony_ci	cfg80211_parse_mbssid_data(wiphy, &inform_data, res, gfp);
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	cfg80211_parse_ml_sta_data(wiphy, &inform_data, res, gfp);
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	return res;
278362306a36Sopenharmony_ci}
278462306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_inform_bss_data);
278562306a36Sopenharmony_ci
278662306a36Sopenharmony_ci/* cfg80211_inform_bss_width_frame helper */
278762306a36Sopenharmony_cistatic struct cfg80211_bss *
278862306a36Sopenharmony_cicfg80211_inform_single_bss_frame_data(struct wiphy *wiphy,
278962306a36Sopenharmony_ci				      struct cfg80211_inform_bss *data,
279062306a36Sopenharmony_ci				      struct ieee80211_mgmt *mgmt, size_t len,
279162306a36Sopenharmony_ci				      gfp_t gfp)
279262306a36Sopenharmony_ci{
279362306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
279462306a36Sopenharmony_ci	struct cfg80211_internal_bss tmp = {}, *res;
279562306a36Sopenharmony_ci	struct cfg80211_bss_ies *ies;
279662306a36Sopenharmony_ci	struct ieee80211_channel *channel;
279762306a36Sopenharmony_ci	bool signal_valid;
279862306a36Sopenharmony_ci	struct ieee80211_ext *ext = NULL;
279962306a36Sopenharmony_ci	u8 *bssid, *variable;
280062306a36Sopenharmony_ci	u16 capability, beacon_int;
280162306a36Sopenharmony_ci	size_t ielen, min_hdr_len = offsetof(struct ieee80211_mgmt,
280262306a36Sopenharmony_ci					     u.probe_resp.variable);
280362306a36Sopenharmony_ci	int bss_type;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
280662306a36Sopenharmony_ci			offsetof(struct ieee80211_mgmt, u.beacon.variable));
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	if (WARN_ON(!mgmt))
281162306a36Sopenharmony_ci		return NULL;
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci	if (WARN_ON(!wiphy))
281462306a36Sopenharmony_ci		return NULL;
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
281762306a36Sopenharmony_ci		    (data->signal < 0 || data->signal > 100)))
281862306a36Sopenharmony_ci		return NULL;
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
282162306a36Sopenharmony_ci		ext = (void *) mgmt;
282262306a36Sopenharmony_ci		min_hdr_len = offsetof(struct ieee80211_ext, u.s1g_beacon);
282362306a36Sopenharmony_ci		if (ieee80211_is_s1g_short_beacon(mgmt->frame_control))
282462306a36Sopenharmony_ci			min_hdr_len = offsetof(struct ieee80211_ext,
282562306a36Sopenharmony_ci					       u.s1g_short_beacon.variable);
282662306a36Sopenharmony_ci	}
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	if (WARN_ON(len < min_hdr_len))
282962306a36Sopenharmony_ci		return NULL;
283062306a36Sopenharmony_ci
283162306a36Sopenharmony_ci	ielen = len - min_hdr_len;
283262306a36Sopenharmony_ci	variable = mgmt->u.probe_resp.variable;
283362306a36Sopenharmony_ci	if (ext) {
283462306a36Sopenharmony_ci		if (ieee80211_is_s1g_short_beacon(mgmt->frame_control))
283562306a36Sopenharmony_ci			variable = ext->u.s1g_short_beacon.variable;
283662306a36Sopenharmony_ci		else
283762306a36Sopenharmony_ci			variable = ext->u.s1g_beacon.variable;
283862306a36Sopenharmony_ci	}
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	channel = cfg80211_get_bss_channel(wiphy, variable,
284162306a36Sopenharmony_ci					   ielen, data->chan, data->scan_width);
284262306a36Sopenharmony_ci	if (!channel)
284362306a36Sopenharmony_ci		return NULL;
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	if (ext) {
284662306a36Sopenharmony_ci		const struct ieee80211_s1g_bcn_compat_ie *compat;
284762306a36Sopenharmony_ci		const struct element *elem;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci		elem = cfg80211_find_elem(WLAN_EID_S1G_BCN_COMPAT,
285062306a36Sopenharmony_ci					  variable, ielen);
285162306a36Sopenharmony_ci		if (!elem)
285262306a36Sopenharmony_ci			return NULL;
285362306a36Sopenharmony_ci		if (elem->datalen < sizeof(*compat))
285462306a36Sopenharmony_ci			return NULL;
285562306a36Sopenharmony_ci		compat = (void *)elem->data;
285662306a36Sopenharmony_ci		bssid = ext->u.s1g_beacon.sa;
285762306a36Sopenharmony_ci		capability = le16_to_cpu(compat->compat_info);
285862306a36Sopenharmony_ci		beacon_int = le16_to_cpu(compat->beacon_int);
285962306a36Sopenharmony_ci	} else {
286062306a36Sopenharmony_ci		bssid = mgmt->bssid;
286162306a36Sopenharmony_ci		beacon_int = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
286262306a36Sopenharmony_ci		capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
286362306a36Sopenharmony_ci	}
286462306a36Sopenharmony_ci
286562306a36Sopenharmony_ci	if (channel->band == NL80211_BAND_60GHZ) {
286662306a36Sopenharmony_ci		bss_type = capability & WLAN_CAPABILITY_DMG_TYPE_MASK;
286762306a36Sopenharmony_ci		if (bss_type == WLAN_CAPABILITY_DMG_TYPE_AP ||
286862306a36Sopenharmony_ci		    bss_type == WLAN_CAPABILITY_DMG_TYPE_PBSS)
286962306a36Sopenharmony_ci			regulatory_hint_found_beacon(wiphy, channel, gfp);
287062306a36Sopenharmony_ci	} else {
287162306a36Sopenharmony_ci		if (capability & WLAN_CAPABILITY_ESS)
287262306a36Sopenharmony_ci			regulatory_hint_found_beacon(wiphy, channel, gfp);
287362306a36Sopenharmony_ci	}
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	ies = kzalloc(sizeof(*ies) + ielen, gfp);
287662306a36Sopenharmony_ci	if (!ies)
287762306a36Sopenharmony_ci		return NULL;
287862306a36Sopenharmony_ci	ies->len = ielen;
287962306a36Sopenharmony_ci	ies->tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
288062306a36Sopenharmony_ci	ies->from_beacon = ieee80211_is_beacon(mgmt->frame_control) ||
288162306a36Sopenharmony_ci			   ieee80211_is_s1g_beacon(mgmt->frame_control);
288262306a36Sopenharmony_ci	memcpy(ies->data, variable, ielen);
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci	if (ieee80211_is_probe_resp(mgmt->frame_control))
288562306a36Sopenharmony_ci		rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
288662306a36Sopenharmony_ci	else
288762306a36Sopenharmony_ci		rcu_assign_pointer(tmp.pub.beacon_ies, ies);
288862306a36Sopenharmony_ci	rcu_assign_pointer(tmp.pub.ies, ies);
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_ci	memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
289162306a36Sopenharmony_ci	tmp.pub.beacon_interval = beacon_int;
289262306a36Sopenharmony_ci	tmp.pub.capability = capability;
289362306a36Sopenharmony_ci	tmp.pub.channel = channel;
289462306a36Sopenharmony_ci	tmp.pub.scan_width = data->scan_width;
289562306a36Sopenharmony_ci	tmp.pub.signal = data->signal;
289662306a36Sopenharmony_ci	tmp.ts_boottime = data->boottime_ns;
289762306a36Sopenharmony_ci	tmp.parent_tsf = data->parent_tsf;
289862306a36Sopenharmony_ci	tmp.pub.chains = data->chains;
289962306a36Sopenharmony_ci	memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
290062306a36Sopenharmony_ci	ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	signal_valid = data->chan == channel;
290362306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
290462306a36Sopenharmony_ci	res = __cfg80211_bss_update(rdev, &tmp, signal_valid, jiffies);
290562306a36Sopenharmony_ci	if (!res)
290662306a36Sopenharmony_ci		goto drop;
290762306a36Sopenharmony_ci
290862306a36Sopenharmony_ci	rdev_inform_bss(rdev, &res->pub, ies, data->drv_data);
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
291162306a36Sopenharmony_ci
291262306a36Sopenharmony_ci	trace_cfg80211_return_bss(&res->pub);
291362306a36Sopenharmony_ci	/* __cfg80211_bss_update gives us a referenced result */
291462306a36Sopenharmony_ci	return &res->pub;
291562306a36Sopenharmony_ci
291662306a36Sopenharmony_cidrop:
291762306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
291862306a36Sopenharmony_ci	return NULL;
291962306a36Sopenharmony_ci}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_cistruct cfg80211_bss *
292262306a36Sopenharmony_cicfg80211_inform_bss_frame_data(struct wiphy *wiphy,
292362306a36Sopenharmony_ci			       struct cfg80211_inform_bss *data,
292462306a36Sopenharmony_ci			       struct ieee80211_mgmt *mgmt, size_t len,
292562306a36Sopenharmony_ci			       gfp_t gfp)
292662306a36Sopenharmony_ci{
292762306a36Sopenharmony_ci	struct cfg80211_inform_single_bss_data inform_data = {
292862306a36Sopenharmony_ci		.drv_data = data,
292962306a36Sopenharmony_ci		.ie = mgmt->u.probe_resp.variable,
293062306a36Sopenharmony_ci		.ielen = len - offsetof(struct ieee80211_mgmt,
293162306a36Sopenharmony_ci					u.probe_resp.variable),
293262306a36Sopenharmony_ci	};
293362306a36Sopenharmony_ci	struct cfg80211_bss *res;
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	res = cfg80211_inform_single_bss_frame_data(wiphy, data, mgmt,
293662306a36Sopenharmony_ci						    len, gfp);
293762306a36Sopenharmony_ci	if (!res)
293862306a36Sopenharmony_ci		return NULL;
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	/* don't do any further MBSSID/ML handling for S1G */
294162306a36Sopenharmony_ci	if (ieee80211_is_s1g_beacon(mgmt->frame_control))
294262306a36Sopenharmony_ci		return res;
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_ci	inform_data.ftype = ieee80211_is_beacon(mgmt->frame_control) ?
294562306a36Sopenharmony_ci		CFG80211_BSS_FTYPE_BEACON : CFG80211_BSS_FTYPE_PRESP;
294662306a36Sopenharmony_ci	memcpy(inform_data.bssid, mgmt->bssid, ETH_ALEN);
294762306a36Sopenharmony_ci	inform_data.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
294862306a36Sopenharmony_ci	inform_data.beacon_interval =
294962306a36Sopenharmony_ci		le16_to_cpu(mgmt->u.probe_resp.beacon_int);
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	/* process each non-transmitting bss */
295262306a36Sopenharmony_ci	cfg80211_parse_mbssid_data(wiphy, &inform_data, res, gfp);
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	cfg80211_parse_ml_sta_data(wiphy, &inform_data, res, gfp);
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	return res;
295762306a36Sopenharmony_ci}
295862306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_civoid cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
296162306a36Sopenharmony_ci{
296262306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	if (!pub)
296562306a36Sopenharmony_ci		return;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
296862306a36Sopenharmony_ci	bss_ref_get(rdev, bss_from_pub(pub));
296962306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
297062306a36Sopenharmony_ci}
297162306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_ref_bss);
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_civoid cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
297462306a36Sopenharmony_ci{
297562306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	if (!pub)
297862306a36Sopenharmony_ci		return;
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
298162306a36Sopenharmony_ci	bss_ref_put(rdev, bss_from_pub(pub));
298262306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
298362306a36Sopenharmony_ci}
298462306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_put_bss);
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_civoid cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
298762306a36Sopenharmony_ci{
298862306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
298962306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss, *tmp1;
299062306a36Sopenharmony_ci	struct cfg80211_bss *nontrans_bss, *tmp;
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci	if (WARN_ON(!pub))
299362306a36Sopenharmony_ci		return;
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	bss = bss_from_pub(pub);
299662306a36Sopenharmony_ci
299762306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
299862306a36Sopenharmony_ci	if (list_empty(&bss->list))
299962306a36Sopenharmony_ci		goto out;
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci	list_for_each_entry_safe(nontrans_bss, tmp,
300262306a36Sopenharmony_ci				 &pub->nontrans_list,
300362306a36Sopenharmony_ci				 nontrans_list) {
300462306a36Sopenharmony_ci		tmp1 = bss_from_pub(nontrans_bss);
300562306a36Sopenharmony_ci		if (__cfg80211_unlink_bss(rdev, tmp1))
300662306a36Sopenharmony_ci			rdev->bss_generation++;
300762306a36Sopenharmony_ci	}
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	if (__cfg80211_unlink_bss(rdev, bss))
301062306a36Sopenharmony_ci		rdev->bss_generation++;
301162306a36Sopenharmony_ciout:
301262306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
301362306a36Sopenharmony_ci}
301462306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_unlink_bss);
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_civoid cfg80211_bss_iter(struct wiphy *wiphy,
301762306a36Sopenharmony_ci		       struct cfg80211_chan_def *chandef,
301862306a36Sopenharmony_ci		       void (*iter)(struct wiphy *wiphy,
301962306a36Sopenharmony_ci				    struct cfg80211_bss *bss,
302062306a36Sopenharmony_ci				    void *data),
302162306a36Sopenharmony_ci		       void *iter_data)
302262306a36Sopenharmony_ci{
302362306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
302462306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss;
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	list_for_each_entry(bss, &rdev->bss_list, list) {
302962306a36Sopenharmony_ci		if (!chandef || cfg80211_is_sub_chan(chandef, bss->pub.channel,
303062306a36Sopenharmony_ci						     false))
303162306a36Sopenharmony_ci			iter(wiphy, &bss->pub, iter_data);
303262306a36Sopenharmony_ci	}
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
303562306a36Sopenharmony_ci}
303662306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_bss_iter);
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_civoid cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
303962306a36Sopenharmony_ci				     unsigned int link_id,
304062306a36Sopenharmony_ci				     struct ieee80211_channel *chan)
304162306a36Sopenharmony_ci{
304262306a36Sopenharmony_ci	struct wiphy *wiphy = wdev->wiphy;
304362306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
304462306a36Sopenharmony_ci	struct cfg80211_internal_bss *cbss = wdev->links[link_id].client.current_bss;
304562306a36Sopenharmony_ci	struct cfg80211_internal_bss *new = NULL;
304662306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss;
304762306a36Sopenharmony_ci	struct cfg80211_bss *nontrans_bss;
304862306a36Sopenharmony_ci	struct cfg80211_bss *tmp;
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
305162306a36Sopenharmony_ci
305262306a36Sopenharmony_ci	/*
305362306a36Sopenharmony_ci	 * Some APs use CSA also for bandwidth changes, i.e., without actually
305462306a36Sopenharmony_ci	 * changing the control channel, so no need to update in such a case.
305562306a36Sopenharmony_ci	 */
305662306a36Sopenharmony_ci	if (cbss->pub.channel == chan)
305762306a36Sopenharmony_ci		goto done;
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ci	/* use transmitting bss */
306062306a36Sopenharmony_ci	if (cbss->pub.transmitted_bss)
306162306a36Sopenharmony_ci		cbss = bss_from_pub(cbss->pub.transmitted_bss);
306262306a36Sopenharmony_ci
306362306a36Sopenharmony_ci	cbss->pub.channel = chan;
306462306a36Sopenharmony_ci
306562306a36Sopenharmony_ci	list_for_each_entry(bss, &rdev->bss_list, list) {
306662306a36Sopenharmony_ci		if (!cfg80211_bss_type_match(bss->pub.capability,
306762306a36Sopenharmony_ci					     bss->pub.channel->band,
306862306a36Sopenharmony_ci					     wdev->conn_bss_type))
306962306a36Sopenharmony_ci			continue;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci		if (bss == cbss)
307262306a36Sopenharmony_ci			continue;
307362306a36Sopenharmony_ci
307462306a36Sopenharmony_ci		if (!cmp_bss(&bss->pub, &cbss->pub, BSS_CMP_REGULAR)) {
307562306a36Sopenharmony_ci			new = bss;
307662306a36Sopenharmony_ci			break;
307762306a36Sopenharmony_ci		}
307862306a36Sopenharmony_ci	}
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	if (new) {
308162306a36Sopenharmony_ci		/* to save time, update IEs for transmitting bss only */
308262306a36Sopenharmony_ci		if (cfg80211_update_known_bss(rdev, cbss, new, false)) {
308362306a36Sopenharmony_ci			new->pub.proberesp_ies = NULL;
308462306a36Sopenharmony_ci			new->pub.beacon_ies = NULL;
308562306a36Sopenharmony_ci		}
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci		list_for_each_entry_safe(nontrans_bss, tmp,
308862306a36Sopenharmony_ci					 &new->pub.nontrans_list,
308962306a36Sopenharmony_ci					 nontrans_list) {
309062306a36Sopenharmony_ci			bss = bss_from_pub(nontrans_bss);
309162306a36Sopenharmony_ci			if (__cfg80211_unlink_bss(rdev, bss))
309262306a36Sopenharmony_ci				rdev->bss_generation++;
309362306a36Sopenharmony_ci		}
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci		WARN_ON(atomic_read(&new->hold));
309662306a36Sopenharmony_ci		if (!WARN_ON(!__cfg80211_unlink_bss(rdev, new)))
309762306a36Sopenharmony_ci			rdev->bss_generation++;
309862306a36Sopenharmony_ci	}
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci	rb_erase(&cbss->rbn, &rdev->bss_tree);
310162306a36Sopenharmony_ci	rb_insert_bss(rdev, cbss);
310262306a36Sopenharmony_ci	rdev->bss_generation++;
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci	list_for_each_entry_safe(nontrans_bss, tmp,
310562306a36Sopenharmony_ci				 &cbss->pub.nontrans_list,
310662306a36Sopenharmony_ci				 nontrans_list) {
310762306a36Sopenharmony_ci		bss = bss_from_pub(nontrans_bss);
310862306a36Sopenharmony_ci		bss->pub.channel = chan;
310962306a36Sopenharmony_ci		rb_erase(&bss->rbn, &rdev->bss_tree);
311062306a36Sopenharmony_ci		rb_insert_bss(rdev, bss);
311162306a36Sopenharmony_ci		rdev->bss_generation++;
311262306a36Sopenharmony_ci	}
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_cidone:
311562306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
311662306a36Sopenharmony_ci}
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT
311962306a36Sopenharmony_cistatic struct cfg80211_registered_device *
312062306a36Sopenharmony_cicfg80211_get_dev_from_ifindex(struct net *net, int ifindex)
312162306a36Sopenharmony_ci{
312262306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev;
312362306a36Sopenharmony_ci	struct net_device *dev;
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci	ASSERT_RTNL();
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci	dev = dev_get_by_index(net, ifindex);
312862306a36Sopenharmony_ci	if (!dev)
312962306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
313062306a36Sopenharmony_ci	if (dev->ieee80211_ptr)
313162306a36Sopenharmony_ci		rdev = wiphy_to_rdev(dev->ieee80211_ptr->wiphy);
313262306a36Sopenharmony_ci	else
313362306a36Sopenharmony_ci		rdev = ERR_PTR(-ENODEV);
313462306a36Sopenharmony_ci	dev_put(dev);
313562306a36Sopenharmony_ci	return rdev;
313662306a36Sopenharmony_ci}
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ciint cfg80211_wext_siwscan(struct net_device *dev,
313962306a36Sopenharmony_ci			  struct iw_request_info *info,
314062306a36Sopenharmony_ci			  union iwreq_data *wrqu, char *extra)
314162306a36Sopenharmony_ci{
314262306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev;
314362306a36Sopenharmony_ci	struct wiphy *wiphy;
314462306a36Sopenharmony_ci	struct iw_scan_req *wreq = NULL;
314562306a36Sopenharmony_ci	struct cfg80211_scan_request *creq;
314662306a36Sopenharmony_ci	int i, err, n_channels = 0;
314762306a36Sopenharmony_ci	enum nl80211_band band;
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	if (!netif_running(dev))
315062306a36Sopenharmony_ci		return -ENETDOWN;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	if (wrqu->data.length == sizeof(struct iw_scan_req))
315362306a36Sopenharmony_ci		wreq = (struct iw_scan_req *)extra;
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci	rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	if (IS_ERR(rdev))
315862306a36Sopenharmony_ci		return PTR_ERR(rdev);
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci	if (rdev->scan_req || rdev->scan_msg)
316162306a36Sopenharmony_ci		return -EBUSY;
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	wiphy = &rdev->wiphy;
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci	/* Determine number of channels, needed to allocate creq */
316662306a36Sopenharmony_ci	if (wreq && wreq->num_channels)
316762306a36Sopenharmony_ci		n_channels = wreq->num_channels;
316862306a36Sopenharmony_ci	else
316962306a36Sopenharmony_ci		n_channels = ieee80211_get_num_supported_channels(wiphy);
317062306a36Sopenharmony_ci
317162306a36Sopenharmony_ci	creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
317262306a36Sopenharmony_ci		       n_channels * sizeof(void *),
317362306a36Sopenharmony_ci		       GFP_ATOMIC);
317462306a36Sopenharmony_ci	if (!creq)
317562306a36Sopenharmony_ci		return -ENOMEM;
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	creq->wiphy = wiphy;
317862306a36Sopenharmony_ci	creq->wdev = dev->ieee80211_ptr;
317962306a36Sopenharmony_ci	/* SSIDs come after channels */
318062306a36Sopenharmony_ci	creq->ssids = (void *)&creq->channels[n_channels];
318162306a36Sopenharmony_ci	creq->n_channels = n_channels;
318262306a36Sopenharmony_ci	creq->n_ssids = 1;
318362306a36Sopenharmony_ci	creq->scan_start = jiffies;
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	/* translate "Scan on frequencies" request */
318662306a36Sopenharmony_ci	i = 0;
318762306a36Sopenharmony_ci	for (band = 0; band < NUM_NL80211_BANDS; band++) {
318862306a36Sopenharmony_ci		int j;
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci		if (!wiphy->bands[band])
319162306a36Sopenharmony_ci			continue;
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci		for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
319462306a36Sopenharmony_ci			/* ignore disabled channels */
319562306a36Sopenharmony_ci			if (wiphy->bands[band]->channels[j].flags &
319662306a36Sopenharmony_ci						IEEE80211_CHAN_DISABLED)
319762306a36Sopenharmony_ci				continue;
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci			/* If we have a wireless request structure and the
320062306a36Sopenharmony_ci			 * wireless request specifies frequencies, then search
320162306a36Sopenharmony_ci			 * for the matching hardware channel.
320262306a36Sopenharmony_ci			 */
320362306a36Sopenharmony_ci			if (wreq && wreq->num_channels) {
320462306a36Sopenharmony_ci				int k;
320562306a36Sopenharmony_ci				int wiphy_freq = wiphy->bands[band]->channels[j].center_freq;
320662306a36Sopenharmony_ci				for (k = 0; k < wreq->num_channels; k++) {
320762306a36Sopenharmony_ci					struct iw_freq *freq =
320862306a36Sopenharmony_ci						&wreq->channel_list[k];
320962306a36Sopenharmony_ci					int wext_freq =
321062306a36Sopenharmony_ci						cfg80211_wext_freq(freq);
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci					if (wext_freq == wiphy_freq)
321362306a36Sopenharmony_ci						goto wext_freq_found;
321462306a36Sopenharmony_ci				}
321562306a36Sopenharmony_ci				goto wext_freq_not_found;
321662306a36Sopenharmony_ci			}
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci		wext_freq_found:
321962306a36Sopenharmony_ci			creq->channels[i] = &wiphy->bands[band]->channels[j];
322062306a36Sopenharmony_ci			i++;
322162306a36Sopenharmony_ci		wext_freq_not_found: ;
322262306a36Sopenharmony_ci		}
322362306a36Sopenharmony_ci	}
322462306a36Sopenharmony_ci	/* No channels found? */
322562306a36Sopenharmony_ci	if (!i) {
322662306a36Sopenharmony_ci		err = -EINVAL;
322762306a36Sopenharmony_ci		goto out;
322862306a36Sopenharmony_ci	}
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci	/* Set real number of channels specified in creq->channels[] */
323162306a36Sopenharmony_ci	creq->n_channels = i;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	/* translate "Scan for SSID" request */
323462306a36Sopenharmony_ci	if (wreq) {
323562306a36Sopenharmony_ci		if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
323662306a36Sopenharmony_ci			if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
323762306a36Sopenharmony_ci				err = -EINVAL;
323862306a36Sopenharmony_ci				goto out;
323962306a36Sopenharmony_ci			}
324062306a36Sopenharmony_ci			memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
324162306a36Sopenharmony_ci			creq->ssids[0].ssid_len = wreq->essid_len;
324262306a36Sopenharmony_ci		}
324362306a36Sopenharmony_ci		if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
324462306a36Sopenharmony_ci			creq->n_ssids = 0;
324562306a36Sopenharmony_ci	}
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	for (i = 0; i < NUM_NL80211_BANDS; i++)
324862306a36Sopenharmony_ci		if (wiphy->bands[i])
324962306a36Sopenharmony_ci			creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci	eth_broadcast_addr(creq->bssid);
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	wiphy_lock(&rdev->wiphy);
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	rdev->scan_req = creq;
325662306a36Sopenharmony_ci	err = rdev_scan(rdev, creq);
325762306a36Sopenharmony_ci	if (err) {
325862306a36Sopenharmony_ci		rdev->scan_req = NULL;
325962306a36Sopenharmony_ci		/* creq will be freed below */
326062306a36Sopenharmony_ci	} else {
326162306a36Sopenharmony_ci		nl80211_send_scan_start(rdev, dev->ieee80211_ptr);
326262306a36Sopenharmony_ci		/* creq now owned by driver */
326362306a36Sopenharmony_ci		creq = NULL;
326462306a36Sopenharmony_ci		dev_hold(dev);
326562306a36Sopenharmony_ci	}
326662306a36Sopenharmony_ci	wiphy_unlock(&rdev->wiphy);
326762306a36Sopenharmony_ci out:
326862306a36Sopenharmony_ci	kfree(creq);
326962306a36Sopenharmony_ci	return err;
327062306a36Sopenharmony_ci}
327162306a36Sopenharmony_ciEXPORT_WEXT_HANDLER(cfg80211_wext_siwscan);
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_cistatic char *ieee80211_scan_add_ies(struct iw_request_info *info,
327462306a36Sopenharmony_ci				    const struct cfg80211_bss_ies *ies,
327562306a36Sopenharmony_ci				    char *current_ev, char *end_buf)
327662306a36Sopenharmony_ci{
327762306a36Sopenharmony_ci	const u8 *pos, *end, *next;
327862306a36Sopenharmony_ci	struct iw_event iwe;
327962306a36Sopenharmony_ci
328062306a36Sopenharmony_ci	if (!ies)
328162306a36Sopenharmony_ci		return current_ev;
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	/*
328462306a36Sopenharmony_ci	 * If needed, fragment the IEs buffer (at IE boundaries) into short
328562306a36Sopenharmony_ci	 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
328662306a36Sopenharmony_ci	 */
328762306a36Sopenharmony_ci	pos = ies->data;
328862306a36Sopenharmony_ci	end = pos + ies->len;
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	while (end - pos > IW_GENERIC_IE_MAX) {
329162306a36Sopenharmony_ci		next = pos + 2 + pos[1];
329262306a36Sopenharmony_ci		while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
329362306a36Sopenharmony_ci			next = next + 2 + next[1];
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
329662306a36Sopenharmony_ci		iwe.cmd = IWEVGENIE;
329762306a36Sopenharmony_ci		iwe.u.data.length = next - pos;
329862306a36Sopenharmony_ci		current_ev = iwe_stream_add_point_check(info, current_ev,
329962306a36Sopenharmony_ci							end_buf, &iwe,
330062306a36Sopenharmony_ci							(void *)pos);
330162306a36Sopenharmony_ci		if (IS_ERR(current_ev))
330262306a36Sopenharmony_ci			return current_ev;
330362306a36Sopenharmony_ci		pos = next;
330462306a36Sopenharmony_ci	}
330562306a36Sopenharmony_ci
330662306a36Sopenharmony_ci	if (end > pos) {
330762306a36Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
330862306a36Sopenharmony_ci		iwe.cmd = IWEVGENIE;
330962306a36Sopenharmony_ci		iwe.u.data.length = end - pos;
331062306a36Sopenharmony_ci		current_ev = iwe_stream_add_point_check(info, current_ev,
331162306a36Sopenharmony_ci							end_buf, &iwe,
331262306a36Sopenharmony_ci							(void *)pos);
331362306a36Sopenharmony_ci		if (IS_ERR(current_ev))
331462306a36Sopenharmony_ci			return current_ev;
331562306a36Sopenharmony_ci	}
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_ci	return current_ev;
331862306a36Sopenharmony_ci}
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_cistatic char *
332162306a36Sopenharmony_ciieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
332262306a36Sopenharmony_ci	      struct cfg80211_internal_bss *bss, char *current_ev,
332362306a36Sopenharmony_ci	      char *end_buf)
332462306a36Sopenharmony_ci{
332562306a36Sopenharmony_ci	const struct cfg80211_bss_ies *ies;
332662306a36Sopenharmony_ci	struct iw_event iwe;
332762306a36Sopenharmony_ci	const u8 *ie;
332862306a36Sopenharmony_ci	u8 buf[50];
332962306a36Sopenharmony_ci	u8 *cfg, *p, *tmp;
333062306a36Sopenharmony_ci	int rem, i, sig;
333162306a36Sopenharmony_ci	bool ismesh = false;
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
333462306a36Sopenharmony_ci	iwe.cmd = SIOCGIWAP;
333562306a36Sopenharmony_ci	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
333662306a36Sopenharmony_ci	memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
333762306a36Sopenharmony_ci	current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
333862306a36Sopenharmony_ci						IW_EV_ADDR_LEN);
333962306a36Sopenharmony_ci	if (IS_ERR(current_ev))
334062306a36Sopenharmony_ci		return current_ev;
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
334362306a36Sopenharmony_ci	iwe.cmd = SIOCGIWFREQ;
334462306a36Sopenharmony_ci	iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
334562306a36Sopenharmony_ci	iwe.u.freq.e = 0;
334662306a36Sopenharmony_ci	current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
334762306a36Sopenharmony_ci						IW_EV_FREQ_LEN);
334862306a36Sopenharmony_ci	if (IS_ERR(current_ev))
334962306a36Sopenharmony_ci		return current_ev;
335062306a36Sopenharmony_ci
335162306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
335262306a36Sopenharmony_ci	iwe.cmd = SIOCGIWFREQ;
335362306a36Sopenharmony_ci	iwe.u.freq.m = bss->pub.channel->center_freq;
335462306a36Sopenharmony_ci	iwe.u.freq.e = 6;
335562306a36Sopenharmony_ci	current_ev = iwe_stream_add_event_check(info, current_ev, end_buf, &iwe,
335662306a36Sopenharmony_ci						IW_EV_FREQ_LEN);
335762306a36Sopenharmony_ci	if (IS_ERR(current_ev))
335862306a36Sopenharmony_ci		return current_ev;
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
336162306a36Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
336262306a36Sopenharmony_ci		iwe.cmd = IWEVQUAL;
336362306a36Sopenharmony_ci		iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
336462306a36Sopenharmony_ci				     IW_QUAL_NOISE_INVALID |
336562306a36Sopenharmony_ci				     IW_QUAL_QUAL_UPDATED;
336662306a36Sopenharmony_ci		switch (wiphy->signal_type) {
336762306a36Sopenharmony_ci		case CFG80211_SIGNAL_TYPE_MBM:
336862306a36Sopenharmony_ci			sig = bss->pub.signal / 100;
336962306a36Sopenharmony_ci			iwe.u.qual.level = sig;
337062306a36Sopenharmony_ci			iwe.u.qual.updated |= IW_QUAL_DBM;
337162306a36Sopenharmony_ci			if (sig < -110)		/* rather bad */
337262306a36Sopenharmony_ci				sig = -110;
337362306a36Sopenharmony_ci			else if (sig > -40)	/* perfect */
337462306a36Sopenharmony_ci				sig = -40;
337562306a36Sopenharmony_ci			/* will give a range of 0 .. 70 */
337662306a36Sopenharmony_ci			iwe.u.qual.qual = sig + 110;
337762306a36Sopenharmony_ci			break;
337862306a36Sopenharmony_ci		case CFG80211_SIGNAL_TYPE_UNSPEC:
337962306a36Sopenharmony_ci			iwe.u.qual.level = bss->pub.signal;
338062306a36Sopenharmony_ci			/* will give range 0 .. 100 */
338162306a36Sopenharmony_ci			iwe.u.qual.qual = bss->pub.signal;
338262306a36Sopenharmony_ci			break;
338362306a36Sopenharmony_ci		default:
338462306a36Sopenharmony_ci			/* not reached */
338562306a36Sopenharmony_ci			break;
338662306a36Sopenharmony_ci		}
338762306a36Sopenharmony_ci		current_ev = iwe_stream_add_event_check(info, current_ev,
338862306a36Sopenharmony_ci							end_buf, &iwe,
338962306a36Sopenharmony_ci							IW_EV_QUAL_LEN);
339062306a36Sopenharmony_ci		if (IS_ERR(current_ev))
339162306a36Sopenharmony_ci			return current_ev;
339262306a36Sopenharmony_ci	}
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
339562306a36Sopenharmony_ci	iwe.cmd = SIOCGIWENCODE;
339662306a36Sopenharmony_ci	if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
339762306a36Sopenharmony_ci		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
339862306a36Sopenharmony_ci	else
339962306a36Sopenharmony_ci		iwe.u.data.flags = IW_ENCODE_DISABLED;
340062306a36Sopenharmony_ci	iwe.u.data.length = 0;
340162306a36Sopenharmony_ci	current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
340262306a36Sopenharmony_ci						&iwe, "");
340362306a36Sopenharmony_ci	if (IS_ERR(current_ev))
340462306a36Sopenharmony_ci		return current_ev;
340562306a36Sopenharmony_ci
340662306a36Sopenharmony_ci	rcu_read_lock();
340762306a36Sopenharmony_ci	ies = rcu_dereference(bss->pub.ies);
340862306a36Sopenharmony_ci	rem = ies->len;
340962306a36Sopenharmony_ci	ie = ies->data;
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci	while (rem >= 2) {
341262306a36Sopenharmony_ci		/* invalid data */
341362306a36Sopenharmony_ci		if (ie[1] > rem - 2)
341462306a36Sopenharmony_ci			break;
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci		switch (ie[0]) {
341762306a36Sopenharmony_ci		case WLAN_EID_SSID:
341862306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
341962306a36Sopenharmony_ci			iwe.cmd = SIOCGIWESSID;
342062306a36Sopenharmony_ci			iwe.u.data.length = ie[1];
342162306a36Sopenharmony_ci			iwe.u.data.flags = 1;
342262306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
342362306a36Sopenharmony_ci								current_ev,
342462306a36Sopenharmony_ci								end_buf, &iwe,
342562306a36Sopenharmony_ci								(u8 *)ie + 2);
342662306a36Sopenharmony_ci			if (IS_ERR(current_ev))
342762306a36Sopenharmony_ci				goto unlock;
342862306a36Sopenharmony_ci			break;
342962306a36Sopenharmony_ci		case WLAN_EID_MESH_ID:
343062306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
343162306a36Sopenharmony_ci			iwe.cmd = SIOCGIWESSID;
343262306a36Sopenharmony_ci			iwe.u.data.length = ie[1];
343362306a36Sopenharmony_ci			iwe.u.data.flags = 1;
343462306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
343562306a36Sopenharmony_ci								current_ev,
343662306a36Sopenharmony_ci								end_buf, &iwe,
343762306a36Sopenharmony_ci								(u8 *)ie + 2);
343862306a36Sopenharmony_ci			if (IS_ERR(current_ev))
343962306a36Sopenharmony_ci				goto unlock;
344062306a36Sopenharmony_ci			break;
344162306a36Sopenharmony_ci		case WLAN_EID_MESH_CONFIG:
344262306a36Sopenharmony_ci			ismesh = true;
344362306a36Sopenharmony_ci			if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
344462306a36Sopenharmony_ci				break;
344562306a36Sopenharmony_ci			cfg = (u8 *)ie + 2;
344662306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
344762306a36Sopenharmony_ci			iwe.cmd = IWEVCUSTOM;
344862306a36Sopenharmony_ci			sprintf(buf, "Mesh Network Path Selection Protocol ID: "
344962306a36Sopenharmony_ci				"0x%02X", cfg[0]);
345062306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
345162306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
345262306a36Sopenharmony_ci								current_ev,
345362306a36Sopenharmony_ci								end_buf,
345462306a36Sopenharmony_ci								&iwe, buf);
345562306a36Sopenharmony_ci			if (IS_ERR(current_ev))
345662306a36Sopenharmony_ci				goto unlock;
345762306a36Sopenharmony_ci			sprintf(buf, "Path Selection Metric ID: 0x%02X",
345862306a36Sopenharmony_ci				cfg[1]);
345962306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
346062306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
346162306a36Sopenharmony_ci								current_ev,
346262306a36Sopenharmony_ci								end_buf,
346362306a36Sopenharmony_ci								&iwe, buf);
346462306a36Sopenharmony_ci			if (IS_ERR(current_ev))
346562306a36Sopenharmony_ci				goto unlock;
346662306a36Sopenharmony_ci			sprintf(buf, "Congestion Control Mode ID: 0x%02X",
346762306a36Sopenharmony_ci				cfg[2]);
346862306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
346962306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
347062306a36Sopenharmony_ci								current_ev,
347162306a36Sopenharmony_ci								end_buf,
347262306a36Sopenharmony_ci								&iwe, buf);
347362306a36Sopenharmony_ci			if (IS_ERR(current_ev))
347462306a36Sopenharmony_ci				goto unlock;
347562306a36Sopenharmony_ci			sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]);
347662306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
347762306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
347862306a36Sopenharmony_ci								current_ev,
347962306a36Sopenharmony_ci								end_buf,
348062306a36Sopenharmony_ci								&iwe, buf);
348162306a36Sopenharmony_ci			if (IS_ERR(current_ev))
348262306a36Sopenharmony_ci				goto unlock;
348362306a36Sopenharmony_ci			sprintf(buf, "Authentication ID: 0x%02X", cfg[4]);
348462306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
348562306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
348662306a36Sopenharmony_ci								current_ev,
348762306a36Sopenharmony_ci								end_buf,
348862306a36Sopenharmony_ci								&iwe, buf);
348962306a36Sopenharmony_ci			if (IS_ERR(current_ev))
349062306a36Sopenharmony_ci				goto unlock;
349162306a36Sopenharmony_ci			sprintf(buf, "Formation Info: 0x%02X", cfg[5]);
349262306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
349362306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
349462306a36Sopenharmony_ci								current_ev,
349562306a36Sopenharmony_ci								end_buf,
349662306a36Sopenharmony_ci								&iwe, buf);
349762306a36Sopenharmony_ci			if (IS_ERR(current_ev))
349862306a36Sopenharmony_ci				goto unlock;
349962306a36Sopenharmony_ci			sprintf(buf, "Capabilities: 0x%02X", cfg[6]);
350062306a36Sopenharmony_ci			iwe.u.data.length = strlen(buf);
350162306a36Sopenharmony_ci			current_ev = iwe_stream_add_point_check(info,
350262306a36Sopenharmony_ci								current_ev,
350362306a36Sopenharmony_ci								end_buf,
350462306a36Sopenharmony_ci								&iwe, buf);
350562306a36Sopenharmony_ci			if (IS_ERR(current_ev))
350662306a36Sopenharmony_ci				goto unlock;
350762306a36Sopenharmony_ci			break;
350862306a36Sopenharmony_ci		case WLAN_EID_SUPP_RATES:
350962306a36Sopenharmony_ci		case WLAN_EID_EXT_SUPP_RATES:
351062306a36Sopenharmony_ci			/* display all supported rates in readable format */
351162306a36Sopenharmony_ci			p = current_ev + iwe_stream_lcp_len(info);
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci			memset(&iwe, 0, sizeof(iwe));
351462306a36Sopenharmony_ci			iwe.cmd = SIOCGIWRATE;
351562306a36Sopenharmony_ci			/* Those two flags are ignored... */
351662306a36Sopenharmony_ci			iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
351762306a36Sopenharmony_ci
351862306a36Sopenharmony_ci			for (i = 0; i < ie[1]; i++) {
351962306a36Sopenharmony_ci				iwe.u.bitrate.value =
352062306a36Sopenharmony_ci					((ie[i + 2] & 0x7f) * 500000);
352162306a36Sopenharmony_ci				tmp = p;
352262306a36Sopenharmony_ci				p = iwe_stream_add_value(info, current_ev, p,
352362306a36Sopenharmony_ci							 end_buf, &iwe,
352462306a36Sopenharmony_ci							 IW_EV_PARAM_LEN);
352562306a36Sopenharmony_ci				if (p == tmp) {
352662306a36Sopenharmony_ci					current_ev = ERR_PTR(-E2BIG);
352762306a36Sopenharmony_ci					goto unlock;
352862306a36Sopenharmony_ci				}
352962306a36Sopenharmony_ci			}
353062306a36Sopenharmony_ci			current_ev = p;
353162306a36Sopenharmony_ci			break;
353262306a36Sopenharmony_ci		}
353362306a36Sopenharmony_ci		rem -= ie[1] + 2;
353462306a36Sopenharmony_ci		ie += ie[1] + 2;
353562306a36Sopenharmony_ci	}
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) ||
353862306a36Sopenharmony_ci	    ismesh) {
353962306a36Sopenharmony_ci		memset(&iwe, 0, sizeof(iwe));
354062306a36Sopenharmony_ci		iwe.cmd = SIOCGIWMODE;
354162306a36Sopenharmony_ci		if (ismesh)
354262306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_MESH;
354362306a36Sopenharmony_ci		else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
354462306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_MASTER;
354562306a36Sopenharmony_ci		else
354662306a36Sopenharmony_ci			iwe.u.mode = IW_MODE_ADHOC;
354762306a36Sopenharmony_ci		current_ev = iwe_stream_add_event_check(info, current_ev,
354862306a36Sopenharmony_ci							end_buf, &iwe,
354962306a36Sopenharmony_ci							IW_EV_UINT_LEN);
355062306a36Sopenharmony_ci		if (IS_ERR(current_ev))
355162306a36Sopenharmony_ci			goto unlock;
355262306a36Sopenharmony_ci	}
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
355562306a36Sopenharmony_ci	iwe.cmd = IWEVCUSTOM;
355662306a36Sopenharmony_ci	sprintf(buf, "tsf=%016llx", (unsigned long long)(ies->tsf));
355762306a36Sopenharmony_ci	iwe.u.data.length = strlen(buf);
355862306a36Sopenharmony_ci	current_ev = iwe_stream_add_point_check(info, current_ev, end_buf,
355962306a36Sopenharmony_ci						&iwe, buf);
356062306a36Sopenharmony_ci	if (IS_ERR(current_ev))
356162306a36Sopenharmony_ci		goto unlock;
356262306a36Sopenharmony_ci	memset(&iwe, 0, sizeof(iwe));
356362306a36Sopenharmony_ci	iwe.cmd = IWEVCUSTOM;
356462306a36Sopenharmony_ci	sprintf(buf, " Last beacon: %ums ago",
356562306a36Sopenharmony_ci		elapsed_jiffies_msecs(bss->ts));
356662306a36Sopenharmony_ci	iwe.u.data.length = strlen(buf);
356762306a36Sopenharmony_ci	current_ev = iwe_stream_add_point_check(info, current_ev,
356862306a36Sopenharmony_ci						end_buf, &iwe, buf);
356962306a36Sopenharmony_ci	if (IS_ERR(current_ev))
357062306a36Sopenharmony_ci		goto unlock;
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci	current_ev = ieee80211_scan_add_ies(info, ies, current_ev, end_buf);
357362306a36Sopenharmony_ci
357462306a36Sopenharmony_ci unlock:
357562306a36Sopenharmony_ci	rcu_read_unlock();
357662306a36Sopenharmony_ci	return current_ev;
357762306a36Sopenharmony_ci}
357862306a36Sopenharmony_ci
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_cistatic int ieee80211_scan_results(struct cfg80211_registered_device *rdev,
358162306a36Sopenharmony_ci				  struct iw_request_info *info,
358262306a36Sopenharmony_ci				  char *buf, size_t len)
358362306a36Sopenharmony_ci{
358462306a36Sopenharmony_ci	char *current_ev = buf;
358562306a36Sopenharmony_ci	char *end_buf = buf + len;
358662306a36Sopenharmony_ci	struct cfg80211_internal_bss *bss;
358762306a36Sopenharmony_ci	int err = 0;
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	spin_lock_bh(&rdev->bss_lock);
359062306a36Sopenharmony_ci	cfg80211_bss_expire(rdev);
359162306a36Sopenharmony_ci
359262306a36Sopenharmony_ci	list_for_each_entry(bss, &rdev->bss_list, list) {
359362306a36Sopenharmony_ci		if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
359462306a36Sopenharmony_ci			err = -E2BIG;
359562306a36Sopenharmony_ci			break;
359662306a36Sopenharmony_ci		}
359762306a36Sopenharmony_ci		current_ev = ieee80211_bss(&rdev->wiphy, info, bss,
359862306a36Sopenharmony_ci					   current_ev, end_buf);
359962306a36Sopenharmony_ci		if (IS_ERR(current_ev)) {
360062306a36Sopenharmony_ci			err = PTR_ERR(current_ev);
360162306a36Sopenharmony_ci			break;
360262306a36Sopenharmony_ci		}
360362306a36Sopenharmony_ci	}
360462306a36Sopenharmony_ci	spin_unlock_bh(&rdev->bss_lock);
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci	if (err)
360762306a36Sopenharmony_ci		return err;
360862306a36Sopenharmony_ci	return current_ev - buf;
360962306a36Sopenharmony_ci}
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci
361262306a36Sopenharmony_ciint cfg80211_wext_giwscan(struct net_device *dev,
361362306a36Sopenharmony_ci			  struct iw_request_info *info,
361462306a36Sopenharmony_ci			  union iwreq_data *wrqu, char *extra)
361562306a36Sopenharmony_ci{
361662306a36Sopenharmony_ci	struct iw_point *data = &wrqu->data;
361762306a36Sopenharmony_ci	struct cfg80211_registered_device *rdev;
361862306a36Sopenharmony_ci	int res;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	if (!netif_running(dev))
362162306a36Sopenharmony_ci		return -ENETDOWN;
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci	rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex);
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci	if (IS_ERR(rdev))
362662306a36Sopenharmony_ci		return PTR_ERR(rdev);
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci	if (rdev->scan_req || rdev->scan_msg)
362962306a36Sopenharmony_ci		return -EAGAIN;
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	res = ieee80211_scan_results(rdev, info, extra, data->length);
363262306a36Sopenharmony_ci	data->length = 0;
363362306a36Sopenharmony_ci	if (res >= 0) {
363462306a36Sopenharmony_ci		data->length = res;
363562306a36Sopenharmony_ci		res = 0;
363662306a36Sopenharmony_ci	}
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_ci	return res;
363962306a36Sopenharmony_ci}
364062306a36Sopenharmony_ciEXPORT_WEXT_HANDLER(cfg80211_wext_giwscan);
364162306a36Sopenharmony_ci#endif
3642