162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * mac80211 configuration hooks for cfg80211
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net>
662306a36Sopenharmony_ci * Copyright 2013-2015  Intel Mobile Communications GmbH
762306a36Sopenharmony_ci * Copyright (C) 2015-2017 Intel Deutschland GmbH
862306a36Sopenharmony_ci * Copyright (C) 2018-2022 Intel Corporation
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/ieee80211.h>
1262306a36Sopenharmony_ci#include <linux/nl80211.h>
1362306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <net/net_namespace.h>
1662306a36Sopenharmony_ci#include <linux/rcupdate.h>
1762306a36Sopenharmony_ci#include <linux/fips.h>
1862306a36Sopenharmony_ci#include <linux/if_ether.h>
1962306a36Sopenharmony_ci#include <net/cfg80211.h>
2062306a36Sopenharmony_ci#include "ieee80211_i.h"
2162306a36Sopenharmony_ci#include "driver-ops.h"
2262306a36Sopenharmony_ci#include "rate.h"
2362306a36Sopenharmony_ci#include "mesh.h"
2462306a36Sopenharmony_ci#include "wme.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct ieee80211_link_data *
2762306a36Sopenharmony_ciieee80211_link_or_deflink(struct ieee80211_sub_if_data *sdata, int link_id,
2862306a36Sopenharmony_ci			  bool require_valid)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct ieee80211_link_data *link;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (link_id < 0) {
3362306a36Sopenharmony_ci		/*
3462306a36Sopenharmony_ci		 * For keys, if sdata is not an MLD, we might not use
3562306a36Sopenharmony_ci		 * the return value at all (if it's not a pairwise key),
3662306a36Sopenharmony_ci		 * so in that case (require_valid==false) don't error.
3762306a36Sopenharmony_ci		 */
3862306a36Sopenharmony_ci		if (require_valid && ieee80211_vif_is_mld(&sdata->vif))
3962306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		return &sdata->deflink;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	link = sdata_dereference(sdata->link[link_id], sdata);
4562306a36Sopenharmony_ci	if (!link)
4662306a36Sopenharmony_ci		return ERR_PTR(-ENOLINK);
4762306a36Sopenharmony_ci	return link;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata,
5162306a36Sopenharmony_ci					 struct vif_params *params)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	bool mu_mimo_groups = false;
5462306a36Sopenharmony_ci	bool mu_mimo_follow = false;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (params->vht_mumimo_groups) {
5762306a36Sopenharmony_ci		u64 membership;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		BUILD_BUG_ON(sizeof(membership) != WLAN_MEMBERSHIP_LEN);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci		memcpy(sdata->vif.bss_conf.mu_group.membership,
6262306a36Sopenharmony_ci		       params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN);
6362306a36Sopenharmony_ci		memcpy(sdata->vif.bss_conf.mu_group.position,
6462306a36Sopenharmony_ci		       params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
6562306a36Sopenharmony_ci		       WLAN_USER_POSITION_LEN);
6662306a36Sopenharmony_ci		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
6762306a36Sopenharmony_ci						  BSS_CHANGED_MU_GROUPS);
6862306a36Sopenharmony_ci		/* don't care about endianness - just check for 0 */
6962306a36Sopenharmony_ci		memcpy(&membership, params->vht_mumimo_groups,
7062306a36Sopenharmony_ci		       WLAN_MEMBERSHIP_LEN);
7162306a36Sopenharmony_ci		mu_mimo_groups = membership != 0;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (params->vht_mumimo_follow_addr) {
7562306a36Sopenharmony_ci		mu_mimo_follow =
7662306a36Sopenharmony_ci			is_valid_ether_addr(params->vht_mumimo_follow_addr);
7762306a36Sopenharmony_ci		ether_addr_copy(sdata->u.mntr.mu_follow_addr,
7862306a36Sopenharmony_ci				params->vht_mumimo_follow_addr);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	sdata->vif.bss_conf.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
8562306a36Sopenharmony_ci				     struct vif_params *params)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
8862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *monitor_sdata;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* check flags first */
9162306a36Sopenharmony_ci	if (params->flags && ieee80211_sdata_running(sdata)) {
9262306a36Sopenharmony_ci		u32 mask = MONITOR_FLAG_COOK_FRAMES | MONITOR_FLAG_ACTIVE;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		/*
9562306a36Sopenharmony_ci		 * Prohibit MONITOR_FLAG_COOK_FRAMES and
9662306a36Sopenharmony_ci		 * MONITOR_FLAG_ACTIVE to be changed while the
9762306a36Sopenharmony_ci		 * interface is up.
9862306a36Sopenharmony_ci		 * Else we would need to add a lot of cruft
9962306a36Sopenharmony_ci		 * to update everything:
10062306a36Sopenharmony_ci		 *	cooked_mntrs, monitor and all fif_* counters
10162306a36Sopenharmony_ci		 *	reconfigure hardware
10262306a36Sopenharmony_ci		 */
10362306a36Sopenharmony_ci		if ((params->flags & mask) != (sdata->u.mntr.flags & mask))
10462306a36Sopenharmony_ci			return -EBUSY;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* also validate MU-MIMO change */
10862306a36Sopenharmony_ci	monitor_sdata = wiphy_dereference(local->hw.wiphy,
10962306a36Sopenharmony_ci					  local->monitor_sdata);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (!monitor_sdata &&
11262306a36Sopenharmony_ci	    (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
11362306a36Sopenharmony_ci		return -EOPNOTSUPP;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* apply all changes now - no failures allowed */
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (monitor_sdata)
11862306a36Sopenharmony_ci		ieee80211_set_mu_mimo_follow(monitor_sdata, params);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (params->flags) {
12162306a36Sopenharmony_ci		if (ieee80211_sdata_running(sdata)) {
12262306a36Sopenharmony_ci			ieee80211_adjust_monitor_flags(sdata, -1);
12362306a36Sopenharmony_ci			sdata->u.mntr.flags = params->flags;
12462306a36Sopenharmony_ci			ieee80211_adjust_monitor_flags(sdata, 1);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci			ieee80211_configure_filter(local);
12762306a36Sopenharmony_ci		} else {
12862306a36Sopenharmony_ci			/*
12962306a36Sopenharmony_ci			 * Because the interface is down, ieee80211_do_stop
13062306a36Sopenharmony_ci			 * and ieee80211_do_open take care of "everything"
13162306a36Sopenharmony_ci			 * mentioned in the comment above.
13262306a36Sopenharmony_ci			 */
13362306a36Sopenharmony_ci			sdata->u.mntr.flags = params->flags;
13462306a36Sopenharmony_ci		}
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return 0;
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,
14162306a36Sopenharmony_ci					   struct cfg80211_mbssid_config params,
14262306a36Sopenharmony_ci					   struct ieee80211_bss_conf *link_conf)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *tx_sdata;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	sdata->vif.mbssid_tx_vif = NULL;
14762306a36Sopenharmony_ci	link_conf->bssid_index = 0;
14862306a36Sopenharmony_ci	link_conf->nontransmitted = false;
14962306a36Sopenharmony_ci	link_conf->ema_ap = false;
15062306a36Sopenharmony_ci	link_conf->bssid_indicator = 0;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_AP || !params.tx_wdev)
15362306a36Sopenharmony_ci		return -EINVAL;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	tx_sdata = IEEE80211_WDEV_TO_SUB_IF(params.tx_wdev);
15662306a36Sopenharmony_ci	if (!tx_sdata)
15762306a36Sopenharmony_ci		return -EINVAL;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (tx_sdata == sdata) {
16062306a36Sopenharmony_ci		sdata->vif.mbssid_tx_vif = &sdata->vif;
16162306a36Sopenharmony_ci	} else {
16262306a36Sopenharmony_ci		sdata->vif.mbssid_tx_vif = &tx_sdata->vif;
16362306a36Sopenharmony_ci		link_conf->nontransmitted = true;
16462306a36Sopenharmony_ci		link_conf->bssid_index = params.index;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci	if (params.ema)
16762306a36Sopenharmony_ci		link_conf->ema_ap = true;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
17362306a36Sopenharmony_ci						const char *name,
17462306a36Sopenharmony_ci						unsigned char name_assign_type,
17562306a36Sopenharmony_ci						enum nl80211_iftype type,
17662306a36Sopenharmony_ci						struct vif_params *params)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
17962306a36Sopenharmony_ci	struct wireless_dev *wdev;
18062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
18162306a36Sopenharmony_ci	int err;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	err = ieee80211_if_add(local, name, name_assign_type, &wdev, type, params);
18462306a36Sopenharmony_ci	if (err)
18562306a36Sopenharmony_ci		return ERR_PTR(err);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (type == NL80211_IFTYPE_MONITOR) {
19062306a36Sopenharmony_ci		err = ieee80211_set_mon_options(sdata, params);
19162306a36Sopenharmony_ci		if (err) {
19262306a36Sopenharmony_ci			ieee80211_if_remove(sdata);
19362306a36Sopenharmony_ci			return NULL;
19462306a36Sopenharmony_ci		}
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return wdev;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int ieee80211_change_iface(struct wiphy *wiphy,
20862306a36Sopenharmony_ci				  struct net_device *dev,
20962306a36Sopenharmony_ci				  enum nl80211_iftype type,
21062306a36Sopenharmony_ci				  struct vif_params *params)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
21362306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
21462306a36Sopenharmony_ci	struct sta_info *sta;
21562306a36Sopenharmony_ci	int ret;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	ret = ieee80211_if_change_type(sdata, type);
21862306a36Sopenharmony_ci	if (ret)
21962306a36Sopenharmony_ci		return ret;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (type == NL80211_IFTYPE_AP_VLAN && params->use_4addr == 0) {
22262306a36Sopenharmony_ci		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
22362306a36Sopenharmony_ci		ieee80211_check_fast_rx_iface(sdata);
22462306a36Sopenharmony_ci	} else if (type == NL80211_IFTYPE_STATION && params->use_4addr >= 0) {
22562306a36Sopenharmony_ci		struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci		if (params->use_4addr == ifmgd->use_4addr)
22862306a36Sopenharmony_ci			return 0;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		/* FIXME: no support for 4-addr MLO yet */
23162306a36Sopenharmony_ci		if (ieee80211_vif_is_mld(&sdata->vif))
23262306a36Sopenharmony_ci			return -EOPNOTSUPP;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		sdata->u.mgd.use_4addr = params->use_4addr;
23562306a36Sopenharmony_ci		if (!ifmgd->associated)
23662306a36Sopenharmony_ci			return 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		mutex_lock(&local->sta_mtx);
23962306a36Sopenharmony_ci		sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
24062306a36Sopenharmony_ci		if (sta)
24162306a36Sopenharmony_ci			drv_sta_set_4addr(local, sdata, &sta->sta,
24262306a36Sopenharmony_ci					  params->use_4addr);
24362306a36Sopenharmony_ci		mutex_unlock(&local->sta_mtx);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		if (params->use_4addr)
24662306a36Sopenharmony_ci			ieee80211_send_4addr_nullfunc(local, sdata);
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
25062306a36Sopenharmony_ci		ret = ieee80211_set_mon_options(sdata, params);
25162306a36Sopenharmony_ci		if (ret)
25262306a36Sopenharmony_ci			return ret;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int ieee80211_start_p2p_device(struct wiphy *wiphy,
25962306a36Sopenharmony_ci				      struct wireless_dev *wdev)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
26262306a36Sopenharmony_ci	int ret;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	mutex_lock(&sdata->local->chanctx_mtx);
26562306a36Sopenharmony_ci	ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
26662306a36Sopenharmony_ci	mutex_unlock(&sdata->local->chanctx_mtx);
26762306a36Sopenharmony_ci	if (ret < 0)
26862306a36Sopenharmony_ci		return ret;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return ieee80211_do_open(wdev, true);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void ieee80211_stop_p2p_device(struct wiphy *wiphy,
27462306a36Sopenharmony_ci				      struct wireless_dev *wdev)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev));
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int ieee80211_start_nan(struct wiphy *wiphy,
28062306a36Sopenharmony_ci			       struct wireless_dev *wdev,
28162306a36Sopenharmony_ci			       struct cfg80211_nan_conf *conf)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
28462306a36Sopenharmony_ci	int ret;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	mutex_lock(&sdata->local->chanctx_mtx);
28762306a36Sopenharmony_ci	ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
28862306a36Sopenharmony_ci	mutex_unlock(&sdata->local->chanctx_mtx);
28962306a36Sopenharmony_ci	if (ret < 0)
29062306a36Sopenharmony_ci		return ret;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ret = ieee80211_do_open(wdev, true);
29362306a36Sopenharmony_ci	if (ret)
29462306a36Sopenharmony_ci		return ret;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	ret = drv_start_nan(sdata->local, sdata, conf);
29762306a36Sopenharmony_ci	if (ret)
29862306a36Sopenharmony_ci		ieee80211_sdata_stop(sdata);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	sdata->u.nan.conf = *conf;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return ret;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic void ieee80211_stop_nan(struct wiphy *wiphy,
30662306a36Sopenharmony_ci			       struct wireless_dev *wdev)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	drv_stop_nan(sdata->local, sdata);
31162306a36Sopenharmony_ci	ieee80211_sdata_stop(sdata);
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int ieee80211_nan_change_conf(struct wiphy *wiphy,
31562306a36Sopenharmony_ci				     struct wireless_dev *wdev,
31662306a36Sopenharmony_ci				     struct cfg80211_nan_conf *conf,
31762306a36Sopenharmony_ci				     u32 changes)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
32062306a36Sopenharmony_ci	struct cfg80211_nan_conf new_conf;
32162306a36Sopenharmony_ci	int ret = 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_NAN)
32462306a36Sopenharmony_ci		return -EOPNOTSUPP;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (!ieee80211_sdata_running(sdata))
32762306a36Sopenharmony_ci		return -ENETDOWN;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	new_conf = sdata->u.nan.conf;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (changes & CFG80211_NAN_CONF_CHANGED_PREF)
33262306a36Sopenharmony_ci		new_conf.master_pref = conf->master_pref;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (changes & CFG80211_NAN_CONF_CHANGED_BANDS)
33562306a36Sopenharmony_ci		new_conf.bands = conf->bands;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	ret = drv_nan_change_conf(sdata->local, sdata, &new_conf, changes);
33862306a36Sopenharmony_ci	if (!ret)
33962306a36Sopenharmony_ci		sdata->u.nan.conf = new_conf;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return ret;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int ieee80211_add_nan_func(struct wiphy *wiphy,
34562306a36Sopenharmony_ci				  struct wireless_dev *wdev,
34662306a36Sopenharmony_ci				  struct cfg80211_nan_func *nan_func)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
34962306a36Sopenharmony_ci	int ret;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_NAN)
35262306a36Sopenharmony_ci		return -EOPNOTSUPP;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	if (!ieee80211_sdata_running(sdata))
35562306a36Sopenharmony_ci		return -ENETDOWN;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	spin_lock_bh(&sdata->u.nan.func_lock);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	ret = idr_alloc(&sdata->u.nan.function_inst_ids,
36062306a36Sopenharmony_ci			nan_func, 1, sdata->local->hw.max_nan_de_entries + 1,
36162306a36Sopenharmony_ci			GFP_ATOMIC);
36262306a36Sopenharmony_ci	spin_unlock_bh(&sdata->u.nan.func_lock);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (ret < 0)
36562306a36Sopenharmony_ci		return ret;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	nan_func->instance_id = ret;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	WARN_ON(nan_func->instance_id == 0);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ret = drv_add_nan_func(sdata->local, sdata, nan_func);
37262306a36Sopenharmony_ci	if (ret) {
37362306a36Sopenharmony_ci		spin_lock_bh(&sdata->u.nan.func_lock);
37462306a36Sopenharmony_ci		idr_remove(&sdata->u.nan.function_inst_ids,
37562306a36Sopenharmony_ci			   nan_func->instance_id);
37662306a36Sopenharmony_ci		spin_unlock_bh(&sdata->u.nan.func_lock);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return ret;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic struct cfg80211_nan_func *
38362306a36Sopenharmony_ciieee80211_find_nan_func_by_cookie(struct ieee80211_sub_if_data *sdata,
38462306a36Sopenharmony_ci				  u64 cookie)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct cfg80211_nan_func *func;
38762306a36Sopenharmony_ci	int id;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	lockdep_assert_held(&sdata->u.nan.func_lock);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) {
39262306a36Sopenharmony_ci		if (func->cookie == cookie)
39362306a36Sopenharmony_ci			return func;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return NULL;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void ieee80211_del_nan_func(struct wiphy *wiphy,
40062306a36Sopenharmony_ci				  struct wireless_dev *wdev, u64 cookie)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
40362306a36Sopenharmony_ci	struct cfg80211_nan_func *func;
40462306a36Sopenharmony_ci	u8 instance_id = 0;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_NAN ||
40762306a36Sopenharmony_ci	    !ieee80211_sdata_running(sdata))
40862306a36Sopenharmony_ci		return;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	spin_lock_bh(&sdata->u.nan.func_lock);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	func = ieee80211_find_nan_func_by_cookie(sdata, cookie);
41362306a36Sopenharmony_ci	if (func)
41462306a36Sopenharmony_ci		instance_id = func->instance_id;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	spin_unlock_bh(&sdata->u.nan.func_lock);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (instance_id)
41962306a36Sopenharmony_ci		drv_del_nan_func(sdata->local, sdata, instance_id);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic int ieee80211_set_noack_map(struct wiphy *wiphy,
42362306a36Sopenharmony_ci				  struct net_device *dev,
42462306a36Sopenharmony_ci				  u16 noack_map)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	sdata->noack_map = noack_map;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	ieee80211_check_fast_xmit_iface(sdata);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int ieee80211_set_tx(struct ieee80211_sub_if_data *sdata,
43662306a36Sopenharmony_ci			    const u8 *mac_addr, u8 key_idx)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
43962306a36Sopenharmony_ci	struct ieee80211_key *key;
44062306a36Sopenharmony_ci	struct sta_info *sta;
44162306a36Sopenharmony_ci	int ret = -EINVAL;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (!wiphy_ext_feature_isset(local->hw.wiphy,
44462306a36Sopenharmony_ci				     NL80211_EXT_FEATURE_EXT_KEY_ID))
44562306a36Sopenharmony_ci		return -EINVAL;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, mac_addr);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (!sta)
45062306a36Sopenharmony_ci		return -EINVAL;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (sta->ptk_idx == key_idx)
45362306a36Sopenharmony_ci		return 0;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	mutex_lock(&local->key_mtx);
45662306a36Sopenharmony_ci	key = key_mtx_dereference(local, sta->ptk[key_idx]);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (key && key->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)
45962306a36Sopenharmony_ci		ret = ieee80211_set_tx_key(key);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	mutex_unlock(&local->key_mtx);
46262306a36Sopenharmony_ci	return ret;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
46662306a36Sopenharmony_ci			     int link_id, u8 key_idx, bool pairwise,
46762306a36Sopenharmony_ci			     const u8 *mac_addr, struct key_params *params)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
47062306a36Sopenharmony_ci	struct ieee80211_link_data *link =
47162306a36Sopenharmony_ci		ieee80211_link_or_deflink(sdata, link_id, false);
47262306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
47362306a36Sopenharmony_ci	struct sta_info *sta = NULL;
47462306a36Sopenharmony_ci	struct ieee80211_key *key;
47562306a36Sopenharmony_ci	int err;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (!ieee80211_sdata_running(sdata))
47862306a36Sopenharmony_ci		return -ENETDOWN;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (IS_ERR(link))
48162306a36Sopenharmony_ci		return PTR_ERR(link);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (pairwise && params->mode == NL80211_KEY_SET_TX)
48462306a36Sopenharmony_ci		return ieee80211_set_tx(sdata, mac_addr, key_idx);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/* reject WEP and TKIP keys if WEP failed to initialize */
48762306a36Sopenharmony_ci	switch (params->cipher) {
48862306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
48962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
49062306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
49162306a36Sopenharmony_ci		if (link_id >= 0)
49262306a36Sopenharmony_ci			return -EINVAL;
49362306a36Sopenharmony_ci		if (WARN_ON_ONCE(fips_enabled))
49462306a36Sopenharmony_ci			return -EINVAL;
49562306a36Sopenharmony_ci		break;
49662306a36Sopenharmony_ci	default:
49762306a36Sopenharmony_ci		break;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
50162306a36Sopenharmony_ci				  params->key, params->seq_len, params->seq);
50262306a36Sopenharmony_ci	if (IS_ERR(key))
50362306a36Sopenharmony_ci		return PTR_ERR(key);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	key->conf.link_id = link_id;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (pairwise)
50862306a36Sopenharmony_ci		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (params->mode == NL80211_KEY_NO_TX)
51162306a36Sopenharmony_ci		key->conf.flags |= IEEE80211_KEY_FLAG_NO_AUTO_TX;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	mutex_lock(&local->sta_mtx);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (mac_addr) {
51662306a36Sopenharmony_ci		sta = sta_info_get_bss(sdata, mac_addr);
51762306a36Sopenharmony_ci		/*
51862306a36Sopenharmony_ci		 * The ASSOC test makes sure the driver is ready to
51962306a36Sopenharmony_ci		 * receive the key. When wpa_supplicant has roamed
52062306a36Sopenharmony_ci		 * using FT, it attempts to set the key before
52162306a36Sopenharmony_ci		 * association has completed, this rejects that attempt
52262306a36Sopenharmony_ci		 * so it will set the key again after association.
52362306a36Sopenharmony_ci		 *
52462306a36Sopenharmony_ci		 * TODO: accept the key if we have a station entry and
52562306a36Sopenharmony_ci		 *       add it to the device after the station.
52662306a36Sopenharmony_ci		 */
52762306a36Sopenharmony_ci		if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
52862306a36Sopenharmony_ci			ieee80211_key_free_unused(key);
52962306a36Sopenharmony_ci			err = -ENOENT;
53062306a36Sopenharmony_ci			goto out_unlock;
53162306a36Sopenharmony_ci		}
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	switch (sdata->vif.type) {
53562306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
53662306a36Sopenharmony_ci		if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
53762306a36Sopenharmony_ci			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
53862306a36Sopenharmony_ci		break;
53962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
54062306a36Sopenharmony_ci	case NL80211_IFTYPE_AP_VLAN:
54162306a36Sopenharmony_ci		/* Keys without a station are used for TX only */
54262306a36Sopenharmony_ci		if (sta && test_sta_flag(sta, WLAN_STA_MFP))
54362306a36Sopenharmony_ci			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
54462306a36Sopenharmony_ci		break;
54562306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
54662306a36Sopenharmony_ci		/* no MFP (yet) */
54762306a36Sopenharmony_ci		break;
54862306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
54962306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
55062306a36Sopenharmony_ci		if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
55162306a36Sopenharmony_ci			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
55262306a36Sopenharmony_ci		break;
55362306a36Sopenharmony_ci#endif
55462306a36Sopenharmony_ci	case NL80211_IFTYPE_WDS:
55562306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
55662306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
55762306a36Sopenharmony_ci	case NL80211_IFTYPE_NAN:
55862306a36Sopenharmony_ci	case NL80211_IFTYPE_UNSPECIFIED:
55962306a36Sopenharmony_ci	case NUM_NL80211_IFTYPES:
56062306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
56162306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
56262306a36Sopenharmony_ci	case NL80211_IFTYPE_OCB:
56362306a36Sopenharmony_ci		/* shouldn't happen */
56462306a36Sopenharmony_ci		WARN_ON_ONCE(1);
56562306a36Sopenharmony_ci		break;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	err = ieee80211_key_link(key, link, sta);
56962306a36Sopenharmony_ci	/* KRACK protection, shouldn't happen but just silently accept key */
57062306a36Sopenharmony_ci	if (err == -EALREADY)
57162306a36Sopenharmony_ci		err = 0;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci out_unlock:
57462306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return err;
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic struct ieee80211_key *
58062306a36Sopenharmony_ciieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
58162306a36Sopenharmony_ci		     u8 key_idx, bool pairwise, const u8 *mac_addr)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct ieee80211_local *local __maybe_unused = sdata->local;
58462306a36Sopenharmony_ci	struct ieee80211_link_data *link = &sdata->deflink;
58562306a36Sopenharmony_ci	struct ieee80211_key *key;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if (link_id >= 0) {
58862306a36Sopenharmony_ci		link = rcu_dereference_check(sdata->link[link_id],
58962306a36Sopenharmony_ci					     lockdep_is_held(&sdata->wdev.mtx));
59062306a36Sopenharmony_ci		if (!link)
59162306a36Sopenharmony_ci			return NULL;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (mac_addr) {
59562306a36Sopenharmony_ci		struct sta_info *sta;
59662306a36Sopenharmony_ci		struct link_sta_info *link_sta;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci		sta = sta_info_get_bss(sdata, mac_addr);
59962306a36Sopenharmony_ci		if (!sta)
60062306a36Sopenharmony_ci			return NULL;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		if (link_id >= 0) {
60362306a36Sopenharmony_ci			link_sta = rcu_dereference_check(sta->link[link_id],
60462306a36Sopenharmony_ci							 lockdep_is_held(&local->sta_mtx));
60562306a36Sopenharmony_ci			if (!link_sta)
60662306a36Sopenharmony_ci				return NULL;
60762306a36Sopenharmony_ci		} else {
60862306a36Sopenharmony_ci			link_sta = &sta->deflink;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		if (pairwise && key_idx < NUM_DEFAULT_KEYS)
61262306a36Sopenharmony_ci			return rcu_dereference_check_key_mtx(local,
61362306a36Sopenharmony_ci							     sta->ptk[key_idx]);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		if (!pairwise &&
61662306a36Sopenharmony_ci		    key_idx < NUM_DEFAULT_KEYS +
61762306a36Sopenharmony_ci			      NUM_DEFAULT_MGMT_KEYS +
61862306a36Sopenharmony_ci			      NUM_DEFAULT_BEACON_KEYS)
61962306a36Sopenharmony_ci			return rcu_dereference_check_key_mtx(local,
62062306a36Sopenharmony_ci							     link_sta->gtk[key_idx]);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		return NULL;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (pairwise && key_idx < NUM_DEFAULT_KEYS)
62662306a36Sopenharmony_ci		return rcu_dereference_check_key_mtx(local,
62762306a36Sopenharmony_ci						     sdata->keys[key_idx]);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	key = rcu_dereference_check_key_mtx(local, link->gtk[key_idx]);
63062306a36Sopenharmony_ci	if (key)
63162306a36Sopenharmony_ci		return key;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* or maybe it was a WEP key */
63462306a36Sopenharmony_ci	if (key_idx < NUM_DEFAULT_KEYS)
63562306a36Sopenharmony_ci		return rcu_dereference_check_key_mtx(local, sdata->keys[key_idx]);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	return NULL;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
64162306a36Sopenharmony_ci			     int link_id, u8 key_idx, bool pairwise,
64262306a36Sopenharmony_ci			     const u8 *mac_addr)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
64562306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
64662306a36Sopenharmony_ci	struct ieee80211_key *key;
64762306a36Sopenharmony_ci	int ret;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	mutex_lock(&local->sta_mtx);
65062306a36Sopenharmony_ci	mutex_lock(&local->key_mtx);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
65362306a36Sopenharmony_ci	if (!key) {
65462306a36Sopenharmony_ci		ret = -ENOENT;
65562306a36Sopenharmony_ci		goto out_unlock;
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	ieee80211_key_free(key, sdata->vif.type == NL80211_IFTYPE_STATION);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	ret = 0;
66162306a36Sopenharmony_ci out_unlock:
66262306a36Sopenharmony_ci	mutex_unlock(&local->key_mtx);
66362306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return ret;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
66962306a36Sopenharmony_ci			     int link_id, u8 key_idx, bool pairwise,
67062306a36Sopenharmony_ci			     const u8 *mac_addr, void *cookie,
67162306a36Sopenharmony_ci			     void (*callback)(void *cookie,
67262306a36Sopenharmony_ci					      struct key_params *params))
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
67562306a36Sopenharmony_ci	u8 seq[6] = {0};
67662306a36Sopenharmony_ci	struct key_params params;
67762306a36Sopenharmony_ci	struct ieee80211_key *key;
67862306a36Sopenharmony_ci	u64 pn64;
67962306a36Sopenharmony_ci	u32 iv32;
68062306a36Sopenharmony_ci	u16 iv16;
68162306a36Sopenharmony_ci	int err = -ENOENT;
68262306a36Sopenharmony_ci	struct ieee80211_key_seq kseq = {};
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	rcu_read_lock();
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
68962306a36Sopenharmony_ci	if (!key)
69062306a36Sopenharmony_ci		goto out;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	memset(&params, 0, sizeof(params));
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	params.cipher = key->conf.cipher;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	switch (key->conf.cipher) {
69762306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
69862306a36Sopenharmony_ci		pn64 = atomic64_read(&key->conf.tx_pn);
69962306a36Sopenharmony_ci		iv32 = TKIP_PN_TO_IV32(pn64);
70062306a36Sopenharmony_ci		iv16 = TKIP_PN_TO_IV16(pn64);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
70362306a36Sopenharmony_ci		    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
70462306a36Sopenharmony_ci			drv_get_key_seq(sdata->local, key, &kseq);
70562306a36Sopenharmony_ci			iv32 = kseq.tkip.iv32;
70662306a36Sopenharmony_ci			iv16 = kseq.tkip.iv16;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		seq[0] = iv16 & 0xff;
71062306a36Sopenharmony_ci		seq[1] = (iv16 >> 8) & 0xff;
71162306a36Sopenharmony_ci		seq[2] = iv32 & 0xff;
71262306a36Sopenharmony_ci		seq[3] = (iv32 >> 8) & 0xff;
71362306a36Sopenharmony_ci		seq[4] = (iv32 >> 16) & 0xff;
71462306a36Sopenharmony_ci		seq[5] = (iv32 >> 24) & 0xff;
71562306a36Sopenharmony_ci		params.seq = seq;
71662306a36Sopenharmony_ci		params.seq_len = 6;
71762306a36Sopenharmony_ci		break;
71862306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
71962306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP_256:
72062306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_AES_CMAC:
72162306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
72262306a36Sopenharmony_ci		BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
72362306a36Sopenharmony_ci			     offsetof(typeof(kseq), aes_cmac));
72462306a36Sopenharmony_ci		fallthrough;
72562306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
72662306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
72762306a36Sopenharmony_ci		BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
72862306a36Sopenharmony_ci			     offsetof(typeof(kseq), aes_gmac));
72962306a36Sopenharmony_ci		fallthrough;
73062306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_GCMP:
73162306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_GCMP_256:
73262306a36Sopenharmony_ci		BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
73362306a36Sopenharmony_ci			     offsetof(typeof(kseq), gcmp));
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
73662306a36Sopenharmony_ci		    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
73762306a36Sopenharmony_ci			drv_get_key_seq(sdata->local, key, &kseq);
73862306a36Sopenharmony_ci			memcpy(seq, kseq.ccmp.pn, 6);
73962306a36Sopenharmony_ci		} else {
74062306a36Sopenharmony_ci			pn64 = atomic64_read(&key->conf.tx_pn);
74162306a36Sopenharmony_ci			seq[0] = pn64;
74262306a36Sopenharmony_ci			seq[1] = pn64 >> 8;
74362306a36Sopenharmony_ci			seq[2] = pn64 >> 16;
74462306a36Sopenharmony_ci			seq[3] = pn64 >> 24;
74562306a36Sopenharmony_ci			seq[4] = pn64 >> 32;
74662306a36Sopenharmony_ci			seq[5] = pn64 >> 40;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci		params.seq = seq;
74962306a36Sopenharmony_ci		params.seq_len = 6;
75062306a36Sopenharmony_ci		break;
75162306a36Sopenharmony_ci	default:
75262306a36Sopenharmony_ci		if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
75362306a36Sopenharmony_ci			break;
75462306a36Sopenharmony_ci		if (WARN_ON(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
75562306a36Sopenharmony_ci			break;
75662306a36Sopenharmony_ci		drv_get_key_seq(sdata->local, key, &kseq);
75762306a36Sopenharmony_ci		params.seq = kseq.hw.seq;
75862306a36Sopenharmony_ci		params.seq_len = kseq.hw.seq_len;
75962306a36Sopenharmony_ci		break;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	params.key = key->conf.key;
76362306a36Sopenharmony_ci	params.key_len = key->conf.keylen;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	callback(cookie, &params);
76662306a36Sopenharmony_ci	err = 0;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci out:
76962306a36Sopenharmony_ci	rcu_read_unlock();
77062306a36Sopenharmony_ci	return err;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic int ieee80211_config_default_key(struct wiphy *wiphy,
77462306a36Sopenharmony_ci					struct net_device *dev,
77562306a36Sopenharmony_ci					int link_id, u8 key_idx, bool uni,
77662306a36Sopenharmony_ci					bool multi)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
77962306a36Sopenharmony_ci	struct ieee80211_link_data *link =
78062306a36Sopenharmony_ci		ieee80211_link_or_deflink(sdata, link_id, false);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (IS_ERR(link))
78362306a36Sopenharmony_ci		return PTR_ERR(link);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	ieee80211_set_default_key(link, key_idx, uni, multi);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
79162306a36Sopenharmony_ci					     struct net_device *dev,
79262306a36Sopenharmony_ci					     int link_id, u8 key_idx)
79362306a36Sopenharmony_ci{
79462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
79562306a36Sopenharmony_ci	struct ieee80211_link_data *link =
79662306a36Sopenharmony_ci		ieee80211_link_or_deflink(sdata, link_id, true);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	if (IS_ERR(link))
79962306a36Sopenharmony_ci		return PTR_ERR(link);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	ieee80211_set_default_mgmt_key(link, key_idx);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	return 0;
80462306a36Sopenharmony_ci}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_cistatic int ieee80211_config_default_beacon_key(struct wiphy *wiphy,
80762306a36Sopenharmony_ci					       struct net_device *dev,
80862306a36Sopenharmony_ci					       int link_id, u8 key_idx)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
81162306a36Sopenharmony_ci	struct ieee80211_link_data *link =
81262306a36Sopenharmony_ci		ieee80211_link_or_deflink(sdata, link_id, true);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	if (IS_ERR(link))
81562306a36Sopenharmony_ci		return PTR_ERR(link);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	ieee80211_set_default_beacon_key(link, key_idx);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_civoid sta_set_rate_info_tx(struct sta_info *sta,
82362306a36Sopenharmony_ci			  const struct ieee80211_tx_rate *rate,
82462306a36Sopenharmony_ci			  struct rate_info *rinfo)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	rinfo->flags = 0;
82762306a36Sopenharmony_ci	if (rate->flags & IEEE80211_TX_RC_MCS) {
82862306a36Sopenharmony_ci		rinfo->flags |= RATE_INFO_FLAGS_MCS;
82962306a36Sopenharmony_ci		rinfo->mcs = rate->idx;
83062306a36Sopenharmony_ci	} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
83162306a36Sopenharmony_ci		rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
83262306a36Sopenharmony_ci		rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
83362306a36Sopenharmony_ci		rinfo->nss = ieee80211_rate_get_vht_nss(rate);
83462306a36Sopenharmony_ci	} else {
83562306a36Sopenharmony_ci		struct ieee80211_supported_band *sband;
83662306a36Sopenharmony_ci		int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
83762306a36Sopenharmony_ci		u16 brate;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		sband = ieee80211_get_sband(sta->sdata);
84062306a36Sopenharmony_ci		WARN_ON_ONCE(sband && !sband->bitrates);
84162306a36Sopenharmony_ci		if (sband && sband->bitrates) {
84262306a36Sopenharmony_ci			brate = sband->bitrates[rate->idx].bitrate;
84362306a36Sopenharmony_ci			rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
84462306a36Sopenharmony_ci		}
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci	if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
84762306a36Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_40;
84862306a36Sopenharmony_ci	else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
84962306a36Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_80;
85062306a36Sopenharmony_ci	else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
85162306a36Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_160;
85262306a36Sopenharmony_ci	else
85362306a36Sopenharmony_ci		rinfo->bw = RATE_INFO_BW_20;
85462306a36Sopenharmony_ci	if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
85562306a36Sopenharmony_ci		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
85662306a36Sopenharmony_ci}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_cistatic int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
85962306a36Sopenharmony_ci				  int idx, u8 *mac, struct station_info *sinfo)
86062306a36Sopenharmony_ci{
86162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
86262306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
86362306a36Sopenharmony_ci	struct sta_info *sta;
86462306a36Sopenharmony_ci	int ret = -ENOENT;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	mutex_lock(&local->sta_mtx);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	sta = sta_info_get_by_idx(sdata, idx);
86962306a36Sopenharmony_ci	if (sta) {
87062306a36Sopenharmony_ci		ret = 0;
87162306a36Sopenharmony_ci		memcpy(mac, sta->sta.addr, ETH_ALEN);
87262306a36Sopenharmony_ci		sta_set_sinfo(sta, sinfo, true);
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return ret;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
88162306a36Sopenharmony_ci				 int idx, struct survey_info *survey)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	return drv_get_survey(local, idx, survey);
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
88962306a36Sopenharmony_ci				 const u8 *mac, struct station_info *sinfo)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
89262306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
89362306a36Sopenharmony_ci	struct sta_info *sta;
89462306a36Sopenharmony_ci	int ret = -ENOENT;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	mutex_lock(&local->sta_mtx);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, mac);
89962306a36Sopenharmony_ci	if (sta) {
90062306a36Sopenharmony_ci		ret = 0;
90162306a36Sopenharmony_ci		sta_set_sinfo(sta, sinfo, true);
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return ret;
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_cistatic int ieee80211_set_monitor_channel(struct wiphy *wiphy,
91062306a36Sopenharmony_ci					 struct cfg80211_chan_def *chandef)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
91362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
91462306a36Sopenharmony_ci	int ret = 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
91762306a36Sopenharmony_ci		return 0;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (local->use_chanctx) {
92062306a36Sopenharmony_ci		sdata = wiphy_dereference(local->hw.wiphy,
92162306a36Sopenharmony_ci					  local->monitor_sdata);
92262306a36Sopenharmony_ci		if (sdata) {
92362306a36Sopenharmony_ci			sdata_lock(sdata);
92462306a36Sopenharmony_ci			mutex_lock(&local->mtx);
92562306a36Sopenharmony_ci			ieee80211_link_release_channel(&sdata->deflink);
92662306a36Sopenharmony_ci			ret = ieee80211_link_use_channel(&sdata->deflink,
92762306a36Sopenharmony_ci							 chandef,
92862306a36Sopenharmony_ci							 IEEE80211_CHANCTX_EXCLUSIVE);
92962306a36Sopenharmony_ci			mutex_unlock(&local->mtx);
93062306a36Sopenharmony_ci			sdata_unlock(sdata);
93162306a36Sopenharmony_ci		}
93262306a36Sopenharmony_ci	} else {
93362306a36Sopenharmony_ci		mutex_lock(&local->mtx);
93462306a36Sopenharmony_ci		if (local->open_count == local->monitors) {
93562306a36Sopenharmony_ci			local->_oper_chandef = *chandef;
93662306a36Sopenharmony_ci			ieee80211_hw_config(local, 0);
93762306a36Sopenharmony_ci		}
93862306a36Sopenharmony_ci		mutex_unlock(&local->mtx);
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (ret == 0)
94262306a36Sopenharmony_ci		local->monitor_chandef = *chandef;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	return ret;
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic int
94862306a36Sopenharmony_ciieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
94962306a36Sopenharmony_ci			 const u8 *resp, size_t resp_len,
95062306a36Sopenharmony_ci			 const struct ieee80211_csa_settings *csa,
95162306a36Sopenharmony_ci			 const struct ieee80211_color_change_settings *cca,
95262306a36Sopenharmony_ci			 struct ieee80211_link_data *link)
95362306a36Sopenharmony_ci{
95462306a36Sopenharmony_ci	struct probe_resp *new, *old;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	if (!resp || !resp_len)
95762306a36Sopenharmony_ci		return 1;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	old = sdata_dereference(link->u.ap.probe_resp, sdata);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
96262306a36Sopenharmony_ci	if (!new)
96362306a36Sopenharmony_ci		return -ENOMEM;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	new->len = resp_len;
96662306a36Sopenharmony_ci	memcpy(new->data, resp, resp_len);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (csa)
96962306a36Sopenharmony_ci		memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_presp,
97062306a36Sopenharmony_ci		       csa->n_counter_offsets_presp *
97162306a36Sopenharmony_ci		       sizeof(new->cntdwn_counter_offsets[0]));
97262306a36Sopenharmony_ci	else if (cca)
97362306a36Sopenharmony_ci		new->cntdwn_counter_offsets[0] = cca->counter_offset_presp;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	rcu_assign_pointer(link->u.ap.probe_resp, new);
97662306a36Sopenharmony_ci	if (old)
97762306a36Sopenharmony_ci		kfree_rcu(old, rcu_head);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	return 0;
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
98362306a36Sopenharmony_ci					struct cfg80211_fils_discovery *params,
98462306a36Sopenharmony_ci					struct ieee80211_link_data *link,
98562306a36Sopenharmony_ci					struct ieee80211_bss_conf *link_conf)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct fils_discovery_data *new, *old = NULL;
98862306a36Sopenharmony_ci	struct ieee80211_fils_discovery *fd;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (!params->tmpl || !params->tmpl_len)
99162306a36Sopenharmony_ci		return -EINVAL;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	fd = &link_conf->fils_discovery;
99462306a36Sopenharmony_ci	fd->min_interval = params->min_interval;
99562306a36Sopenharmony_ci	fd->max_interval = params->max_interval;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	old = sdata_dereference(link->u.ap.fils_discovery, sdata);
99862306a36Sopenharmony_ci	new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
99962306a36Sopenharmony_ci	if (!new)
100062306a36Sopenharmony_ci		return -ENOMEM;
100162306a36Sopenharmony_ci	new->len = params->tmpl_len;
100262306a36Sopenharmony_ci	memcpy(new->data, params->tmpl, params->tmpl_len);
100362306a36Sopenharmony_ci	rcu_assign_pointer(link->u.ap.fils_discovery, new);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	if (old)
100662306a36Sopenharmony_ci		kfree_rcu(old, rcu_head);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic int
101262306a36Sopenharmony_ciieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
101362306a36Sopenharmony_ci				     struct cfg80211_unsol_bcast_probe_resp *params,
101462306a36Sopenharmony_ci				     struct ieee80211_link_data *link,
101562306a36Sopenharmony_ci				     struct ieee80211_bss_conf *link_conf)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct unsol_bcast_probe_resp_data *new, *old = NULL;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (!params->tmpl || !params->tmpl_len)
102062306a36Sopenharmony_ci		return -EINVAL;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata);
102362306a36Sopenharmony_ci	new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
102462306a36Sopenharmony_ci	if (!new)
102562306a36Sopenharmony_ci		return -ENOMEM;
102662306a36Sopenharmony_ci	new->len = params->tmpl_len;
102762306a36Sopenharmony_ci	memcpy(new->data, params->tmpl, params->tmpl_len);
102862306a36Sopenharmony_ci	rcu_assign_pointer(link->u.ap.unsol_bcast_probe_resp, new);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (old)
103162306a36Sopenharmony_ci		kfree_rcu(old, rcu_head);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	link_conf->unsol_bcast_probe_resp_interval = params->interval;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	return 0;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int ieee80211_set_ftm_responder_params(
103962306a36Sopenharmony_ci				struct ieee80211_sub_if_data *sdata,
104062306a36Sopenharmony_ci				const u8 *lci, size_t lci_len,
104162306a36Sopenharmony_ci				const u8 *civicloc, size_t civicloc_len,
104262306a36Sopenharmony_ci				struct ieee80211_bss_conf *link_conf)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	struct ieee80211_ftm_responder_params *new, *old;
104562306a36Sopenharmony_ci	u8 *pos;
104662306a36Sopenharmony_ci	int len;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (!lci_len && !civicloc_len)
104962306a36Sopenharmony_ci		return 0;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	old = link_conf->ftmr_params;
105262306a36Sopenharmony_ci	len = lci_len + civicloc_len;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	new = kzalloc(sizeof(*new) + len, GFP_KERNEL);
105562306a36Sopenharmony_ci	if (!new)
105662306a36Sopenharmony_ci		return -ENOMEM;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	pos = (u8 *)(new + 1);
105962306a36Sopenharmony_ci	if (lci_len) {
106062306a36Sopenharmony_ci		new->lci_len = lci_len;
106162306a36Sopenharmony_ci		new->lci = pos;
106262306a36Sopenharmony_ci		memcpy(pos, lci, lci_len);
106362306a36Sopenharmony_ci		pos += lci_len;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (civicloc_len) {
106762306a36Sopenharmony_ci		new->civicloc_len = civicloc_len;
106862306a36Sopenharmony_ci		new->civicloc = pos;
106962306a36Sopenharmony_ci		memcpy(pos, civicloc, civicloc_len);
107062306a36Sopenharmony_ci		pos += civicloc_len;
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	link_conf->ftmr_params = new;
107462306a36Sopenharmony_ci	kfree(old);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	return 0;
107762306a36Sopenharmony_ci}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic int
108062306a36Sopenharmony_ciieee80211_copy_mbssid_beacon(u8 *pos, struct cfg80211_mbssid_elems *dst,
108162306a36Sopenharmony_ci			     struct cfg80211_mbssid_elems *src)
108262306a36Sopenharmony_ci{
108362306a36Sopenharmony_ci	int i, offset = 0;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	for (i = 0; i < src->cnt; i++) {
108662306a36Sopenharmony_ci		memcpy(pos + offset, src->elem[i].data, src->elem[i].len);
108762306a36Sopenharmony_ci		dst->elem[i].len = src->elem[i].len;
108862306a36Sopenharmony_ci		dst->elem[i].data = pos + offset;
108962306a36Sopenharmony_ci		offset += dst->elem[i].len;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci	dst->cnt = src->cnt;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	return offset;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_cistatic int
109762306a36Sopenharmony_ciieee80211_copy_rnr_beacon(u8 *pos, struct cfg80211_rnr_elems *dst,
109862306a36Sopenharmony_ci			  struct cfg80211_rnr_elems *src)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	int i, offset = 0;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	for (i = 0; i < src->cnt; i++) {
110362306a36Sopenharmony_ci		memcpy(pos + offset, src->elem[i].data, src->elem[i].len);
110462306a36Sopenharmony_ci		dst->elem[i].len = src->elem[i].len;
110562306a36Sopenharmony_ci		dst->elem[i].data = pos + offset;
110662306a36Sopenharmony_ci		offset += dst->elem[i].len;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci	dst->cnt = src->cnt;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	return offset;
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic int
111462306a36Sopenharmony_ciieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
111562306a36Sopenharmony_ci			struct ieee80211_link_data *link,
111662306a36Sopenharmony_ci			struct cfg80211_beacon_data *params,
111762306a36Sopenharmony_ci			const struct ieee80211_csa_settings *csa,
111862306a36Sopenharmony_ci			const struct ieee80211_color_change_settings *cca,
111962306a36Sopenharmony_ci			u64 *changed)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	struct cfg80211_mbssid_elems *mbssid = NULL;
112262306a36Sopenharmony_ci	struct cfg80211_rnr_elems *rnr = NULL;
112362306a36Sopenharmony_ci	struct beacon_data *new, *old;
112462306a36Sopenharmony_ci	int new_head_len, new_tail_len;
112562306a36Sopenharmony_ci	int size, err;
112662306a36Sopenharmony_ci	u64 _changed = BSS_CHANGED_BEACON;
112762306a36Sopenharmony_ci	struct ieee80211_bss_conf *link_conf = link->conf;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	old = sdata_dereference(link->u.ap.beacon, sdata);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* Need to have a beacon head if we don't have one yet */
113262306a36Sopenharmony_ci	if (!params->head && !old)
113362306a36Sopenharmony_ci		return -EINVAL;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	/* new or old head? */
113662306a36Sopenharmony_ci	if (params->head)
113762306a36Sopenharmony_ci		new_head_len = params->head_len;
113862306a36Sopenharmony_ci	else
113962306a36Sopenharmony_ci		new_head_len = old->head_len;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/* new or old tail? */
114262306a36Sopenharmony_ci	if (params->tail || !old)
114362306a36Sopenharmony_ci		/* params->tail_len will be zero for !params->tail */
114462306a36Sopenharmony_ci		new_tail_len = params->tail_len;
114562306a36Sopenharmony_ci	else
114662306a36Sopenharmony_ci		new_tail_len = old->tail_len;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	size = sizeof(*new) + new_head_len + new_tail_len;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* new or old multiple BSSID elements? */
115162306a36Sopenharmony_ci	if (params->mbssid_ies) {
115262306a36Sopenharmony_ci		mbssid = params->mbssid_ies;
115362306a36Sopenharmony_ci		size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
115462306a36Sopenharmony_ci		if (params->rnr_ies) {
115562306a36Sopenharmony_ci			rnr = params->rnr_ies;
115662306a36Sopenharmony_ci			size += struct_size(new->rnr_ies, elem, rnr->cnt);
115762306a36Sopenharmony_ci		}
115862306a36Sopenharmony_ci		size += ieee80211_get_mbssid_beacon_len(mbssid, rnr,
115962306a36Sopenharmony_ci							mbssid->cnt);
116062306a36Sopenharmony_ci	} else if (old && old->mbssid_ies) {
116162306a36Sopenharmony_ci		mbssid = old->mbssid_ies;
116262306a36Sopenharmony_ci		size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
116362306a36Sopenharmony_ci		if (old && old->rnr_ies) {
116462306a36Sopenharmony_ci			rnr = old->rnr_ies;
116562306a36Sopenharmony_ci			size += struct_size(new->rnr_ies, elem, rnr->cnt);
116662306a36Sopenharmony_ci		}
116762306a36Sopenharmony_ci		size += ieee80211_get_mbssid_beacon_len(mbssid, rnr,
116862306a36Sopenharmony_ci							mbssid->cnt);
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	new = kzalloc(size, GFP_KERNEL);
117262306a36Sopenharmony_ci	if (!new)
117362306a36Sopenharmony_ci		return -ENOMEM;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	/* start filling the new info now */
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/*
117862306a36Sopenharmony_ci	 * pointers go into the block we allocated,
117962306a36Sopenharmony_ci	 * memory is | beacon_data | head | tail | mbssid_ies | rnr_ies
118062306a36Sopenharmony_ci	 */
118162306a36Sopenharmony_ci	new->head = ((u8 *) new) + sizeof(*new);
118262306a36Sopenharmony_ci	new->tail = new->head + new_head_len;
118362306a36Sopenharmony_ci	new->head_len = new_head_len;
118462306a36Sopenharmony_ci	new->tail_len = new_tail_len;
118562306a36Sopenharmony_ci	/* copy in optional mbssid_ies */
118662306a36Sopenharmony_ci	if (mbssid) {
118762306a36Sopenharmony_ci		u8 *pos = new->tail + new->tail_len;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci		new->mbssid_ies = (void *)pos;
119062306a36Sopenharmony_ci		pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
119162306a36Sopenharmony_ci		pos += ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies,
119262306a36Sopenharmony_ci						    mbssid);
119362306a36Sopenharmony_ci		if (rnr) {
119462306a36Sopenharmony_ci			new->rnr_ies = (void *)pos;
119562306a36Sopenharmony_ci			pos += struct_size(new->rnr_ies, elem, rnr->cnt);
119662306a36Sopenharmony_ci			ieee80211_copy_rnr_beacon(pos, new->rnr_ies, rnr);
119762306a36Sopenharmony_ci		}
119862306a36Sopenharmony_ci		/* update bssid_indicator */
119962306a36Sopenharmony_ci		link_conf->bssid_indicator =
120062306a36Sopenharmony_ci			ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	if (csa) {
120462306a36Sopenharmony_ci		new->cntdwn_current_counter = csa->count;
120562306a36Sopenharmony_ci		memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_beacon,
120662306a36Sopenharmony_ci		       csa->n_counter_offsets_beacon *
120762306a36Sopenharmony_ci		       sizeof(new->cntdwn_counter_offsets[0]));
120862306a36Sopenharmony_ci	} else if (cca) {
120962306a36Sopenharmony_ci		new->cntdwn_current_counter = cca->count;
121062306a36Sopenharmony_ci		new->cntdwn_counter_offsets[0] = cca->counter_offset_beacon;
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	/* copy in head */
121462306a36Sopenharmony_ci	if (params->head)
121562306a36Sopenharmony_ci		memcpy(new->head, params->head, new_head_len);
121662306a36Sopenharmony_ci	else
121762306a36Sopenharmony_ci		memcpy(new->head, old->head, new_head_len);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	/* copy in optional tail */
122062306a36Sopenharmony_ci	if (params->tail)
122162306a36Sopenharmony_ci		memcpy(new->tail, params->tail, new_tail_len);
122262306a36Sopenharmony_ci	else
122362306a36Sopenharmony_ci		if (old)
122462306a36Sopenharmony_ci			memcpy(new->tail, old->tail, new_tail_len);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	err = ieee80211_set_probe_resp(sdata, params->probe_resp,
122762306a36Sopenharmony_ci				       params->probe_resp_len, csa, cca, link);
122862306a36Sopenharmony_ci	if (err < 0) {
122962306a36Sopenharmony_ci		kfree(new);
123062306a36Sopenharmony_ci		return err;
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci	if (err == 0)
123362306a36Sopenharmony_ci		_changed |= BSS_CHANGED_AP_PROBE_RESP;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if (params->ftm_responder != -1) {
123662306a36Sopenharmony_ci		link_conf->ftm_responder = params->ftm_responder;
123762306a36Sopenharmony_ci		err = ieee80211_set_ftm_responder_params(sdata,
123862306a36Sopenharmony_ci							 params->lci,
123962306a36Sopenharmony_ci							 params->lci_len,
124062306a36Sopenharmony_ci							 params->civicloc,
124162306a36Sopenharmony_ci							 params->civicloc_len,
124262306a36Sopenharmony_ci							 link_conf);
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci		if (err < 0) {
124562306a36Sopenharmony_ci			kfree(new);
124662306a36Sopenharmony_ci			return err;
124762306a36Sopenharmony_ci		}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		_changed |= BSS_CHANGED_FTM_RESPONDER;
125062306a36Sopenharmony_ci	}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	rcu_assign_pointer(link->u.ap.beacon, new);
125362306a36Sopenharmony_ci	sdata->u.ap.active = true;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	if (old)
125662306a36Sopenharmony_ci		kfree_rcu(old, rcu_head);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	*changed |= _changed;
125962306a36Sopenharmony_ci	return 0;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
126362306a36Sopenharmony_ci			      struct cfg80211_ap_settings *params)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
126662306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
126762306a36Sopenharmony_ci	struct beacon_data *old;
126862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *vlan;
126962306a36Sopenharmony_ci	u64 changed = BSS_CHANGED_BEACON_INT |
127062306a36Sopenharmony_ci		      BSS_CHANGED_BEACON_ENABLED |
127162306a36Sopenharmony_ci		      BSS_CHANGED_BEACON |
127262306a36Sopenharmony_ci		      BSS_CHANGED_P2P_PS |
127362306a36Sopenharmony_ci		      BSS_CHANGED_TXPOWER |
127462306a36Sopenharmony_ci		      BSS_CHANGED_TWT;
127562306a36Sopenharmony_ci	int i, err;
127662306a36Sopenharmony_ci	int prev_beacon_int;
127762306a36Sopenharmony_ci	unsigned int link_id = params->beacon.link_id;
127862306a36Sopenharmony_ci	struct ieee80211_link_data *link;
127962306a36Sopenharmony_ci	struct ieee80211_bss_conf *link_conf;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	link = sdata_dereference(sdata->link[link_id], sdata);
128262306a36Sopenharmony_ci	if (!link)
128362306a36Sopenharmony_ci		return -ENOLINK;
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	link_conf = link->conf;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	old = sdata_dereference(link->u.ap.beacon, sdata);
128862306a36Sopenharmony_ci	if (old)
128962306a36Sopenharmony_ci		return -EALREADY;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (params->smps_mode != NL80211_SMPS_OFF)
129262306a36Sopenharmony_ci		return -ENOTSUPP;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	link->smps_mode = IEEE80211_SMPS_OFF;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	link->needed_rx_chains = sdata->local->rx_chains;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	prev_beacon_int = link_conf->beacon_int;
129962306a36Sopenharmony_ci	link_conf->beacon_int = params->beacon_interval;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (params->ht_cap)
130262306a36Sopenharmony_ci		link_conf->ht_ldpc =
130362306a36Sopenharmony_ci			params->ht_cap->cap_info &
130462306a36Sopenharmony_ci				cpu_to_le16(IEEE80211_HT_CAP_LDPC_CODING);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	if (params->vht_cap) {
130762306a36Sopenharmony_ci		link_conf->vht_ldpc =
130862306a36Sopenharmony_ci			params->vht_cap->vht_cap_info &
130962306a36Sopenharmony_ci				cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC);
131062306a36Sopenharmony_ci		link_conf->vht_su_beamformer =
131162306a36Sopenharmony_ci			params->vht_cap->vht_cap_info &
131262306a36Sopenharmony_ci				cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
131362306a36Sopenharmony_ci		link_conf->vht_su_beamformee =
131462306a36Sopenharmony_ci			params->vht_cap->vht_cap_info &
131562306a36Sopenharmony_ci				cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
131662306a36Sopenharmony_ci		link_conf->vht_mu_beamformer =
131762306a36Sopenharmony_ci			params->vht_cap->vht_cap_info &
131862306a36Sopenharmony_ci				cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
131962306a36Sopenharmony_ci		link_conf->vht_mu_beamformee =
132062306a36Sopenharmony_ci			params->vht_cap->vht_cap_info &
132162306a36Sopenharmony_ci				cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if (params->he_cap && params->he_oper) {
132562306a36Sopenharmony_ci		link_conf->he_support = true;
132662306a36Sopenharmony_ci		link_conf->htc_trig_based_pkt_ext =
132762306a36Sopenharmony_ci			le32_get_bits(params->he_oper->he_oper_params,
132862306a36Sopenharmony_ci			      IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK);
132962306a36Sopenharmony_ci		link_conf->frame_time_rts_th =
133062306a36Sopenharmony_ci			le32_get_bits(params->he_oper->he_oper_params,
133162306a36Sopenharmony_ci			      IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
133262306a36Sopenharmony_ci		changed |= BSS_CHANGED_HE_OBSS_PD;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci		if (params->beacon.he_bss_color.enabled)
133562306a36Sopenharmony_ci			changed |= BSS_CHANGED_HE_BSS_COLOR;
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	if (params->he_cap) {
133962306a36Sopenharmony_ci		link_conf->he_ldpc =
134062306a36Sopenharmony_ci			params->he_cap->phy_cap_info[1] &
134162306a36Sopenharmony_ci				IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
134262306a36Sopenharmony_ci		link_conf->he_su_beamformer =
134362306a36Sopenharmony_ci			params->he_cap->phy_cap_info[3] &
134462306a36Sopenharmony_ci				IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
134562306a36Sopenharmony_ci		link_conf->he_su_beamformee =
134662306a36Sopenharmony_ci			params->he_cap->phy_cap_info[4] &
134762306a36Sopenharmony_ci				IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
134862306a36Sopenharmony_ci		link_conf->he_mu_beamformer =
134962306a36Sopenharmony_ci			params->he_cap->phy_cap_info[4] &
135062306a36Sopenharmony_ci				IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
135162306a36Sopenharmony_ci		link_conf->he_full_ul_mumimo =
135262306a36Sopenharmony_ci			params->he_cap->phy_cap_info[2] &
135362306a36Sopenharmony_ci				IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	if (params->eht_cap) {
135762306a36Sopenharmony_ci		if (!link_conf->he_support)
135862306a36Sopenharmony_ci			return -EOPNOTSUPP;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci		link_conf->eht_support = true;
136162306a36Sopenharmony_ci		link_conf->eht_puncturing = params->punct_bitmap;
136262306a36Sopenharmony_ci		changed |= BSS_CHANGED_EHT_PUNCTURING;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci		link_conf->eht_su_beamformer =
136562306a36Sopenharmony_ci			params->eht_cap->fixed.phy_cap_info[0] &
136662306a36Sopenharmony_ci				IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER;
136762306a36Sopenharmony_ci		link_conf->eht_su_beamformee =
136862306a36Sopenharmony_ci			params->eht_cap->fixed.phy_cap_info[0] &
136962306a36Sopenharmony_ci				IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
137062306a36Sopenharmony_ci		link_conf->eht_mu_beamformer =
137162306a36Sopenharmony_ci			params->eht_cap->fixed.phy_cap_info[7] &
137262306a36Sopenharmony_ci				(IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
137362306a36Sopenharmony_ci				 IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ |
137462306a36Sopenharmony_ci				 IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ);
137562306a36Sopenharmony_ci	} else {
137662306a36Sopenharmony_ci		link_conf->eht_su_beamformer = false;
137762306a36Sopenharmony_ci		link_conf->eht_su_beamformee = false;
137862306a36Sopenharmony_ci		link_conf->eht_mu_beamformer = false;
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	if (sdata->vif.type == NL80211_IFTYPE_AP &&
138262306a36Sopenharmony_ci	    params->mbssid_config.tx_wdev) {
138362306a36Sopenharmony_ci		err = ieee80211_set_ap_mbssid_options(sdata,
138462306a36Sopenharmony_ci						      params->mbssid_config,
138562306a36Sopenharmony_ci						      link_conf);
138662306a36Sopenharmony_ci		if (err)
138762306a36Sopenharmony_ci			return err;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	mutex_lock(&local->mtx);
139162306a36Sopenharmony_ci	err = ieee80211_link_use_channel(link, &params->chandef,
139262306a36Sopenharmony_ci					 IEEE80211_CHANCTX_SHARED);
139362306a36Sopenharmony_ci	if (!err)
139462306a36Sopenharmony_ci		ieee80211_link_copy_chanctx_to_vlans(link, false);
139562306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
139662306a36Sopenharmony_ci	if (err) {
139762306a36Sopenharmony_ci		link_conf->beacon_int = prev_beacon_int;
139862306a36Sopenharmony_ci		return err;
139962306a36Sopenharmony_ci	}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	/*
140262306a36Sopenharmony_ci	 * Apply control port protocol, this allows us to
140362306a36Sopenharmony_ci	 * not encrypt dynamic WEP control frames.
140462306a36Sopenharmony_ci	 */
140562306a36Sopenharmony_ci	sdata->control_port_protocol = params->crypto.control_port_ethertype;
140662306a36Sopenharmony_ci	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
140762306a36Sopenharmony_ci	sdata->control_port_over_nl80211 =
140862306a36Sopenharmony_ci				params->crypto.control_port_over_nl80211;
140962306a36Sopenharmony_ci	sdata->control_port_no_preauth =
141062306a36Sopenharmony_ci				params->crypto.control_port_no_preauth;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
141362306a36Sopenharmony_ci		vlan->control_port_protocol =
141462306a36Sopenharmony_ci			params->crypto.control_port_ethertype;
141562306a36Sopenharmony_ci		vlan->control_port_no_encrypt =
141662306a36Sopenharmony_ci			params->crypto.control_port_no_encrypt;
141762306a36Sopenharmony_ci		vlan->control_port_over_nl80211 =
141862306a36Sopenharmony_ci			params->crypto.control_port_over_nl80211;
141962306a36Sopenharmony_ci		vlan->control_port_no_preauth =
142062306a36Sopenharmony_ci			params->crypto.control_port_no_preauth;
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	link_conf->dtim_period = params->dtim_period;
142462306a36Sopenharmony_ci	link_conf->enable_beacon = true;
142562306a36Sopenharmony_ci	link_conf->allow_p2p_go_ps = sdata->vif.p2p;
142662306a36Sopenharmony_ci	link_conf->twt_responder = params->twt_responder;
142762306a36Sopenharmony_ci	link_conf->he_obss_pd = params->he_obss_pd;
142862306a36Sopenharmony_ci	link_conf->he_bss_color = params->beacon.he_bss_color;
142962306a36Sopenharmony_ci	sdata->vif.cfg.s1g = params->chandef.chan->band ==
143062306a36Sopenharmony_ci				  NL80211_BAND_S1GHZ;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	sdata->vif.cfg.ssid_len = params->ssid_len;
143362306a36Sopenharmony_ci	if (params->ssid_len)
143462306a36Sopenharmony_ci		memcpy(sdata->vif.cfg.ssid, params->ssid,
143562306a36Sopenharmony_ci		       params->ssid_len);
143662306a36Sopenharmony_ci	link_conf->hidden_ssid =
143762306a36Sopenharmony_ci		(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	memset(&link_conf->p2p_noa_attr, 0,
144062306a36Sopenharmony_ci	       sizeof(link_conf->p2p_noa_attr));
144162306a36Sopenharmony_ci	link_conf->p2p_noa_attr.oppps_ctwindow =
144262306a36Sopenharmony_ci		params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
144362306a36Sopenharmony_ci	if (params->p2p_opp_ps)
144462306a36Sopenharmony_ci		link_conf->p2p_noa_attr.oppps_ctwindow |=
144562306a36Sopenharmony_ci					IEEE80211_P2P_OPPPS_ENABLE_BIT;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	sdata->beacon_rate_set = false;
144862306a36Sopenharmony_ci	if (wiphy_ext_feature_isset(local->hw.wiphy,
144962306a36Sopenharmony_ci				    NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) {
145062306a36Sopenharmony_ci		for (i = 0; i < NUM_NL80211_BANDS; i++) {
145162306a36Sopenharmony_ci			sdata->beacon_rateidx_mask[i] =
145262306a36Sopenharmony_ci				params->beacon_rate.control[i].legacy;
145362306a36Sopenharmony_ci			if (sdata->beacon_rateidx_mask[i])
145462306a36Sopenharmony_ci				sdata->beacon_rate_set = true;
145562306a36Sopenharmony_ci		}
145662306a36Sopenharmony_ci	}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
145962306a36Sopenharmony_ci		link_conf->beacon_tx_rate = params->beacon_rate;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	err = ieee80211_assign_beacon(sdata, link, &params->beacon, NULL, NULL,
146262306a36Sopenharmony_ci				      &changed);
146362306a36Sopenharmony_ci	if (err < 0)
146462306a36Sopenharmony_ci		goto error;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (params->fils_discovery.max_interval) {
146762306a36Sopenharmony_ci		err = ieee80211_set_fils_discovery(sdata,
146862306a36Sopenharmony_ci						   &params->fils_discovery,
146962306a36Sopenharmony_ci						   link, link_conf);
147062306a36Sopenharmony_ci		if (err < 0)
147162306a36Sopenharmony_ci			goto error;
147262306a36Sopenharmony_ci		changed |= BSS_CHANGED_FILS_DISCOVERY;
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	if (params->unsol_bcast_probe_resp.interval) {
147662306a36Sopenharmony_ci		err = ieee80211_set_unsol_bcast_probe_resp(sdata,
147762306a36Sopenharmony_ci							   &params->unsol_bcast_probe_resp,
147862306a36Sopenharmony_ci							   link, link_conf);
147962306a36Sopenharmony_ci		if (err < 0)
148062306a36Sopenharmony_ci			goto error;
148162306a36Sopenharmony_ci		changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	err = drv_start_ap(sdata->local, sdata, link_conf);
148562306a36Sopenharmony_ci	if (err) {
148662306a36Sopenharmony_ci		old = sdata_dereference(link->u.ap.beacon, sdata);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci		if (old)
148962306a36Sopenharmony_ci			kfree_rcu(old, rcu_head);
149062306a36Sopenharmony_ci		RCU_INIT_POINTER(link->u.ap.beacon, NULL);
149162306a36Sopenharmony_ci		sdata->u.ap.active = false;
149262306a36Sopenharmony_ci		goto error;
149362306a36Sopenharmony_ci	}
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	ieee80211_recalc_dtim(local, sdata);
149662306a36Sopenharmony_ci	ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_SSID);
149762306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, link, changed);
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	netif_carrier_on(dev);
150062306a36Sopenharmony_ci	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
150162306a36Sopenharmony_ci		netif_carrier_on(vlan->dev);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	return 0;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_cierror:
150662306a36Sopenharmony_ci	mutex_lock(&local->mtx);
150762306a36Sopenharmony_ci	ieee80211_link_release_channel(link);
150862306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	return err;
151162306a36Sopenharmony_ci}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_cistatic int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
151462306a36Sopenharmony_ci				   struct cfg80211_beacon_data *params)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
151762306a36Sopenharmony_ci	struct ieee80211_link_data *link;
151862306a36Sopenharmony_ci	struct beacon_data *old;
151962306a36Sopenharmony_ci	int err;
152062306a36Sopenharmony_ci	struct ieee80211_bss_conf *link_conf;
152162306a36Sopenharmony_ci	u64 changed = 0;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	sdata_assert_lock(sdata);
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	link = sdata_dereference(sdata->link[params->link_id], sdata);
152662306a36Sopenharmony_ci	if (!link)
152762306a36Sopenharmony_ci		return -ENOLINK;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	link_conf = link->conf;
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/* don't allow changing the beacon while a countdown is in place - offset
153262306a36Sopenharmony_ci	 * of channel switch counter may change
153362306a36Sopenharmony_ci	 */
153462306a36Sopenharmony_ci	if (link_conf->csa_active || link_conf->color_change_active)
153562306a36Sopenharmony_ci		return -EBUSY;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	old = sdata_dereference(link->u.ap.beacon, sdata);
153862306a36Sopenharmony_ci	if (!old)
153962306a36Sopenharmony_ci		return -ENOENT;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	err = ieee80211_assign_beacon(sdata, link, params, NULL, NULL,
154262306a36Sopenharmony_ci				      &changed);
154362306a36Sopenharmony_ci	if (err < 0)
154462306a36Sopenharmony_ci		return err;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	if (params->he_bss_color_valid &&
154762306a36Sopenharmony_ci	    params->he_bss_color.enabled != link_conf->he_bss_color.enabled) {
154862306a36Sopenharmony_ci		link_conf->he_bss_color.enabled = params->he_bss_color.enabled;
154962306a36Sopenharmony_ci		changed |= BSS_CHANGED_HE_BSS_COLOR;
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, link, changed);
155362306a36Sopenharmony_ci	return 0;
155462306a36Sopenharmony_ci}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_cistatic void ieee80211_free_next_beacon(struct ieee80211_link_data *link)
155762306a36Sopenharmony_ci{
155862306a36Sopenharmony_ci	if (!link->u.ap.next_beacon)
155962306a36Sopenharmony_ci		return;
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	kfree(link->u.ap.next_beacon->mbssid_ies);
156262306a36Sopenharmony_ci	kfree(link->u.ap.next_beacon->rnr_ies);
156362306a36Sopenharmony_ci	kfree(link->u.ap.next_beacon);
156462306a36Sopenharmony_ci	link->u.ap.next_beacon = NULL;
156562306a36Sopenharmony_ci}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_cistatic int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
156862306a36Sopenharmony_ci			     unsigned int link_id)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
157162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *vlan;
157262306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
157362306a36Sopenharmony_ci	struct beacon_data *old_beacon;
157462306a36Sopenharmony_ci	struct probe_resp *old_probe_resp;
157562306a36Sopenharmony_ci	struct fils_discovery_data *old_fils_discovery;
157662306a36Sopenharmony_ci	struct unsol_bcast_probe_resp_data *old_unsol_bcast_probe_resp;
157762306a36Sopenharmony_ci	struct cfg80211_chan_def chandef;
157862306a36Sopenharmony_ci	struct ieee80211_link_data *link =
157962306a36Sopenharmony_ci		sdata_dereference(sdata->link[link_id], sdata);
158062306a36Sopenharmony_ci	struct ieee80211_bss_conf *link_conf = link->conf;
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci	sdata_assert_lock(sdata);
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	old_beacon = sdata_dereference(link->u.ap.beacon, sdata);
158562306a36Sopenharmony_ci	if (!old_beacon)
158662306a36Sopenharmony_ci		return -ENOENT;
158762306a36Sopenharmony_ci	old_probe_resp = sdata_dereference(link->u.ap.probe_resp,
158862306a36Sopenharmony_ci					   sdata);
158962306a36Sopenharmony_ci	old_fils_discovery = sdata_dereference(link->u.ap.fils_discovery,
159062306a36Sopenharmony_ci					       sdata);
159162306a36Sopenharmony_ci	old_unsol_bcast_probe_resp =
159262306a36Sopenharmony_ci		sdata_dereference(link->u.ap.unsol_bcast_probe_resp,
159362306a36Sopenharmony_ci				  sdata);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	/* abort any running channel switch or color change */
159662306a36Sopenharmony_ci	mutex_lock(&local->mtx);
159762306a36Sopenharmony_ci	link_conf->csa_active = false;
159862306a36Sopenharmony_ci	link_conf->color_change_active = false;
159962306a36Sopenharmony_ci	if (link->csa_block_tx) {
160062306a36Sopenharmony_ci		ieee80211_wake_vif_queues(local, sdata,
160162306a36Sopenharmony_ci					  IEEE80211_QUEUE_STOP_REASON_CSA);
160262306a36Sopenharmony_ci		link->csa_block_tx = false;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	ieee80211_free_next_beacon(link);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/* turn off carrier for this interface and dependent VLANs */
161062306a36Sopenharmony_ci	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
161162306a36Sopenharmony_ci		netif_carrier_off(vlan->dev);
161262306a36Sopenharmony_ci	netif_carrier_off(dev);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	/* remove beacon and probe response */
161562306a36Sopenharmony_ci	sdata->u.ap.active = false;
161662306a36Sopenharmony_ci	RCU_INIT_POINTER(link->u.ap.beacon, NULL);
161762306a36Sopenharmony_ci	RCU_INIT_POINTER(link->u.ap.probe_resp, NULL);
161862306a36Sopenharmony_ci	RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
161962306a36Sopenharmony_ci	RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
162062306a36Sopenharmony_ci	kfree_rcu(old_beacon, rcu_head);
162162306a36Sopenharmony_ci	if (old_probe_resp)
162262306a36Sopenharmony_ci		kfree_rcu(old_probe_resp, rcu_head);
162362306a36Sopenharmony_ci	if (old_fils_discovery)
162462306a36Sopenharmony_ci		kfree_rcu(old_fils_discovery, rcu_head);
162562306a36Sopenharmony_ci	if (old_unsol_bcast_probe_resp)
162662306a36Sopenharmony_ci		kfree_rcu(old_unsol_bcast_probe_resp, rcu_head);
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	kfree(link_conf->ftmr_params);
162962306a36Sopenharmony_ci	link_conf->ftmr_params = NULL;
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	sdata->vif.mbssid_tx_vif = NULL;
163262306a36Sopenharmony_ci	link_conf->bssid_index = 0;
163362306a36Sopenharmony_ci	link_conf->nontransmitted = false;
163462306a36Sopenharmony_ci	link_conf->ema_ap = false;
163562306a36Sopenharmony_ci	link_conf->bssid_indicator = 0;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	__sta_info_flush(sdata, true);
163862306a36Sopenharmony_ci	ieee80211_free_keys(sdata, true);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	link_conf->enable_beacon = false;
164162306a36Sopenharmony_ci	sdata->beacon_rate_set = false;
164262306a36Sopenharmony_ci	sdata->vif.cfg.ssid_len = 0;
164362306a36Sopenharmony_ci	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
164462306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, link,
164562306a36Sopenharmony_ci					  BSS_CHANGED_BEACON_ENABLED);
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci	if (sdata->wdev.cac_started) {
164862306a36Sopenharmony_ci		chandef = link_conf->chandef;
164962306a36Sopenharmony_ci		cancel_delayed_work_sync(&link->dfs_cac_timer_work);
165062306a36Sopenharmony_ci		cfg80211_cac_event(sdata->dev, &chandef,
165162306a36Sopenharmony_ci				   NL80211_RADAR_CAC_ABORTED,
165262306a36Sopenharmony_ci				   GFP_KERNEL);
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	drv_stop_ap(sdata->local, sdata, link_conf);
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	/* free all potentially still buffered bcast frames */
165862306a36Sopenharmony_ci	local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
165962306a36Sopenharmony_ci	ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	mutex_lock(&local->mtx);
166262306a36Sopenharmony_ci	ieee80211_link_copy_chanctx_to_vlans(link, true);
166362306a36Sopenharmony_ci	ieee80211_link_release_channel(link);
166462306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	return 0;
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic int sta_apply_auth_flags(struct ieee80211_local *local,
167062306a36Sopenharmony_ci				struct sta_info *sta,
167162306a36Sopenharmony_ci				u32 mask, u32 set)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	int ret;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
167662306a36Sopenharmony_ci	    set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
167762306a36Sopenharmony_ci	    !test_sta_flag(sta, WLAN_STA_AUTH)) {
167862306a36Sopenharmony_ci		ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
167962306a36Sopenharmony_ci		if (ret)
168062306a36Sopenharmony_ci			return ret;
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
168462306a36Sopenharmony_ci	    set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
168562306a36Sopenharmony_ci	    !test_sta_flag(sta, WLAN_STA_ASSOC)) {
168662306a36Sopenharmony_ci		/*
168762306a36Sopenharmony_ci		 * When peer becomes associated, init rate control as
168862306a36Sopenharmony_ci		 * well. Some drivers require rate control initialized
168962306a36Sopenharmony_ci		 * before drv_sta_state() is called.
169062306a36Sopenharmony_ci		 */
169162306a36Sopenharmony_ci		if (!test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
169262306a36Sopenharmony_ci			rate_control_rate_init(sta);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci		ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
169562306a36Sopenharmony_ci		if (ret)
169662306a36Sopenharmony_ci			return ret;
169762306a36Sopenharmony_ci	}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
170062306a36Sopenharmony_ci		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
170162306a36Sopenharmony_ci			ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
170262306a36Sopenharmony_ci		else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
170362306a36Sopenharmony_ci			ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
170462306a36Sopenharmony_ci		else
170562306a36Sopenharmony_ci			ret = 0;
170662306a36Sopenharmony_ci		if (ret)
170762306a36Sopenharmony_ci			return ret;
170862306a36Sopenharmony_ci	}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
171162306a36Sopenharmony_ci	    !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
171262306a36Sopenharmony_ci	    test_sta_flag(sta, WLAN_STA_ASSOC)) {
171362306a36Sopenharmony_ci		ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
171462306a36Sopenharmony_ci		if (ret)
171562306a36Sopenharmony_ci			return ret;
171662306a36Sopenharmony_ci	}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
171962306a36Sopenharmony_ci	    !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
172062306a36Sopenharmony_ci	    test_sta_flag(sta, WLAN_STA_AUTH)) {
172162306a36Sopenharmony_ci		ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
172262306a36Sopenharmony_ci		if (ret)
172362306a36Sopenharmony_ci			return ret;
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	return 0;
172762306a36Sopenharmony_ci}
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_cistatic void sta_apply_mesh_params(struct ieee80211_local *local,
173062306a36Sopenharmony_ci				  struct sta_info *sta,
173162306a36Sopenharmony_ci				  struct station_parameters *params)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
173462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = sta->sdata;
173562306a36Sopenharmony_ci	u64 changed = 0;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
173862306a36Sopenharmony_ci		switch (params->plink_state) {
173962306a36Sopenharmony_ci		case NL80211_PLINK_ESTAB:
174062306a36Sopenharmony_ci			if (sta->mesh->plink_state != NL80211_PLINK_ESTAB)
174162306a36Sopenharmony_ci				changed = mesh_plink_inc_estab_count(sdata);
174262306a36Sopenharmony_ci			sta->mesh->plink_state = params->plink_state;
174362306a36Sopenharmony_ci			sta->mesh->aid = params->peer_aid;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci			ieee80211_mps_sta_status_update(sta);
174662306a36Sopenharmony_ci			changed |= ieee80211_mps_set_sta_local_pm(sta,
174762306a36Sopenharmony_ci				      sdata->u.mesh.mshcfg.power_mode);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci			ewma_mesh_tx_rate_avg_init(&sta->mesh->tx_rate_avg);
175062306a36Sopenharmony_ci			/* init at low value */
175162306a36Sopenharmony_ci			ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg, 10);
175262306a36Sopenharmony_ci
175362306a36Sopenharmony_ci			break;
175462306a36Sopenharmony_ci		case NL80211_PLINK_LISTEN:
175562306a36Sopenharmony_ci		case NL80211_PLINK_BLOCKED:
175662306a36Sopenharmony_ci		case NL80211_PLINK_OPN_SNT:
175762306a36Sopenharmony_ci		case NL80211_PLINK_OPN_RCVD:
175862306a36Sopenharmony_ci		case NL80211_PLINK_CNF_RCVD:
175962306a36Sopenharmony_ci		case NL80211_PLINK_HOLDING:
176062306a36Sopenharmony_ci			if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
176162306a36Sopenharmony_ci				changed = mesh_plink_dec_estab_count(sdata);
176262306a36Sopenharmony_ci			sta->mesh->plink_state = params->plink_state;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci			ieee80211_mps_sta_status_update(sta);
176562306a36Sopenharmony_ci			changed |= ieee80211_mps_set_sta_local_pm(sta,
176662306a36Sopenharmony_ci					NL80211_MESH_POWER_UNKNOWN);
176762306a36Sopenharmony_ci			break;
176862306a36Sopenharmony_ci		default:
176962306a36Sopenharmony_ci			/*  nothing  */
177062306a36Sopenharmony_ci			break;
177162306a36Sopenharmony_ci		}
177262306a36Sopenharmony_ci	}
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	switch (params->plink_action) {
177562306a36Sopenharmony_ci	case NL80211_PLINK_ACTION_NO_ACTION:
177662306a36Sopenharmony_ci		/* nothing */
177762306a36Sopenharmony_ci		break;
177862306a36Sopenharmony_ci	case NL80211_PLINK_ACTION_OPEN:
177962306a36Sopenharmony_ci		changed |= mesh_plink_open(sta);
178062306a36Sopenharmony_ci		break;
178162306a36Sopenharmony_ci	case NL80211_PLINK_ACTION_BLOCK:
178262306a36Sopenharmony_ci		changed |= mesh_plink_block(sta);
178362306a36Sopenharmony_ci		break;
178462306a36Sopenharmony_ci	}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	if (params->local_pm)
178762306a36Sopenharmony_ci		changed |= ieee80211_mps_set_sta_local_pm(sta,
178862306a36Sopenharmony_ci							  params->local_pm);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	ieee80211_mbss_info_change_notify(sdata, changed);
179162306a36Sopenharmony_ci#endif
179262306a36Sopenharmony_ci}
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_cistatic int sta_link_apply_parameters(struct ieee80211_local *local,
179562306a36Sopenharmony_ci				     struct sta_info *sta, bool new_link,
179662306a36Sopenharmony_ci				     struct link_station_parameters *params)
179762306a36Sopenharmony_ci{
179862306a36Sopenharmony_ci	int ret = 0;
179962306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
180062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = sta->sdata;
180162306a36Sopenharmony_ci	u32 link_id = params->link_id < 0 ? 0 : params->link_id;
180262306a36Sopenharmony_ci	struct ieee80211_link_data *link =
180362306a36Sopenharmony_ci		sdata_dereference(sdata->link[link_id], sdata);
180462306a36Sopenharmony_ci	struct link_sta_info *link_sta =
180562306a36Sopenharmony_ci		rcu_dereference_protected(sta->link[link_id],
180662306a36Sopenharmony_ci					  lockdep_is_held(&local->sta_mtx));
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	/*
180962306a36Sopenharmony_ci	 * If there are no changes, then accept a link that exist,
181062306a36Sopenharmony_ci	 * unless it's a new link.
181162306a36Sopenharmony_ci	 */
181262306a36Sopenharmony_ci	if (params->link_id >= 0 && !new_link &&
181362306a36Sopenharmony_ci	    !params->link_mac && !params->txpwr_set &&
181462306a36Sopenharmony_ci	    !params->supported_rates_len &&
181562306a36Sopenharmony_ci	    !params->ht_capa && !params->vht_capa &&
181662306a36Sopenharmony_ci	    !params->he_capa && !params->eht_capa &&
181762306a36Sopenharmony_ci	    !params->opmode_notif_used)
181862306a36Sopenharmony_ci		return 0;
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	if (!link || !link_sta)
182162306a36Sopenharmony_ci		return -EINVAL;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	sband = ieee80211_get_link_sband(link);
182462306a36Sopenharmony_ci	if (!sband)
182562306a36Sopenharmony_ci		return -EINVAL;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	if (params->link_mac) {
182862306a36Sopenharmony_ci		if (new_link) {
182962306a36Sopenharmony_ci			memcpy(link_sta->addr, params->link_mac, ETH_ALEN);
183062306a36Sopenharmony_ci			memcpy(link_sta->pub->addr, params->link_mac, ETH_ALEN);
183162306a36Sopenharmony_ci		} else if (!ether_addr_equal(link_sta->addr,
183262306a36Sopenharmony_ci					     params->link_mac)) {
183362306a36Sopenharmony_ci			return -EINVAL;
183462306a36Sopenharmony_ci		}
183562306a36Sopenharmony_ci	} else if (new_link) {
183662306a36Sopenharmony_ci		return -EINVAL;
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	if (params->txpwr_set) {
184062306a36Sopenharmony_ci		link_sta->pub->txpwr.type = params->txpwr.type;
184162306a36Sopenharmony_ci		if (params->txpwr.type == NL80211_TX_POWER_LIMITED)
184262306a36Sopenharmony_ci			link_sta->pub->txpwr.power = params->txpwr.power;
184362306a36Sopenharmony_ci		ret = drv_sta_set_txpwr(local, sdata, sta);
184462306a36Sopenharmony_ci		if (ret)
184562306a36Sopenharmony_ci			return ret;
184662306a36Sopenharmony_ci	}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	if (params->supported_rates &&
184962306a36Sopenharmony_ci	    params->supported_rates_len) {
185062306a36Sopenharmony_ci		ieee80211_parse_bitrates(link->conf->chandef.width,
185162306a36Sopenharmony_ci					 sband, params->supported_rates,
185262306a36Sopenharmony_ci					 params->supported_rates_len,
185362306a36Sopenharmony_ci					 &link_sta->pub->supp_rates[sband->band]);
185462306a36Sopenharmony_ci	}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	if (params->ht_capa)
185762306a36Sopenharmony_ci		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
185862306a36Sopenharmony_ci						  params->ht_capa, link_sta);
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	/* VHT can override some HT caps such as the A-MSDU max length */
186162306a36Sopenharmony_ci	if (params->vht_capa)
186262306a36Sopenharmony_ci		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
186362306a36Sopenharmony_ci						    params->vht_capa, NULL,
186462306a36Sopenharmony_ci						    link_sta);
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	if (params->he_capa)
186762306a36Sopenharmony_ci		ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
186862306a36Sopenharmony_ci						  (void *)params->he_capa,
186962306a36Sopenharmony_ci						  params->he_capa_len,
187062306a36Sopenharmony_ci						  (void *)params->he_6ghz_capa,
187162306a36Sopenharmony_ci						  link_sta);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (params->he_capa && params->eht_capa)
187462306a36Sopenharmony_ci		ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband,
187562306a36Sopenharmony_ci						    (u8 *)params->he_capa,
187662306a36Sopenharmony_ci						    params->he_capa_len,
187762306a36Sopenharmony_ci						    params->eht_capa,
187862306a36Sopenharmony_ci						    params->eht_capa_len,
187962306a36Sopenharmony_ci						    link_sta);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	if (params->opmode_notif_used) {
188262306a36Sopenharmony_ci		/* returned value is only needed for rc update, but the
188362306a36Sopenharmony_ci		 * rc isn't initialized here yet, so ignore it
188462306a36Sopenharmony_ci		 */
188562306a36Sopenharmony_ci		__ieee80211_vht_handle_opmode(sdata, link_sta,
188662306a36Sopenharmony_ci					      params->opmode_notif,
188762306a36Sopenharmony_ci					      sband->band);
188862306a36Sopenharmony_ci	}
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	ieee80211_sta_set_rx_nss(link_sta);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	return ret;
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic int sta_apply_parameters(struct ieee80211_local *local,
189662306a36Sopenharmony_ci				struct sta_info *sta,
189762306a36Sopenharmony_ci				struct station_parameters *params)
189862306a36Sopenharmony_ci{
189962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = sta->sdata;
190062306a36Sopenharmony_ci	u32 mask, set;
190162306a36Sopenharmony_ci	int ret = 0;
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_ci	mask = params->sta_flags_mask;
190462306a36Sopenharmony_ci	set = params->sta_flags_set;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	if (ieee80211_vif_is_mesh(&sdata->vif)) {
190762306a36Sopenharmony_ci		/*
190862306a36Sopenharmony_ci		 * In mesh mode, ASSOCIATED isn't part of the nl80211
190962306a36Sopenharmony_ci		 * API but must follow AUTHENTICATED for driver state.
191062306a36Sopenharmony_ci		 */
191162306a36Sopenharmony_ci		if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED))
191262306a36Sopenharmony_ci			mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
191362306a36Sopenharmony_ci		if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
191462306a36Sopenharmony_ci			set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
191562306a36Sopenharmony_ci	} else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
191662306a36Sopenharmony_ci		/*
191762306a36Sopenharmony_ci		 * TDLS -- everything follows authorized, but
191862306a36Sopenharmony_ci		 * only becoming authorized is possible, not
191962306a36Sopenharmony_ci		 * going back
192062306a36Sopenharmony_ci		 */
192162306a36Sopenharmony_ci		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
192262306a36Sopenharmony_ci			set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
192362306a36Sopenharmony_ci			       BIT(NL80211_STA_FLAG_ASSOCIATED);
192462306a36Sopenharmony_ci			mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
192562306a36Sopenharmony_ci				BIT(NL80211_STA_FLAG_ASSOCIATED);
192662306a36Sopenharmony_ci		}
192762306a36Sopenharmony_ci	}
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_WME) &&
193062306a36Sopenharmony_ci	    local->hw.queues >= IEEE80211_NUM_ACS)
193162306a36Sopenharmony_ci		sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	/* auth flags will be set later for TDLS,
193462306a36Sopenharmony_ci	 * and for unassociated stations that move to associated */
193562306a36Sopenharmony_ci	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
193662306a36Sopenharmony_ci	    !((mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
193762306a36Sopenharmony_ci	      (set & BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
193862306a36Sopenharmony_ci		ret = sta_apply_auth_flags(local, sta, mask, set);
193962306a36Sopenharmony_ci		if (ret)
194062306a36Sopenharmony_ci			return ret;
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
194462306a36Sopenharmony_ci		if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
194562306a36Sopenharmony_ci			set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
194662306a36Sopenharmony_ci		else
194762306a36Sopenharmony_ci			clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
194862306a36Sopenharmony_ci	}
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_MFP)) {
195162306a36Sopenharmony_ci		sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
195262306a36Sopenharmony_ci		if (set & BIT(NL80211_STA_FLAG_MFP))
195362306a36Sopenharmony_ci			set_sta_flag(sta, WLAN_STA_MFP);
195462306a36Sopenharmony_ci		else
195562306a36Sopenharmony_ci			clear_sta_flag(sta, WLAN_STA_MFP);
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
195962306a36Sopenharmony_ci		if (set & BIT(NL80211_STA_FLAG_TDLS_PEER))
196062306a36Sopenharmony_ci			set_sta_flag(sta, WLAN_STA_TDLS_PEER);
196162306a36Sopenharmony_ci		else
196262306a36Sopenharmony_ci			clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
196362306a36Sopenharmony_ci	}
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	/* mark TDLS channel switch support, if the AP allows it */
196662306a36Sopenharmony_ci	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
196762306a36Sopenharmony_ci	    !sdata->deflink.u.mgd.tdls_chan_switch_prohibited &&
196862306a36Sopenharmony_ci	    params->ext_capab_len >= 4 &&
196962306a36Sopenharmony_ci	    params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
197062306a36Sopenharmony_ci		set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
197362306a36Sopenharmony_ci	    !sdata->u.mgd.tdls_wider_bw_prohibited &&
197462306a36Sopenharmony_ci	    ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
197562306a36Sopenharmony_ci	    params->ext_capab_len >= 8 &&
197662306a36Sopenharmony_ci	    params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
197762306a36Sopenharmony_ci		set_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW);
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
198062306a36Sopenharmony_ci		sta->sta.uapsd_queues = params->uapsd_queues;
198162306a36Sopenharmony_ci		sta->sta.max_sp = params->max_sp;
198262306a36Sopenharmony_ci	}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_ci	ieee80211_sta_set_max_amsdu_subframes(sta, params->ext_capab,
198562306a36Sopenharmony_ci					      params->ext_capab_len);
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	/*
198862306a36Sopenharmony_ci	 * cfg80211 validates this (1-2007) and allows setting the AID
198962306a36Sopenharmony_ci	 * only when creating a new station entry
199062306a36Sopenharmony_ci	 */
199162306a36Sopenharmony_ci	if (params->aid)
199262306a36Sopenharmony_ci		sta->sta.aid = params->aid;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	/*
199562306a36Sopenharmony_ci	 * Some of the following updates would be racy if called on an
199662306a36Sopenharmony_ci	 * existing station, via ieee80211_change_station(). However,
199762306a36Sopenharmony_ci	 * all such changes are rejected by cfg80211 except for updates
199862306a36Sopenharmony_ci	 * changing the supported rates on an existing but not yet used
199962306a36Sopenharmony_ci	 * TDLS peer.
200062306a36Sopenharmony_ci	 */
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	if (params->listen_interval >= 0)
200362306a36Sopenharmony_ci		sta->listen_interval = params->listen_interval;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	ret = sta_link_apply_parameters(local, sta, false,
200662306a36Sopenharmony_ci					&params->link_sta_params);
200762306a36Sopenharmony_ci	if (ret)
200862306a36Sopenharmony_ci		return ret;
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	if (params->support_p2p_ps >= 0)
201162306a36Sopenharmony_ci		sta->sta.support_p2p_ps = params->support_p2p_ps;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	if (ieee80211_vif_is_mesh(&sdata->vif))
201462306a36Sopenharmony_ci		sta_apply_mesh_params(local, sta, params);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	if (params->airtime_weight)
201762306a36Sopenharmony_ci		sta->airtime_weight = params->airtime_weight;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	/* set the STA state after all sta info from usermode has been set */
202062306a36Sopenharmony_ci	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
202162306a36Sopenharmony_ci	    set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
202262306a36Sopenharmony_ci		ret = sta_apply_auth_flags(local, sta, mask, set);
202362306a36Sopenharmony_ci		if (ret)
202462306a36Sopenharmony_ci			return ret;
202562306a36Sopenharmony_ci	}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	/* Mark the STA as MLO if MLD MAC address is available */
202862306a36Sopenharmony_ci	if (params->link_sta_params.mld_mac)
202962306a36Sopenharmony_ci		sta->sta.mlo = true;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	return 0;
203262306a36Sopenharmony_ci}
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_cistatic int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
203562306a36Sopenharmony_ci				 const u8 *mac,
203662306a36Sopenharmony_ci				 struct station_parameters *params)
203762306a36Sopenharmony_ci{
203862306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
203962306a36Sopenharmony_ci	struct sta_info *sta;
204062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
204162306a36Sopenharmony_ci	int err;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	if (params->vlan) {
204462306a36Sopenharmony_ci		sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
204762306a36Sopenharmony_ci		    sdata->vif.type != NL80211_IFTYPE_AP)
204862306a36Sopenharmony_ci			return -EINVAL;
204962306a36Sopenharmony_ci	} else
205062306a36Sopenharmony_ci		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	if (ether_addr_equal(mac, sdata->vif.addr))
205362306a36Sopenharmony_ci		return -EINVAL;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	if (!is_valid_ether_addr(mac))
205662306a36Sopenharmony_ci		return -EINVAL;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER) &&
205962306a36Sopenharmony_ci	    sdata->vif.type == NL80211_IFTYPE_STATION &&
206062306a36Sopenharmony_ci	    !sdata->u.mgd.associated)
206162306a36Sopenharmony_ci		return -EINVAL;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	/*
206462306a36Sopenharmony_ci	 * If we have a link ID, it can be a non-MLO station on an AP MLD,
206562306a36Sopenharmony_ci	 * but we need to have a link_mac in that case as well, so use the
206662306a36Sopenharmony_ci	 * STA's MAC address in that case.
206762306a36Sopenharmony_ci	 */
206862306a36Sopenharmony_ci	if (params->link_sta_params.link_id >= 0)
206962306a36Sopenharmony_ci		sta = sta_info_alloc_with_link(sdata, mac,
207062306a36Sopenharmony_ci					       params->link_sta_params.link_id,
207162306a36Sopenharmony_ci					       params->link_sta_params.link_mac ?: mac,
207262306a36Sopenharmony_ci					       GFP_KERNEL);
207362306a36Sopenharmony_ci	else
207462306a36Sopenharmony_ci		sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	if (!sta)
207762306a36Sopenharmony_ci		return -ENOMEM;
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
208062306a36Sopenharmony_ci		sta->sta.tdls = true;
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	/* Though the mutex is not needed here (since the station is not
208362306a36Sopenharmony_ci	 * visible yet), sta_apply_parameters (and inner functions) require
208462306a36Sopenharmony_ci	 * the mutex due to other paths.
208562306a36Sopenharmony_ci	 */
208662306a36Sopenharmony_ci	mutex_lock(&local->sta_mtx);
208762306a36Sopenharmony_ci	err = sta_apply_parameters(local, sta, params);
208862306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
208962306a36Sopenharmony_ci	if (err) {
209062306a36Sopenharmony_ci		sta_info_free(local, sta);
209162306a36Sopenharmony_ci		return err;
209262306a36Sopenharmony_ci	}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci	/*
209562306a36Sopenharmony_ci	 * for TDLS and for unassociated station, rate control should be
209662306a36Sopenharmony_ci	 * initialized only when rates are known and station is marked
209762306a36Sopenharmony_ci	 * authorized/associated
209862306a36Sopenharmony_ci	 */
209962306a36Sopenharmony_ci	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
210062306a36Sopenharmony_ci	    test_sta_flag(sta, WLAN_STA_ASSOC))
210162306a36Sopenharmony_ci		rate_control_rate_init(sta);
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	return sta_info_insert(sta);
210462306a36Sopenharmony_ci}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_cistatic int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
210762306a36Sopenharmony_ci				 struct station_del_parameters *params)
210862306a36Sopenharmony_ci{
210962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	if (params->mac)
211462306a36Sopenharmony_ci		return sta_info_destroy_addr_bss(sdata, params->mac);
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	sta_info_flush(sdata);
211762306a36Sopenharmony_ci	return 0;
211862306a36Sopenharmony_ci}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_cistatic int ieee80211_change_station(struct wiphy *wiphy,
212162306a36Sopenharmony_ci				    struct net_device *dev, const u8 *mac,
212262306a36Sopenharmony_ci				    struct station_parameters *params)
212362306a36Sopenharmony_ci{
212462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
212562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
212662306a36Sopenharmony_ci	struct sta_info *sta;
212762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *vlansdata;
212862306a36Sopenharmony_ci	enum cfg80211_station_type statype;
212962306a36Sopenharmony_ci	int err;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	mutex_lock(&local->sta_mtx);
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, mac);
213462306a36Sopenharmony_ci	if (!sta) {
213562306a36Sopenharmony_ci		err = -ENOENT;
213662306a36Sopenharmony_ci		goto out_err;
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	switch (sdata->vif.type) {
214062306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
214162306a36Sopenharmony_ci		if (sdata->u.mesh.user_mpm)
214262306a36Sopenharmony_ci			statype = CFG80211_STA_MESH_PEER_USER;
214362306a36Sopenharmony_ci		else
214462306a36Sopenharmony_ci			statype = CFG80211_STA_MESH_PEER_KERNEL;
214562306a36Sopenharmony_ci		break;
214662306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
214762306a36Sopenharmony_ci		statype = CFG80211_STA_IBSS;
214862306a36Sopenharmony_ci		break;
214962306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
215062306a36Sopenharmony_ci		if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
215162306a36Sopenharmony_ci			statype = CFG80211_STA_AP_STA;
215262306a36Sopenharmony_ci			break;
215362306a36Sopenharmony_ci		}
215462306a36Sopenharmony_ci		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
215562306a36Sopenharmony_ci			statype = CFG80211_STA_TDLS_PEER_ACTIVE;
215662306a36Sopenharmony_ci		else
215762306a36Sopenharmony_ci			statype = CFG80211_STA_TDLS_PEER_SETUP;
215862306a36Sopenharmony_ci		break;
215962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
216062306a36Sopenharmony_ci	case NL80211_IFTYPE_AP_VLAN:
216162306a36Sopenharmony_ci		if (test_sta_flag(sta, WLAN_STA_ASSOC))
216262306a36Sopenharmony_ci			statype = CFG80211_STA_AP_CLIENT;
216362306a36Sopenharmony_ci		else
216462306a36Sopenharmony_ci			statype = CFG80211_STA_AP_CLIENT_UNASSOC;
216562306a36Sopenharmony_ci		break;
216662306a36Sopenharmony_ci	default:
216762306a36Sopenharmony_ci		err = -EOPNOTSUPP;
216862306a36Sopenharmony_ci		goto out_err;
216962306a36Sopenharmony_ci	}
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	err = cfg80211_check_station_change(wiphy, params, statype);
217262306a36Sopenharmony_ci	if (err)
217362306a36Sopenharmony_ci		goto out_err;
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	if (params->vlan && params->vlan != sta->sdata->dev) {
217662306a36Sopenharmony_ci		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci		if (params->vlan->ieee80211_ptr->use_4addr) {
217962306a36Sopenharmony_ci			if (vlansdata->u.vlan.sta) {
218062306a36Sopenharmony_ci				err = -EBUSY;
218162306a36Sopenharmony_ci				goto out_err;
218262306a36Sopenharmony_ci			}
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci			rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
218562306a36Sopenharmony_ci			__ieee80211_check_fast_rx_iface(vlansdata);
218662306a36Sopenharmony_ci			drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
218762306a36Sopenharmony_ci		}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci		if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
219062306a36Sopenharmony_ci		    sta->sdata->u.vlan.sta) {
219162306a36Sopenharmony_ci			ieee80211_clear_fast_rx(sta);
219262306a36Sopenharmony_ci			RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
219362306a36Sopenharmony_ci		}
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
219662306a36Sopenharmony_ci			ieee80211_vif_dec_num_mcast(sta->sdata);
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci		sta->sdata = vlansdata;
219962306a36Sopenharmony_ci		ieee80211_check_fast_xmit(sta);
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
220262306a36Sopenharmony_ci			ieee80211_vif_inc_num_mcast(sta->sdata);
220362306a36Sopenharmony_ci			cfg80211_send_layer2_update(sta->sdata->dev,
220462306a36Sopenharmony_ci						    sta->sta.addr);
220562306a36Sopenharmony_ci		}
220662306a36Sopenharmony_ci	}
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	/* we use sta_info_get_bss() so this might be different */
220962306a36Sopenharmony_ci	if (sdata != sta->sdata) {
221062306a36Sopenharmony_ci		mutex_lock_nested(&sta->sdata->wdev.mtx, 1);
221162306a36Sopenharmony_ci		err = sta_apply_parameters(local, sta, params);
221262306a36Sopenharmony_ci		mutex_unlock(&sta->sdata->wdev.mtx);
221362306a36Sopenharmony_ci	} else {
221462306a36Sopenharmony_ci		err = sta_apply_parameters(local, sta, params);
221562306a36Sopenharmony_ci	}
221662306a36Sopenharmony_ci	if (err)
221762306a36Sopenharmony_ci		goto out_err;
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
222262306a36Sopenharmony_ci	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
222362306a36Sopenharmony_ci		ieee80211_recalc_ps(local);
222462306a36Sopenharmony_ci		ieee80211_recalc_ps_vif(sdata);
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	return 0;
222862306a36Sopenharmony_ciout_err:
222962306a36Sopenharmony_ci	mutex_unlock(&local->sta_mtx);
223062306a36Sopenharmony_ci	return err;
223162306a36Sopenharmony_ci}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
223462306a36Sopenharmony_cistatic int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
223562306a36Sopenharmony_ci			       const u8 *dst, const u8 *next_hop)
223662306a36Sopenharmony_ci{
223762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
223862306a36Sopenharmony_ci	struct mesh_path *mpath;
223962306a36Sopenharmony_ci	struct sta_info *sta;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	rcu_read_lock();
224462306a36Sopenharmony_ci	sta = sta_info_get(sdata, next_hop);
224562306a36Sopenharmony_ci	if (!sta) {
224662306a36Sopenharmony_ci		rcu_read_unlock();
224762306a36Sopenharmony_ci		return -ENOENT;
224862306a36Sopenharmony_ci	}
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	mpath = mesh_path_add(sdata, dst);
225162306a36Sopenharmony_ci	if (IS_ERR(mpath)) {
225262306a36Sopenharmony_ci		rcu_read_unlock();
225362306a36Sopenharmony_ci		return PTR_ERR(mpath);
225462306a36Sopenharmony_ci	}
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	mesh_path_fix_nexthop(mpath, sta);
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	rcu_read_unlock();
225962306a36Sopenharmony_ci	return 0;
226062306a36Sopenharmony_ci}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_cistatic int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
226362306a36Sopenharmony_ci			       const u8 *dst)
226462306a36Sopenharmony_ci{
226562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	if (dst)
226862306a36Sopenharmony_ci		return mesh_path_del(sdata, dst);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	mesh_path_flush_by_iface(sdata);
227162306a36Sopenharmony_ci	return 0;
227262306a36Sopenharmony_ci}
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_cistatic int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
227562306a36Sopenharmony_ci				  const u8 *dst, const u8 *next_hop)
227662306a36Sopenharmony_ci{
227762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
227862306a36Sopenharmony_ci	struct mesh_path *mpath;
227962306a36Sopenharmony_ci	struct sta_info *sta;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	rcu_read_lock();
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	sta = sta_info_get(sdata, next_hop);
228662306a36Sopenharmony_ci	if (!sta) {
228762306a36Sopenharmony_ci		rcu_read_unlock();
228862306a36Sopenharmony_ci		return -ENOENT;
228962306a36Sopenharmony_ci	}
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_ci	mpath = mesh_path_lookup(sdata, dst);
229262306a36Sopenharmony_ci	if (!mpath) {
229362306a36Sopenharmony_ci		rcu_read_unlock();
229462306a36Sopenharmony_ci		return -ENOENT;
229562306a36Sopenharmony_ci	}
229662306a36Sopenharmony_ci
229762306a36Sopenharmony_ci	mesh_path_fix_nexthop(mpath, sta);
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	rcu_read_unlock();
230062306a36Sopenharmony_ci	return 0;
230162306a36Sopenharmony_ci}
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_cistatic void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
230462306a36Sopenharmony_ci			    struct mpath_info *pinfo)
230562306a36Sopenharmony_ci{
230662306a36Sopenharmony_ci	struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop);
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci	if (next_hop_sta)
230962306a36Sopenharmony_ci		memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);
231062306a36Sopenharmony_ci	else
231162306a36Sopenharmony_ci		eth_zero_addr(next_hop);
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	memset(pinfo, 0, sizeof(*pinfo));
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	pinfo->generation = mpath->sdata->u.mesh.mesh_paths_generation;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	pinfo->filled = MPATH_INFO_FRAME_QLEN |
231862306a36Sopenharmony_ci			MPATH_INFO_SN |
231962306a36Sopenharmony_ci			MPATH_INFO_METRIC |
232062306a36Sopenharmony_ci			MPATH_INFO_EXPTIME |
232162306a36Sopenharmony_ci			MPATH_INFO_DISCOVERY_TIMEOUT |
232262306a36Sopenharmony_ci			MPATH_INFO_DISCOVERY_RETRIES |
232362306a36Sopenharmony_ci			MPATH_INFO_FLAGS |
232462306a36Sopenharmony_ci			MPATH_INFO_HOP_COUNT |
232562306a36Sopenharmony_ci			MPATH_INFO_PATH_CHANGE;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	pinfo->frame_qlen = mpath->frame_queue.qlen;
232862306a36Sopenharmony_ci	pinfo->sn = mpath->sn;
232962306a36Sopenharmony_ci	pinfo->metric = mpath->metric;
233062306a36Sopenharmony_ci	if (time_before(jiffies, mpath->exp_time))
233162306a36Sopenharmony_ci		pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
233262306a36Sopenharmony_ci	pinfo->discovery_timeout =
233362306a36Sopenharmony_ci			jiffies_to_msecs(mpath->discovery_timeout);
233462306a36Sopenharmony_ci	pinfo->discovery_retries = mpath->discovery_retries;
233562306a36Sopenharmony_ci	if (mpath->flags & MESH_PATH_ACTIVE)
233662306a36Sopenharmony_ci		pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
233762306a36Sopenharmony_ci	if (mpath->flags & MESH_PATH_RESOLVING)
233862306a36Sopenharmony_ci		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
233962306a36Sopenharmony_ci	if (mpath->flags & MESH_PATH_SN_VALID)
234062306a36Sopenharmony_ci		pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID;
234162306a36Sopenharmony_ci	if (mpath->flags & MESH_PATH_FIXED)
234262306a36Sopenharmony_ci		pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
234362306a36Sopenharmony_ci	if (mpath->flags & MESH_PATH_RESOLVED)
234462306a36Sopenharmony_ci		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED;
234562306a36Sopenharmony_ci	pinfo->hop_count = mpath->hop_count;
234662306a36Sopenharmony_ci	pinfo->path_change_count = mpath->path_change_count;
234762306a36Sopenharmony_ci}
234862306a36Sopenharmony_ci
234962306a36Sopenharmony_cistatic int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
235062306a36Sopenharmony_ci			       u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci{
235362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
235462306a36Sopenharmony_ci	struct mesh_path *mpath;
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci	rcu_read_lock();
235962306a36Sopenharmony_ci	mpath = mesh_path_lookup(sdata, dst);
236062306a36Sopenharmony_ci	if (!mpath) {
236162306a36Sopenharmony_ci		rcu_read_unlock();
236262306a36Sopenharmony_ci		return -ENOENT;
236362306a36Sopenharmony_ci	}
236462306a36Sopenharmony_ci	memcpy(dst, mpath->dst, ETH_ALEN);
236562306a36Sopenharmony_ci	mpath_set_pinfo(mpath, next_hop, pinfo);
236662306a36Sopenharmony_ci	rcu_read_unlock();
236762306a36Sopenharmony_ci	return 0;
236862306a36Sopenharmony_ci}
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_cistatic int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
237162306a36Sopenharmony_ci				int idx, u8 *dst, u8 *next_hop,
237262306a36Sopenharmony_ci				struct mpath_info *pinfo)
237362306a36Sopenharmony_ci{
237462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
237562306a36Sopenharmony_ci	struct mesh_path *mpath;
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	rcu_read_lock();
238062306a36Sopenharmony_ci	mpath = mesh_path_lookup_by_idx(sdata, idx);
238162306a36Sopenharmony_ci	if (!mpath) {
238262306a36Sopenharmony_ci		rcu_read_unlock();
238362306a36Sopenharmony_ci		return -ENOENT;
238462306a36Sopenharmony_ci	}
238562306a36Sopenharmony_ci	memcpy(dst, mpath->dst, ETH_ALEN);
238662306a36Sopenharmony_ci	mpath_set_pinfo(mpath, next_hop, pinfo);
238762306a36Sopenharmony_ci	rcu_read_unlock();
238862306a36Sopenharmony_ci	return 0;
238962306a36Sopenharmony_ci}
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_cistatic void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
239262306a36Sopenharmony_ci			  struct mpath_info *pinfo)
239362306a36Sopenharmony_ci{
239462306a36Sopenharmony_ci	memset(pinfo, 0, sizeof(*pinfo));
239562306a36Sopenharmony_ci	memcpy(mpp, mpath->mpp, ETH_ALEN);
239662306a36Sopenharmony_ci
239762306a36Sopenharmony_ci	pinfo->generation = mpath->sdata->u.mesh.mpp_paths_generation;
239862306a36Sopenharmony_ci}
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_cistatic int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
240162306a36Sopenharmony_ci			     u8 *dst, u8 *mpp, struct mpath_info *pinfo)
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci{
240462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
240562306a36Sopenharmony_ci	struct mesh_path *mpath;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
240862306a36Sopenharmony_ci
240962306a36Sopenharmony_ci	rcu_read_lock();
241062306a36Sopenharmony_ci	mpath = mpp_path_lookup(sdata, dst);
241162306a36Sopenharmony_ci	if (!mpath) {
241262306a36Sopenharmony_ci		rcu_read_unlock();
241362306a36Sopenharmony_ci		return -ENOENT;
241462306a36Sopenharmony_ci	}
241562306a36Sopenharmony_ci	memcpy(dst, mpath->dst, ETH_ALEN);
241662306a36Sopenharmony_ci	mpp_set_pinfo(mpath, mpp, pinfo);
241762306a36Sopenharmony_ci	rcu_read_unlock();
241862306a36Sopenharmony_ci	return 0;
241962306a36Sopenharmony_ci}
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_cistatic int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
242262306a36Sopenharmony_ci			      int idx, u8 *dst, u8 *mpp,
242362306a36Sopenharmony_ci			      struct mpath_info *pinfo)
242462306a36Sopenharmony_ci{
242562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
242662306a36Sopenharmony_ci	struct mesh_path *mpath;
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	rcu_read_lock();
243162306a36Sopenharmony_ci	mpath = mpp_path_lookup_by_idx(sdata, idx);
243262306a36Sopenharmony_ci	if (!mpath) {
243362306a36Sopenharmony_ci		rcu_read_unlock();
243462306a36Sopenharmony_ci		return -ENOENT;
243562306a36Sopenharmony_ci	}
243662306a36Sopenharmony_ci	memcpy(dst, mpath->dst, ETH_ALEN);
243762306a36Sopenharmony_ci	mpp_set_pinfo(mpath, mpp, pinfo);
243862306a36Sopenharmony_ci	rcu_read_unlock();
243962306a36Sopenharmony_ci	return 0;
244062306a36Sopenharmony_ci}
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_cistatic int ieee80211_get_mesh_config(struct wiphy *wiphy,
244362306a36Sopenharmony_ci				struct net_device *dev,
244462306a36Sopenharmony_ci				struct mesh_config *conf)
244562306a36Sopenharmony_ci{
244662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
244762306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci	memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
245062306a36Sopenharmony_ci	return 0;
245162306a36Sopenharmony_ci}
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_cistatic inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
245462306a36Sopenharmony_ci{
245562306a36Sopenharmony_ci	return (mask >> (parm-1)) & 0x1;
245662306a36Sopenharmony_ci}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_cistatic int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
245962306a36Sopenharmony_ci		const struct mesh_setup *setup)
246062306a36Sopenharmony_ci{
246162306a36Sopenharmony_ci	u8 *new_ie;
246262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = container_of(ifmsh,
246362306a36Sopenharmony_ci					struct ieee80211_sub_if_data, u.mesh);
246462306a36Sopenharmony_ci	int i;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	/* allocate information elements */
246762306a36Sopenharmony_ci	new_ie = NULL;
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	if (setup->ie_len) {
247062306a36Sopenharmony_ci		new_ie = kmemdup(setup->ie, setup->ie_len,
247162306a36Sopenharmony_ci				GFP_KERNEL);
247262306a36Sopenharmony_ci		if (!new_ie)
247362306a36Sopenharmony_ci			return -ENOMEM;
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci	ifmsh->ie_len = setup->ie_len;
247662306a36Sopenharmony_ci	ifmsh->ie = new_ie;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	/* now copy the rest of the setup parameters */
247962306a36Sopenharmony_ci	ifmsh->mesh_id_len = setup->mesh_id_len;
248062306a36Sopenharmony_ci	memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
248162306a36Sopenharmony_ci	ifmsh->mesh_sp_id = setup->sync_method;
248262306a36Sopenharmony_ci	ifmsh->mesh_pp_id = setup->path_sel_proto;
248362306a36Sopenharmony_ci	ifmsh->mesh_pm_id = setup->path_metric;
248462306a36Sopenharmony_ci	ifmsh->user_mpm = setup->user_mpm;
248562306a36Sopenharmony_ci	ifmsh->mesh_auth_id = setup->auth_id;
248662306a36Sopenharmony_ci	ifmsh->security = IEEE80211_MESH_SEC_NONE;
248762306a36Sopenharmony_ci	ifmsh->userspace_handles_dfs = setup->userspace_handles_dfs;
248862306a36Sopenharmony_ci	if (setup->is_authenticated)
248962306a36Sopenharmony_ci		ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
249062306a36Sopenharmony_ci	if (setup->is_secure)
249162306a36Sopenharmony_ci		ifmsh->security |= IEEE80211_MESH_SEC_SECURED;
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	/* mcast rate setting in Mesh Node */
249462306a36Sopenharmony_ci	memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
249562306a36Sopenharmony_ci						sizeof(setup->mcast_rate));
249662306a36Sopenharmony_ci	sdata->vif.bss_conf.basic_rates = setup->basic_rates;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
249962306a36Sopenharmony_ci	sdata->vif.bss_conf.dtim_period = setup->dtim_period;
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	sdata->beacon_rate_set = false;
250262306a36Sopenharmony_ci	if (wiphy_ext_feature_isset(sdata->local->hw.wiphy,
250362306a36Sopenharmony_ci				    NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) {
250462306a36Sopenharmony_ci		for (i = 0; i < NUM_NL80211_BANDS; i++) {
250562306a36Sopenharmony_ci			sdata->beacon_rateidx_mask[i] =
250662306a36Sopenharmony_ci				setup->beacon_rate.control[i].legacy;
250762306a36Sopenharmony_ci			if (sdata->beacon_rateidx_mask[i])
250862306a36Sopenharmony_ci				sdata->beacon_rate_set = true;
250962306a36Sopenharmony_ci		}
251062306a36Sopenharmony_ci	}
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci	return 0;
251362306a36Sopenharmony_ci}
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_cistatic int ieee80211_update_mesh_config(struct wiphy *wiphy,
251662306a36Sopenharmony_ci					struct net_device *dev, u32 mask,
251762306a36Sopenharmony_ci					const struct mesh_config *nconf)
251862306a36Sopenharmony_ci{
251962306a36Sopenharmony_ci	struct mesh_config *conf;
252062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
252162306a36Sopenharmony_ci	struct ieee80211_if_mesh *ifmsh;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
252462306a36Sopenharmony_ci	ifmsh = &sdata->u.mesh;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	/* Set the config options which we are interested in setting */
252762306a36Sopenharmony_ci	conf = &(sdata->u.mesh.mshcfg);
252862306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
252962306a36Sopenharmony_ci		conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout;
253062306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask))
253162306a36Sopenharmony_ci		conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout;
253262306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask))
253362306a36Sopenharmony_ci		conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout;
253462306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask))
253562306a36Sopenharmony_ci		conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
253662306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
253762306a36Sopenharmony_ci		conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
253862306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
253962306a36Sopenharmony_ci		conf->dot11MeshTTL = nconf->dot11MeshTTL;
254062306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
254162306a36Sopenharmony_ci		conf->element_ttl = nconf->element_ttl;
254262306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) {
254362306a36Sopenharmony_ci		if (ifmsh->user_mpm)
254462306a36Sopenharmony_ci			return -EBUSY;
254562306a36Sopenharmony_ci		conf->auto_open_plinks = nconf->auto_open_plinks;
254662306a36Sopenharmony_ci	}
254762306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))
254862306a36Sopenharmony_ci		conf->dot11MeshNbrOffsetMaxNeighbor =
254962306a36Sopenharmony_ci			nconf->dot11MeshNbrOffsetMaxNeighbor;
255062306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
255162306a36Sopenharmony_ci		conf->dot11MeshHWMPmaxPREQretries =
255262306a36Sopenharmony_ci			nconf->dot11MeshHWMPmaxPREQretries;
255362306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
255462306a36Sopenharmony_ci		conf->path_refresh_time = nconf->path_refresh_time;
255562306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
255662306a36Sopenharmony_ci		conf->min_discovery_timeout = nconf->min_discovery_timeout;
255762306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
255862306a36Sopenharmony_ci		conf->dot11MeshHWMPactivePathTimeout =
255962306a36Sopenharmony_ci			nconf->dot11MeshHWMPactivePathTimeout;
256062306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))
256162306a36Sopenharmony_ci		conf->dot11MeshHWMPpreqMinInterval =
256262306a36Sopenharmony_ci			nconf->dot11MeshHWMPpreqMinInterval;
256362306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask))
256462306a36Sopenharmony_ci		conf->dot11MeshHWMPperrMinInterval =
256562306a36Sopenharmony_ci			nconf->dot11MeshHWMPperrMinInterval;
256662306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
256762306a36Sopenharmony_ci			   mask))
256862306a36Sopenharmony_ci		conf->dot11MeshHWMPnetDiameterTraversalTime =
256962306a36Sopenharmony_ci			nconf->dot11MeshHWMPnetDiameterTraversalTime;
257062306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOTMODE, mask)) {
257162306a36Sopenharmony_ci		conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode;
257262306a36Sopenharmony_ci		ieee80211_mesh_root_setup(ifmsh);
257362306a36Sopenharmony_ci	}
257462306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_GATE_ANNOUNCEMENTS, mask)) {
257562306a36Sopenharmony_ci		/* our current gate announcement implementation rides on root
257662306a36Sopenharmony_ci		 * announcements, so require this ifmsh to also be a root node
257762306a36Sopenharmony_ci		 * */
257862306a36Sopenharmony_ci		if (nconf->dot11MeshGateAnnouncementProtocol &&
257962306a36Sopenharmony_ci		    !(conf->dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)) {
258062306a36Sopenharmony_ci			conf->dot11MeshHWMPRootMode = IEEE80211_PROACTIVE_RANN;
258162306a36Sopenharmony_ci			ieee80211_mesh_root_setup(ifmsh);
258262306a36Sopenharmony_ci		}
258362306a36Sopenharmony_ci		conf->dot11MeshGateAnnouncementProtocol =
258462306a36Sopenharmony_ci			nconf->dot11MeshGateAnnouncementProtocol;
258562306a36Sopenharmony_ci	}
258662306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask))
258762306a36Sopenharmony_ci		conf->dot11MeshHWMPRannInterval =
258862306a36Sopenharmony_ci			nconf->dot11MeshHWMPRannInterval;
258962306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask))
259062306a36Sopenharmony_ci		conf->dot11MeshForwarding = nconf->dot11MeshForwarding;
259162306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) {
259262306a36Sopenharmony_ci		/* our RSSI threshold implementation is supported only for
259362306a36Sopenharmony_ci		 * devices that report signal in dBm.
259462306a36Sopenharmony_ci		 */
259562306a36Sopenharmony_ci		if (!ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM))
259662306a36Sopenharmony_ci			return -ENOTSUPP;
259762306a36Sopenharmony_ci		conf->rssi_threshold = nconf->rssi_threshold;
259862306a36Sopenharmony_ci	}
259962306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) {
260062306a36Sopenharmony_ci		conf->ht_opmode = nconf->ht_opmode;
260162306a36Sopenharmony_ci		sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode;
260262306a36Sopenharmony_ci		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
260362306a36Sopenharmony_ci						  BSS_CHANGED_HT);
260462306a36Sopenharmony_ci	}
260562306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, mask))
260662306a36Sopenharmony_ci		conf->dot11MeshHWMPactivePathToRootTimeout =
260762306a36Sopenharmony_ci			nconf->dot11MeshHWMPactivePathToRootTimeout;
260862306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOT_INTERVAL, mask))
260962306a36Sopenharmony_ci		conf->dot11MeshHWMProotInterval =
261062306a36Sopenharmony_ci			nconf->dot11MeshHWMProotInterval;
261162306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask))
261262306a36Sopenharmony_ci		conf->dot11MeshHWMPconfirmationInterval =
261362306a36Sopenharmony_ci			nconf->dot11MeshHWMPconfirmationInterval;
261462306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
261562306a36Sopenharmony_ci		conf->power_mode = nconf->power_mode;
261662306a36Sopenharmony_ci		ieee80211_mps_local_status_update(sdata);
261762306a36Sopenharmony_ci	}
261862306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
261962306a36Sopenharmony_ci		conf->dot11MeshAwakeWindowDuration =
262062306a36Sopenharmony_ci			nconf->dot11MeshAwakeWindowDuration;
262162306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
262262306a36Sopenharmony_ci		conf->plink_timeout = nconf->plink_timeout;
262362306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_GATE, mask))
262462306a36Sopenharmony_ci		conf->dot11MeshConnectedToMeshGate =
262562306a36Sopenharmony_ci			nconf->dot11MeshConnectedToMeshGate;
262662306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_NOLEARN, mask))
262762306a36Sopenharmony_ci		conf->dot11MeshNolearn = nconf->dot11MeshNolearn;
262862306a36Sopenharmony_ci	if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_AS, mask))
262962306a36Sopenharmony_ci		conf->dot11MeshConnectedToAuthServer =
263062306a36Sopenharmony_ci			nconf->dot11MeshConnectedToAuthServer;
263162306a36Sopenharmony_ci	ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
263262306a36Sopenharmony_ci	return 0;
263362306a36Sopenharmony_ci}
263462306a36Sopenharmony_ci
263562306a36Sopenharmony_cistatic int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
263662306a36Sopenharmony_ci			       const struct mesh_config *conf,
263762306a36Sopenharmony_ci			       const struct mesh_setup *setup)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
264062306a36Sopenharmony_ci	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
264162306a36Sopenharmony_ci	int err;
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci	memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
264462306a36Sopenharmony_ci	err = copy_mesh_setup(ifmsh, setup);
264562306a36Sopenharmony_ci	if (err)
264662306a36Sopenharmony_ci		return err;
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	sdata->control_port_over_nl80211 = setup->control_port_over_nl80211;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	/* can mesh use other SMPS modes? */
265162306a36Sopenharmony_ci	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
265262306a36Sopenharmony_ci	sdata->deflink.needed_rx_chains = sdata->local->rx_chains;
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	mutex_lock(&sdata->local->mtx);
265562306a36Sopenharmony_ci	err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef,
265662306a36Sopenharmony_ci					 IEEE80211_CHANCTX_SHARED);
265762306a36Sopenharmony_ci	mutex_unlock(&sdata->local->mtx);
265862306a36Sopenharmony_ci	if (err)
265962306a36Sopenharmony_ci		return err;
266062306a36Sopenharmony_ci
266162306a36Sopenharmony_ci	return ieee80211_start_mesh(sdata);
266262306a36Sopenharmony_ci}
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_cistatic int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
266562306a36Sopenharmony_ci{
266662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	ieee80211_stop_mesh(sdata);
266962306a36Sopenharmony_ci	mutex_lock(&sdata->local->mtx);
267062306a36Sopenharmony_ci	ieee80211_link_release_channel(&sdata->deflink);
267162306a36Sopenharmony_ci	kfree(sdata->u.mesh.ie);
267262306a36Sopenharmony_ci	mutex_unlock(&sdata->local->mtx);
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	return 0;
267562306a36Sopenharmony_ci}
267662306a36Sopenharmony_ci#endif
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_cistatic int ieee80211_change_bss(struct wiphy *wiphy,
267962306a36Sopenharmony_ci				struct net_device *dev,
268062306a36Sopenharmony_ci				struct bss_parameters *params)
268162306a36Sopenharmony_ci{
268262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
268362306a36Sopenharmony_ci	struct ieee80211_link_data *link;
268462306a36Sopenharmony_ci	struct ieee80211_supported_band *sband;
268562306a36Sopenharmony_ci	u64 changed = 0;
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	link = ieee80211_link_or_deflink(sdata, params->link_id, true);
268862306a36Sopenharmony_ci	if (IS_ERR(link))
268962306a36Sopenharmony_ci		return PTR_ERR(link);
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci	if (!sdata_dereference(link->u.ap.beacon, sdata))
269262306a36Sopenharmony_ci		return -ENOENT;
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci	sband = ieee80211_get_link_sband(link);
269562306a36Sopenharmony_ci	if (!sband)
269662306a36Sopenharmony_ci		return -EINVAL;
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	if (params->basic_rates) {
269962306a36Sopenharmony_ci		if (!ieee80211_parse_bitrates(link->conf->chandef.width,
270062306a36Sopenharmony_ci					      wiphy->bands[sband->band],
270162306a36Sopenharmony_ci					      params->basic_rates,
270262306a36Sopenharmony_ci					      params->basic_rates_len,
270362306a36Sopenharmony_ci					      &link->conf->basic_rates))
270462306a36Sopenharmony_ci			return -EINVAL;
270562306a36Sopenharmony_ci		changed |= BSS_CHANGED_BASIC_RATES;
270662306a36Sopenharmony_ci		ieee80211_check_rate_mask(link);
270762306a36Sopenharmony_ci	}
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	if (params->use_cts_prot >= 0) {
271062306a36Sopenharmony_ci		link->conf->use_cts_prot = params->use_cts_prot;
271162306a36Sopenharmony_ci		changed |= BSS_CHANGED_ERP_CTS_PROT;
271262306a36Sopenharmony_ci	}
271362306a36Sopenharmony_ci	if (params->use_short_preamble >= 0) {
271462306a36Sopenharmony_ci		link->conf->use_short_preamble = params->use_short_preamble;
271562306a36Sopenharmony_ci		changed |= BSS_CHANGED_ERP_PREAMBLE;
271662306a36Sopenharmony_ci	}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	if (!link->conf->use_short_slot &&
271962306a36Sopenharmony_ci	    (sband->band == NL80211_BAND_5GHZ ||
272062306a36Sopenharmony_ci	     sband->band == NL80211_BAND_6GHZ)) {
272162306a36Sopenharmony_ci		link->conf->use_short_slot = true;
272262306a36Sopenharmony_ci		changed |= BSS_CHANGED_ERP_SLOT;
272362306a36Sopenharmony_ci	}
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci	if (params->use_short_slot_time >= 0) {
272662306a36Sopenharmony_ci		link->conf->use_short_slot = params->use_short_slot_time;
272762306a36Sopenharmony_ci		changed |= BSS_CHANGED_ERP_SLOT;
272862306a36Sopenharmony_ci	}
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	if (params->ap_isolate >= 0) {
273162306a36Sopenharmony_ci		if (params->ap_isolate)
273262306a36Sopenharmony_ci			sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
273362306a36Sopenharmony_ci		else
273462306a36Sopenharmony_ci			sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
273562306a36Sopenharmony_ci		ieee80211_check_fast_rx_iface(sdata);
273662306a36Sopenharmony_ci	}
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	if (params->ht_opmode >= 0) {
273962306a36Sopenharmony_ci		link->conf->ht_operation_mode = (u16)params->ht_opmode;
274062306a36Sopenharmony_ci		changed |= BSS_CHANGED_HT;
274162306a36Sopenharmony_ci	}
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	if (params->p2p_ctwindow >= 0) {
274462306a36Sopenharmony_ci		link->conf->p2p_noa_attr.oppps_ctwindow &=
274562306a36Sopenharmony_ci					~IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
274662306a36Sopenharmony_ci		link->conf->p2p_noa_attr.oppps_ctwindow |=
274762306a36Sopenharmony_ci			params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
274862306a36Sopenharmony_ci		changed |= BSS_CHANGED_P2P_PS;
274962306a36Sopenharmony_ci	}
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	if (params->p2p_opp_ps > 0) {
275262306a36Sopenharmony_ci		link->conf->p2p_noa_attr.oppps_ctwindow |=
275362306a36Sopenharmony_ci					IEEE80211_P2P_OPPPS_ENABLE_BIT;
275462306a36Sopenharmony_ci		changed |= BSS_CHANGED_P2P_PS;
275562306a36Sopenharmony_ci	} else if (params->p2p_opp_ps == 0) {
275662306a36Sopenharmony_ci		link->conf->p2p_noa_attr.oppps_ctwindow &=
275762306a36Sopenharmony_ci					~IEEE80211_P2P_OPPPS_ENABLE_BIT;
275862306a36Sopenharmony_ci		changed |= BSS_CHANGED_P2P_PS;
275962306a36Sopenharmony_ci	}
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, link, changed);
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_ci	return 0;
276462306a36Sopenharmony_ci}
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_cistatic int ieee80211_set_txq_params(struct wiphy *wiphy,
276762306a36Sopenharmony_ci				    struct net_device *dev,
276862306a36Sopenharmony_ci				    struct ieee80211_txq_params *params)
276962306a36Sopenharmony_ci{
277062306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
277162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
277262306a36Sopenharmony_ci	struct ieee80211_link_data *link =
277362306a36Sopenharmony_ci		ieee80211_link_or_deflink(sdata, params->link_id, true);
277462306a36Sopenharmony_ci	struct ieee80211_tx_queue_params p;
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci	if (!local->ops->conf_tx)
277762306a36Sopenharmony_ci		return -EOPNOTSUPP;
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	if (local->hw.queues < IEEE80211_NUM_ACS)
278062306a36Sopenharmony_ci		return -EOPNOTSUPP;
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_ci	if (IS_ERR(link))
278362306a36Sopenharmony_ci		return PTR_ERR(link);
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	memset(&p, 0, sizeof(p));
278662306a36Sopenharmony_ci	p.aifs = params->aifs;
278762306a36Sopenharmony_ci	p.cw_max = params->cwmax;
278862306a36Sopenharmony_ci	p.cw_min = params->cwmin;
278962306a36Sopenharmony_ci	p.txop = params->txop;
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	/*
279262306a36Sopenharmony_ci	 * Setting tx queue params disables u-apsd because it's only
279362306a36Sopenharmony_ci	 * called in master mode.
279462306a36Sopenharmony_ci	 */
279562306a36Sopenharmony_ci	p.uapsd = false;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	ieee80211_regulatory_limit_wmm_params(sdata, &p, params->ac);
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	link->tx_conf[params->ac] = p;
280062306a36Sopenharmony_ci	if (drv_conf_tx(local, link, params->ac, &p)) {
280162306a36Sopenharmony_ci		wiphy_debug(local->hw.wiphy,
280262306a36Sopenharmony_ci			    "failed to set TX queue parameters for AC %d\n",
280362306a36Sopenharmony_ci			    params->ac);
280462306a36Sopenharmony_ci		return -EINVAL;
280562306a36Sopenharmony_ci	}
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, link,
280862306a36Sopenharmony_ci					  BSS_CHANGED_QOS);
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	return 0;
281162306a36Sopenharmony_ci}
281262306a36Sopenharmony_ci
281362306a36Sopenharmony_ci#ifdef CONFIG_PM
281462306a36Sopenharmony_cistatic int ieee80211_suspend(struct wiphy *wiphy,
281562306a36Sopenharmony_ci			     struct cfg80211_wowlan *wowlan)
281662306a36Sopenharmony_ci{
281762306a36Sopenharmony_ci	return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
281862306a36Sopenharmony_ci}
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_cistatic int ieee80211_resume(struct wiphy *wiphy)
282162306a36Sopenharmony_ci{
282262306a36Sopenharmony_ci	return __ieee80211_resume(wiphy_priv(wiphy));
282362306a36Sopenharmony_ci}
282462306a36Sopenharmony_ci#else
282562306a36Sopenharmony_ci#define ieee80211_suspend NULL
282662306a36Sopenharmony_ci#define ieee80211_resume NULL
282762306a36Sopenharmony_ci#endif
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_cistatic int ieee80211_scan(struct wiphy *wiphy,
283062306a36Sopenharmony_ci			  struct cfg80211_scan_request *req)
283162306a36Sopenharmony_ci{
283262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_ci	sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev);
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_ci	switch (ieee80211_vif_type_p2p(&sdata->vif)) {
283762306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
283862306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
283962306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
284062306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
284162306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
284262306a36Sopenharmony_ci		break;
284362306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
284462306a36Sopenharmony_ci		if (sdata->local->ops->hw_scan)
284562306a36Sopenharmony_ci			break;
284662306a36Sopenharmony_ci		/*
284762306a36Sopenharmony_ci		 * FIXME: implement NoA while scanning in software,
284862306a36Sopenharmony_ci		 * for now fall through to allow scanning only when
284962306a36Sopenharmony_ci		 * beaconing hasn't been configured yet
285062306a36Sopenharmony_ci		 */
285162306a36Sopenharmony_ci		fallthrough;
285262306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
285362306a36Sopenharmony_ci		/*
285462306a36Sopenharmony_ci		 * If the scan has been forced (and the driver supports
285562306a36Sopenharmony_ci		 * forcing), don't care about being beaconing already.
285662306a36Sopenharmony_ci		 * This will create problems to the attached stations (e.g. all
285762306a36Sopenharmony_ci		 * the frames sent while scanning on other channel will be
285862306a36Sopenharmony_ci		 * lost)
285962306a36Sopenharmony_ci		 */
286062306a36Sopenharmony_ci		if (sdata->deflink.u.ap.beacon &&
286162306a36Sopenharmony_ci		    (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
286262306a36Sopenharmony_ci		     !(req->flags & NL80211_SCAN_FLAG_AP)))
286362306a36Sopenharmony_ci			return -EOPNOTSUPP;
286462306a36Sopenharmony_ci		break;
286562306a36Sopenharmony_ci	case NL80211_IFTYPE_NAN:
286662306a36Sopenharmony_ci	default:
286762306a36Sopenharmony_ci		return -EOPNOTSUPP;
286862306a36Sopenharmony_ci	}
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci	return ieee80211_request_scan(sdata, req);
287162306a36Sopenharmony_ci}
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_cistatic void ieee80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
287462306a36Sopenharmony_ci{
287562306a36Sopenharmony_ci	ieee80211_scan_cancel(wiphy_priv(wiphy));
287662306a36Sopenharmony_ci}
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_cistatic int
287962306a36Sopenharmony_ciieee80211_sched_scan_start(struct wiphy *wiphy,
288062306a36Sopenharmony_ci			   struct net_device *dev,
288162306a36Sopenharmony_ci			   struct cfg80211_sched_scan_request *req)
288262306a36Sopenharmony_ci{
288362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	if (!sdata->local->ops->sched_scan_start)
288662306a36Sopenharmony_ci		return -EOPNOTSUPP;
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci	return ieee80211_request_sched_scan_start(sdata, req);
288962306a36Sopenharmony_ci}
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_cistatic int
289262306a36Sopenharmony_ciieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
289362306a36Sopenharmony_ci			  u64 reqid)
289462306a36Sopenharmony_ci{
289562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	if (!local->ops->sched_scan_stop)
289862306a36Sopenharmony_ci		return -EOPNOTSUPP;
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	return ieee80211_request_sched_scan_stop(local);
290162306a36Sopenharmony_ci}
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_cistatic int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
290462306a36Sopenharmony_ci			  struct cfg80211_auth_request *req)
290562306a36Sopenharmony_ci{
290662306a36Sopenharmony_ci	return ieee80211_mgd_auth(IEEE80211_DEV_TO_SUB_IF(dev), req);
290762306a36Sopenharmony_ci}
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_cistatic int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
291062306a36Sopenharmony_ci			   struct cfg80211_assoc_request *req)
291162306a36Sopenharmony_ci{
291262306a36Sopenharmony_ci	return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
291362306a36Sopenharmony_ci}
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_cistatic int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
291662306a36Sopenharmony_ci			    struct cfg80211_deauth_request *req)
291762306a36Sopenharmony_ci{
291862306a36Sopenharmony_ci	return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req);
291962306a36Sopenharmony_ci}
292062306a36Sopenharmony_ci
292162306a36Sopenharmony_cistatic int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
292262306a36Sopenharmony_ci			      struct cfg80211_disassoc_request *req)
292362306a36Sopenharmony_ci{
292462306a36Sopenharmony_ci	return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_cistatic int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
292862306a36Sopenharmony_ci			       struct cfg80211_ibss_params *params)
292962306a36Sopenharmony_ci{
293062306a36Sopenharmony_ci	return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);
293162306a36Sopenharmony_ci}
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_cistatic int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
293462306a36Sopenharmony_ci{
293562306a36Sopenharmony_ci	return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
293662306a36Sopenharmony_ci}
293762306a36Sopenharmony_ci
293862306a36Sopenharmony_cistatic int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
293962306a36Sopenharmony_ci			      struct ocb_setup *setup)
294062306a36Sopenharmony_ci{
294162306a36Sopenharmony_ci	return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
294262306a36Sopenharmony_ci}
294362306a36Sopenharmony_ci
294462306a36Sopenharmony_cistatic int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
294562306a36Sopenharmony_ci{
294662306a36Sopenharmony_ci	return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
294762306a36Sopenharmony_ci}
294862306a36Sopenharmony_ci
294962306a36Sopenharmony_cistatic int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
295062306a36Sopenharmony_ci				    int rate[NUM_NL80211_BANDS])
295162306a36Sopenharmony_ci{
295262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
295362306a36Sopenharmony_ci
295462306a36Sopenharmony_ci	memcpy(sdata->vif.bss_conf.mcast_rate, rate,
295562306a36Sopenharmony_ci	       sizeof(int) * NUM_NL80211_BANDS);
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, &sdata->deflink,
295862306a36Sopenharmony_ci					  BSS_CHANGED_MCAST_RATE);
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	return 0;
296162306a36Sopenharmony_ci}
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_cistatic int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
296462306a36Sopenharmony_ci{
296562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
296662306a36Sopenharmony_ci	int err;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
296962306a36Sopenharmony_ci		ieee80211_check_fast_xmit_all(local);
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci		err = drv_set_frag_threshold(local, wiphy->frag_threshold);
297262306a36Sopenharmony_ci
297362306a36Sopenharmony_ci		if (err) {
297462306a36Sopenharmony_ci			ieee80211_check_fast_xmit_all(local);
297562306a36Sopenharmony_ci			return err;
297662306a36Sopenharmony_ci		}
297762306a36Sopenharmony_ci	}
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	if ((changed & WIPHY_PARAM_COVERAGE_CLASS) ||
298062306a36Sopenharmony_ci	    (changed & WIPHY_PARAM_DYN_ACK)) {
298162306a36Sopenharmony_ci		s16 coverage_class;
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci		coverage_class = changed & WIPHY_PARAM_COVERAGE_CLASS ?
298462306a36Sopenharmony_ci					wiphy->coverage_class : -1;
298562306a36Sopenharmony_ci		err = drv_set_coverage_class(local, coverage_class);
298662306a36Sopenharmony_ci
298762306a36Sopenharmony_ci		if (err)
298862306a36Sopenharmony_ci			return err;
298962306a36Sopenharmony_ci	}
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
299262306a36Sopenharmony_ci		err = drv_set_rts_threshold(local, wiphy->rts_threshold);
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci		if (err)
299562306a36Sopenharmony_ci			return err;
299662306a36Sopenharmony_ci	}
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_SHORT) {
299962306a36Sopenharmony_ci		if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY)
300062306a36Sopenharmony_ci			return -EINVAL;
300162306a36Sopenharmony_ci		local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
300262306a36Sopenharmony_ci	}
300362306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_LONG) {
300462306a36Sopenharmony_ci		if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY)
300562306a36Sopenharmony_ci			return -EINVAL;
300662306a36Sopenharmony_ci		local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
300762306a36Sopenharmony_ci	}
300862306a36Sopenharmony_ci	if (changed &
300962306a36Sopenharmony_ci	    (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
301062306a36Sopenharmony_ci		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	if (changed & (WIPHY_PARAM_TXQ_LIMIT |
301362306a36Sopenharmony_ci		       WIPHY_PARAM_TXQ_MEMORY_LIMIT |
301462306a36Sopenharmony_ci		       WIPHY_PARAM_TXQ_QUANTUM))
301562306a36Sopenharmony_ci		ieee80211_txq_set_params(local);
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci	return 0;
301862306a36Sopenharmony_ci}
301962306a36Sopenharmony_ci
302062306a36Sopenharmony_cistatic int ieee80211_set_tx_power(struct wiphy *wiphy,
302162306a36Sopenharmony_ci				  struct wireless_dev *wdev,
302262306a36Sopenharmony_ci				  enum nl80211_tx_power_setting type, int mbm)
302362306a36Sopenharmony_ci{
302462306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
302562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
302662306a36Sopenharmony_ci	enum nl80211_tx_power_setting txp_type = type;
302762306a36Sopenharmony_ci	bool update_txp_type = false;
302862306a36Sopenharmony_ci	bool has_monitor = false;
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci	if (wdev) {
303162306a36Sopenharmony_ci		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
303462306a36Sopenharmony_ci			sdata = wiphy_dereference(local->hw.wiphy,
303562306a36Sopenharmony_ci						  local->monitor_sdata);
303662306a36Sopenharmony_ci			if (!sdata)
303762306a36Sopenharmony_ci				return -EOPNOTSUPP;
303862306a36Sopenharmony_ci		}
303962306a36Sopenharmony_ci
304062306a36Sopenharmony_ci		switch (type) {
304162306a36Sopenharmony_ci		case NL80211_TX_POWER_AUTOMATIC:
304262306a36Sopenharmony_ci			sdata->deflink.user_power_level =
304362306a36Sopenharmony_ci				IEEE80211_UNSET_POWER_LEVEL;
304462306a36Sopenharmony_ci			txp_type = NL80211_TX_POWER_LIMITED;
304562306a36Sopenharmony_ci			break;
304662306a36Sopenharmony_ci		case NL80211_TX_POWER_LIMITED:
304762306a36Sopenharmony_ci		case NL80211_TX_POWER_FIXED:
304862306a36Sopenharmony_ci			if (mbm < 0 || (mbm % 100))
304962306a36Sopenharmony_ci				return -EOPNOTSUPP;
305062306a36Sopenharmony_ci			sdata->deflink.user_power_level = MBM_TO_DBM(mbm);
305162306a36Sopenharmony_ci			break;
305262306a36Sopenharmony_ci		}
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci		if (txp_type != sdata->vif.bss_conf.txpower_type) {
305562306a36Sopenharmony_ci			update_txp_type = true;
305662306a36Sopenharmony_ci			sdata->vif.bss_conf.txpower_type = txp_type;
305762306a36Sopenharmony_ci		}
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ci		ieee80211_recalc_txpower(sdata, update_txp_type);
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci		return 0;
306262306a36Sopenharmony_ci	}
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	switch (type) {
306562306a36Sopenharmony_ci	case NL80211_TX_POWER_AUTOMATIC:
306662306a36Sopenharmony_ci		local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
306762306a36Sopenharmony_ci		txp_type = NL80211_TX_POWER_LIMITED;
306862306a36Sopenharmony_ci		break;
306962306a36Sopenharmony_ci	case NL80211_TX_POWER_LIMITED:
307062306a36Sopenharmony_ci	case NL80211_TX_POWER_FIXED:
307162306a36Sopenharmony_ci		if (mbm < 0 || (mbm % 100))
307262306a36Sopenharmony_ci			return -EOPNOTSUPP;
307362306a36Sopenharmony_ci		local->user_power_level = MBM_TO_DBM(mbm);
307462306a36Sopenharmony_ci		break;
307562306a36Sopenharmony_ci	}
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	mutex_lock(&local->iflist_mtx);
307862306a36Sopenharmony_ci	list_for_each_entry(sdata, &local->interfaces, list) {
307962306a36Sopenharmony_ci		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
308062306a36Sopenharmony_ci			has_monitor = true;
308162306a36Sopenharmony_ci			continue;
308262306a36Sopenharmony_ci		}
308362306a36Sopenharmony_ci		sdata->deflink.user_power_level = local->user_power_level;
308462306a36Sopenharmony_ci		if (txp_type != sdata->vif.bss_conf.txpower_type)
308562306a36Sopenharmony_ci			update_txp_type = true;
308662306a36Sopenharmony_ci		sdata->vif.bss_conf.txpower_type = txp_type;
308762306a36Sopenharmony_ci	}
308862306a36Sopenharmony_ci	list_for_each_entry(sdata, &local->interfaces, list) {
308962306a36Sopenharmony_ci		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
309062306a36Sopenharmony_ci			continue;
309162306a36Sopenharmony_ci		ieee80211_recalc_txpower(sdata, update_txp_type);
309262306a36Sopenharmony_ci	}
309362306a36Sopenharmony_ci	mutex_unlock(&local->iflist_mtx);
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	if (has_monitor) {
309662306a36Sopenharmony_ci		sdata = wiphy_dereference(local->hw.wiphy,
309762306a36Sopenharmony_ci					  local->monitor_sdata);
309862306a36Sopenharmony_ci		if (sdata) {
309962306a36Sopenharmony_ci			sdata->deflink.user_power_level = local->user_power_level;
310062306a36Sopenharmony_ci			if (txp_type != sdata->vif.bss_conf.txpower_type)
310162306a36Sopenharmony_ci				update_txp_type = true;
310262306a36Sopenharmony_ci			sdata->vif.bss_conf.txpower_type = txp_type;
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci			ieee80211_recalc_txpower(sdata, update_txp_type);
310562306a36Sopenharmony_ci		}
310662306a36Sopenharmony_ci	}
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_ci	return 0;
310962306a36Sopenharmony_ci}
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_cistatic int ieee80211_get_tx_power(struct wiphy *wiphy,
311262306a36Sopenharmony_ci				  struct wireless_dev *wdev,
311362306a36Sopenharmony_ci				  int *dbm)
311462306a36Sopenharmony_ci{
311562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
311662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	if (local->ops->get_txpower)
311962306a36Sopenharmony_ci		return drv_get_txpower(local, sdata, dbm);
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	if (!local->use_chanctx)
312262306a36Sopenharmony_ci		*dbm = local->hw.conf.power_level;
312362306a36Sopenharmony_ci	else
312462306a36Sopenharmony_ci		*dbm = sdata->vif.bss_conf.txpower;
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ci	/* INT_MIN indicates no power level was set yet */
312762306a36Sopenharmony_ci	if (*dbm == INT_MIN)
312862306a36Sopenharmony_ci		return -EINVAL;
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	return 0;
313162306a36Sopenharmony_ci}
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_cistatic void ieee80211_rfkill_poll(struct wiphy *wiphy)
313462306a36Sopenharmony_ci{
313562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
313662306a36Sopenharmony_ci
313762306a36Sopenharmony_ci	drv_rfkill_poll(local);
313862306a36Sopenharmony_ci}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci#ifdef CONFIG_NL80211_TESTMODE
314162306a36Sopenharmony_cistatic int ieee80211_testmode_cmd(struct wiphy *wiphy,
314262306a36Sopenharmony_ci				  struct wireless_dev *wdev,
314362306a36Sopenharmony_ci				  void *data, int len)
314462306a36Sopenharmony_ci{
314562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
314662306a36Sopenharmony_ci	struct ieee80211_vif *vif = NULL;
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	if (!local->ops->testmode_cmd)
314962306a36Sopenharmony_ci		return -EOPNOTSUPP;
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_ci	if (wdev) {
315262306a36Sopenharmony_ci		struct ieee80211_sub_if_data *sdata;
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
315562306a36Sopenharmony_ci		if (sdata->flags & IEEE80211_SDATA_IN_DRIVER)
315662306a36Sopenharmony_ci			vif = &sdata->vif;
315762306a36Sopenharmony_ci	}
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	return local->ops->testmode_cmd(&local->hw, vif, data, len);
316062306a36Sopenharmony_ci}
316162306a36Sopenharmony_ci
316262306a36Sopenharmony_cistatic int ieee80211_testmode_dump(struct wiphy *wiphy,
316362306a36Sopenharmony_ci				   struct sk_buff *skb,
316462306a36Sopenharmony_ci				   struct netlink_callback *cb,
316562306a36Sopenharmony_ci				   void *data, int len)
316662306a36Sopenharmony_ci{
316762306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
316862306a36Sopenharmony_ci
316962306a36Sopenharmony_ci	if (!local->ops->testmode_dump)
317062306a36Sopenharmony_ci		return -EOPNOTSUPP;
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci	return local->ops->testmode_dump(&local->hw, skb, cb, data, len);
317362306a36Sopenharmony_ci}
317462306a36Sopenharmony_ci#endif
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ciint __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
317762306a36Sopenharmony_ci				 struct ieee80211_link_data *link,
317862306a36Sopenharmony_ci				 enum ieee80211_smps_mode smps_mode)
317962306a36Sopenharmony_ci{
318062306a36Sopenharmony_ci	const u8 *ap;
318162306a36Sopenharmony_ci	enum ieee80211_smps_mode old_req;
318262306a36Sopenharmony_ci	int err;
318362306a36Sopenharmony_ci	struct sta_info *sta;
318462306a36Sopenharmony_ci	bool tdls_peer_found = false;
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	lockdep_assert_held(&sdata->wdev.mtx);
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
318962306a36Sopenharmony_ci		return -EINVAL;
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	old_req = link->u.mgd.req_smps;
319262306a36Sopenharmony_ci	link->u.mgd.req_smps = smps_mode;
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci	if (old_req == smps_mode &&
319562306a36Sopenharmony_ci	    smps_mode != IEEE80211_SMPS_AUTOMATIC)
319662306a36Sopenharmony_ci		return 0;
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci	/*
319962306a36Sopenharmony_ci	 * If not associated, or current association is not an HT
320062306a36Sopenharmony_ci	 * association, there's no need to do anything, just store
320162306a36Sopenharmony_ci	 * the new value until we associate.
320262306a36Sopenharmony_ci	 */
320362306a36Sopenharmony_ci	if (!sdata->u.mgd.associated ||
320462306a36Sopenharmony_ci	    link->conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
320562306a36Sopenharmony_ci		return 0;
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci	ap = link->u.mgd.bssid;
320862306a36Sopenharmony_ci
320962306a36Sopenharmony_ci	rcu_read_lock();
321062306a36Sopenharmony_ci	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
321162306a36Sopenharmony_ci		if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
321262306a36Sopenharmony_ci		    !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
321362306a36Sopenharmony_ci			continue;
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci		tdls_peer_found = true;
321662306a36Sopenharmony_ci		break;
321762306a36Sopenharmony_ci	}
321862306a36Sopenharmony_ci	rcu_read_unlock();
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci	if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
322162306a36Sopenharmony_ci		if (tdls_peer_found || !sdata->u.mgd.powersave)
322262306a36Sopenharmony_ci			smps_mode = IEEE80211_SMPS_OFF;
322362306a36Sopenharmony_ci		else
322462306a36Sopenharmony_ci			smps_mode = IEEE80211_SMPS_DYNAMIC;
322562306a36Sopenharmony_ci	}
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	/* send SM PS frame to AP */
322862306a36Sopenharmony_ci	err = ieee80211_send_smps_action(sdata, smps_mode,
322962306a36Sopenharmony_ci					 ap, ap);
323062306a36Sopenharmony_ci	if (err)
323162306a36Sopenharmony_ci		link->u.mgd.req_smps = old_req;
323262306a36Sopenharmony_ci	else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
323362306a36Sopenharmony_ci		ieee80211_teardown_tdls_peers(sdata);
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci	return err;
323662306a36Sopenharmony_ci}
323762306a36Sopenharmony_ci
323862306a36Sopenharmony_cistatic int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
323962306a36Sopenharmony_ci				    bool enabled, int timeout)
324062306a36Sopenharmony_ci{
324162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
324262306a36Sopenharmony_ci	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
324362306a36Sopenharmony_ci	unsigned int link_id;
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_STATION)
324662306a36Sopenharmony_ci		return -EOPNOTSUPP;
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci	if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS))
324962306a36Sopenharmony_ci		return -EOPNOTSUPP;
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ci	if (enabled == sdata->u.mgd.powersave &&
325262306a36Sopenharmony_ci	    timeout == local->dynamic_ps_forced_timeout)
325362306a36Sopenharmony_ci		return 0;
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	sdata->u.mgd.powersave = enabled;
325662306a36Sopenharmony_ci	local->dynamic_ps_forced_timeout = timeout;
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	/* no change, but if automatic follow powersave */
325962306a36Sopenharmony_ci	sdata_lock(sdata);
326062306a36Sopenharmony_ci	for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
326162306a36Sopenharmony_ci		struct ieee80211_link_data *link;
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci		link = sdata_dereference(sdata->link[link_id], sdata);
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_ci		if (!link)
326662306a36Sopenharmony_ci			continue;
326762306a36Sopenharmony_ci		__ieee80211_request_smps_mgd(sdata, link,
326862306a36Sopenharmony_ci					     link->u.mgd.req_smps);
326962306a36Sopenharmony_ci	}
327062306a36Sopenharmony_ci	sdata_unlock(sdata);
327162306a36Sopenharmony_ci
327262306a36Sopenharmony_ci	if (ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS))
327362306a36Sopenharmony_ci		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	ieee80211_recalc_ps(local);
327662306a36Sopenharmony_ci	ieee80211_recalc_ps_vif(sdata);
327762306a36Sopenharmony_ci	ieee80211_check_fast_rx_iface(sdata);
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	return 0;
328062306a36Sopenharmony_ci}
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_cistatic int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
328362306a36Sopenharmony_ci					 struct net_device *dev,
328462306a36Sopenharmony_ci					 s32 rssi_thold, u32 rssi_hyst)
328562306a36Sopenharmony_ci{
328662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
328762306a36Sopenharmony_ci	struct ieee80211_vif *vif = &sdata->vif;
328862306a36Sopenharmony_ci	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	if (rssi_thold == bss_conf->cqm_rssi_thold &&
329162306a36Sopenharmony_ci	    rssi_hyst == bss_conf->cqm_rssi_hyst)
329262306a36Sopenharmony_ci		return 0;
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER &&
329562306a36Sopenharmony_ci	    !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
329662306a36Sopenharmony_ci		return -EOPNOTSUPP;
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	bss_conf->cqm_rssi_thold = rssi_thold;
329962306a36Sopenharmony_ci	bss_conf->cqm_rssi_hyst = rssi_hyst;
330062306a36Sopenharmony_ci	bss_conf->cqm_rssi_low = 0;
330162306a36Sopenharmony_ci	bss_conf->cqm_rssi_high = 0;
330262306a36Sopenharmony_ci	sdata->deflink.u.mgd.last_cqm_event_signal = 0;
330362306a36Sopenharmony_ci
330462306a36Sopenharmony_ci	/* tell the driver upon association, unless already associated */
330562306a36Sopenharmony_ci	if (sdata->u.mgd.associated &&
330662306a36Sopenharmony_ci	    sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
330762306a36Sopenharmony_ci		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
330862306a36Sopenharmony_ci						  BSS_CHANGED_CQM);
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci	return 0;
331162306a36Sopenharmony_ci}
331262306a36Sopenharmony_ci
331362306a36Sopenharmony_cistatic int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
331462306a36Sopenharmony_ci					       struct net_device *dev,
331562306a36Sopenharmony_ci					       s32 rssi_low, s32 rssi_high)
331662306a36Sopenharmony_ci{
331762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
331862306a36Sopenharmony_ci	struct ieee80211_vif *vif = &sdata->vif;
331962306a36Sopenharmony_ci	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_ci	if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
332262306a36Sopenharmony_ci		return -EOPNOTSUPP;
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_ci	bss_conf->cqm_rssi_low = rssi_low;
332562306a36Sopenharmony_ci	bss_conf->cqm_rssi_high = rssi_high;
332662306a36Sopenharmony_ci	bss_conf->cqm_rssi_thold = 0;
332762306a36Sopenharmony_ci	bss_conf->cqm_rssi_hyst = 0;
332862306a36Sopenharmony_ci	sdata->deflink.u.mgd.last_cqm_event_signal = 0;
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	/* tell the driver upon association, unless already associated */
333162306a36Sopenharmony_ci	if (sdata->u.mgd.associated &&
333262306a36Sopenharmony_ci	    sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
333362306a36Sopenharmony_ci		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
333462306a36Sopenharmony_ci						  BSS_CHANGED_CQM);
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	return 0;
333762306a36Sopenharmony_ci}
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_cistatic int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
334062306a36Sopenharmony_ci				      struct net_device *dev,
334162306a36Sopenharmony_ci				      unsigned int link_id,
334262306a36Sopenharmony_ci				      const u8 *addr,
334362306a36Sopenharmony_ci				      const struct cfg80211_bitrate_mask *mask)
334462306a36Sopenharmony_ci{
334562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
334662306a36Sopenharmony_ci	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
334762306a36Sopenharmony_ci	int i, ret;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	if (!ieee80211_sdata_running(sdata))
335062306a36Sopenharmony_ci		return -ENETDOWN;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	/*
335362306a36Sopenharmony_ci	 * If active validate the setting and reject it if it doesn't leave
335462306a36Sopenharmony_ci	 * at least one basic rate usable, since we really have to be able
335562306a36Sopenharmony_ci	 * to send something, and if we're an AP we have to be able to do
335662306a36Sopenharmony_ci	 * so at a basic rate so that all clients can receive it.
335762306a36Sopenharmony_ci	 */
335862306a36Sopenharmony_ci	if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) &&
335962306a36Sopenharmony_ci	    sdata->vif.bss_conf.chandef.chan) {
336062306a36Sopenharmony_ci		u32 basic_rates = sdata->vif.bss_conf.basic_rates;
336162306a36Sopenharmony_ci		enum nl80211_band band = sdata->vif.bss_conf.chandef.chan->band;
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci		if (!(mask->control[band].legacy & basic_rates))
336462306a36Sopenharmony_ci			return -EINVAL;
336562306a36Sopenharmony_ci	}
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
336862306a36Sopenharmony_ci		ret = drv_set_bitrate_mask(local, sdata, mask);
336962306a36Sopenharmony_ci		if (ret)
337062306a36Sopenharmony_ci			return ret;
337162306a36Sopenharmony_ci	}
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	for (i = 0; i < NUM_NL80211_BANDS; i++) {
337462306a36Sopenharmony_ci		struct ieee80211_supported_band *sband = wiphy->bands[i];
337562306a36Sopenharmony_ci		int j;
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci		sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
337862306a36Sopenharmony_ci		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
337962306a36Sopenharmony_ci		       sizeof(mask->control[i].ht_mcs));
338062306a36Sopenharmony_ci		memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
338162306a36Sopenharmony_ci		       mask->control[i].vht_mcs,
338262306a36Sopenharmony_ci		       sizeof(mask->control[i].vht_mcs));
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci		sdata->rc_has_mcs_mask[i] = false;
338562306a36Sopenharmony_ci		sdata->rc_has_vht_mcs_mask[i] = false;
338662306a36Sopenharmony_ci		if (!sband)
338762306a36Sopenharmony_ci			continue;
338862306a36Sopenharmony_ci
338962306a36Sopenharmony_ci		for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
339062306a36Sopenharmony_ci			if (sdata->rc_rateidx_mcs_mask[i][j] != 0xff) {
339162306a36Sopenharmony_ci				sdata->rc_has_mcs_mask[i] = true;
339262306a36Sopenharmony_ci				break;
339362306a36Sopenharmony_ci			}
339462306a36Sopenharmony_ci		}
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_ci		for (j = 0; j < NL80211_VHT_NSS_MAX; j++) {
339762306a36Sopenharmony_ci			if (sdata->rc_rateidx_vht_mcs_mask[i][j] != 0xffff) {
339862306a36Sopenharmony_ci				sdata->rc_has_vht_mcs_mask[i] = true;
339962306a36Sopenharmony_ci				break;
340062306a36Sopenharmony_ci			}
340162306a36Sopenharmony_ci		}
340262306a36Sopenharmony_ci	}
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci	return 0;
340562306a36Sopenharmony_ci}
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_cistatic int ieee80211_start_radar_detection(struct wiphy *wiphy,
340862306a36Sopenharmony_ci					   struct net_device *dev,
340962306a36Sopenharmony_ci					   struct cfg80211_chan_def *chandef,
341062306a36Sopenharmony_ci					   u32 cac_time_ms)
341162306a36Sopenharmony_ci{
341262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
341362306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
341462306a36Sopenharmony_ci	int err;
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci	mutex_lock(&local->mtx);
341762306a36Sopenharmony_ci	if (!list_empty(&local->roc_list) || local->scanning) {
341862306a36Sopenharmony_ci		err = -EBUSY;
341962306a36Sopenharmony_ci		goto out_unlock;
342062306a36Sopenharmony_ci	}
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci	/* whatever, but channel contexts should not complain about that one */
342362306a36Sopenharmony_ci	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
342462306a36Sopenharmony_ci	sdata->deflink.needed_rx_chains = local->rx_chains;
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci	err = ieee80211_link_use_channel(&sdata->deflink, chandef,
342762306a36Sopenharmony_ci					 IEEE80211_CHANCTX_SHARED);
342862306a36Sopenharmony_ci	if (err)
342962306a36Sopenharmony_ci		goto out_unlock;
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci	ieee80211_queue_delayed_work(&sdata->local->hw,
343262306a36Sopenharmony_ci				     &sdata->deflink.dfs_cac_timer_work,
343362306a36Sopenharmony_ci				     msecs_to_jiffies(cac_time_ms));
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci out_unlock:
343662306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
343762306a36Sopenharmony_ci	return err;
343862306a36Sopenharmony_ci}
343962306a36Sopenharmony_ci
344062306a36Sopenharmony_cistatic void ieee80211_end_cac(struct wiphy *wiphy,
344162306a36Sopenharmony_ci			      struct net_device *dev)
344262306a36Sopenharmony_ci{
344362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
344462306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
344562306a36Sopenharmony_ci
344662306a36Sopenharmony_ci	mutex_lock(&local->mtx);
344762306a36Sopenharmony_ci	list_for_each_entry(sdata, &local->interfaces, list) {
344862306a36Sopenharmony_ci		/* it might be waiting for the local->mtx, but then
344962306a36Sopenharmony_ci		 * by the time it gets it, sdata->wdev.cac_started
345062306a36Sopenharmony_ci		 * will no longer be true
345162306a36Sopenharmony_ci		 */
345262306a36Sopenharmony_ci		cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work);
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci		if (sdata->wdev.cac_started) {
345562306a36Sopenharmony_ci			ieee80211_link_release_channel(&sdata->deflink);
345662306a36Sopenharmony_ci			sdata->wdev.cac_started = false;
345762306a36Sopenharmony_ci		}
345862306a36Sopenharmony_ci	}
345962306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
346062306a36Sopenharmony_ci}
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_cistatic struct cfg80211_beacon_data *
346362306a36Sopenharmony_cicfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
346462306a36Sopenharmony_ci{
346562306a36Sopenharmony_ci	struct cfg80211_beacon_data *new_beacon;
346662306a36Sopenharmony_ci	u8 *pos;
346762306a36Sopenharmony_ci	int len;
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
347062306a36Sopenharmony_ci	      beacon->proberesp_ies_len + beacon->assocresp_ies_len +
347162306a36Sopenharmony_ci	      beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	if (beacon->mbssid_ies)
347462306a36Sopenharmony_ci		len += ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies,
347562306a36Sopenharmony_ci						       beacon->rnr_ies,
347662306a36Sopenharmony_ci						       beacon->mbssid_ies->cnt);
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
347962306a36Sopenharmony_ci	if (!new_beacon)
348062306a36Sopenharmony_ci		return NULL;
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci	if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
348362306a36Sopenharmony_ci		new_beacon->mbssid_ies =
348462306a36Sopenharmony_ci			kzalloc(struct_size(new_beacon->mbssid_ies,
348562306a36Sopenharmony_ci					    elem, beacon->mbssid_ies->cnt),
348662306a36Sopenharmony_ci				GFP_KERNEL);
348762306a36Sopenharmony_ci		if (!new_beacon->mbssid_ies) {
348862306a36Sopenharmony_ci			kfree(new_beacon);
348962306a36Sopenharmony_ci			return NULL;
349062306a36Sopenharmony_ci		}
349162306a36Sopenharmony_ci
349262306a36Sopenharmony_ci		if (beacon->rnr_ies && beacon->rnr_ies->cnt) {
349362306a36Sopenharmony_ci			new_beacon->rnr_ies =
349462306a36Sopenharmony_ci				kzalloc(struct_size(new_beacon->rnr_ies,
349562306a36Sopenharmony_ci						    elem, beacon->rnr_ies->cnt),
349662306a36Sopenharmony_ci					GFP_KERNEL);
349762306a36Sopenharmony_ci			if (!new_beacon->rnr_ies) {
349862306a36Sopenharmony_ci				kfree(new_beacon->mbssid_ies);
349962306a36Sopenharmony_ci				kfree(new_beacon);
350062306a36Sopenharmony_ci				return NULL;
350162306a36Sopenharmony_ci			}
350262306a36Sopenharmony_ci		}
350362306a36Sopenharmony_ci	}
350462306a36Sopenharmony_ci
350562306a36Sopenharmony_ci	pos = (u8 *)(new_beacon + 1);
350662306a36Sopenharmony_ci	if (beacon->head_len) {
350762306a36Sopenharmony_ci		new_beacon->head_len = beacon->head_len;
350862306a36Sopenharmony_ci		new_beacon->head = pos;
350962306a36Sopenharmony_ci		memcpy(pos, beacon->head, beacon->head_len);
351062306a36Sopenharmony_ci		pos += beacon->head_len;
351162306a36Sopenharmony_ci	}
351262306a36Sopenharmony_ci	if (beacon->tail_len) {
351362306a36Sopenharmony_ci		new_beacon->tail_len = beacon->tail_len;
351462306a36Sopenharmony_ci		new_beacon->tail = pos;
351562306a36Sopenharmony_ci		memcpy(pos, beacon->tail, beacon->tail_len);
351662306a36Sopenharmony_ci		pos += beacon->tail_len;
351762306a36Sopenharmony_ci	}
351862306a36Sopenharmony_ci	if (beacon->beacon_ies_len) {
351962306a36Sopenharmony_ci		new_beacon->beacon_ies_len = beacon->beacon_ies_len;
352062306a36Sopenharmony_ci		new_beacon->beacon_ies = pos;
352162306a36Sopenharmony_ci		memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
352262306a36Sopenharmony_ci		pos += beacon->beacon_ies_len;
352362306a36Sopenharmony_ci	}
352462306a36Sopenharmony_ci	if (beacon->proberesp_ies_len) {
352562306a36Sopenharmony_ci		new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
352662306a36Sopenharmony_ci		new_beacon->proberesp_ies = pos;
352762306a36Sopenharmony_ci		memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
352862306a36Sopenharmony_ci		pos += beacon->proberesp_ies_len;
352962306a36Sopenharmony_ci	}
353062306a36Sopenharmony_ci	if (beacon->assocresp_ies_len) {
353162306a36Sopenharmony_ci		new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
353262306a36Sopenharmony_ci		new_beacon->assocresp_ies = pos;
353362306a36Sopenharmony_ci		memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
353462306a36Sopenharmony_ci		pos += beacon->assocresp_ies_len;
353562306a36Sopenharmony_ci	}
353662306a36Sopenharmony_ci	if (beacon->probe_resp_len) {
353762306a36Sopenharmony_ci		new_beacon->probe_resp_len = beacon->probe_resp_len;
353862306a36Sopenharmony_ci		new_beacon->probe_resp = pos;
353962306a36Sopenharmony_ci		memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
354062306a36Sopenharmony_ci		pos += beacon->probe_resp_len;
354162306a36Sopenharmony_ci	}
354262306a36Sopenharmony_ci	if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
354362306a36Sopenharmony_ci		pos += ieee80211_copy_mbssid_beacon(pos,
354462306a36Sopenharmony_ci						    new_beacon->mbssid_ies,
354562306a36Sopenharmony_ci						    beacon->mbssid_ies);
354662306a36Sopenharmony_ci		if (beacon->rnr_ies && beacon->rnr_ies->cnt)
354762306a36Sopenharmony_ci			pos += ieee80211_copy_rnr_beacon(pos,
354862306a36Sopenharmony_ci							 new_beacon->rnr_ies,
354962306a36Sopenharmony_ci							 beacon->rnr_ies);
355062306a36Sopenharmony_ci	}
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	/* might copy -1, meaning no changes requested */
355362306a36Sopenharmony_ci	new_beacon->ftm_responder = beacon->ftm_responder;
355462306a36Sopenharmony_ci	if (beacon->lci) {
355562306a36Sopenharmony_ci		new_beacon->lci_len = beacon->lci_len;
355662306a36Sopenharmony_ci		new_beacon->lci = pos;
355762306a36Sopenharmony_ci		memcpy(pos, beacon->lci, beacon->lci_len);
355862306a36Sopenharmony_ci		pos += beacon->lci_len;
355962306a36Sopenharmony_ci	}
356062306a36Sopenharmony_ci	if (beacon->civicloc) {
356162306a36Sopenharmony_ci		new_beacon->civicloc_len = beacon->civicloc_len;
356262306a36Sopenharmony_ci		new_beacon->civicloc = pos;
356362306a36Sopenharmony_ci		memcpy(pos, beacon->civicloc, beacon->civicloc_len);
356462306a36Sopenharmony_ci		pos += beacon->civicloc_len;
356562306a36Sopenharmony_ci	}
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci	return new_beacon;
356862306a36Sopenharmony_ci}
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_civoid ieee80211_csa_finish(struct ieee80211_vif *vif)
357162306a36Sopenharmony_ci{
357262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
357362306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ci	rcu_read_lock();
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci	if (vif->mbssid_tx_vif == vif) {
357862306a36Sopenharmony_ci		/* Trigger ieee80211_csa_finish() on the non-transmitting
357962306a36Sopenharmony_ci		 * interfaces when channel switch is received on
358062306a36Sopenharmony_ci		 * transmitting interface
358162306a36Sopenharmony_ci		 */
358262306a36Sopenharmony_ci		struct ieee80211_sub_if_data *iter;
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci		list_for_each_entry_rcu(iter, &local->interfaces, list) {
358562306a36Sopenharmony_ci			if (!ieee80211_sdata_running(iter))
358662306a36Sopenharmony_ci				continue;
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci			if (iter == sdata || iter->vif.mbssid_tx_vif != vif)
358962306a36Sopenharmony_ci				continue;
359062306a36Sopenharmony_ci
359162306a36Sopenharmony_ci			ieee80211_queue_work(&iter->local->hw,
359262306a36Sopenharmony_ci					     &iter->deflink.csa_finalize_work);
359362306a36Sopenharmony_ci		}
359462306a36Sopenharmony_ci	}
359562306a36Sopenharmony_ci	ieee80211_queue_work(&local->hw, &sdata->deflink.csa_finalize_work);
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_ci	rcu_read_unlock();
359862306a36Sopenharmony_ci}
359962306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_csa_finish);
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_civoid ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif, bool block_tx)
360262306a36Sopenharmony_ci{
360362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
360462306a36Sopenharmony_ci	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
360562306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci	sdata->deflink.csa_block_tx = block_tx;
360862306a36Sopenharmony_ci	sdata_info(sdata, "channel switch failed, disconnecting\n");
360962306a36Sopenharmony_ci	wiphy_work_queue(local->hw.wiphy, &ifmgd->csa_connection_drop_work);
361062306a36Sopenharmony_ci}
361162306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_channel_switch_disconnect);
361262306a36Sopenharmony_ci
361362306a36Sopenharmony_cistatic int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
361462306a36Sopenharmony_ci					  u64 *changed)
361562306a36Sopenharmony_ci{
361662306a36Sopenharmony_ci	int err;
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci	switch (sdata->vif.type) {
361962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
362062306a36Sopenharmony_ci		if (!sdata->deflink.u.ap.next_beacon)
362162306a36Sopenharmony_ci			return -EINVAL;
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_ci		err = ieee80211_assign_beacon(sdata, &sdata->deflink,
362462306a36Sopenharmony_ci					      sdata->deflink.u.ap.next_beacon,
362562306a36Sopenharmony_ci					      NULL, NULL, changed);
362662306a36Sopenharmony_ci		ieee80211_free_next_beacon(&sdata->deflink);
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci		if (err < 0)
362962306a36Sopenharmony_ci			return err;
363062306a36Sopenharmony_ci		break;
363162306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
363262306a36Sopenharmony_ci		err = ieee80211_ibss_finish_csa(sdata, changed);
363362306a36Sopenharmony_ci		if (err < 0)
363462306a36Sopenharmony_ci			return err;
363562306a36Sopenharmony_ci		break;
363662306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
363762306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
363862306a36Sopenharmony_ci		err = ieee80211_mesh_finish_csa(sdata, changed);
363962306a36Sopenharmony_ci		if (err < 0)
364062306a36Sopenharmony_ci			return err;
364162306a36Sopenharmony_ci		break;
364262306a36Sopenharmony_ci#endif
364362306a36Sopenharmony_ci	default:
364462306a36Sopenharmony_ci		WARN_ON(1);
364562306a36Sopenharmony_ci		return -EINVAL;
364662306a36Sopenharmony_ci	}
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci	return 0;
364962306a36Sopenharmony_ci}
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_cistatic int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
365262306a36Sopenharmony_ci{
365362306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
365462306a36Sopenharmony_ci	u64 changed = 0;
365562306a36Sopenharmony_ci	int err;
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci	sdata_assert_lock(sdata);
365862306a36Sopenharmony_ci	lockdep_assert_held(&local->mtx);
365962306a36Sopenharmony_ci	lockdep_assert_held(&local->chanctx_mtx);
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	/*
366262306a36Sopenharmony_ci	 * using reservation isn't immediate as it may be deferred until later
366362306a36Sopenharmony_ci	 * with multi-vif. once reservation is complete it will re-schedule the
366462306a36Sopenharmony_ci	 * work with no reserved_chanctx so verify chandef to check if it
366562306a36Sopenharmony_ci	 * completed successfully
366662306a36Sopenharmony_ci	 */
366762306a36Sopenharmony_ci
366862306a36Sopenharmony_ci	if (sdata->deflink.reserved_chanctx) {
366962306a36Sopenharmony_ci		/*
367062306a36Sopenharmony_ci		 * with multi-vif csa driver may call ieee80211_csa_finish()
367162306a36Sopenharmony_ci		 * many times while waiting for other interfaces to use their
367262306a36Sopenharmony_ci		 * reservations
367362306a36Sopenharmony_ci		 */
367462306a36Sopenharmony_ci		if (sdata->deflink.reserved_ready)
367562306a36Sopenharmony_ci			return 0;
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci		return ieee80211_link_use_reserved_context(&sdata->deflink);
367862306a36Sopenharmony_ci	}
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_ci	if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
368162306a36Sopenharmony_ci					&sdata->deflink.csa_chandef))
368262306a36Sopenharmony_ci		return -EINVAL;
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci	sdata->vif.bss_conf.csa_active = false;
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	err = ieee80211_set_after_csa_beacon(sdata, &changed);
368762306a36Sopenharmony_ci	if (err)
368862306a36Sopenharmony_ci		return err;
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci	if (sdata->vif.bss_conf.eht_puncturing != sdata->vif.bss_conf.csa_punct_bitmap) {
369162306a36Sopenharmony_ci		sdata->vif.bss_conf.eht_puncturing =
369262306a36Sopenharmony_ci					sdata->vif.bss_conf.csa_punct_bitmap;
369362306a36Sopenharmony_ci		changed |= BSS_CHANGED_EHT_PUNCTURING;
369462306a36Sopenharmony_ci	}
369562306a36Sopenharmony_ci
369662306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
369762306a36Sopenharmony_ci
369862306a36Sopenharmony_ci	if (sdata->deflink.csa_block_tx) {
369962306a36Sopenharmony_ci		ieee80211_wake_vif_queues(local, sdata,
370062306a36Sopenharmony_ci					  IEEE80211_QUEUE_STOP_REASON_CSA);
370162306a36Sopenharmony_ci		sdata->deflink.csa_block_tx = false;
370262306a36Sopenharmony_ci	}
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci	err = drv_post_channel_switch(sdata);
370562306a36Sopenharmony_ci	if (err)
370662306a36Sopenharmony_ci		return err;
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ci	cfg80211_ch_switch_notify(sdata->dev, &sdata->deflink.csa_chandef, 0,
370962306a36Sopenharmony_ci				  sdata->vif.bss_conf.eht_puncturing);
371062306a36Sopenharmony_ci
371162306a36Sopenharmony_ci	return 0;
371262306a36Sopenharmony_ci}
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_cistatic void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
371562306a36Sopenharmony_ci{
371662306a36Sopenharmony_ci	if (__ieee80211_csa_finalize(sdata)) {
371762306a36Sopenharmony_ci		sdata_info(sdata, "failed to finalize CSA, disconnecting\n");
371862306a36Sopenharmony_ci		cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev,
371962306a36Sopenharmony_ci				    GFP_KERNEL);
372062306a36Sopenharmony_ci	}
372162306a36Sopenharmony_ci}
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_civoid ieee80211_csa_finalize_work(struct work_struct *work)
372462306a36Sopenharmony_ci{
372562306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata =
372662306a36Sopenharmony_ci		container_of(work, struct ieee80211_sub_if_data,
372762306a36Sopenharmony_ci			     deflink.csa_finalize_work);
372862306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	sdata_lock(sdata);
373162306a36Sopenharmony_ci	mutex_lock(&local->mtx);
373262306a36Sopenharmony_ci	mutex_lock(&local->chanctx_mtx);
373362306a36Sopenharmony_ci
373462306a36Sopenharmony_ci	/* AP might have been stopped while waiting for the lock. */
373562306a36Sopenharmony_ci	if (!sdata->vif.bss_conf.csa_active)
373662306a36Sopenharmony_ci		goto unlock;
373762306a36Sopenharmony_ci
373862306a36Sopenharmony_ci	if (!ieee80211_sdata_running(sdata))
373962306a36Sopenharmony_ci		goto unlock;
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci	ieee80211_csa_finalize(sdata);
374262306a36Sopenharmony_ci
374362306a36Sopenharmony_ciunlock:
374462306a36Sopenharmony_ci	mutex_unlock(&local->chanctx_mtx);
374562306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
374662306a36Sopenharmony_ci	sdata_unlock(sdata);
374762306a36Sopenharmony_ci}
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_cistatic int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
375062306a36Sopenharmony_ci				    struct cfg80211_csa_settings *params,
375162306a36Sopenharmony_ci				    u64 *changed)
375262306a36Sopenharmony_ci{
375362306a36Sopenharmony_ci	struct ieee80211_csa_settings csa = {};
375462306a36Sopenharmony_ci	int err;
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci	switch (sdata->vif.type) {
375762306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
375862306a36Sopenharmony_ci		sdata->deflink.u.ap.next_beacon =
375962306a36Sopenharmony_ci			cfg80211_beacon_dup(&params->beacon_after);
376062306a36Sopenharmony_ci		if (!sdata->deflink.u.ap.next_beacon)
376162306a36Sopenharmony_ci			return -ENOMEM;
376262306a36Sopenharmony_ci
376362306a36Sopenharmony_ci		/*
376462306a36Sopenharmony_ci		 * With a count of 0, we don't have to wait for any
376562306a36Sopenharmony_ci		 * TBTT before switching, so complete the CSA
376662306a36Sopenharmony_ci		 * immediately.  In theory, with a count == 1 we
376762306a36Sopenharmony_ci		 * should delay the switch until just before the next
376862306a36Sopenharmony_ci		 * TBTT, but that would complicate things so we switch
376962306a36Sopenharmony_ci		 * immediately too.  If we would delay the switch
377062306a36Sopenharmony_ci		 * until the next TBTT, we would have to set the probe
377162306a36Sopenharmony_ci		 * response here.
377262306a36Sopenharmony_ci		 *
377362306a36Sopenharmony_ci		 * TODO: A channel switch with count <= 1 without
377462306a36Sopenharmony_ci		 * sending a CSA action frame is kind of useless,
377562306a36Sopenharmony_ci		 * because the clients won't know we're changing
377662306a36Sopenharmony_ci		 * channels.  The action frame must be implemented
377762306a36Sopenharmony_ci		 * either here or in the userspace.
377862306a36Sopenharmony_ci		 */
377962306a36Sopenharmony_ci		if (params->count <= 1)
378062306a36Sopenharmony_ci			break;
378162306a36Sopenharmony_ci
378262306a36Sopenharmony_ci		if ((params->n_counter_offsets_beacon >
378362306a36Sopenharmony_ci		     IEEE80211_MAX_CNTDWN_COUNTERS_NUM) ||
378462306a36Sopenharmony_ci		    (params->n_counter_offsets_presp >
378562306a36Sopenharmony_ci		     IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) {
378662306a36Sopenharmony_ci			ieee80211_free_next_beacon(&sdata->deflink);
378762306a36Sopenharmony_ci			return -EINVAL;
378862306a36Sopenharmony_ci		}
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci		csa.counter_offsets_beacon = params->counter_offsets_beacon;
379162306a36Sopenharmony_ci		csa.counter_offsets_presp = params->counter_offsets_presp;
379262306a36Sopenharmony_ci		csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon;
379362306a36Sopenharmony_ci		csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
379462306a36Sopenharmony_ci		csa.count = params->count;
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_ci		err = ieee80211_assign_beacon(sdata, &sdata->deflink,
379762306a36Sopenharmony_ci					      &params->beacon_csa, &csa,
379862306a36Sopenharmony_ci					      NULL, changed);
379962306a36Sopenharmony_ci		if (err < 0) {
380062306a36Sopenharmony_ci			ieee80211_free_next_beacon(&sdata->deflink);
380162306a36Sopenharmony_ci			return err;
380262306a36Sopenharmony_ci		}
380362306a36Sopenharmony_ci
380462306a36Sopenharmony_ci		break;
380562306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
380662306a36Sopenharmony_ci		if (!sdata->vif.cfg.ibss_joined)
380762306a36Sopenharmony_ci			return -EINVAL;
380862306a36Sopenharmony_ci
380962306a36Sopenharmony_ci		if (params->chandef.width != sdata->u.ibss.chandef.width)
381062306a36Sopenharmony_ci			return -EINVAL;
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci		switch (params->chandef.width) {
381362306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_40:
381462306a36Sopenharmony_ci			if (cfg80211_get_chandef_type(&params->chandef) !=
381562306a36Sopenharmony_ci			    cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
381662306a36Sopenharmony_ci				return -EINVAL;
381762306a36Sopenharmony_ci			break;
381862306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_5:
381962306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_10:
382062306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_20_NOHT:
382162306a36Sopenharmony_ci		case NL80211_CHAN_WIDTH_20:
382262306a36Sopenharmony_ci			break;
382362306a36Sopenharmony_ci		default:
382462306a36Sopenharmony_ci			return -EINVAL;
382562306a36Sopenharmony_ci		}
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_ci		/* changes into another band are not supported */
382862306a36Sopenharmony_ci		if (sdata->u.ibss.chandef.chan->band !=
382962306a36Sopenharmony_ci		    params->chandef.chan->band)
383062306a36Sopenharmony_ci			return -EINVAL;
383162306a36Sopenharmony_ci
383262306a36Sopenharmony_ci		/* see comments in the NL80211_IFTYPE_AP block */
383362306a36Sopenharmony_ci		if (params->count > 1) {
383462306a36Sopenharmony_ci			err = ieee80211_ibss_csa_beacon(sdata, params, changed);
383562306a36Sopenharmony_ci			if (err < 0)
383662306a36Sopenharmony_ci				return err;
383762306a36Sopenharmony_ci		}
383862306a36Sopenharmony_ci
383962306a36Sopenharmony_ci		ieee80211_send_action_csa(sdata, params);
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci		break;
384262306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
384362306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT: {
384462306a36Sopenharmony_ci		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
384562306a36Sopenharmony_ci
384662306a36Sopenharmony_ci		/* changes into another band are not supported */
384762306a36Sopenharmony_ci		if (sdata->vif.bss_conf.chandef.chan->band !=
384862306a36Sopenharmony_ci		    params->chandef.chan->band)
384962306a36Sopenharmony_ci			return -EINVAL;
385062306a36Sopenharmony_ci
385162306a36Sopenharmony_ci		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) {
385262306a36Sopenharmony_ci			ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
385362306a36Sopenharmony_ci			if (!ifmsh->pre_value)
385462306a36Sopenharmony_ci				ifmsh->pre_value = 1;
385562306a36Sopenharmony_ci			else
385662306a36Sopenharmony_ci				ifmsh->pre_value++;
385762306a36Sopenharmony_ci		}
385862306a36Sopenharmony_ci
385962306a36Sopenharmony_ci		/* see comments in the NL80211_IFTYPE_AP block */
386062306a36Sopenharmony_ci		if (params->count > 1) {
386162306a36Sopenharmony_ci			err = ieee80211_mesh_csa_beacon(sdata, params, changed);
386262306a36Sopenharmony_ci			if (err < 0) {
386362306a36Sopenharmony_ci				ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
386462306a36Sopenharmony_ci				return err;
386562306a36Sopenharmony_ci			}
386662306a36Sopenharmony_ci		}
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
386962306a36Sopenharmony_ci			ieee80211_send_action_csa(sdata, params);
387062306a36Sopenharmony_ci
387162306a36Sopenharmony_ci		break;
387262306a36Sopenharmony_ci		}
387362306a36Sopenharmony_ci#endif
387462306a36Sopenharmony_ci	default:
387562306a36Sopenharmony_ci		return -EOPNOTSUPP;
387662306a36Sopenharmony_ci	}
387762306a36Sopenharmony_ci
387862306a36Sopenharmony_ci	return 0;
387962306a36Sopenharmony_ci}
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_cistatic void ieee80211_color_change_abort(struct ieee80211_sub_if_data  *sdata)
388262306a36Sopenharmony_ci{
388362306a36Sopenharmony_ci	sdata->vif.bss_conf.color_change_active = false;
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ci	ieee80211_free_next_beacon(&sdata->deflink);
388662306a36Sopenharmony_ci
388762306a36Sopenharmony_ci	cfg80211_color_change_aborted_notify(sdata->dev);
388862306a36Sopenharmony_ci}
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_cistatic int
389162306a36Sopenharmony_ci__ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
389262306a36Sopenharmony_ci			   struct cfg80211_csa_settings *params)
389362306a36Sopenharmony_ci{
389462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
389562306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
389662306a36Sopenharmony_ci	struct ieee80211_channel_switch ch_switch;
389762306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *conf;
389862306a36Sopenharmony_ci	struct ieee80211_chanctx *chanctx;
389962306a36Sopenharmony_ci	u64 changed = 0;
390062306a36Sopenharmony_ci	int err;
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci	sdata_assert_lock(sdata);
390362306a36Sopenharmony_ci	lockdep_assert_held(&local->mtx);
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci	if (!list_empty(&local->roc_list) || local->scanning)
390662306a36Sopenharmony_ci		return -EBUSY;
390762306a36Sopenharmony_ci
390862306a36Sopenharmony_ci	if (sdata->wdev.cac_started)
390962306a36Sopenharmony_ci		return -EBUSY;
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	if (cfg80211_chandef_identical(&params->chandef,
391262306a36Sopenharmony_ci				       &sdata->vif.bss_conf.chandef))
391362306a36Sopenharmony_ci		return -EINVAL;
391462306a36Sopenharmony_ci
391562306a36Sopenharmony_ci	/* don't allow another channel switch if one is already active. */
391662306a36Sopenharmony_ci	if (sdata->vif.bss_conf.csa_active)
391762306a36Sopenharmony_ci		return -EBUSY;
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci	mutex_lock(&local->chanctx_mtx);
392062306a36Sopenharmony_ci	conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf,
392162306a36Sopenharmony_ci					 lockdep_is_held(&local->chanctx_mtx));
392262306a36Sopenharmony_ci	if (!conf) {
392362306a36Sopenharmony_ci		err = -EBUSY;
392462306a36Sopenharmony_ci		goto out;
392562306a36Sopenharmony_ci	}
392662306a36Sopenharmony_ci
392762306a36Sopenharmony_ci	if (params->chandef.chan->freq_offset) {
392862306a36Sopenharmony_ci		/* this may work, but is untested */
392962306a36Sopenharmony_ci		err = -EOPNOTSUPP;
393062306a36Sopenharmony_ci		goto out;
393162306a36Sopenharmony_ci	}
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ci	chanctx = container_of(conf, struct ieee80211_chanctx, conf);
393462306a36Sopenharmony_ci
393562306a36Sopenharmony_ci	ch_switch.timestamp = 0;
393662306a36Sopenharmony_ci	ch_switch.device_timestamp = 0;
393762306a36Sopenharmony_ci	ch_switch.block_tx = params->block_tx;
393862306a36Sopenharmony_ci	ch_switch.chandef = params->chandef;
393962306a36Sopenharmony_ci	ch_switch.count = params->count;
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci	err = drv_pre_channel_switch(sdata, &ch_switch);
394262306a36Sopenharmony_ci	if (err)
394362306a36Sopenharmony_ci		goto out;
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ci	err = ieee80211_link_reserve_chanctx(&sdata->deflink, &params->chandef,
394662306a36Sopenharmony_ci					     chanctx->mode,
394762306a36Sopenharmony_ci					     params->radar_required);
394862306a36Sopenharmony_ci	if (err)
394962306a36Sopenharmony_ci		goto out;
395062306a36Sopenharmony_ci
395162306a36Sopenharmony_ci	/* if reservation is invalid then this will fail */
395262306a36Sopenharmony_ci	err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
395362306a36Sopenharmony_ci	if (err) {
395462306a36Sopenharmony_ci		ieee80211_link_unreserve_chanctx(&sdata->deflink);
395562306a36Sopenharmony_ci		goto out;
395662306a36Sopenharmony_ci	}
395762306a36Sopenharmony_ci
395862306a36Sopenharmony_ci	/* if there is a color change in progress, abort it */
395962306a36Sopenharmony_ci	if (sdata->vif.bss_conf.color_change_active)
396062306a36Sopenharmony_ci		ieee80211_color_change_abort(sdata);
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci	err = ieee80211_set_csa_beacon(sdata, params, &changed);
396362306a36Sopenharmony_ci	if (err) {
396462306a36Sopenharmony_ci		ieee80211_link_unreserve_chanctx(&sdata->deflink);
396562306a36Sopenharmony_ci		goto out;
396662306a36Sopenharmony_ci	}
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	if (params->punct_bitmap && !sdata->vif.bss_conf.eht_support)
396962306a36Sopenharmony_ci		goto out;
397062306a36Sopenharmony_ci
397162306a36Sopenharmony_ci	sdata->deflink.csa_chandef = params->chandef;
397262306a36Sopenharmony_ci	sdata->deflink.csa_block_tx = params->block_tx;
397362306a36Sopenharmony_ci	sdata->vif.bss_conf.csa_active = true;
397462306a36Sopenharmony_ci	sdata->vif.bss_conf.csa_punct_bitmap = params->punct_bitmap;
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	if (sdata->deflink.csa_block_tx)
397762306a36Sopenharmony_ci		ieee80211_stop_vif_queues(local, sdata,
397862306a36Sopenharmony_ci					  IEEE80211_QUEUE_STOP_REASON_CSA);
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	cfg80211_ch_switch_started_notify(sdata->dev,
398162306a36Sopenharmony_ci					  &sdata->deflink.csa_chandef, 0,
398262306a36Sopenharmony_ci					  params->count, params->block_tx,
398362306a36Sopenharmony_ci					  sdata->vif.bss_conf.csa_punct_bitmap);
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci	if (changed) {
398662306a36Sopenharmony_ci		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
398762306a36Sopenharmony_ci						  changed);
398862306a36Sopenharmony_ci		drv_channel_switch_beacon(sdata, &params->chandef);
398962306a36Sopenharmony_ci	} else {
399062306a36Sopenharmony_ci		/* if the beacon didn't change, we can finalize immediately */
399162306a36Sopenharmony_ci		ieee80211_csa_finalize(sdata);
399262306a36Sopenharmony_ci	}
399362306a36Sopenharmony_ci
399462306a36Sopenharmony_ciout:
399562306a36Sopenharmony_ci	mutex_unlock(&local->chanctx_mtx);
399662306a36Sopenharmony_ci	return err;
399762306a36Sopenharmony_ci}
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_ciint ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
400062306a36Sopenharmony_ci			     struct cfg80211_csa_settings *params)
400162306a36Sopenharmony_ci{
400262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
400362306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
400462306a36Sopenharmony_ci	int err;
400562306a36Sopenharmony_ci
400662306a36Sopenharmony_ci	mutex_lock(&local->mtx);
400762306a36Sopenharmony_ci	err = __ieee80211_channel_switch(wiphy, dev, params);
400862306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci	return err;
401162306a36Sopenharmony_ci}
401262306a36Sopenharmony_ci
401362306a36Sopenharmony_ciu64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
401462306a36Sopenharmony_ci{
401562306a36Sopenharmony_ci	lockdep_assert_held(&local->mtx);
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_ci	local->roc_cookie_counter++;
401862306a36Sopenharmony_ci
401962306a36Sopenharmony_ci	/* wow, you wrapped 64 bits ... more likely a bug */
402062306a36Sopenharmony_ci	if (WARN_ON(local->roc_cookie_counter == 0))
402162306a36Sopenharmony_ci		local->roc_cookie_counter++;
402262306a36Sopenharmony_ci
402362306a36Sopenharmony_ci	return local->roc_cookie_counter;
402462306a36Sopenharmony_ci}
402562306a36Sopenharmony_ci
402662306a36Sopenharmony_ciint ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
402762306a36Sopenharmony_ci			     u64 *cookie, gfp_t gfp)
402862306a36Sopenharmony_ci{
402962306a36Sopenharmony_ci	unsigned long spin_flags;
403062306a36Sopenharmony_ci	struct sk_buff *ack_skb;
403162306a36Sopenharmony_ci	int id;
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci	ack_skb = skb_copy(skb, gfp);
403462306a36Sopenharmony_ci	if (!ack_skb)
403562306a36Sopenharmony_ci		return -ENOMEM;
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci	spin_lock_irqsave(&local->ack_status_lock, spin_flags);
403862306a36Sopenharmony_ci	id = idr_alloc(&local->ack_status_frames, ack_skb,
403962306a36Sopenharmony_ci		       1, 0x2000, GFP_ATOMIC);
404062306a36Sopenharmony_ci	spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
404162306a36Sopenharmony_ci
404262306a36Sopenharmony_ci	if (id < 0) {
404362306a36Sopenharmony_ci		kfree_skb(ack_skb);
404462306a36Sopenharmony_ci		return -ENOMEM;
404562306a36Sopenharmony_ci	}
404662306a36Sopenharmony_ci
404762306a36Sopenharmony_ci	IEEE80211_SKB_CB(skb)->ack_frame_id = id;
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci	*cookie = ieee80211_mgmt_tx_cookie(local);
405062306a36Sopenharmony_ci	IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
405162306a36Sopenharmony_ci
405262306a36Sopenharmony_ci	return 0;
405362306a36Sopenharmony_ci}
405462306a36Sopenharmony_ci
405562306a36Sopenharmony_cistatic void
405662306a36Sopenharmony_ciieee80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
405762306a36Sopenharmony_ci					  struct wireless_dev *wdev,
405862306a36Sopenharmony_ci					  struct mgmt_frame_regs *upd)
405962306a36Sopenharmony_ci{
406062306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
406162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
406262306a36Sopenharmony_ci	u32 preq_mask = BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
406362306a36Sopenharmony_ci	u32 action_mask = BIT(IEEE80211_STYPE_ACTION >> 4);
406462306a36Sopenharmony_ci	bool global_change, intf_change;
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci	global_change =
406762306a36Sopenharmony_ci		(local->probe_req_reg != !!(upd->global_stypes & preq_mask)) ||
406862306a36Sopenharmony_ci		(local->rx_mcast_action_reg !=
406962306a36Sopenharmony_ci		 !!(upd->global_mcast_stypes & action_mask));
407062306a36Sopenharmony_ci	local->probe_req_reg = upd->global_stypes & preq_mask;
407162306a36Sopenharmony_ci	local->rx_mcast_action_reg = upd->global_mcast_stypes & action_mask;
407262306a36Sopenharmony_ci
407362306a36Sopenharmony_ci	intf_change = (sdata->vif.probe_req_reg !=
407462306a36Sopenharmony_ci		       !!(upd->interface_stypes & preq_mask)) ||
407562306a36Sopenharmony_ci		(sdata->vif.rx_mcast_action_reg !=
407662306a36Sopenharmony_ci		 !!(upd->interface_mcast_stypes & action_mask));
407762306a36Sopenharmony_ci	sdata->vif.probe_req_reg = upd->interface_stypes & preq_mask;
407862306a36Sopenharmony_ci	sdata->vif.rx_mcast_action_reg =
407962306a36Sopenharmony_ci		upd->interface_mcast_stypes & action_mask;
408062306a36Sopenharmony_ci
408162306a36Sopenharmony_ci	if (!local->open_count)
408262306a36Sopenharmony_ci		return;
408362306a36Sopenharmony_ci
408462306a36Sopenharmony_ci	if (intf_change && ieee80211_sdata_running(sdata))
408562306a36Sopenharmony_ci		drv_config_iface_filter(local, sdata,
408662306a36Sopenharmony_ci					sdata->vif.probe_req_reg ?
408762306a36Sopenharmony_ci						FIF_PROBE_REQ : 0,
408862306a36Sopenharmony_ci					FIF_PROBE_REQ);
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci	if (global_change)
409162306a36Sopenharmony_ci		ieee80211_configure_filter(local);
409262306a36Sopenharmony_ci}
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_cistatic int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
409562306a36Sopenharmony_ci{
409662306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
409762306a36Sopenharmony_ci
409862306a36Sopenharmony_ci	if (local->started)
409962306a36Sopenharmony_ci		return -EOPNOTSUPP;
410062306a36Sopenharmony_ci
410162306a36Sopenharmony_ci	return drv_set_antenna(local, tx_ant, rx_ant);
410262306a36Sopenharmony_ci}
410362306a36Sopenharmony_ci
410462306a36Sopenharmony_cistatic int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
410562306a36Sopenharmony_ci{
410662306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
410762306a36Sopenharmony_ci
410862306a36Sopenharmony_ci	return drv_get_antenna(local, tx_ant, rx_ant);
410962306a36Sopenharmony_ci}
411062306a36Sopenharmony_ci
411162306a36Sopenharmony_cistatic int ieee80211_set_rekey_data(struct wiphy *wiphy,
411262306a36Sopenharmony_ci				    struct net_device *dev,
411362306a36Sopenharmony_ci				    struct cfg80211_gtk_rekey_data *data)
411462306a36Sopenharmony_ci{
411562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
411662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
411762306a36Sopenharmony_ci
411862306a36Sopenharmony_ci	if (!local->ops->set_rekey_data)
411962306a36Sopenharmony_ci		return -EOPNOTSUPP;
412062306a36Sopenharmony_ci
412162306a36Sopenharmony_ci	drv_set_rekey_data(local, sdata, data);
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci	return 0;
412462306a36Sopenharmony_ci}
412562306a36Sopenharmony_ci
412662306a36Sopenharmony_cistatic int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
412762306a36Sopenharmony_ci				  const u8 *peer, u64 *cookie)
412862306a36Sopenharmony_ci{
412962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
413062306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
413162306a36Sopenharmony_ci	struct ieee80211_qos_hdr *nullfunc;
413262306a36Sopenharmony_ci	struct sk_buff *skb;
413362306a36Sopenharmony_ci	int size = sizeof(*nullfunc);
413462306a36Sopenharmony_ci	__le16 fc;
413562306a36Sopenharmony_ci	bool qos;
413662306a36Sopenharmony_ci	struct ieee80211_tx_info *info;
413762306a36Sopenharmony_ci	struct sta_info *sta;
413862306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *chanctx_conf;
413962306a36Sopenharmony_ci	enum nl80211_band band;
414062306a36Sopenharmony_ci	int ret;
414162306a36Sopenharmony_ci
414262306a36Sopenharmony_ci	/* the lock is needed to assign the cookie later */
414362306a36Sopenharmony_ci	mutex_lock(&local->mtx);
414462306a36Sopenharmony_ci
414562306a36Sopenharmony_ci	rcu_read_lock();
414662306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, peer);
414762306a36Sopenharmony_ci	if (!sta) {
414862306a36Sopenharmony_ci		ret = -ENOLINK;
414962306a36Sopenharmony_ci		goto unlock;
415062306a36Sopenharmony_ci	}
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci	qos = sta->sta.wme;
415362306a36Sopenharmony_ci
415462306a36Sopenharmony_ci	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
415562306a36Sopenharmony_ci	if (WARN_ON(!chanctx_conf)) {
415662306a36Sopenharmony_ci		ret = -EINVAL;
415762306a36Sopenharmony_ci		goto unlock;
415862306a36Sopenharmony_ci	}
415962306a36Sopenharmony_ci	band = chanctx_conf->def.chan->band;
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_ci	if (qos) {
416262306a36Sopenharmony_ci		fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
416362306a36Sopenharmony_ci				 IEEE80211_STYPE_QOS_NULLFUNC |
416462306a36Sopenharmony_ci				 IEEE80211_FCTL_FROMDS);
416562306a36Sopenharmony_ci	} else {
416662306a36Sopenharmony_ci		size -= 2;
416762306a36Sopenharmony_ci		fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
416862306a36Sopenharmony_ci				 IEEE80211_STYPE_NULLFUNC |
416962306a36Sopenharmony_ci				 IEEE80211_FCTL_FROMDS);
417062306a36Sopenharmony_ci	}
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
417362306a36Sopenharmony_ci	if (!skb) {
417462306a36Sopenharmony_ci		ret = -ENOMEM;
417562306a36Sopenharmony_ci		goto unlock;
417662306a36Sopenharmony_ci	}
417762306a36Sopenharmony_ci
417862306a36Sopenharmony_ci	skb->dev = dev;
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci	skb_reserve(skb, local->hw.extra_tx_headroom);
418162306a36Sopenharmony_ci
418262306a36Sopenharmony_ci	nullfunc = skb_put(skb, size);
418362306a36Sopenharmony_ci	nullfunc->frame_control = fc;
418462306a36Sopenharmony_ci	nullfunc->duration_id = 0;
418562306a36Sopenharmony_ci	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
418662306a36Sopenharmony_ci	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
418762306a36Sopenharmony_ci	memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
418862306a36Sopenharmony_ci	nullfunc->seq_ctrl = 0;
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
419362306a36Sopenharmony_ci		       IEEE80211_TX_INTFL_NL80211_FRAME_TX;
419462306a36Sopenharmony_ci	info->band = band;
419562306a36Sopenharmony_ci
419662306a36Sopenharmony_ci	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
419762306a36Sopenharmony_ci	skb->priority = 7;
419862306a36Sopenharmony_ci	if (qos)
419962306a36Sopenharmony_ci		nullfunc->qos_ctrl = cpu_to_le16(7);
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci	ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_ATOMIC);
420262306a36Sopenharmony_ci	if (ret) {
420362306a36Sopenharmony_ci		kfree_skb(skb);
420462306a36Sopenharmony_ci		goto unlock;
420562306a36Sopenharmony_ci	}
420662306a36Sopenharmony_ci
420762306a36Sopenharmony_ci	local_bh_disable();
420862306a36Sopenharmony_ci	ieee80211_xmit(sdata, sta, skb);
420962306a36Sopenharmony_ci	local_bh_enable();
421062306a36Sopenharmony_ci
421162306a36Sopenharmony_ci	ret = 0;
421262306a36Sopenharmony_ciunlock:
421362306a36Sopenharmony_ci	rcu_read_unlock();
421462306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
421562306a36Sopenharmony_ci
421662306a36Sopenharmony_ci	return ret;
421762306a36Sopenharmony_ci}
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_cistatic int ieee80211_cfg_get_channel(struct wiphy *wiphy,
422062306a36Sopenharmony_ci				     struct wireless_dev *wdev,
422162306a36Sopenharmony_ci				     unsigned int link_id,
422262306a36Sopenharmony_ci				     struct cfg80211_chan_def *chandef)
422362306a36Sopenharmony_ci{
422462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
422562306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
422662306a36Sopenharmony_ci	struct ieee80211_chanctx_conf *chanctx_conf;
422762306a36Sopenharmony_ci	struct ieee80211_link_data *link;
422862306a36Sopenharmony_ci	int ret = -ENODATA;
422962306a36Sopenharmony_ci
423062306a36Sopenharmony_ci	rcu_read_lock();
423162306a36Sopenharmony_ci	link = rcu_dereference(sdata->link[link_id]);
423262306a36Sopenharmony_ci	if (!link) {
423362306a36Sopenharmony_ci		ret = -ENOLINK;
423462306a36Sopenharmony_ci		goto out;
423562306a36Sopenharmony_ci	}
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_ci	chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
423862306a36Sopenharmony_ci	if (chanctx_conf) {
423962306a36Sopenharmony_ci		*chandef = link->conf->chandef;
424062306a36Sopenharmony_ci		ret = 0;
424162306a36Sopenharmony_ci	} else if (local->open_count > 0 &&
424262306a36Sopenharmony_ci		   local->open_count == local->monitors &&
424362306a36Sopenharmony_ci		   sdata->vif.type == NL80211_IFTYPE_MONITOR) {
424462306a36Sopenharmony_ci		if (local->use_chanctx)
424562306a36Sopenharmony_ci			*chandef = local->monitor_chandef;
424662306a36Sopenharmony_ci		else
424762306a36Sopenharmony_ci			*chandef = local->_oper_chandef;
424862306a36Sopenharmony_ci		ret = 0;
424962306a36Sopenharmony_ci	}
425062306a36Sopenharmony_ciout:
425162306a36Sopenharmony_ci	rcu_read_unlock();
425262306a36Sopenharmony_ci
425362306a36Sopenharmony_ci	return ret;
425462306a36Sopenharmony_ci}
425562306a36Sopenharmony_ci
425662306a36Sopenharmony_ci#ifdef CONFIG_PM
425762306a36Sopenharmony_cistatic void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)
425862306a36Sopenharmony_ci{
425962306a36Sopenharmony_ci	drv_set_wakeup(wiphy_priv(wiphy), enabled);
426062306a36Sopenharmony_ci}
426162306a36Sopenharmony_ci#endif
426262306a36Sopenharmony_ci
426362306a36Sopenharmony_cistatic int ieee80211_set_qos_map(struct wiphy *wiphy,
426462306a36Sopenharmony_ci				 struct net_device *dev,
426562306a36Sopenharmony_ci				 struct cfg80211_qos_map *qos_map)
426662306a36Sopenharmony_ci{
426762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
426862306a36Sopenharmony_ci	struct mac80211_qos_map *new_qos_map, *old_qos_map;
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci	if (qos_map) {
427162306a36Sopenharmony_ci		new_qos_map = kzalloc(sizeof(*new_qos_map), GFP_KERNEL);
427262306a36Sopenharmony_ci		if (!new_qos_map)
427362306a36Sopenharmony_ci			return -ENOMEM;
427462306a36Sopenharmony_ci		memcpy(&new_qos_map->qos_map, qos_map, sizeof(*qos_map));
427562306a36Sopenharmony_ci	} else {
427662306a36Sopenharmony_ci		/* A NULL qos_map was passed to disable QoS mapping */
427762306a36Sopenharmony_ci		new_qos_map = NULL;
427862306a36Sopenharmony_ci	}
427962306a36Sopenharmony_ci
428062306a36Sopenharmony_ci	old_qos_map = sdata_dereference(sdata->qos_map, sdata);
428162306a36Sopenharmony_ci	rcu_assign_pointer(sdata->qos_map, new_qos_map);
428262306a36Sopenharmony_ci	if (old_qos_map)
428362306a36Sopenharmony_ci		kfree_rcu(old_qos_map, rcu_head);
428462306a36Sopenharmony_ci
428562306a36Sopenharmony_ci	return 0;
428662306a36Sopenharmony_ci}
428762306a36Sopenharmony_ci
428862306a36Sopenharmony_cistatic int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
428962306a36Sopenharmony_ci				      struct net_device *dev,
429062306a36Sopenharmony_ci				      unsigned int link_id,
429162306a36Sopenharmony_ci				      struct cfg80211_chan_def *chandef)
429262306a36Sopenharmony_ci{
429362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
429462306a36Sopenharmony_ci	struct ieee80211_link_data *link;
429562306a36Sopenharmony_ci	int ret;
429662306a36Sopenharmony_ci	u64 changed = 0;
429762306a36Sopenharmony_ci
429862306a36Sopenharmony_ci	link = sdata_dereference(sdata->link[link_id], sdata);
429962306a36Sopenharmony_ci
430062306a36Sopenharmony_ci	ret = ieee80211_link_change_bandwidth(link, chandef, &changed);
430162306a36Sopenharmony_ci	if (ret == 0)
430262306a36Sopenharmony_ci		ieee80211_link_info_change_notify(sdata, link, changed);
430362306a36Sopenharmony_ci
430462306a36Sopenharmony_ci	return ret;
430562306a36Sopenharmony_ci}
430662306a36Sopenharmony_ci
430762306a36Sopenharmony_cistatic int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev,
430862306a36Sopenharmony_ci			       u8 tsid, const u8 *peer, u8 up,
430962306a36Sopenharmony_ci			       u16 admitted_time)
431062306a36Sopenharmony_ci{
431162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
431262306a36Sopenharmony_ci	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
431362306a36Sopenharmony_ci	int ac = ieee802_1d_to_ac[up];
431462306a36Sopenharmony_ci
431562306a36Sopenharmony_ci	if (sdata->vif.type != NL80211_IFTYPE_STATION)
431662306a36Sopenharmony_ci		return -EOPNOTSUPP;
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	if (!(sdata->wmm_acm & BIT(up)))
431962306a36Sopenharmony_ci		return -EINVAL;
432062306a36Sopenharmony_ci
432162306a36Sopenharmony_ci	if (ifmgd->tx_tspec[ac].admitted_time)
432262306a36Sopenharmony_ci		return -EBUSY;
432362306a36Sopenharmony_ci
432462306a36Sopenharmony_ci	if (admitted_time) {
432562306a36Sopenharmony_ci		ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time;
432662306a36Sopenharmony_ci		ifmgd->tx_tspec[ac].tsid = tsid;
432762306a36Sopenharmony_ci		ifmgd->tx_tspec[ac].up = up;
432862306a36Sopenharmony_ci	}
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci	return 0;
433162306a36Sopenharmony_ci}
433262306a36Sopenharmony_ci
433362306a36Sopenharmony_cistatic int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
433462306a36Sopenharmony_ci			       u8 tsid, const u8 *peer)
433562306a36Sopenharmony_ci{
433662306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
433762306a36Sopenharmony_ci	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
433862306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
433962306a36Sopenharmony_ci	int ac;
434062306a36Sopenharmony_ci
434162306a36Sopenharmony_ci	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
434262306a36Sopenharmony_ci		struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
434362306a36Sopenharmony_ci
434462306a36Sopenharmony_ci		/* skip unused entries */
434562306a36Sopenharmony_ci		if (!tx_tspec->admitted_time)
434662306a36Sopenharmony_ci			continue;
434762306a36Sopenharmony_ci
434862306a36Sopenharmony_ci		if (tx_tspec->tsid != tsid)
434962306a36Sopenharmony_ci			continue;
435062306a36Sopenharmony_ci
435162306a36Sopenharmony_ci		/* due to this new packets will be reassigned to non-ACM ACs */
435262306a36Sopenharmony_ci		tx_tspec->up = -1;
435362306a36Sopenharmony_ci
435462306a36Sopenharmony_ci		/* Make sure that all packets have been sent to avoid to
435562306a36Sopenharmony_ci		 * restore the QoS params on packets that are still on the
435662306a36Sopenharmony_ci		 * queues.
435762306a36Sopenharmony_ci		 */
435862306a36Sopenharmony_ci		synchronize_net();
435962306a36Sopenharmony_ci		ieee80211_flush_queues(local, sdata, false);
436062306a36Sopenharmony_ci
436162306a36Sopenharmony_ci		/* restore the normal QoS parameters
436262306a36Sopenharmony_ci		 * (unconditionally to avoid races)
436362306a36Sopenharmony_ci		 */
436462306a36Sopenharmony_ci		tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
436562306a36Sopenharmony_ci		tx_tspec->downgraded = false;
436662306a36Sopenharmony_ci		ieee80211_sta_handle_tspec_ac_params(sdata);
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_ci		/* finally clear all the data */
436962306a36Sopenharmony_ci		memset(tx_tspec, 0, sizeof(*tx_tspec));
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci		return 0;
437262306a36Sopenharmony_ci	}
437362306a36Sopenharmony_ci
437462306a36Sopenharmony_ci	return -ENOENT;
437562306a36Sopenharmony_ci}
437662306a36Sopenharmony_ci
437762306a36Sopenharmony_civoid ieee80211_nan_func_terminated(struct ieee80211_vif *vif,
437862306a36Sopenharmony_ci				   u8 inst_id,
437962306a36Sopenharmony_ci				   enum nl80211_nan_func_term_reason reason,
438062306a36Sopenharmony_ci				   gfp_t gfp)
438162306a36Sopenharmony_ci{
438262306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
438362306a36Sopenharmony_ci	struct cfg80211_nan_func *func;
438462306a36Sopenharmony_ci	u64 cookie;
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_ci	if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
438762306a36Sopenharmony_ci		return;
438862306a36Sopenharmony_ci
438962306a36Sopenharmony_ci	spin_lock_bh(&sdata->u.nan.func_lock);
439062306a36Sopenharmony_ci
439162306a36Sopenharmony_ci	func = idr_find(&sdata->u.nan.function_inst_ids, inst_id);
439262306a36Sopenharmony_ci	if (WARN_ON(!func)) {
439362306a36Sopenharmony_ci		spin_unlock_bh(&sdata->u.nan.func_lock);
439462306a36Sopenharmony_ci		return;
439562306a36Sopenharmony_ci	}
439662306a36Sopenharmony_ci
439762306a36Sopenharmony_ci	cookie = func->cookie;
439862306a36Sopenharmony_ci	idr_remove(&sdata->u.nan.function_inst_ids, inst_id);
439962306a36Sopenharmony_ci
440062306a36Sopenharmony_ci	spin_unlock_bh(&sdata->u.nan.func_lock);
440162306a36Sopenharmony_ci
440262306a36Sopenharmony_ci	cfg80211_free_nan_func(func);
440362306a36Sopenharmony_ci
440462306a36Sopenharmony_ci	cfg80211_nan_func_terminated(ieee80211_vif_to_wdev(vif), inst_id,
440562306a36Sopenharmony_ci				     reason, cookie, gfp);
440662306a36Sopenharmony_ci}
440762306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_nan_func_terminated);
440862306a36Sopenharmony_ci
440962306a36Sopenharmony_civoid ieee80211_nan_func_match(struct ieee80211_vif *vif,
441062306a36Sopenharmony_ci			      struct cfg80211_nan_match_params *match,
441162306a36Sopenharmony_ci			      gfp_t gfp)
441262306a36Sopenharmony_ci{
441362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
441462306a36Sopenharmony_ci	struct cfg80211_nan_func *func;
441562306a36Sopenharmony_ci
441662306a36Sopenharmony_ci	if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
441762306a36Sopenharmony_ci		return;
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci	spin_lock_bh(&sdata->u.nan.func_lock);
442062306a36Sopenharmony_ci
442162306a36Sopenharmony_ci	func = idr_find(&sdata->u.nan.function_inst_ids,  match->inst_id);
442262306a36Sopenharmony_ci	if (WARN_ON(!func)) {
442362306a36Sopenharmony_ci		spin_unlock_bh(&sdata->u.nan.func_lock);
442462306a36Sopenharmony_ci		return;
442562306a36Sopenharmony_ci	}
442662306a36Sopenharmony_ci	match->cookie = func->cookie;
442762306a36Sopenharmony_ci
442862306a36Sopenharmony_ci	spin_unlock_bh(&sdata->u.nan.func_lock);
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	cfg80211_nan_match(ieee80211_vif_to_wdev(vif), match, gfp);
443162306a36Sopenharmony_ci}
443262306a36Sopenharmony_ciEXPORT_SYMBOL(ieee80211_nan_func_match);
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_cistatic int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
443562306a36Sopenharmony_ci					      struct net_device *dev,
443662306a36Sopenharmony_ci					      const bool enabled)
443762306a36Sopenharmony_ci{
443862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci	sdata->u.ap.multicast_to_unicast = enabled;
444162306a36Sopenharmony_ci
444262306a36Sopenharmony_ci	return 0;
444362306a36Sopenharmony_ci}
444462306a36Sopenharmony_ci
444562306a36Sopenharmony_civoid ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats,
444662306a36Sopenharmony_ci			      struct txq_info *txqi)
444762306a36Sopenharmony_ci{
444862306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_BYTES))) {
444962306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_BYTES);
445062306a36Sopenharmony_ci		txqstats->backlog_bytes = txqi->tin.backlog_bytes;
445162306a36Sopenharmony_ci	}
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS))) {
445462306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS);
445562306a36Sopenharmony_ci		txqstats->backlog_packets = txqi->tin.backlog_packets;
445662306a36Sopenharmony_ci	}
445762306a36Sopenharmony_ci
445862306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_FLOWS))) {
445962306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_FLOWS);
446062306a36Sopenharmony_ci		txqstats->flows = txqi->tin.flows;
446162306a36Sopenharmony_ci	}
446262306a36Sopenharmony_ci
446362306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_DROPS))) {
446462306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_DROPS);
446562306a36Sopenharmony_ci		txqstats->drops = txqi->cstats.drop_count;
446662306a36Sopenharmony_ci	}
446762306a36Sopenharmony_ci
446862306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_ECN_MARKS))) {
446962306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_ECN_MARKS);
447062306a36Sopenharmony_ci		txqstats->ecn_marks = txqi->cstats.ecn_mark;
447162306a36Sopenharmony_ci	}
447262306a36Sopenharmony_ci
447362306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_OVERLIMIT))) {
447462306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_OVERLIMIT);
447562306a36Sopenharmony_ci		txqstats->overlimit = txqi->tin.overlimit;
447662306a36Sopenharmony_ci	}
447762306a36Sopenharmony_ci
447862306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_COLLISIONS))) {
447962306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_COLLISIONS);
448062306a36Sopenharmony_ci		txqstats->collisions = txqi->tin.collisions;
448162306a36Sopenharmony_ci	}
448262306a36Sopenharmony_ci
448362306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_BYTES))) {
448462306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_BYTES);
448562306a36Sopenharmony_ci		txqstats->tx_bytes = txqi->tin.tx_bytes;
448662306a36Sopenharmony_ci	}
448762306a36Sopenharmony_ci
448862306a36Sopenharmony_ci	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_PACKETS))) {
448962306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_PACKETS);
449062306a36Sopenharmony_ci		txqstats->tx_packets = txqi->tin.tx_packets;
449162306a36Sopenharmony_ci	}
449262306a36Sopenharmony_ci}
449362306a36Sopenharmony_ci
449462306a36Sopenharmony_cistatic int ieee80211_get_txq_stats(struct wiphy *wiphy,
449562306a36Sopenharmony_ci				   struct wireless_dev *wdev,
449662306a36Sopenharmony_ci				   struct cfg80211_txq_stats *txqstats)
449762306a36Sopenharmony_ci{
449862306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
449962306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata;
450062306a36Sopenharmony_ci	int ret = 0;
450162306a36Sopenharmony_ci
450262306a36Sopenharmony_ci	spin_lock_bh(&local->fq.lock);
450362306a36Sopenharmony_ci	rcu_read_lock();
450462306a36Sopenharmony_ci
450562306a36Sopenharmony_ci	if (wdev) {
450662306a36Sopenharmony_ci		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
450762306a36Sopenharmony_ci		if (!sdata->vif.txq) {
450862306a36Sopenharmony_ci			ret = 1;
450962306a36Sopenharmony_ci			goto out;
451062306a36Sopenharmony_ci		}
451162306a36Sopenharmony_ci		ieee80211_fill_txq_stats(txqstats, to_txq_info(sdata->vif.txq));
451262306a36Sopenharmony_ci	} else {
451362306a36Sopenharmony_ci		/* phy stats */
451462306a36Sopenharmony_ci		txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS) |
451562306a36Sopenharmony_ci				    BIT(NL80211_TXQ_STATS_BACKLOG_BYTES) |
451662306a36Sopenharmony_ci				    BIT(NL80211_TXQ_STATS_OVERLIMIT) |
451762306a36Sopenharmony_ci				    BIT(NL80211_TXQ_STATS_OVERMEMORY) |
451862306a36Sopenharmony_ci				    BIT(NL80211_TXQ_STATS_COLLISIONS) |
451962306a36Sopenharmony_ci				    BIT(NL80211_TXQ_STATS_MAX_FLOWS);
452062306a36Sopenharmony_ci		txqstats->backlog_packets = local->fq.backlog;
452162306a36Sopenharmony_ci		txqstats->backlog_bytes = local->fq.memory_usage;
452262306a36Sopenharmony_ci		txqstats->overlimit = local->fq.overlimit;
452362306a36Sopenharmony_ci		txqstats->overmemory = local->fq.overmemory;
452462306a36Sopenharmony_ci		txqstats->collisions = local->fq.collisions;
452562306a36Sopenharmony_ci		txqstats->max_flows = local->fq.flows_cnt;
452662306a36Sopenharmony_ci	}
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_ciout:
452962306a36Sopenharmony_ci	rcu_read_unlock();
453062306a36Sopenharmony_ci	spin_unlock_bh(&local->fq.lock);
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_ci	return ret;
453362306a36Sopenharmony_ci}
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_cistatic int
453662306a36Sopenharmony_ciieee80211_get_ftm_responder_stats(struct wiphy *wiphy,
453762306a36Sopenharmony_ci				  struct net_device *dev,
453862306a36Sopenharmony_ci				  struct cfg80211_ftm_responder_stats *ftm_stats)
453962306a36Sopenharmony_ci{
454062306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
454162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
454262306a36Sopenharmony_ci
454362306a36Sopenharmony_ci	return drv_get_ftm_responder_stats(local, sdata, ftm_stats);
454462306a36Sopenharmony_ci}
454562306a36Sopenharmony_ci
454662306a36Sopenharmony_cistatic int
454762306a36Sopenharmony_ciieee80211_start_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
454862306a36Sopenharmony_ci		     struct cfg80211_pmsr_request *request)
454962306a36Sopenharmony_ci{
455062306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
455162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
455262306a36Sopenharmony_ci
455362306a36Sopenharmony_ci	return drv_start_pmsr(local, sdata, request);
455462306a36Sopenharmony_ci}
455562306a36Sopenharmony_ci
455662306a36Sopenharmony_cistatic void
455762306a36Sopenharmony_ciieee80211_abort_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
455862306a36Sopenharmony_ci		     struct cfg80211_pmsr_request *request)
455962306a36Sopenharmony_ci{
456062306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
456162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
456262306a36Sopenharmony_ci
456362306a36Sopenharmony_ci	return drv_abort_pmsr(local, sdata, request);
456462306a36Sopenharmony_ci}
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_cistatic int ieee80211_set_tid_config(struct wiphy *wiphy,
456762306a36Sopenharmony_ci				    struct net_device *dev,
456862306a36Sopenharmony_ci				    struct cfg80211_tid_config *tid_conf)
456962306a36Sopenharmony_ci{
457062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
457162306a36Sopenharmony_ci	struct sta_info *sta;
457262306a36Sopenharmony_ci	int ret;
457362306a36Sopenharmony_ci
457462306a36Sopenharmony_ci	if (!sdata->local->ops->set_tid_config)
457562306a36Sopenharmony_ci		return -EOPNOTSUPP;
457662306a36Sopenharmony_ci
457762306a36Sopenharmony_ci	if (!tid_conf->peer)
457862306a36Sopenharmony_ci		return drv_set_tid_config(sdata->local, sdata, NULL, tid_conf);
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_ci	mutex_lock(&sdata->local->sta_mtx);
458162306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, tid_conf->peer);
458262306a36Sopenharmony_ci	if (!sta) {
458362306a36Sopenharmony_ci		mutex_unlock(&sdata->local->sta_mtx);
458462306a36Sopenharmony_ci		return -ENOENT;
458562306a36Sopenharmony_ci	}
458662306a36Sopenharmony_ci
458762306a36Sopenharmony_ci	ret = drv_set_tid_config(sdata->local, sdata, &sta->sta, tid_conf);
458862306a36Sopenharmony_ci	mutex_unlock(&sdata->local->sta_mtx);
458962306a36Sopenharmony_ci
459062306a36Sopenharmony_ci	return ret;
459162306a36Sopenharmony_ci}
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_cistatic int ieee80211_reset_tid_config(struct wiphy *wiphy,
459462306a36Sopenharmony_ci				      struct net_device *dev,
459562306a36Sopenharmony_ci				      const u8 *peer, u8 tids)
459662306a36Sopenharmony_ci{
459762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
459862306a36Sopenharmony_ci	struct sta_info *sta;
459962306a36Sopenharmony_ci	int ret;
460062306a36Sopenharmony_ci
460162306a36Sopenharmony_ci	if (!sdata->local->ops->reset_tid_config)
460262306a36Sopenharmony_ci		return -EOPNOTSUPP;
460362306a36Sopenharmony_ci
460462306a36Sopenharmony_ci	if (!peer)
460562306a36Sopenharmony_ci		return drv_reset_tid_config(sdata->local, sdata, NULL, tids);
460662306a36Sopenharmony_ci
460762306a36Sopenharmony_ci	mutex_lock(&sdata->local->sta_mtx);
460862306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, peer);
460962306a36Sopenharmony_ci	if (!sta) {
461062306a36Sopenharmony_ci		mutex_unlock(&sdata->local->sta_mtx);
461162306a36Sopenharmony_ci		return -ENOENT;
461262306a36Sopenharmony_ci	}
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci	ret = drv_reset_tid_config(sdata->local, sdata, &sta->sta, tids);
461562306a36Sopenharmony_ci	mutex_unlock(&sdata->local->sta_mtx);
461662306a36Sopenharmony_ci
461762306a36Sopenharmony_ci	return ret;
461862306a36Sopenharmony_ci}
461962306a36Sopenharmony_ci
462062306a36Sopenharmony_cistatic int ieee80211_set_sar_specs(struct wiphy *wiphy,
462162306a36Sopenharmony_ci				   struct cfg80211_sar_specs *sar)
462262306a36Sopenharmony_ci{
462362306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
462462306a36Sopenharmony_ci
462562306a36Sopenharmony_ci	if (!local->ops->set_sar_specs)
462662306a36Sopenharmony_ci		return -EOPNOTSUPP;
462762306a36Sopenharmony_ci
462862306a36Sopenharmony_ci	return local->ops->set_sar_specs(&local->hw, sar);
462962306a36Sopenharmony_ci}
463062306a36Sopenharmony_ci
463162306a36Sopenharmony_cistatic int
463262306a36Sopenharmony_ciieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata,
463362306a36Sopenharmony_ci					u64 *changed)
463462306a36Sopenharmony_ci{
463562306a36Sopenharmony_ci	switch (sdata->vif.type) {
463662306a36Sopenharmony_ci	case NL80211_IFTYPE_AP: {
463762306a36Sopenharmony_ci		int ret;
463862306a36Sopenharmony_ci
463962306a36Sopenharmony_ci		if (!sdata->deflink.u.ap.next_beacon)
464062306a36Sopenharmony_ci			return -EINVAL;
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci		ret = ieee80211_assign_beacon(sdata, &sdata->deflink,
464362306a36Sopenharmony_ci					      sdata->deflink.u.ap.next_beacon,
464462306a36Sopenharmony_ci					      NULL, NULL, changed);
464562306a36Sopenharmony_ci		ieee80211_free_next_beacon(&sdata->deflink);
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_ci		if (ret < 0)
464862306a36Sopenharmony_ci			return ret;
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci		break;
465162306a36Sopenharmony_ci	}
465262306a36Sopenharmony_ci	default:
465362306a36Sopenharmony_ci		WARN_ON_ONCE(1);
465462306a36Sopenharmony_ci		return -EINVAL;
465562306a36Sopenharmony_ci	}
465662306a36Sopenharmony_ci
465762306a36Sopenharmony_ci	return 0;
465862306a36Sopenharmony_ci}
465962306a36Sopenharmony_ci
466062306a36Sopenharmony_cistatic int
466162306a36Sopenharmony_ciieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata,
466262306a36Sopenharmony_ci				  struct cfg80211_color_change_settings *params,
466362306a36Sopenharmony_ci				  u64 *changed)
466462306a36Sopenharmony_ci{
466562306a36Sopenharmony_ci	struct ieee80211_color_change_settings color_change = {};
466662306a36Sopenharmony_ci	int err;
466762306a36Sopenharmony_ci
466862306a36Sopenharmony_ci	switch (sdata->vif.type) {
466962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
467062306a36Sopenharmony_ci		sdata->deflink.u.ap.next_beacon =
467162306a36Sopenharmony_ci			cfg80211_beacon_dup(&params->beacon_next);
467262306a36Sopenharmony_ci		if (!sdata->deflink.u.ap.next_beacon)
467362306a36Sopenharmony_ci			return -ENOMEM;
467462306a36Sopenharmony_ci
467562306a36Sopenharmony_ci		if (params->count <= 1)
467662306a36Sopenharmony_ci			break;
467762306a36Sopenharmony_ci
467862306a36Sopenharmony_ci		color_change.counter_offset_beacon =
467962306a36Sopenharmony_ci			params->counter_offset_beacon;
468062306a36Sopenharmony_ci		color_change.counter_offset_presp =
468162306a36Sopenharmony_ci			params->counter_offset_presp;
468262306a36Sopenharmony_ci		color_change.count = params->count;
468362306a36Sopenharmony_ci
468462306a36Sopenharmony_ci		err = ieee80211_assign_beacon(sdata, &sdata->deflink,
468562306a36Sopenharmony_ci					      &params->beacon_color_change,
468662306a36Sopenharmony_ci					      NULL, &color_change, changed);
468762306a36Sopenharmony_ci		if (err < 0) {
468862306a36Sopenharmony_ci			ieee80211_free_next_beacon(&sdata->deflink);
468962306a36Sopenharmony_ci			return err;
469062306a36Sopenharmony_ci		}
469162306a36Sopenharmony_ci		break;
469262306a36Sopenharmony_ci	default:
469362306a36Sopenharmony_ci		return -EOPNOTSUPP;
469462306a36Sopenharmony_ci	}
469562306a36Sopenharmony_ci
469662306a36Sopenharmony_ci	return 0;
469762306a36Sopenharmony_ci}
469862306a36Sopenharmony_ci
469962306a36Sopenharmony_cistatic void
470062306a36Sopenharmony_ciieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata,
470162306a36Sopenharmony_ci					 u8 color, int enable, u64 changed)
470262306a36Sopenharmony_ci{
470362306a36Sopenharmony_ci	sdata->vif.bss_conf.he_bss_color.color = color;
470462306a36Sopenharmony_ci	sdata->vif.bss_conf.he_bss_color.enabled = enable;
470562306a36Sopenharmony_ci	changed |= BSS_CHANGED_HE_BSS_COLOR;
470662306a36Sopenharmony_ci
470762306a36Sopenharmony_ci	ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci	if (!sdata->vif.bss_conf.nontransmitted && sdata->vif.mbssid_tx_vif) {
471062306a36Sopenharmony_ci		struct ieee80211_sub_if_data *child;
471162306a36Sopenharmony_ci
471262306a36Sopenharmony_ci		mutex_lock(&sdata->local->iflist_mtx);
471362306a36Sopenharmony_ci		list_for_each_entry(child, &sdata->local->interfaces, list) {
471462306a36Sopenharmony_ci			if (child != sdata && child->vif.mbssid_tx_vif == &sdata->vif) {
471562306a36Sopenharmony_ci				child->vif.bss_conf.he_bss_color.color = color;
471662306a36Sopenharmony_ci				child->vif.bss_conf.he_bss_color.enabled = enable;
471762306a36Sopenharmony_ci				ieee80211_link_info_change_notify(child,
471862306a36Sopenharmony_ci								  &child->deflink,
471962306a36Sopenharmony_ci								  BSS_CHANGED_HE_BSS_COLOR);
472062306a36Sopenharmony_ci			}
472162306a36Sopenharmony_ci		}
472262306a36Sopenharmony_ci		mutex_unlock(&sdata->local->iflist_mtx);
472362306a36Sopenharmony_ci	}
472462306a36Sopenharmony_ci}
472562306a36Sopenharmony_ci
472662306a36Sopenharmony_cistatic int ieee80211_color_change_finalize(struct ieee80211_sub_if_data *sdata)
472762306a36Sopenharmony_ci{
472862306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
472962306a36Sopenharmony_ci	u64 changed = 0;
473062306a36Sopenharmony_ci	int err;
473162306a36Sopenharmony_ci
473262306a36Sopenharmony_ci	sdata_assert_lock(sdata);
473362306a36Sopenharmony_ci	lockdep_assert_held(&local->mtx);
473462306a36Sopenharmony_ci
473562306a36Sopenharmony_ci	sdata->vif.bss_conf.color_change_active = false;
473662306a36Sopenharmony_ci
473762306a36Sopenharmony_ci	err = ieee80211_set_after_color_change_beacon(sdata, &changed);
473862306a36Sopenharmony_ci	if (err) {
473962306a36Sopenharmony_ci		cfg80211_color_change_aborted_notify(sdata->dev);
474062306a36Sopenharmony_ci		return err;
474162306a36Sopenharmony_ci	}
474262306a36Sopenharmony_ci
474362306a36Sopenharmony_ci	ieee80211_color_change_bss_config_notify(sdata,
474462306a36Sopenharmony_ci						 sdata->vif.bss_conf.color_change_color,
474562306a36Sopenharmony_ci						 1, changed);
474662306a36Sopenharmony_ci	cfg80211_color_change_notify(sdata->dev);
474762306a36Sopenharmony_ci
474862306a36Sopenharmony_ci	return 0;
474962306a36Sopenharmony_ci}
475062306a36Sopenharmony_ci
475162306a36Sopenharmony_civoid ieee80211_color_change_finalize_work(struct work_struct *work)
475262306a36Sopenharmony_ci{
475362306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata =
475462306a36Sopenharmony_ci		container_of(work, struct ieee80211_sub_if_data,
475562306a36Sopenharmony_ci			     deflink.color_change_finalize_work);
475662306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
475762306a36Sopenharmony_ci
475862306a36Sopenharmony_ci	sdata_lock(sdata);
475962306a36Sopenharmony_ci	mutex_lock(&local->mtx);
476062306a36Sopenharmony_ci
476162306a36Sopenharmony_ci	/* AP might have been stopped while waiting for the lock. */
476262306a36Sopenharmony_ci	if (!sdata->vif.bss_conf.color_change_active)
476362306a36Sopenharmony_ci		goto unlock;
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci	if (!ieee80211_sdata_running(sdata))
476662306a36Sopenharmony_ci		goto unlock;
476762306a36Sopenharmony_ci
476862306a36Sopenharmony_ci	ieee80211_color_change_finalize(sdata);
476962306a36Sopenharmony_ci
477062306a36Sopenharmony_ciunlock:
477162306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
477262306a36Sopenharmony_ci	sdata_unlock(sdata);
477362306a36Sopenharmony_ci}
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_civoid ieee80211_color_collision_detection_work(struct work_struct *work)
477662306a36Sopenharmony_ci{
477762306a36Sopenharmony_ci	struct delayed_work *delayed_work = to_delayed_work(work);
477862306a36Sopenharmony_ci	struct ieee80211_link_data *link =
477962306a36Sopenharmony_ci		container_of(delayed_work, struct ieee80211_link_data,
478062306a36Sopenharmony_ci			     color_collision_detect_work);
478162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = link->sdata;
478262306a36Sopenharmony_ci
478362306a36Sopenharmony_ci	sdata_lock(sdata);
478462306a36Sopenharmony_ci	cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap);
478562306a36Sopenharmony_ci	sdata_unlock(sdata);
478662306a36Sopenharmony_ci}
478762306a36Sopenharmony_ci
478862306a36Sopenharmony_civoid ieee80211_color_change_finish(struct ieee80211_vif *vif)
478962306a36Sopenharmony_ci{
479062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_ci	ieee80211_queue_work(&sdata->local->hw,
479362306a36Sopenharmony_ci			     &sdata->deflink.color_change_finalize_work);
479462306a36Sopenharmony_ci}
479562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_color_change_finish);
479662306a36Sopenharmony_ci
479762306a36Sopenharmony_civoid
479862306a36Sopenharmony_ciieee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
479962306a36Sopenharmony_ci				       u64 color_bitmap, gfp_t gfp)
480062306a36Sopenharmony_ci{
480162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
480262306a36Sopenharmony_ci	struct ieee80211_link_data *link = &sdata->deflink;
480362306a36Sopenharmony_ci
480462306a36Sopenharmony_ci	if (sdata->vif.bss_conf.color_change_active || sdata->vif.bss_conf.csa_active)
480562306a36Sopenharmony_ci		return;
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci	if (delayed_work_pending(&link->color_collision_detect_work))
480862306a36Sopenharmony_ci		return;
480962306a36Sopenharmony_ci
481062306a36Sopenharmony_ci	link->color_bitmap = color_bitmap;
481162306a36Sopenharmony_ci	/* queue the color collision detection event every 500 ms in order to
481262306a36Sopenharmony_ci	 * avoid sending too much netlink messages to userspace.
481362306a36Sopenharmony_ci	 */
481462306a36Sopenharmony_ci	ieee80211_queue_delayed_work(&sdata->local->hw,
481562306a36Sopenharmony_ci				     &link->color_collision_detect_work,
481662306a36Sopenharmony_ci				     msecs_to_jiffies(500));
481762306a36Sopenharmony_ci}
481862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ieee80211_obss_color_collision_notify);
481962306a36Sopenharmony_ci
482062306a36Sopenharmony_cistatic int
482162306a36Sopenharmony_ciieee80211_color_change(struct wiphy *wiphy, struct net_device *dev,
482262306a36Sopenharmony_ci		       struct cfg80211_color_change_settings *params)
482362306a36Sopenharmony_ci{
482462306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
482562306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
482662306a36Sopenharmony_ci	u64 changed = 0;
482762306a36Sopenharmony_ci	int err;
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci	sdata_assert_lock(sdata);
483062306a36Sopenharmony_ci
483162306a36Sopenharmony_ci	if (sdata->vif.bss_conf.nontransmitted)
483262306a36Sopenharmony_ci		return -EINVAL;
483362306a36Sopenharmony_ci
483462306a36Sopenharmony_ci	mutex_lock(&local->mtx);
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci	/* don't allow another color change if one is already active or if csa
483762306a36Sopenharmony_ci	 * is active
483862306a36Sopenharmony_ci	 */
483962306a36Sopenharmony_ci	if (sdata->vif.bss_conf.color_change_active || sdata->vif.bss_conf.csa_active) {
484062306a36Sopenharmony_ci		err = -EBUSY;
484162306a36Sopenharmony_ci		goto out;
484262306a36Sopenharmony_ci	}
484362306a36Sopenharmony_ci
484462306a36Sopenharmony_ci	err = ieee80211_set_color_change_beacon(sdata, params, &changed);
484562306a36Sopenharmony_ci	if (err)
484662306a36Sopenharmony_ci		goto out;
484762306a36Sopenharmony_ci
484862306a36Sopenharmony_ci	sdata->vif.bss_conf.color_change_active = true;
484962306a36Sopenharmony_ci	sdata->vif.bss_conf.color_change_color = params->color;
485062306a36Sopenharmony_ci
485162306a36Sopenharmony_ci	cfg80211_color_change_started_notify(sdata->dev, params->count);
485262306a36Sopenharmony_ci
485362306a36Sopenharmony_ci	if (changed)
485462306a36Sopenharmony_ci		ieee80211_color_change_bss_config_notify(sdata, 0, 0, changed);
485562306a36Sopenharmony_ci	else
485662306a36Sopenharmony_ci		/* if the beacon didn't change, we can finalize immediately */
485762306a36Sopenharmony_ci		ieee80211_color_change_finalize(sdata);
485862306a36Sopenharmony_ci
485962306a36Sopenharmony_ciout:
486062306a36Sopenharmony_ci	mutex_unlock(&local->mtx);
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci	return err;
486362306a36Sopenharmony_ci}
486462306a36Sopenharmony_ci
486562306a36Sopenharmony_cistatic int
486662306a36Sopenharmony_ciieee80211_set_radar_background(struct wiphy *wiphy,
486762306a36Sopenharmony_ci			       struct cfg80211_chan_def *chandef)
486862306a36Sopenharmony_ci{
486962306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
487062306a36Sopenharmony_ci
487162306a36Sopenharmony_ci	if (!local->ops->set_radar_background)
487262306a36Sopenharmony_ci		return -EOPNOTSUPP;
487362306a36Sopenharmony_ci
487462306a36Sopenharmony_ci	return local->ops->set_radar_background(&local->hw, chandef);
487562306a36Sopenharmony_ci}
487662306a36Sopenharmony_ci
487762306a36Sopenharmony_cistatic int ieee80211_add_intf_link(struct wiphy *wiphy,
487862306a36Sopenharmony_ci				   struct wireless_dev *wdev,
487962306a36Sopenharmony_ci				   unsigned int link_id)
488062306a36Sopenharmony_ci{
488162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
488262306a36Sopenharmony_ci	int res;
488362306a36Sopenharmony_ci
488462306a36Sopenharmony_ci	if (wdev->use_4addr)
488562306a36Sopenharmony_ci		return -EOPNOTSUPP;
488662306a36Sopenharmony_ci
488762306a36Sopenharmony_ci	mutex_lock(&sdata->local->mtx);
488862306a36Sopenharmony_ci	res = ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
488962306a36Sopenharmony_ci	mutex_unlock(&sdata->local->mtx);
489062306a36Sopenharmony_ci
489162306a36Sopenharmony_ci	return res;
489262306a36Sopenharmony_ci}
489362306a36Sopenharmony_ci
489462306a36Sopenharmony_cistatic void ieee80211_del_intf_link(struct wiphy *wiphy,
489562306a36Sopenharmony_ci				    struct wireless_dev *wdev,
489662306a36Sopenharmony_ci				    unsigned int link_id)
489762306a36Sopenharmony_ci{
489862306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
489962306a36Sopenharmony_ci
490062306a36Sopenharmony_ci	mutex_lock(&sdata->local->mtx);
490162306a36Sopenharmony_ci	ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
490262306a36Sopenharmony_ci	mutex_unlock(&sdata->local->mtx);
490362306a36Sopenharmony_ci}
490462306a36Sopenharmony_ci
490562306a36Sopenharmony_cistatic int sta_add_link_station(struct ieee80211_local *local,
490662306a36Sopenharmony_ci				struct ieee80211_sub_if_data *sdata,
490762306a36Sopenharmony_ci				struct link_station_parameters *params)
490862306a36Sopenharmony_ci{
490962306a36Sopenharmony_ci	struct sta_info *sta;
491062306a36Sopenharmony_ci	int ret;
491162306a36Sopenharmony_ci
491262306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, params->mld_mac);
491362306a36Sopenharmony_ci	if (!sta)
491462306a36Sopenharmony_ci		return -ENOENT;
491562306a36Sopenharmony_ci
491662306a36Sopenharmony_ci	if (!sta->sta.valid_links)
491762306a36Sopenharmony_ci		return -EINVAL;
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci	if (sta->sta.valid_links & BIT(params->link_id))
492062306a36Sopenharmony_ci		return -EALREADY;
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_ci	ret = ieee80211_sta_allocate_link(sta, params->link_id);
492362306a36Sopenharmony_ci	if (ret)
492462306a36Sopenharmony_ci		return ret;
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci	ret = sta_link_apply_parameters(local, sta, true, params);
492762306a36Sopenharmony_ci	if (ret) {
492862306a36Sopenharmony_ci		ieee80211_sta_free_link(sta, params->link_id);
492962306a36Sopenharmony_ci		return ret;
493062306a36Sopenharmony_ci	}
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci	/* ieee80211_sta_activate_link frees the link upon failure */
493362306a36Sopenharmony_ci	return ieee80211_sta_activate_link(sta, params->link_id);
493462306a36Sopenharmony_ci}
493562306a36Sopenharmony_ci
493662306a36Sopenharmony_cistatic int
493762306a36Sopenharmony_ciieee80211_add_link_station(struct wiphy *wiphy, struct net_device *dev,
493862306a36Sopenharmony_ci			   struct link_station_parameters *params)
493962306a36Sopenharmony_ci{
494062306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
494162306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
494262306a36Sopenharmony_ci	int ret;
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci	mutex_lock(&sdata->local->sta_mtx);
494562306a36Sopenharmony_ci	ret = sta_add_link_station(local, sdata, params);
494662306a36Sopenharmony_ci	mutex_unlock(&sdata->local->sta_mtx);
494762306a36Sopenharmony_ci
494862306a36Sopenharmony_ci	return ret;
494962306a36Sopenharmony_ci}
495062306a36Sopenharmony_ci
495162306a36Sopenharmony_cistatic int sta_mod_link_station(struct ieee80211_local *local,
495262306a36Sopenharmony_ci				struct ieee80211_sub_if_data *sdata,
495362306a36Sopenharmony_ci				struct link_station_parameters *params)
495462306a36Sopenharmony_ci{
495562306a36Sopenharmony_ci	struct sta_info *sta;
495662306a36Sopenharmony_ci
495762306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, params->mld_mac);
495862306a36Sopenharmony_ci	if (!sta)
495962306a36Sopenharmony_ci		return -ENOENT;
496062306a36Sopenharmony_ci
496162306a36Sopenharmony_ci	if (!(sta->sta.valid_links & BIT(params->link_id)))
496262306a36Sopenharmony_ci		return -EINVAL;
496362306a36Sopenharmony_ci
496462306a36Sopenharmony_ci	return sta_link_apply_parameters(local, sta, false, params);
496562306a36Sopenharmony_ci}
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_cistatic int
496862306a36Sopenharmony_ciieee80211_mod_link_station(struct wiphy *wiphy, struct net_device *dev,
496962306a36Sopenharmony_ci			   struct link_station_parameters *params)
497062306a36Sopenharmony_ci{
497162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
497262306a36Sopenharmony_ci	struct ieee80211_local *local = wiphy_priv(wiphy);
497362306a36Sopenharmony_ci	int ret;
497462306a36Sopenharmony_ci
497562306a36Sopenharmony_ci	mutex_lock(&sdata->local->sta_mtx);
497662306a36Sopenharmony_ci	ret = sta_mod_link_station(local, sdata, params);
497762306a36Sopenharmony_ci	mutex_unlock(&sdata->local->sta_mtx);
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	return ret;
498062306a36Sopenharmony_ci}
498162306a36Sopenharmony_ci
498262306a36Sopenharmony_cistatic int sta_del_link_station(struct ieee80211_sub_if_data *sdata,
498362306a36Sopenharmony_ci				struct link_station_del_parameters *params)
498462306a36Sopenharmony_ci{
498562306a36Sopenharmony_ci	struct sta_info *sta;
498662306a36Sopenharmony_ci
498762306a36Sopenharmony_ci	sta = sta_info_get_bss(sdata, params->mld_mac);
498862306a36Sopenharmony_ci	if (!sta)
498962306a36Sopenharmony_ci		return -ENOENT;
499062306a36Sopenharmony_ci
499162306a36Sopenharmony_ci	if (!(sta->sta.valid_links & BIT(params->link_id)))
499262306a36Sopenharmony_ci		return -EINVAL;
499362306a36Sopenharmony_ci
499462306a36Sopenharmony_ci	/* must not create a STA without links */
499562306a36Sopenharmony_ci	if (sta->sta.valid_links == BIT(params->link_id))
499662306a36Sopenharmony_ci		return -EINVAL;
499762306a36Sopenharmony_ci
499862306a36Sopenharmony_ci	ieee80211_sta_remove_link(sta, params->link_id);
499962306a36Sopenharmony_ci
500062306a36Sopenharmony_ci	return 0;
500162306a36Sopenharmony_ci}
500262306a36Sopenharmony_ci
500362306a36Sopenharmony_cistatic int
500462306a36Sopenharmony_ciieee80211_del_link_station(struct wiphy *wiphy, struct net_device *dev,
500562306a36Sopenharmony_ci			   struct link_station_del_parameters *params)
500662306a36Sopenharmony_ci{
500762306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
500862306a36Sopenharmony_ci	int ret;
500962306a36Sopenharmony_ci
501062306a36Sopenharmony_ci	mutex_lock(&sdata->local->sta_mtx);
501162306a36Sopenharmony_ci	ret = sta_del_link_station(sdata, params);
501262306a36Sopenharmony_ci	mutex_unlock(&sdata->local->sta_mtx);
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_ci	return ret;
501562306a36Sopenharmony_ci}
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_cistatic int ieee80211_set_hw_timestamp(struct wiphy *wiphy,
501862306a36Sopenharmony_ci				      struct net_device *dev,
501962306a36Sopenharmony_ci				      struct cfg80211_set_hw_timestamp *hwts)
502062306a36Sopenharmony_ci{
502162306a36Sopenharmony_ci	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
502262306a36Sopenharmony_ci	struct ieee80211_local *local = sdata->local;
502362306a36Sopenharmony_ci
502462306a36Sopenharmony_ci	if (!local->ops->set_hw_timestamp)
502562306a36Sopenharmony_ci		return -EOPNOTSUPP;
502662306a36Sopenharmony_ci
502762306a36Sopenharmony_ci	if (!check_sdata_in_driver(sdata))
502862306a36Sopenharmony_ci		return -EIO;
502962306a36Sopenharmony_ci
503062306a36Sopenharmony_ci	return local->ops->set_hw_timestamp(&local->hw, &sdata->vif, hwts);
503162306a36Sopenharmony_ci}
503262306a36Sopenharmony_ci
503362306a36Sopenharmony_ciconst struct cfg80211_ops mac80211_config_ops = {
503462306a36Sopenharmony_ci	.add_virtual_intf = ieee80211_add_iface,
503562306a36Sopenharmony_ci	.del_virtual_intf = ieee80211_del_iface,
503662306a36Sopenharmony_ci	.change_virtual_intf = ieee80211_change_iface,
503762306a36Sopenharmony_ci	.start_p2p_device = ieee80211_start_p2p_device,
503862306a36Sopenharmony_ci	.stop_p2p_device = ieee80211_stop_p2p_device,
503962306a36Sopenharmony_ci	.add_key = ieee80211_add_key,
504062306a36Sopenharmony_ci	.del_key = ieee80211_del_key,
504162306a36Sopenharmony_ci	.get_key = ieee80211_get_key,
504262306a36Sopenharmony_ci	.set_default_key = ieee80211_config_default_key,
504362306a36Sopenharmony_ci	.set_default_mgmt_key = ieee80211_config_default_mgmt_key,
504462306a36Sopenharmony_ci	.set_default_beacon_key = ieee80211_config_default_beacon_key,
504562306a36Sopenharmony_ci	.start_ap = ieee80211_start_ap,
504662306a36Sopenharmony_ci	.change_beacon = ieee80211_change_beacon,
504762306a36Sopenharmony_ci	.stop_ap = ieee80211_stop_ap,
504862306a36Sopenharmony_ci	.add_station = ieee80211_add_station,
504962306a36Sopenharmony_ci	.del_station = ieee80211_del_station,
505062306a36Sopenharmony_ci	.change_station = ieee80211_change_station,
505162306a36Sopenharmony_ci	.get_station = ieee80211_get_station,
505262306a36Sopenharmony_ci	.dump_station = ieee80211_dump_station,
505362306a36Sopenharmony_ci	.dump_survey = ieee80211_dump_survey,
505462306a36Sopenharmony_ci#ifdef CONFIG_MAC80211_MESH
505562306a36Sopenharmony_ci	.add_mpath = ieee80211_add_mpath,
505662306a36Sopenharmony_ci	.del_mpath = ieee80211_del_mpath,
505762306a36Sopenharmony_ci	.change_mpath = ieee80211_change_mpath,
505862306a36Sopenharmony_ci	.get_mpath = ieee80211_get_mpath,
505962306a36Sopenharmony_ci	.dump_mpath = ieee80211_dump_mpath,
506062306a36Sopenharmony_ci	.get_mpp = ieee80211_get_mpp,
506162306a36Sopenharmony_ci	.dump_mpp = ieee80211_dump_mpp,
506262306a36Sopenharmony_ci	.update_mesh_config = ieee80211_update_mesh_config,
506362306a36Sopenharmony_ci	.get_mesh_config = ieee80211_get_mesh_config,
506462306a36Sopenharmony_ci	.join_mesh = ieee80211_join_mesh,
506562306a36Sopenharmony_ci	.leave_mesh = ieee80211_leave_mesh,
506662306a36Sopenharmony_ci#endif
506762306a36Sopenharmony_ci	.join_ocb = ieee80211_join_ocb,
506862306a36Sopenharmony_ci	.leave_ocb = ieee80211_leave_ocb,
506962306a36Sopenharmony_ci	.change_bss = ieee80211_change_bss,
507062306a36Sopenharmony_ci	.inform_bss = ieee80211_inform_bss,
507162306a36Sopenharmony_ci	.set_txq_params = ieee80211_set_txq_params,
507262306a36Sopenharmony_ci	.set_monitor_channel = ieee80211_set_monitor_channel,
507362306a36Sopenharmony_ci	.suspend = ieee80211_suspend,
507462306a36Sopenharmony_ci	.resume = ieee80211_resume,
507562306a36Sopenharmony_ci	.scan = ieee80211_scan,
507662306a36Sopenharmony_ci	.abort_scan = ieee80211_abort_scan,
507762306a36Sopenharmony_ci	.sched_scan_start = ieee80211_sched_scan_start,
507862306a36Sopenharmony_ci	.sched_scan_stop = ieee80211_sched_scan_stop,
507962306a36Sopenharmony_ci	.auth = ieee80211_auth,
508062306a36Sopenharmony_ci	.assoc = ieee80211_assoc,
508162306a36Sopenharmony_ci	.deauth = ieee80211_deauth,
508262306a36Sopenharmony_ci	.disassoc = ieee80211_disassoc,
508362306a36Sopenharmony_ci	.join_ibss = ieee80211_join_ibss,
508462306a36Sopenharmony_ci	.leave_ibss = ieee80211_leave_ibss,
508562306a36Sopenharmony_ci	.set_mcast_rate = ieee80211_set_mcast_rate,
508662306a36Sopenharmony_ci	.set_wiphy_params = ieee80211_set_wiphy_params,
508762306a36Sopenharmony_ci	.set_tx_power = ieee80211_set_tx_power,
508862306a36Sopenharmony_ci	.get_tx_power = ieee80211_get_tx_power,
508962306a36Sopenharmony_ci	.rfkill_poll = ieee80211_rfkill_poll,
509062306a36Sopenharmony_ci	CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
509162306a36Sopenharmony_ci	CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
509262306a36Sopenharmony_ci	.set_power_mgmt = ieee80211_set_power_mgmt,
509362306a36Sopenharmony_ci	.set_bitrate_mask = ieee80211_set_bitrate_mask,
509462306a36Sopenharmony_ci	.remain_on_channel = ieee80211_remain_on_channel,
509562306a36Sopenharmony_ci	.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
509662306a36Sopenharmony_ci	.mgmt_tx = ieee80211_mgmt_tx,
509762306a36Sopenharmony_ci	.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
509862306a36Sopenharmony_ci	.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
509962306a36Sopenharmony_ci	.set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config,
510062306a36Sopenharmony_ci	.update_mgmt_frame_registrations =
510162306a36Sopenharmony_ci		ieee80211_update_mgmt_frame_registrations,
510262306a36Sopenharmony_ci	.set_antenna = ieee80211_set_antenna,
510362306a36Sopenharmony_ci	.get_antenna = ieee80211_get_antenna,
510462306a36Sopenharmony_ci	.set_rekey_data = ieee80211_set_rekey_data,
510562306a36Sopenharmony_ci	.tdls_oper = ieee80211_tdls_oper,
510662306a36Sopenharmony_ci	.tdls_mgmt = ieee80211_tdls_mgmt,
510762306a36Sopenharmony_ci	.tdls_channel_switch = ieee80211_tdls_channel_switch,
510862306a36Sopenharmony_ci	.tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
510962306a36Sopenharmony_ci	.probe_client = ieee80211_probe_client,
511062306a36Sopenharmony_ci	.set_noack_map = ieee80211_set_noack_map,
511162306a36Sopenharmony_ci#ifdef CONFIG_PM
511262306a36Sopenharmony_ci	.set_wakeup = ieee80211_set_wakeup,
511362306a36Sopenharmony_ci#endif
511462306a36Sopenharmony_ci	.get_channel = ieee80211_cfg_get_channel,
511562306a36Sopenharmony_ci	.start_radar_detection = ieee80211_start_radar_detection,
511662306a36Sopenharmony_ci	.end_cac = ieee80211_end_cac,
511762306a36Sopenharmony_ci	.channel_switch = ieee80211_channel_switch,
511862306a36Sopenharmony_ci	.set_qos_map = ieee80211_set_qos_map,
511962306a36Sopenharmony_ci	.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
512062306a36Sopenharmony_ci	.add_tx_ts = ieee80211_add_tx_ts,
512162306a36Sopenharmony_ci	.del_tx_ts = ieee80211_del_tx_ts,
512262306a36Sopenharmony_ci	.start_nan = ieee80211_start_nan,
512362306a36Sopenharmony_ci	.stop_nan = ieee80211_stop_nan,
512462306a36Sopenharmony_ci	.nan_change_conf = ieee80211_nan_change_conf,
512562306a36Sopenharmony_ci	.add_nan_func = ieee80211_add_nan_func,
512662306a36Sopenharmony_ci	.del_nan_func = ieee80211_del_nan_func,
512762306a36Sopenharmony_ci	.set_multicast_to_unicast = ieee80211_set_multicast_to_unicast,
512862306a36Sopenharmony_ci	.tx_control_port = ieee80211_tx_control_port,
512962306a36Sopenharmony_ci	.get_txq_stats = ieee80211_get_txq_stats,
513062306a36Sopenharmony_ci	.get_ftm_responder_stats = ieee80211_get_ftm_responder_stats,
513162306a36Sopenharmony_ci	.start_pmsr = ieee80211_start_pmsr,
513262306a36Sopenharmony_ci	.abort_pmsr = ieee80211_abort_pmsr,
513362306a36Sopenharmony_ci	.probe_mesh_link = ieee80211_probe_mesh_link,
513462306a36Sopenharmony_ci	.set_tid_config = ieee80211_set_tid_config,
513562306a36Sopenharmony_ci	.reset_tid_config = ieee80211_reset_tid_config,
513662306a36Sopenharmony_ci	.set_sar_specs = ieee80211_set_sar_specs,
513762306a36Sopenharmony_ci	.color_change = ieee80211_color_change,
513862306a36Sopenharmony_ci	.set_radar_background = ieee80211_set_radar_background,
513962306a36Sopenharmony_ci	.add_intf_link = ieee80211_add_intf_link,
514062306a36Sopenharmony_ci	.del_intf_link = ieee80211_del_intf_link,
514162306a36Sopenharmony_ci	.add_link_station = ieee80211_add_link_station,
514262306a36Sopenharmony_ci	.mod_link_station = ieee80211_mod_link_station,
514362306a36Sopenharmony_ci	.del_link_station = ieee80211_del_link_station,
514462306a36Sopenharmony_ci	.set_hw_timestamp = ieee80211_set_hw_timestamp,
514562306a36Sopenharmony_ci};
5146