xref: /openbmc/linux/net/mac80211/cfg.c (revision 16f6ccde74a6f8538c62f127f17207c75f4dba7a)
128c61a66SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2fa5fea71SJohannes Berg /*
3fa5fea71SJohannes Berg  * mac80211 configuration hooks for cfg80211
4fa5fea71SJohannes Berg  *
5026331c4SJouni Malinen  * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net>
6b2eb0ee6SJohannes Berg  * Copyright 2013-2015  Intel Mobile Communications GmbH
7e8e4f528SJohannes Berg  * Copyright (C) 2015-2017 Intel Deutschland GmbH
823a5f0afSJohannes Berg  * Copyright (C) 2018-2022 Intel Corporation
9fa5fea71SJohannes Berg  */
10fa5fea71SJohannes Berg 
11e8cbb4cbSJohannes Berg #include <linux/ieee80211.h>
12fa5fea71SJohannes Berg #include <linux/nl80211.h>
13fa5fea71SJohannes Berg #include <linux/rtnetlink.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
15fa5fea71SJohannes Berg #include <net/net_namespace.h>
165dfdaf58SJohannes Berg #include <linux/rcupdate.h>
175fdb3735SArd Biesheuvel #include <linux/fips.h>
18dfe018bfSArik Nemtsov #include <linux/if_ether.h>
19fa5fea71SJohannes Berg #include <net/cfg80211.h>
20fa5fea71SJohannes Berg #include "ieee80211_i.h"
2124487981SJohannes Berg #include "driver-ops.h"
222c8dccc7SJohannes Berg #include "rate.h"
23c5dd9c2bSLuis Carlos Cobo #include "mesh.h"
2402219b3aSJohannes Berg #include "wme.h"
25c5dd9c2bSLuis Carlos Cobo 
26c88f1542SShaul Triebitz static struct ieee80211_link_data *
ieee80211_link_or_deflink(struct ieee80211_sub_if_data * sdata,int link_id,bool require_valid)27ccdde7c7SJohannes Berg ieee80211_link_or_deflink(struct ieee80211_sub_if_data *sdata, int link_id,
28ccdde7c7SJohannes Berg 			  bool require_valid)
29c88f1542SShaul Triebitz {
30c88f1542SShaul Triebitz 	struct ieee80211_link_data *link;
31c88f1542SShaul Triebitz 
32c88f1542SShaul Triebitz 	if (link_id < 0) {
33ccdde7c7SJohannes Berg 		/*
34ccdde7c7SJohannes Berg 		 * For keys, if sdata is not an MLD, we might not use
35ccdde7c7SJohannes Berg 		 * the return value at all (if it's not a pairwise key),
36ccdde7c7SJohannes Berg 		 * so in that case (require_valid==false) don't error.
37ccdde7c7SJohannes Berg 		 */
38f1871abdSIlan Peer 		if (require_valid && ieee80211_vif_is_mld(&sdata->vif))
39c88f1542SShaul Triebitz 			return ERR_PTR(-EINVAL);
40c88f1542SShaul Triebitz 
41c88f1542SShaul Triebitz 		return &sdata->deflink;
42c88f1542SShaul Triebitz 	}
43c88f1542SShaul Triebitz 
44c88f1542SShaul Triebitz 	link = sdata_dereference(sdata->link[link_id], sdata);
45c88f1542SShaul Triebitz 	if (!link)
46c88f1542SShaul Triebitz 		return ERR_PTR(-ENOLINK);
47c88f1542SShaul Triebitz 	return link;
48c88f1542SShaul Triebitz }
49c88f1542SShaul Triebitz 
ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data * sdata,struct vif_params * params)5065f1d600SJohannes Berg static void ieee80211_set_mu_mimo_follow(struct ieee80211_sub_if_data *sdata,
518c5e6889SJohannes Berg 					 struct vif_params *params)
528c5e6889SJohannes Berg {
538c5e6889SJohannes Berg 	bool mu_mimo_groups = false;
548c5e6889SJohannes Berg 	bool mu_mimo_follow = false;
558c5e6889SJohannes Berg 
568c5e6889SJohannes Berg 	if (params->vht_mumimo_groups) {
578c5e6889SJohannes Berg 		u64 membership;
588c5e6889SJohannes Berg 
598c5e6889SJohannes Berg 		BUILD_BUG_ON(sizeof(membership) != WLAN_MEMBERSHIP_LEN);
608c5e6889SJohannes Berg 
6165f1d600SJohannes Berg 		memcpy(sdata->vif.bss_conf.mu_group.membership,
628c5e6889SJohannes Berg 		       params->vht_mumimo_groups, WLAN_MEMBERSHIP_LEN);
6365f1d600SJohannes Berg 		memcpy(sdata->vif.bss_conf.mu_group.position,
648c5e6889SJohannes Berg 		       params->vht_mumimo_groups + WLAN_MEMBERSHIP_LEN,
658c5e6889SJohannes Berg 		       WLAN_USER_POSITION_LEN);
66d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
677b7090b4SJohannes Berg 						  BSS_CHANGED_MU_GROUPS);
688c5e6889SJohannes Berg 		/* don't care about endianness - just check for 0 */
698c5e6889SJohannes Berg 		memcpy(&membership, params->vht_mumimo_groups,
708c5e6889SJohannes Berg 		       WLAN_MEMBERSHIP_LEN);
718c5e6889SJohannes Berg 		mu_mimo_groups = membership != 0;
728c5e6889SJohannes Berg 	}
738c5e6889SJohannes Berg 
748c5e6889SJohannes Berg 	if (params->vht_mumimo_follow_addr) {
758c5e6889SJohannes Berg 		mu_mimo_follow =
768c5e6889SJohannes Berg 			is_valid_ether_addr(params->vht_mumimo_follow_addr);
7765f1d600SJohannes Berg 		ether_addr_copy(sdata->u.mntr.mu_follow_addr,
788c5e6889SJohannes Berg 				params->vht_mumimo_follow_addr);
798c5e6889SJohannes Berg 	}
808c5e6889SJohannes Berg 
81d0a9123eSJohannes Berg 	sdata->vif.bss_conf.mu_mimo_owner = mu_mimo_groups || mu_mimo_follow;
8265f1d600SJohannes Berg }
8365f1d600SJohannes Berg 
ieee80211_set_mon_options(struct ieee80211_sub_if_data * sdata,struct vif_params * params)8465f1d600SJohannes Berg static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
8565f1d600SJohannes Berg 				     struct vif_params *params)
8665f1d600SJohannes Berg {
8765f1d600SJohannes Berg 	struct ieee80211_local *local = sdata->local;
8865f1d600SJohannes Berg 	struct ieee80211_sub_if_data *monitor_sdata;
8965f1d600SJohannes Berg 
9065f1d600SJohannes Berg 	/* check flags first */
9165f1d600SJohannes Berg 	if (params->flags && ieee80211_sdata_running(sdata)) {
9265f1d600SJohannes Berg 		u32 mask = MONITOR_FLAG_COOK_FRAMES | MONITOR_FLAG_ACTIVE;
9365f1d600SJohannes Berg 
9465f1d600SJohannes Berg 		/*
9565f1d600SJohannes Berg 		 * Prohibit MONITOR_FLAG_COOK_FRAMES and
9665f1d600SJohannes Berg 		 * MONITOR_FLAG_ACTIVE to be changed while the
9765f1d600SJohannes Berg 		 * interface is up.
9865f1d600SJohannes Berg 		 * Else we would need to add a lot of cruft
9965f1d600SJohannes Berg 		 * to update everything:
10065f1d600SJohannes Berg 		 *	cooked_mntrs, monitor and all fif_* counters
10165f1d600SJohannes Berg 		 *	reconfigure hardware
10265f1d600SJohannes Berg 		 */
10365f1d600SJohannes Berg 		if ((params->flags & mask) != (sdata->u.mntr.flags & mask))
10465f1d600SJohannes Berg 			return -EBUSY;
10565f1d600SJohannes Berg 	}
10665f1d600SJohannes Berg 
10765f1d600SJohannes Berg 	/* also validate MU-MIMO change */
1086dd23603SJohannes Berg 	monitor_sdata = wiphy_dereference(local->hw.wiphy,
1096dd23603SJohannes Berg 					  local->monitor_sdata);
11065f1d600SJohannes Berg 
11165f1d600SJohannes Berg 	if (!monitor_sdata &&
11265f1d600SJohannes Berg 	    (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
11365f1d600SJohannes Berg 		return -EOPNOTSUPP;
11465f1d600SJohannes Berg 
11565f1d600SJohannes Berg 	/* apply all changes now - no failures allowed */
11665f1d600SJohannes Berg 
11765f1d600SJohannes Berg 	if (monitor_sdata)
11865f1d600SJohannes Berg 		ieee80211_set_mu_mimo_follow(monitor_sdata, params);
11965f1d600SJohannes Berg 
12065f1d600SJohannes Berg 	if (params->flags) {
12165f1d600SJohannes Berg 		if (ieee80211_sdata_running(sdata)) {
12265f1d600SJohannes Berg 			ieee80211_adjust_monitor_flags(sdata, -1);
12365f1d600SJohannes Berg 			sdata->u.mntr.flags = params->flags;
12465f1d600SJohannes Berg 			ieee80211_adjust_monitor_flags(sdata, 1);
12565f1d600SJohannes Berg 
12665f1d600SJohannes Berg 			ieee80211_configure_filter(local);
12765f1d600SJohannes Berg 		} else {
12865f1d600SJohannes Berg 			/*
12965f1d600SJohannes Berg 			 * Because the interface is down, ieee80211_do_stop
13065f1d600SJohannes Berg 			 * and ieee80211_do_open take care of "everything"
13165f1d600SJohannes Berg 			 * mentioned in the comment above.
13265f1d600SJohannes Berg 			 */
13365f1d600SJohannes Berg 			sdata->u.mntr.flags = params->flags;
13465f1d600SJohannes Berg 		}
13565f1d600SJohannes Berg 	}
1368c5e6889SJohannes Berg 
1378c5e6889SJohannes Berg 	return 0;
1388c5e6889SJohannes Berg }
1398c5e6889SJohannes Berg 
ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data * sdata,struct cfg80211_mbssid_config params,struct ieee80211_bss_conf * link_conf)14017196425SJohn Crispin static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,
141d9f83f22SShaul Triebitz 					   struct cfg80211_mbssid_config params,
142d9f83f22SShaul Triebitz 					   struct ieee80211_bss_conf *link_conf)
14317196425SJohn Crispin {
14417196425SJohn Crispin 	struct ieee80211_sub_if_data *tx_sdata;
14517196425SJohn Crispin 
14617196425SJohn Crispin 	sdata->vif.mbssid_tx_vif = NULL;
147d9f83f22SShaul Triebitz 	link_conf->bssid_index = 0;
148d9f83f22SShaul Triebitz 	link_conf->nontransmitted = false;
149d9f83f22SShaul Triebitz 	link_conf->ema_ap = false;
1500eb38842SAloka Dixit 	link_conf->bssid_indicator = 0;
15117196425SJohn Crispin 
15217196425SJohn Crispin 	if (sdata->vif.type != NL80211_IFTYPE_AP || !params.tx_wdev)
15317196425SJohn Crispin 		return -EINVAL;
15417196425SJohn Crispin 
15517196425SJohn Crispin 	tx_sdata = IEEE80211_WDEV_TO_SUB_IF(params.tx_wdev);
15617196425SJohn Crispin 	if (!tx_sdata)
15717196425SJohn Crispin 		return -EINVAL;
15817196425SJohn Crispin 
15917196425SJohn Crispin 	if (tx_sdata == sdata) {
16017196425SJohn Crispin 		sdata->vif.mbssid_tx_vif = &sdata->vif;
16117196425SJohn Crispin 	} else {
16217196425SJohn Crispin 		sdata->vif.mbssid_tx_vif = &tx_sdata->vif;
163d9f83f22SShaul Triebitz 		link_conf->nontransmitted = true;
164d9f83f22SShaul Triebitz 		link_conf->bssid_index = params.index;
16517196425SJohn Crispin 	}
16617196425SJohn Crispin 	if (params.ema)
167d9f83f22SShaul Triebitz 		link_conf->ema_ap = true;
16817196425SJohn Crispin 
16917196425SJohn Crispin 	return 0;
17017196425SJohn Crispin }
17117196425SJohn Crispin 
ieee80211_add_iface(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,struct vif_params * params)172552bff0cSJohannes Berg static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
173552bff0cSJohannes Berg 						const char *name,
1746bab2e19STom Gundersen 						unsigned char name_assign_type,
175f9e10ce4SJohannes Berg 						enum nl80211_iftype type,
1762ec600d6SLuis Carlos Cobo 						struct vif_params *params)
177fa5fea71SJohannes Berg {
178fa5fea71SJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
17984efbb84SJohannes Berg 	struct wireless_dev *wdev;
1808cc9a739SMichael Wu 	struct ieee80211_sub_if_data *sdata;
1818cc9a739SMichael Wu 	int err;
182fa5fea71SJohannes Berg 
1836bab2e19STom Gundersen 	err = ieee80211_if_add(local, name, name_assign_type, &wdev, type, params);
184f9e10ce4SJohannes Berg 	if (err)
185f9e10ce4SJohannes Berg 		return ERR_PTR(err);
1868cc9a739SMichael Wu 
18784efbb84SJohannes Berg 	sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
1888c5e6889SJohannes Berg 
1898c5e6889SJohannes Berg 	if (type == NL80211_IFTYPE_MONITOR) {
19065f1d600SJohannes Berg 		err = ieee80211_set_mon_options(sdata, params);
1918c5e6889SJohannes Berg 		if (err) {
1928c5e6889SJohannes Berg 			ieee80211_if_remove(sdata);
1938c5e6889SJohannes Berg 			return NULL;
1948c5e6889SJohannes Berg 		}
195f9e10ce4SJohannes Berg 	}
196f9e10ce4SJohannes Berg 
19784efbb84SJohannes Berg 	return wdev;
198fa5fea71SJohannes Berg }
199fa5fea71SJohannes Berg 
ieee80211_del_iface(struct wiphy * wiphy,struct wireless_dev * wdev)20084efbb84SJohannes Berg static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
201fa5fea71SJohannes Berg {
20284efbb84SJohannes Berg 	ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));
20375636525SJohannes Berg 
204fa5fea71SJohannes Berg 	return 0;
205fa5fea71SJohannes Berg }
206fa5fea71SJohannes Berg 
ieee80211_change_iface(struct wiphy * wiphy,struct net_device * dev,enum nl80211_iftype type,struct vif_params * params)207e36d56b6SJohannes Berg static int ieee80211_change_iface(struct wiphy *wiphy,
208e36d56b6SJohannes Berg 				  struct net_device *dev,
209818a986eSJohannes Berg 				  enum nl80211_iftype type,
2102ec600d6SLuis Carlos Cobo 				  struct vif_params *params)
21142613db7SJohannes Berg {
2129607e6b6SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
213a5d3cbdbSFelix Fietkau 	struct ieee80211_local *local = sdata->local;
214a5d3cbdbSFelix Fietkau 	struct sta_info *sta;
215f3947e2dSJohannes Berg 	int ret;
21642613db7SJohannes Berg 
21705c914feSJohannes Berg 	ret = ieee80211_if_change_type(sdata, type);
218f3947e2dSJohannes Berg 	if (ret)
219f3947e2dSJohannes Berg 		return ret;
22042613db7SJohannes Berg 
2216f527287SJohannes Berg 	if (type == NL80211_IFTYPE_AP_VLAN && params->use_4addr == 0) {
222a9b3cd7fSStephen Hemminger 		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);
22349ddf8e6SJohannes Berg 		ieee80211_check_fast_rx_iface(sdata);
2246f527287SJohannes Berg 	} else if (type == NL80211_IFTYPE_STATION && params->use_4addr >= 0) {
225a5d3cbdbSFelix Fietkau 		struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
226a5d3cbdbSFelix Fietkau 
227a5d3cbdbSFelix Fietkau 		if (params->use_4addr == ifmgd->use_4addr)
228a5d3cbdbSFelix Fietkau 			return 0;
229a5d3cbdbSFelix Fietkau 
23090703ba9SJohannes Berg 		/* FIXME: no support for 4-addr MLO yet */
231f1871abdSIlan Peer 		if (ieee80211_vif_is_mld(&sdata->vif))
23290703ba9SJohannes Berg 			return -EOPNOTSUPP;
23390703ba9SJohannes Berg 
2349bc383deSJohannes Berg 		sdata->u.mgd.use_4addr = params->use_4addr;
235a5d3cbdbSFelix Fietkau 		if (!ifmgd->associated)
236a5d3cbdbSFelix Fietkau 			return 0;
237a5d3cbdbSFelix Fietkau 
238a5d3cbdbSFelix Fietkau 		mutex_lock(&local->sta_mtx);
239bfd8403aSJohannes Berg 		sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
240a5d3cbdbSFelix Fietkau 		if (sta)
241a5d3cbdbSFelix Fietkau 			drv_sta_set_4addr(local, sdata, &sta->sta,
242a5d3cbdbSFelix Fietkau 					  params->use_4addr);
243a5d3cbdbSFelix Fietkau 		mutex_unlock(&local->sta_mtx);
244a5d3cbdbSFelix Fietkau 
245a5d3cbdbSFelix Fietkau 		if (params->use_4addr)
246a5d3cbdbSFelix Fietkau 			ieee80211_send_4addr_nullfunc(local, sdata);
24749ddf8e6SJohannes Berg 	}
2489bc383deSJohannes Berg 
24942bd20d9SAviya Erenfeld 	if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
25065f1d600SJohannes Berg 		ret = ieee80211_set_mon_options(sdata, params);
25165f1d600SJohannes Berg 		if (ret)
25265f1d600SJohannes Berg 			return ret;
25385416a4fSChristian Lamparter 	}
254f7917af9SFelix Fietkau 
25542613db7SJohannes Berg 	return 0;
25642613db7SJohannes Berg }
25742613db7SJohannes Berg 
ieee80211_start_p2p_device(struct wiphy * wiphy,struct wireless_dev * wdev)258f142c6b9SJohannes Berg static int ieee80211_start_p2p_device(struct wiphy *wiphy,
259f142c6b9SJohannes Berg 				      struct wireless_dev *wdev)
260f142c6b9SJohannes Berg {
261b6a55015SLuciano Coelho 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
262b6a55015SLuciano Coelho 	int ret;
263b6a55015SLuciano Coelho 
264b6a55015SLuciano Coelho 	mutex_lock(&sdata->local->chanctx_mtx);
265b6a55015SLuciano Coelho 	ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
266b6a55015SLuciano Coelho 	mutex_unlock(&sdata->local->chanctx_mtx);
267b6a55015SLuciano Coelho 	if (ret < 0)
268b6a55015SLuciano Coelho 		return ret;
269b6a55015SLuciano Coelho 
270f142c6b9SJohannes Berg 	return ieee80211_do_open(wdev, true);
271f142c6b9SJohannes Berg }
272f142c6b9SJohannes Berg 
ieee80211_stop_p2p_device(struct wiphy * wiphy,struct wireless_dev * wdev)273f142c6b9SJohannes Berg static void ieee80211_stop_p2p_device(struct wiphy *wiphy,
274f142c6b9SJohannes Berg 				      struct wireless_dev *wdev)
275f142c6b9SJohannes Berg {
276f142c6b9SJohannes Berg 	ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev));
277f142c6b9SJohannes Berg }
278f142c6b9SJohannes Berg 
ieee80211_start_nan(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_nan_conf * conf)279708d50edSAyala Beker static int ieee80211_start_nan(struct wiphy *wiphy,
280708d50edSAyala Beker 			       struct wireless_dev *wdev,
281708d50edSAyala Beker 			       struct cfg80211_nan_conf *conf)
282708d50edSAyala Beker {
283708d50edSAyala Beker 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
284708d50edSAyala Beker 	int ret;
285708d50edSAyala Beker 
286708d50edSAyala Beker 	mutex_lock(&sdata->local->chanctx_mtx);
287708d50edSAyala Beker 	ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
288708d50edSAyala Beker 	mutex_unlock(&sdata->local->chanctx_mtx);
289708d50edSAyala Beker 	if (ret < 0)
290708d50edSAyala Beker 		return ret;
291708d50edSAyala Beker 
292708d50edSAyala Beker 	ret = ieee80211_do_open(wdev, true);
293708d50edSAyala Beker 	if (ret)
294708d50edSAyala Beker 		return ret;
295708d50edSAyala Beker 
296708d50edSAyala Beker 	ret = drv_start_nan(sdata->local, sdata, conf);
297708d50edSAyala Beker 	if (ret)
298708d50edSAyala Beker 		ieee80211_sdata_stop(sdata);
299708d50edSAyala Beker 
300167e33f4SAyala Beker 	sdata->u.nan.conf = *conf;
301167e33f4SAyala Beker 
302708d50edSAyala Beker 	return ret;
303708d50edSAyala Beker }
304708d50edSAyala Beker 
ieee80211_stop_nan(struct wiphy * wiphy,struct wireless_dev * wdev)305708d50edSAyala Beker static void ieee80211_stop_nan(struct wiphy *wiphy,
306708d50edSAyala Beker 			       struct wireless_dev *wdev)
307708d50edSAyala Beker {
308708d50edSAyala Beker 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
309708d50edSAyala Beker 
310708d50edSAyala Beker 	drv_stop_nan(sdata->local, sdata);
311708d50edSAyala Beker 	ieee80211_sdata_stop(sdata);
312708d50edSAyala Beker }
313708d50edSAyala Beker 
ieee80211_nan_change_conf(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_nan_conf * conf,u32 changes)3145953ff6dSAyala Beker static int ieee80211_nan_change_conf(struct wiphy *wiphy,
3155953ff6dSAyala Beker 				     struct wireless_dev *wdev,
3165953ff6dSAyala Beker 				     struct cfg80211_nan_conf *conf,
3175953ff6dSAyala Beker 				     u32 changes)
3185953ff6dSAyala Beker {
3195953ff6dSAyala Beker 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
3205953ff6dSAyala Beker 	struct cfg80211_nan_conf new_conf;
3215953ff6dSAyala Beker 	int ret = 0;
3225953ff6dSAyala Beker 
3235953ff6dSAyala Beker 	if (sdata->vif.type != NL80211_IFTYPE_NAN)
3245953ff6dSAyala Beker 		return -EOPNOTSUPP;
3255953ff6dSAyala Beker 
3265953ff6dSAyala Beker 	if (!ieee80211_sdata_running(sdata))
3275953ff6dSAyala Beker 		return -ENETDOWN;
3285953ff6dSAyala Beker 
3295953ff6dSAyala Beker 	new_conf = sdata->u.nan.conf;
3305953ff6dSAyala Beker 
3315953ff6dSAyala Beker 	if (changes & CFG80211_NAN_CONF_CHANGED_PREF)
3325953ff6dSAyala Beker 		new_conf.master_pref = conf->master_pref;
3335953ff6dSAyala Beker 
3348585989dSLuca Coelho 	if (changes & CFG80211_NAN_CONF_CHANGED_BANDS)
3358585989dSLuca Coelho 		new_conf.bands = conf->bands;
3365953ff6dSAyala Beker 
3375953ff6dSAyala Beker 	ret = drv_nan_change_conf(sdata->local, sdata, &new_conf, changes);
3385953ff6dSAyala Beker 	if (!ret)
3395953ff6dSAyala Beker 		sdata->u.nan.conf = new_conf;
3405953ff6dSAyala Beker 
3415953ff6dSAyala Beker 	return ret;
3425953ff6dSAyala Beker }
3435953ff6dSAyala Beker 
ieee80211_add_nan_func(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_nan_func * nan_func)344167e33f4SAyala Beker static int ieee80211_add_nan_func(struct wiphy *wiphy,
345167e33f4SAyala Beker 				  struct wireless_dev *wdev,
346167e33f4SAyala Beker 				  struct cfg80211_nan_func *nan_func)
347167e33f4SAyala Beker {
348167e33f4SAyala Beker 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
349167e33f4SAyala Beker 	int ret;
350167e33f4SAyala Beker 
351167e33f4SAyala Beker 	if (sdata->vif.type != NL80211_IFTYPE_NAN)
352167e33f4SAyala Beker 		return -EOPNOTSUPP;
353167e33f4SAyala Beker 
354167e33f4SAyala Beker 	if (!ieee80211_sdata_running(sdata))
355167e33f4SAyala Beker 		return -ENETDOWN;
356167e33f4SAyala Beker 
357167e33f4SAyala Beker 	spin_lock_bh(&sdata->u.nan.func_lock);
358167e33f4SAyala Beker 
359167e33f4SAyala Beker 	ret = idr_alloc(&sdata->u.nan.function_inst_ids,
360167e33f4SAyala Beker 			nan_func, 1, sdata->local->hw.max_nan_de_entries + 1,
361167e33f4SAyala Beker 			GFP_ATOMIC);
362167e33f4SAyala Beker 	spin_unlock_bh(&sdata->u.nan.func_lock);
363167e33f4SAyala Beker 
364167e33f4SAyala Beker 	if (ret < 0)
365167e33f4SAyala Beker 		return ret;
366167e33f4SAyala Beker 
367167e33f4SAyala Beker 	nan_func->instance_id = ret;
368167e33f4SAyala Beker 
369167e33f4SAyala Beker 	WARN_ON(nan_func->instance_id == 0);
370167e33f4SAyala Beker 
371167e33f4SAyala Beker 	ret = drv_add_nan_func(sdata->local, sdata, nan_func);
372167e33f4SAyala Beker 	if (ret) {
373167e33f4SAyala Beker 		spin_lock_bh(&sdata->u.nan.func_lock);
374167e33f4SAyala Beker 		idr_remove(&sdata->u.nan.function_inst_ids,
375167e33f4SAyala Beker 			   nan_func->instance_id);
376167e33f4SAyala Beker 		spin_unlock_bh(&sdata->u.nan.func_lock);
377167e33f4SAyala Beker 	}
378167e33f4SAyala Beker 
379167e33f4SAyala Beker 	return ret;
380167e33f4SAyala Beker }
381167e33f4SAyala Beker 
382167e33f4SAyala Beker static struct cfg80211_nan_func *
ieee80211_find_nan_func_by_cookie(struct ieee80211_sub_if_data * sdata,u64 cookie)383167e33f4SAyala Beker ieee80211_find_nan_func_by_cookie(struct ieee80211_sub_if_data *sdata,
384167e33f4SAyala Beker 				  u64 cookie)
385167e33f4SAyala Beker {
386167e33f4SAyala Beker 	struct cfg80211_nan_func *func;
387167e33f4SAyala Beker 	int id;
388167e33f4SAyala Beker 
389167e33f4SAyala Beker 	lockdep_assert_held(&sdata->u.nan.func_lock);
390167e33f4SAyala Beker 
391167e33f4SAyala Beker 	idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) {
392167e33f4SAyala Beker 		if (func->cookie == cookie)
393167e33f4SAyala Beker 			return func;
394167e33f4SAyala Beker 	}
395167e33f4SAyala Beker 
396167e33f4SAyala Beker 	return NULL;
397167e33f4SAyala Beker }
398167e33f4SAyala Beker 
ieee80211_del_nan_func(struct wiphy * wiphy,struct wireless_dev * wdev,u64 cookie)399167e33f4SAyala Beker static void ieee80211_del_nan_func(struct wiphy *wiphy,
400167e33f4SAyala Beker 				  struct wireless_dev *wdev, u64 cookie)
401167e33f4SAyala Beker {
402167e33f4SAyala Beker 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
403167e33f4SAyala Beker 	struct cfg80211_nan_func *func;
404167e33f4SAyala Beker 	u8 instance_id = 0;
405167e33f4SAyala Beker 
406167e33f4SAyala Beker 	if (sdata->vif.type != NL80211_IFTYPE_NAN ||
407167e33f4SAyala Beker 	    !ieee80211_sdata_running(sdata))
408167e33f4SAyala Beker 		return;
409167e33f4SAyala Beker 
410167e33f4SAyala Beker 	spin_lock_bh(&sdata->u.nan.func_lock);
411167e33f4SAyala Beker 
412167e33f4SAyala Beker 	func = ieee80211_find_nan_func_by_cookie(sdata, cookie);
413167e33f4SAyala Beker 	if (func)
414167e33f4SAyala Beker 		instance_id = func->instance_id;
415167e33f4SAyala Beker 
416167e33f4SAyala Beker 	spin_unlock_bh(&sdata->u.nan.func_lock);
417167e33f4SAyala Beker 
418167e33f4SAyala Beker 	if (instance_id)
419167e33f4SAyala Beker 		drv_del_nan_func(sdata->local, sdata, instance_id);
420167e33f4SAyala Beker }
421167e33f4SAyala Beker 
ieee80211_set_noack_map(struct wiphy * wiphy,struct net_device * dev,u16 noack_map)422b53be792SSimon Wunderlich static int ieee80211_set_noack_map(struct wiphy *wiphy,
423b53be792SSimon Wunderlich 				  struct net_device *dev,
424b53be792SSimon Wunderlich 				  u16 noack_map)
425b53be792SSimon Wunderlich {
426b53be792SSimon Wunderlich 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
427b53be792SSimon Wunderlich 
428b53be792SSimon Wunderlich 	sdata->noack_map = noack_map;
42917c18bf8SJohannes Berg 
43017c18bf8SJohannes Berg 	ieee80211_check_fast_xmit_iface(sdata);
43117c18bf8SJohannes Berg 
432b53be792SSimon Wunderlich 	return 0;
433b53be792SSimon Wunderlich }
434b53be792SSimon Wunderlich 
ieee80211_set_tx(struct ieee80211_sub_if_data * sdata,const u8 * mac_addr,u8 key_idx)43596fc6efbSAlexander Wetzel static int ieee80211_set_tx(struct ieee80211_sub_if_data *sdata,
43696fc6efbSAlexander Wetzel 			    const u8 *mac_addr, u8 key_idx)
43796fc6efbSAlexander Wetzel {
43896fc6efbSAlexander Wetzel 	struct ieee80211_local *local = sdata->local;
43996fc6efbSAlexander Wetzel 	struct ieee80211_key *key;
44096fc6efbSAlexander Wetzel 	struct sta_info *sta;
44196fc6efbSAlexander Wetzel 	int ret = -EINVAL;
44296fc6efbSAlexander Wetzel 
44396fc6efbSAlexander Wetzel 	if (!wiphy_ext_feature_isset(local->hw.wiphy,
44496fc6efbSAlexander Wetzel 				     NL80211_EXT_FEATURE_EXT_KEY_ID))
44596fc6efbSAlexander Wetzel 		return -EINVAL;
44696fc6efbSAlexander Wetzel 
44796fc6efbSAlexander Wetzel 	sta = sta_info_get_bss(sdata, mac_addr);
44896fc6efbSAlexander Wetzel 
44996fc6efbSAlexander Wetzel 	if (!sta)
45096fc6efbSAlexander Wetzel 		return -EINVAL;
45196fc6efbSAlexander Wetzel 
45296fc6efbSAlexander Wetzel 	if (sta->ptk_idx == key_idx)
45396fc6efbSAlexander Wetzel 		return 0;
45496fc6efbSAlexander Wetzel 
45596fc6efbSAlexander Wetzel 	mutex_lock(&local->key_mtx);
45696fc6efbSAlexander Wetzel 	key = key_mtx_dereference(local, sta->ptk[key_idx]);
45796fc6efbSAlexander Wetzel 
45896fc6efbSAlexander Wetzel 	if (key && key->conf.flags & IEEE80211_KEY_FLAG_NO_AUTO_TX)
45996fc6efbSAlexander Wetzel 		ret = ieee80211_set_tx_key(key);
46096fc6efbSAlexander Wetzel 
46196fc6efbSAlexander Wetzel 	mutex_unlock(&local->key_mtx);
46296fc6efbSAlexander Wetzel 	return ret;
46396fc6efbSAlexander Wetzel }
46496fc6efbSAlexander Wetzel 
ieee80211_add_key(struct wiphy * wiphy,struct net_device * dev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,struct key_params * params)465e8cbb4cbSJohannes Berg static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
466e7a7b84eSVeerendranath Jakkam 			     int link_id, u8 key_idx, bool pairwise,
467e7a7b84eSVeerendranath Jakkam 			     const u8 *mac_addr, struct key_params *params)
468e8cbb4cbSJohannes Berg {
46926a58456SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
470ccdde7c7SJohannes Berg 	struct ieee80211_link_data *link =
471ccdde7c7SJohannes Berg 		ieee80211_link_or_deflink(sdata, link_id, false);
4722475b1ccSMax Stepanov 	struct ieee80211_local *local = sdata->local;
473e8cbb4cbSJohannes Berg 	struct sta_info *sta = NULL;
474db4d1169SJohannes Berg 	struct ieee80211_key *key;
4753b96766fSJohannes Berg 	int err;
476e8cbb4cbSJohannes Berg 
47726a58456SJohannes Berg 	if (!ieee80211_sdata_running(sdata))
478ad0e2b5aSJohannes Berg 		return -ENETDOWN;
479ad0e2b5aSJohannes Berg 
480ccdde7c7SJohannes Berg 	if (IS_ERR(link))
481ccdde7c7SJohannes Berg 		return PTR_ERR(link);
482ccdde7c7SJohannes Berg 
48396fc6efbSAlexander Wetzel 	if (pairwise && params->mode == NL80211_KEY_SET_TX)
48496fc6efbSAlexander Wetzel 		return ieee80211_set_tx(sdata, mac_addr, key_idx);
48596fc6efbSAlexander Wetzel 
48697359d12SJohannes Berg 	/* reject WEP and TKIP keys if WEP failed to initialize */
487e8cbb4cbSJohannes Berg 	switch (params->cipher) {
488e8cbb4cbSJohannes Berg 	case WLAN_CIPHER_SUITE_WEP40:
489e8cbb4cbSJohannes Berg 	case WLAN_CIPHER_SUITE_TKIP:
49097359d12SJohannes Berg 	case WLAN_CIPHER_SUITE_WEP104:
491ccdde7c7SJohannes Berg 		if (link_id >= 0)
492ccdde7c7SJohannes Berg 			return -EINVAL;
4935fdb3735SArd Biesheuvel 		if (WARN_ON_ONCE(fips_enabled))
49497359d12SJohannes Berg 			return -EINVAL;
495aaaee2d6SGustavo A. R. Silva 		break;
496e8cbb4cbSJohannes Berg 	default:
49797359d12SJohannes Berg 		break;
498e8cbb4cbSJohannes Berg 	}
499e8cbb4cbSJohannes Berg 
50097359d12SJohannes Berg 	key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
50123a5f0afSJohannes Berg 				  params->key, params->seq_len, params->seq);
5021ac62ba7SBen Hutchings 	if (IS_ERR(key))
5031ac62ba7SBen Hutchings 		return PTR_ERR(key);
504db4d1169SJohannes Berg 
505ccdde7c7SJohannes Berg 	key->conf.link_id = link_id;
506ccdde7c7SJohannes Berg 
507e31b8213SJohannes Berg 	if (pairwise)
508e31b8213SJohannes Berg 		key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
509e31b8213SJohannes Berg 
51096fc6efbSAlexander Wetzel 	if (params->mode == NL80211_KEY_NO_TX)
51196fc6efbSAlexander Wetzel 		key->conf.flags |= IEEE80211_KEY_FLAG_NO_AUTO_TX;
51296fc6efbSAlexander Wetzel 
5132475b1ccSMax Stepanov 	mutex_lock(&local->sta_mtx);
5143b96766fSJohannes Berg 
515e8cbb4cbSJohannes Berg 	if (mac_addr) {
5160e5ded5aSFelix Fietkau 		sta = sta_info_get_bss(sdata, mac_addr);
5171626e0faSJohannes Berg 		/*
5181626e0faSJohannes Berg 		 * The ASSOC test makes sure the driver is ready to
5191626e0faSJohannes Berg 		 * receive the key. When wpa_supplicant has roamed
5201626e0faSJohannes Berg 		 * using FT, it attempts to set the key before
5211626e0faSJohannes Berg 		 * association has completed, this rejects that attempt
522d070f913SStephen Hemminger 		 * so it will set the key again after association.
5231626e0faSJohannes Berg 		 *
5241626e0faSJohannes Berg 		 * TODO: accept the key if we have a station entry and
5251626e0faSJohannes Berg 		 *       add it to the device after the station.
5261626e0faSJohannes Berg 		 */
5271626e0faSJohannes Berg 		if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
52879cf2dfaSJohannes Berg 			ieee80211_key_free_unused(key);
5293b96766fSJohannes Berg 			err = -ENOENT;
5303b96766fSJohannes Berg 			goto out_unlock;
531e8cbb4cbSJohannes Berg 		}
532db4d1169SJohannes Berg 	}
533db4d1169SJohannes Berg 
534e548c49eSJohannes Berg 	switch (sdata->vif.type) {
535e548c49eSJohannes Berg 	case NL80211_IFTYPE_STATION:
536e548c49eSJohannes Berg 		if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
537e548c49eSJohannes Berg 			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
538e548c49eSJohannes Berg 		break;
539e548c49eSJohannes Berg 	case NL80211_IFTYPE_AP:
540e548c49eSJohannes Berg 	case NL80211_IFTYPE_AP_VLAN:
541e548c49eSJohannes Berg 		/* Keys without a station are used for TX only */
542211710caSFelix Fietkau 		if (sta && test_sta_flag(sta, WLAN_STA_MFP))
543e548c49eSJohannes Berg 			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
544e548c49eSJohannes Berg 		break;
545e548c49eSJohannes Berg 	case NL80211_IFTYPE_ADHOC:
546e548c49eSJohannes Berg 		/* no MFP (yet) */
547e548c49eSJohannes Berg 		break;
548e548c49eSJohannes Berg 	case NL80211_IFTYPE_MESH_POINT:
549e548c49eSJohannes Berg #ifdef CONFIG_MAC80211_MESH
550e548c49eSJohannes Berg 		if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
551e548c49eSJohannes Berg 			key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
552e548c49eSJohannes Berg 		break;
553e548c49eSJohannes Berg #endif
554e548c49eSJohannes Berg 	case NL80211_IFTYPE_WDS:
555e548c49eSJohannes Berg 	case NL80211_IFTYPE_MONITOR:
556e548c49eSJohannes Berg 	case NL80211_IFTYPE_P2P_DEVICE:
557cb3b7d87SAyala Beker 	case NL80211_IFTYPE_NAN:
558e548c49eSJohannes Berg 	case NL80211_IFTYPE_UNSPECIFIED:
559e548c49eSJohannes Berg 	case NUM_NL80211_IFTYPES:
560e548c49eSJohannes Berg 	case NL80211_IFTYPE_P2P_CLIENT:
561e548c49eSJohannes Berg 	case NL80211_IFTYPE_P2P_GO:
5626e0bd6c3SRostislav Lisovy 	case NL80211_IFTYPE_OCB:
563e548c49eSJohannes Berg 		/* shouldn't happen */
564e548c49eSJohannes Berg 		WARN_ON_ONCE(1);
565e548c49eSJohannes Berg 		break;
566e548c49eSJohannes Berg 	}
567e548c49eSJohannes Berg 
568ccdde7c7SJohannes Berg 	err = ieee80211_key_link(key, link, sta);
56931db78a4SJohannes Berg 	/* KRACK protection, shouldn't happen but just silently accept key */
57031db78a4SJohannes Berg 	if (err == -EALREADY)
57131db78a4SJohannes Berg 		err = 0;
572e8cbb4cbSJohannes Berg 
5733b96766fSJohannes Berg  out_unlock:
5742475b1ccSMax Stepanov 	mutex_unlock(&local->sta_mtx);
5753b96766fSJohannes Berg 
5763b96766fSJohannes Berg 	return err;
577e8cbb4cbSJohannes Berg }
578e8cbb4cbSJohannes Berg 
5798cbf0c2aSJohannes Berg static struct ieee80211_key *
ieee80211_lookup_key(struct ieee80211_sub_if_data * sdata,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr)580ccdde7c7SJohannes Berg ieee80211_lookup_key(struct ieee80211_sub_if_data *sdata, int link_id,
5818cbf0c2aSJohannes Berg 		     u8 key_idx, bool pairwise, const u8 *mac_addr)
5828cbf0c2aSJohannes Berg {
58309d838a4SÍñigo Huguet 	struct ieee80211_local *local __maybe_unused = sdata->local;
584ccdde7c7SJohannes Berg 	struct ieee80211_link_data *link = &sdata->deflink;
585bfd8403aSJohannes Berg 	struct ieee80211_key *key;
586ccdde7c7SJohannes Berg 
587ccdde7c7SJohannes Berg 	if (link_id >= 0) {
588ccdde7c7SJohannes Berg 		link = rcu_dereference_check(sdata->link[link_id],
589ccdde7c7SJohannes Berg 					     lockdep_is_held(&sdata->wdev.mtx));
590ccdde7c7SJohannes Berg 		if (!link)
591ccdde7c7SJohannes Berg 			return NULL;
592ccdde7c7SJohannes Berg 	}
5938cbf0c2aSJohannes Berg 
5948cbf0c2aSJohannes Berg 	if (mac_addr) {
595ccdde7c7SJohannes Berg 		struct sta_info *sta;
596ccdde7c7SJohannes Berg 		struct link_sta_info *link_sta;
597ccdde7c7SJohannes Berg 
5988cbf0c2aSJohannes Berg 		sta = sta_info_get_bss(sdata, mac_addr);
5998cbf0c2aSJohannes Berg 		if (!sta)
6008cbf0c2aSJohannes Berg 			return NULL;
6018cbf0c2aSJohannes Berg 
602ccdde7c7SJohannes Berg 		if (link_id >= 0) {
603ccdde7c7SJohannes Berg 			link_sta = rcu_dereference_check(sta->link[link_id],
604ccdde7c7SJohannes Berg 							 lockdep_is_held(&local->sta_mtx));
605ccdde7c7SJohannes Berg 			if (!link_sta)
606ccdde7c7SJohannes Berg 				return NULL;
607ccdde7c7SJohannes Berg 		} else {
608ccdde7c7SJohannes Berg 			link_sta = &sta->deflink;
609ccdde7c7SJohannes Berg 		}
610ccdde7c7SJohannes Berg 
6118cbf0c2aSJohannes Berg 		if (pairwise && key_idx < NUM_DEFAULT_KEYS)
6128cbf0c2aSJohannes Berg 			return rcu_dereference_check_key_mtx(local,
6138cbf0c2aSJohannes Berg 							     sta->ptk[key_idx]);
6148cbf0c2aSJohannes Berg 
6158cbf0c2aSJohannes Berg 		if (!pairwise &&
6168cbf0c2aSJohannes Berg 		    key_idx < NUM_DEFAULT_KEYS +
6178cbf0c2aSJohannes Berg 			      NUM_DEFAULT_MGMT_KEYS +
6188cbf0c2aSJohannes Berg 			      NUM_DEFAULT_BEACON_KEYS)
6198cbf0c2aSJohannes Berg 			return rcu_dereference_check_key_mtx(local,
620ccdde7c7SJohannes Berg 							     link_sta->gtk[key_idx]);
6218cbf0c2aSJohannes Berg 
6228cbf0c2aSJohannes Berg 		return NULL;
6238cbf0c2aSJohannes Berg 	}
6248cbf0c2aSJohannes Berg 
625bfd8403aSJohannes Berg 	if (pairwise && key_idx < NUM_DEFAULT_KEYS)
6268cbf0c2aSJohannes Berg 		return rcu_dereference_check_key_mtx(local,
6278cbf0c2aSJohannes Berg 						     sdata->keys[key_idx]);
6288cbf0c2aSJohannes Berg 
629ccdde7c7SJohannes Berg 	key = rcu_dereference_check_key_mtx(local, link->gtk[key_idx]);
630bfd8403aSJohannes Berg 	if (key)
631bfd8403aSJohannes Berg 		return key;
632bfd8403aSJohannes Berg 
633e2722d27SJohannes Berg 	/* or maybe it was a WEP key */
634e2722d27SJohannes Berg 	if (key_idx < NUM_DEFAULT_KEYS)
635e2722d27SJohannes Berg 		return rcu_dereference_check_key_mtx(local, sdata->keys[key_idx]);
636e2722d27SJohannes Berg 
6378cbf0c2aSJohannes Berg 	return NULL;
6388cbf0c2aSJohannes Berg }
6398cbf0c2aSJohannes Berg 
ieee80211_del_key(struct wiphy * wiphy,struct net_device * dev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr)640e8cbb4cbSJohannes Berg static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
641e7a7b84eSVeerendranath Jakkam 			     int link_id, u8 key_idx, bool pairwise,
642e7a7b84eSVeerendranath Jakkam 			     const u8 *mac_addr)
643e8cbb4cbSJohannes Berg {
6445c0c3641SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
6455c0c3641SJohannes Berg 	struct ieee80211_local *local = sdata->local;
6468cbf0c2aSJohannes Berg 	struct ieee80211_key *key;
647e8cbb4cbSJohannes Berg 	int ret;
648e8cbb4cbSJohannes Berg 
6495c0c3641SJohannes Berg 	mutex_lock(&local->sta_mtx);
6505c0c3641SJohannes Berg 	mutex_lock(&local->key_mtx);
6513b96766fSJohannes Berg 
652ccdde7c7SJohannes Berg 	key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
6535c0c3641SJohannes Berg 	if (!key) {
6543b96766fSJohannes Berg 		ret = -ENOENT;
6553b96766fSJohannes Berg 		goto out_unlock;
6563b96766fSJohannes Berg 	}
657e8cbb4cbSJohannes Berg 
658133bf90dSManikanta Pubbisetty 	ieee80211_key_free(key, sdata->vif.type == NL80211_IFTYPE_STATION);
659e8cbb4cbSJohannes Berg 
6603b96766fSJohannes Berg 	ret = 0;
6613b96766fSJohannes Berg  out_unlock:
6625c0c3641SJohannes Berg 	mutex_unlock(&local->key_mtx);
6635c0c3641SJohannes Berg 	mutex_unlock(&local->sta_mtx);
6643b96766fSJohannes Berg 
6653b96766fSJohannes Berg 	return ret;
666e8cbb4cbSJohannes Berg }
667e8cbb4cbSJohannes Berg 
ieee80211_get_key(struct wiphy * wiphy,struct net_device * dev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,void * cookie,void (* callback)(void * cookie,struct key_params * params))66862da92fbSJohannes Berg static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
669e7a7b84eSVeerendranath Jakkam 			     int link_id, u8 key_idx, bool pairwise,
670e7a7b84eSVeerendranath Jakkam 			     const u8 *mac_addr, void *cookie,
67162da92fbSJohannes Berg 			     void (*callback)(void *cookie,
67262da92fbSJohannes Berg 					      struct key_params *params))
67362da92fbSJohannes Berg {
67414db74bcSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
67562da92fbSJohannes Berg 	u8 seq[6] = {0};
67662da92fbSJohannes Berg 	struct key_params params;
6778cbf0c2aSJohannes Berg 	struct ieee80211_key *key;
678aba83a0bSJohannes Berg 	u64 pn64;
67962da92fbSJohannes Berg 	u32 iv32;
68062da92fbSJohannes Berg 	u16 iv16;
68162da92fbSJohannes Berg 	int err = -ENOENT;
6829352c19fSJohannes Berg 	struct ieee80211_key_seq kseq = {};
68362da92fbSJohannes Berg 
68414db74bcSJohannes Berg 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
68514db74bcSJohannes Berg 
6863b96766fSJohannes Berg 	rcu_read_lock();
6873b96766fSJohannes Berg 
688ccdde7c7SJohannes Berg 	key = ieee80211_lookup_key(sdata, link_id, key_idx, pairwise, mac_addr);
68962da92fbSJohannes Berg 	if (!key)
69062da92fbSJohannes Berg 		goto out;
69162da92fbSJohannes Berg 
69262da92fbSJohannes Berg 	memset(&params, 0, sizeof(params));
69362da92fbSJohannes Berg 
69497359d12SJohannes Berg 	params.cipher = key->conf.cipher;
69562da92fbSJohannes Berg 
69697359d12SJohannes Berg 	switch (key->conf.cipher) {
69797359d12SJohannes Berg 	case WLAN_CIPHER_SUITE_TKIP:
698f8079d43SEliad Peller 		pn64 = atomic64_read(&key->conf.tx_pn);
699f8079d43SEliad Peller 		iv32 = TKIP_PN_TO_IV32(pn64);
700f8079d43SEliad Peller 		iv16 = TKIP_PN_TO_IV16(pn64);
70162da92fbSJohannes Berg 
7029352c19fSJohannes Berg 		if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
7039352c19fSJohannes Berg 		    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
7049352c19fSJohannes Berg 			drv_get_key_seq(sdata->local, key, &kseq);
7059352c19fSJohannes Berg 			iv32 = kseq.tkip.iv32;
7069352c19fSJohannes Berg 			iv16 = kseq.tkip.iv16;
7079352c19fSJohannes Berg 		}
70862da92fbSJohannes Berg 
70962da92fbSJohannes Berg 		seq[0] = iv16 & 0xff;
71062da92fbSJohannes Berg 		seq[1] = (iv16 >> 8) & 0xff;
71162da92fbSJohannes Berg 		seq[2] = iv32 & 0xff;
71262da92fbSJohannes Berg 		seq[3] = (iv32 >> 8) & 0xff;
71362da92fbSJohannes Berg 		seq[4] = (iv32 >> 16) & 0xff;
71462da92fbSJohannes Berg 		seq[5] = (iv32 >> 24) & 0xff;
71562da92fbSJohannes Berg 		params.seq = seq;
71662da92fbSJohannes Berg 		params.seq_len = 6;
71762da92fbSJohannes Berg 		break;
71897359d12SJohannes Berg 	case WLAN_CIPHER_SUITE_CCMP:
7192b2ba0dbSJouni Malinen 	case WLAN_CIPHER_SUITE_CCMP_256:
720db388a56SJohannes Berg 	case WLAN_CIPHER_SUITE_AES_CMAC:
721db388a56SJohannes Berg 	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
722db388a56SJohannes Berg 		BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
723db388a56SJohannes Berg 			     offsetof(typeof(kseq), aes_cmac));
724fc0561dcSGustavo A. R. Silva 		fallthrough;
725db388a56SJohannes Berg 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
726db388a56SJohannes Berg 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
727db388a56SJohannes Berg 		BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
728db388a56SJohannes Berg 			     offsetof(typeof(kseq), aes_gmac));
729fc0561dcSGustavo A. R. Silva 		fallthrough;
730db388a56SJohannes Berg 	case WLAN_CIPHER_SUITE_GCMP:
731db388a56SJohannes Berg 	case WLAN_CIPHER_SUITE_GCMP_256:
732db388a56SJohannes Berg 		BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
733db388a56SJohannes Berg 			     offsetof(typeof(kseq), gcmp));
734db388a56SJohannes Berg 
7359352c19fSJohannes Berg 		if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
7369352c19fSJohannes Berg 		    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
7379352c19fSJohannes Berg 			drv_get_key_seq(sdata->local, key, &kseq);
7389352c19fSJohannes Berg 			memcpy(seq, kseq.ccmp.pn, 6);
7399352c19fSJohannes Berg 		} else {
740db388a56SJohannes Berg 			pn64 = atomic64_read(&key->conf.tx_pn);
74100b9cfa3SJouni Malinen 			seq[0] = pn64;
74200b9cfa3SJouni Malinen 			seq[1] = pn64 >> 8;
74300b9cfa3SJouni Malinen 			seq[2] = pn64 >> 16;
74400b9cfa3SJouni Malinen 			seq[3] = pn64 >> 24;
74500b9cfa3SJouni Malinen 			seq[4] = pn64 >> 32;
74600b9cfa3SJouni Malinen 			seq[5] = pn64 >> 40;
7479352c19fSJohannes Berg 		}
74800b9cfa3SJouni Malinen 		params.seq = seq;
74900b9cfa3SJouni Malinen 		params.seq_len = 6;
75000b9cfa3SJouni Malinen 		break;
751a31cf1c6SJohannes Berg 	default:
752a31cf1c6SJohannes Berg 		if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
753a31cf1c6SJohannes Berg 			break;
754a31cf1c6SJohannes Berg 		if (WARN_ON(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
755a31cf1c6SJohannes Berg 			break;
756a31cf1c6SJohannes Berg 		drv_get_key_seq(sdata->local, key, &kseq);
757a31cf1c6SJohannes Berg 		params.seq = kseq.hw.seq;
758a31cf1c6SJohannes Berg 		params.seq_len = kseq.hw.seq_len;
759a31cf1c6SJohannes Berg 		break;
76062da92fbSJohannes Berg 	}
76162da92fbSJohannes Berg 
76262da92fbSJohannes Berg 	params.key = key->conf.key;
76362da92fbSJohannes Berg 	params.key_len = key->conf.keylen;
76462da92fbSJohannes Berg 
76562da92fbSJohannes Berg 	callback(cookie, &params);
76662da92fbSJohannes Berg 	err = 0;
76762da92fbSJohannes Berg 
76862da92fbSJohannes Berg  out:
7693b96766fSJohannes Berg 	rcu_read_unlock();
77062da92fbSJohannes Berg 	return err;
77162da92fbSJohannes Berg }
77262da92fbSJohannes Berg 
ieee80211_config_default_key(struct wiphy * wiphy,struct net_device * dev,int link_id,u8 key_idx,bool uni,bool multi)773e8cbb4cbSJohannes Berg static int ieee80211_config_default_key(struct wiphy *wiphy,
774e8cbb4cbSJohannes Berg 					struct net_device *dev,
775e7a7b84eSVeerendranath Jakkam 					int link_id, u8 key_idx, bool uni,
776dbd2fd65SJohannes Berg 					bool multi)
777e8cbb4cbSJohannes Berg {
778ad0e2b5aSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
779ccdde7c7SJohannes Berg 	struct ieee80211_link_data *link =
780ccdde7c7SJohannes Berg 		ieee80211_link_or_deflink(sdata, link_id, false);
781e8cbb4cbSJohannes Berg 
782ccdde7c7SJohannes Berg 	if (IS_ERR(link))
783ccdde7c7SJohannes Berg 		return PTR_ERR(link);
784ccdde7c7SJohannes Berg 
785ccdde7c7SJohannes Berg 	ieee80211_set_default_key(link, key_idx, uni, multi);
786e8cbb4cbSJohannes Berg 
787e8cbb4cbSJohannes Berg 	return 0;
788e8cbb4cbSJohannes Berg }
789e8cbb4cbSJohannes Berg 
ieee80211_config_default_mgmt_key(struct wiphy * wiphy,struct net_device * dev,int link_id,u8 key_idx)7903cfcf6acSJouni Malinen static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
7913cfcf6acSJouni Malinen 					     struct net_device *dev,
792e7a7b84eSVeerendranath Jakkam 					     int link_id, u8 key_idx)
7933cfcf6acSJouni Malinen {
79466c52421SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
795ccdde7c7SJohannes Berg 	struct ieee80211_link_data *link =
796ccdde7c7SJohannes Berg 		ieee80211_link_or_deflink(sdata, link_id, true);
7973cfcf6acSJouni Malinen 
798ccdde7c7SJohannes Berg 	if (IS_ERR(link))
799ccdde7c7SJohannes Berg 		return PTR_ERR(link);
800ccdde7c7SJohannes Berg 
801ccdde7c7SJohannes Berg 	ieee80211_set_default_mgmt_key(link, key_idx);
8023cfcf6acSJouni Malinen 
8033cfcf6acSJouni Malinen 	return 0;
8043cfcf6acSJouni Malinen }
8053cfcf6acSJouni Malinen 
ieee80211_config_default_beacon_key(struct wiphy * wiphy,struct net_device * dev,int link_id,u8 key_idx)806e5473e80SJouni Malinen static int ieee80211_config_default_beacon_key(struct wiphy *wiphy,
807e5473e80SJouni Malinen 					       struct net_device *dev,
808e7a7b84eSVeerendranath Jakkam 					       int link_id, u8 key_idx)
809e5473e80SJouni Malinen {
810e5473e80SJouni Malinen 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
811ccdde7c7SJohannes Berg 	struct ieee80211_link_data *link =
812ccdde7c7SJohannes Berg 		ieee80211_link_or_deflink(sdata, link_id, true);
813e5473e80SJouni Malinen 
814ccdde7c7SJohannes Berg 	if (IS_ERR(link))
815ccdde7c7SJohannes Berg 		return PTR_ERR(link);
816ccdde7c7SJohannes Berg 
817ccdde7c7SJohannes Berg 	ieee80211_set_default_beacon_key(link, key_idx);
818e5473e80SJouni Malinen 
819e5473e80SJouni Malinen 	return 0;
820e5473e80SJouni Malinen }
821e5473e80SJouni Malinen 
sta_set_rate_info_tx(struct sta_info * sta,const struct ieee80211_tx_rate * rate,struct rate_info * rinfo)8226b62bf32SThomas Pedersen void sta_set_rate_info_tx(struct sta_info *sta,
8236b62bf32SThomas Pedersen 			  const struct ieee80211_tx_rate *rate,
8246b62bf32SThomas Pedersen 			  struct rate_info *rinfo)
8256b62bf32SThomas Pedersen {
8266b62bf32SThomas Pedersen 	rinfo->flags = 0;
8278bc83c24SJohannes Berg 	if (rate->flags & IEEE80211_TX_RC_MCS) {
8286b62bf32SThomas Pedersen 		rinfo->flags |= RATE_INFO_FLAGS_MCS;
8298bc83c24SJohannes Berg 		rinfo->mcs = rate->idx;
8308bc83c24SJohannes Berg 	} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
8318bc83c24SJohannes Berg 		rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
8328bc83c24SJohannes Berg 		rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
8338bc83c24SJohannes Berg 		rinfo->nss = ieee80211_rate_get_vht_nss(rate);
8348bc83c24SJohannes Berg 	} else {
8358bc83c24SJohannes Berg 		struct ieee80211_supported_band *sband;
8362103dec1SSimon Wunderlich 		int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
8372103dec1SSimon Wunderlich 		u16 brate;
8382103dec1SSimon Wunderlich 
83921a8e9ddSMohammed Shafi Shajakhan 		sband = ieee80211_get_sband(sta->sdata);
8408b783d10SThomas Pedersen 		WARN_ON_ONCE(sband && !sband->bitrates);
8418b783d10SThomas Pedersen 		if (sband && sband->bitrates) {
8422103dec1SSimon Wunderlich 			brate = sband->bitrates[rate->idx].bitrate;
8432103dec1SSimon Wunderlich 			rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
8448bc83c24SJohannes Berg 		}
84521a8e9ddSMohammed Shafi Shajakhan 	}
8466b62bf32SThomas Pedersen 	if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
847b51f3beeSJohannes Berg 		rinfo->bw = RATE_INFO_BW_40;
848b51f3beeSJohannes Berg 	else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
849b51f3beeSJohannes Berg 		rinfo->bw = RATE_INFO_BW_80;
850b51f3beeSJohannes Berg 	else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
851b51f3beeSJohannes Berg 		rinfo->bw = RATE_INFO_BW_160;
852b51f3beeSJohannes Berg 	else
853b51f3beeSJohannes Berg 		rinfo->bw = RATE_INFO_BW_20;
8546b62bf32SThomas Pedersen 	if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
8556b62bf32SThomas Pedersen 		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
8566b62bf32SThomas Pedersen }
8576b62bf32SThomas Pedersen 
ieee80211_dump_station(struct wiphy * wiphy,struct net_device * dev,int idx,u8 * mac,struct station_info * sinfo)858c5dd9c2bSLuis Carlos Cobo static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
859c5dd9c2bSLuis Carlos Cobo 				  int idx, u8 *mac, struct station_info *sinfo)
860c5dd9c2bSLuis Carlos Cobo {
8613b53fde8SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
86266572cfcSVictor Goldenshtein 	struct ieee80211_local *local = sdata->local;
863c5dd9c2bSLuis Carlos Cobo 	struct sta_info *sta;
864d0709a65SJohannes Berg 	int ret = -ENOENT;
865d0709a65SJohannes Berg 
86666572cfcSVictor Goldenshtein 	mutex_lock(&local->sta_mtx);
867c5dd9c2bSLuis Carlos Cobo 
8683b53fde8SJohannes Berg 	sta = sta_info_get_by_idx(sdata, idx);
869d0709a65SJohannes Berg 	if (sta) {
870d0709a65SJohannes Berg 		ret = 0;
87117741cdcSJohannes Berg 		memcpy(mac, sta->sta.addr, ETH_ALEN);
8720fdf1493SJohannes Berg 		sta_set_sinfo(sta, sinfo, true);
873d0709a65SJohannes Berg 	}
874c5dd9c2bSLuis Carlos Cobo 
87566572cfcSVictor Goldenshtein 	mutex_unlock(&local->sta_mtx);
876d0709a65SJohannes Berg 
877d0709a65SJohannes Berg 	return ret;
878c5dd9c2bSLuis Carlos Cobo }
879c5dd9c2bSLuis Carlos Cobo 
ieee80211_dump_survey(struct wiphy * wiphy,struct net_device * dev,int idx,struct survey_info * survey)8801289723eSHolger Schurig static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
8811289723eSHolger Schurig 				 int idx, struct survey_info *survey)
8821289723eSHolger Schurig {
8831289723eSHolger Schurig 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
8841289723eSHolger Schurig 
8851289723eSHolger Schurig 	return drv_get_survey(local, idx, survey);
8861289723eSHolger Schurig }
8871289723eSHolger Schurig 
ieee80211_get_station(struct wiphy * wiphy,struct net_device * dev,const u8 * mac,struct station_info * sinfo)8887bbdd2d9SJohannes Berg static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
8893b3a0162SJohannes Berg 				 const u8 *mac, struct station_info *sinfo)
8907bbdd2d9SJohannes Berg {
891abe60632SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
89266572cfcSVictor Goldenshtein 	struct ieee80211_local *local = sdata->local;
8937bbdd2d9SJohannes Berg 	struct sta_info *sta;
894d0709a65SJohannes Berg 	int ret = -ENOENT;
8957bbdd2d9SJohannes Berg 
89666572cfcSVictor Goldenshtein 	mutex_lock(&local->sta_mtx);
8977bbdd2d9SJohannes Berg 
8980e5ded5aSFelix Fietkau 	sta = sta_info_get_bss(sdata, mac);
899d0709a65SJohannes Berg 	if (sta) {
900d0709a65SJohannes Berg 		ret = 0;
9010fdf1493SJohannes Berg 		sta_set_sinfo(sta, sinfo, true);
902d0709a65SJohannes Berg 	}
903d0709a65SJohannes Berg 
90466572cfcSVictor Goldenshtein 	mutex_unlock(&local->sta_mtx);
905d0709a65SJohannes Berg 
906d0709a65SJohannes Berg 	return ret;
9077bbdd2d9SJohannes Berg }
9087bbdd2d9SJohannes Berg 
ieee80211_set_monitor_channel(struct wiphy * wiphy,struct cfg80211_chan_def * chandef)909e8c9bd5bSJohannes Berg static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
910683b6d3bSJohannes Berg 					 struct cfg80211_chan_def *chandef)
911e8c9bd5bSJohannes Berg {
91255de908aSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
91355de908aSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
91455de908aSJohannes Berg 	int ret = 0;
91555de908aSJohannes Berg 
9164bf88530SJohannes Berg 	if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
91755de908aSJohannes Berg 		return 0;
91855de908aSJohannes Berg 
91955de908aSJohannes Berg 	if (local->use_chanctx) {
9206dd23603SJohannes Berg 		sdata = wiphy_dereference(local->hw.wiphy,
9216dd23603SJohannes Berg 					  local->monitor_sdata);
92255de908aSJohannes Berg 		if (sdata) {
9234484de23SJohannes Berg 			sdata_lock(sdata);
9244484de23SJohannes Berg 			mutex_lock(&local->mtx);
925d8675a63SJohannes Berg 			ieee80211_link_release_channel(&sdata->deflink);
926d8675a63SJohannes Berg 			ret = ieee80211_link_use_channel(&sdata->deflink,
927b4f85443SJohannes Berg 							 chandef,
92855de908aSJohannes Berg 							 IEEE80211_CHANCTX_EXCLUSIVE);
9294484de23SJohannes Berg 			mutex_unlock(&local->mtx);
9304484de23SJohannes Berg 			sdata_unlock(sdata);
93155de908aSJohannes Berg 		}
9324484de23SJohannes Berg 	} else {
9334484de23SJohannes Berg 		mutex_lock(&local->mtx);
9344484de23SJohannes Berg 		if (local->open_count == local->monitors) {
935675a0b04SKarl Beldan 			local->_oper_chandef = *chandef;
93655de908aSJohannes Berg 			ieee80211_hw_config(local, 0);
93755de908aSJohannes Berg 		}
9384484de23SJohannes Berg 		mutex_unlock(&local->mtx);
9394484de23SJohannes Berg 	}
94055de908aSJohannes Berg 
9414bf88530SJohannes Berg 	if (ret == 0)
9424bf88530SJohannes Berg 		local->monitor_chandef = *chandef;
94355de908aSJohannes Berg 
94455de908aSJohannes Berg 	return ret;
945e8c9bd5bSJohannes Berg }
946e8c9bd5bSJohannes Berg 
9475f9404abSJohn Crispin static int
ieee80211_set_probe_resp(struct ieee80211_sub_if_data * sdata,const u8 * resp,size_t resp_len,const struct ieee80211_csa_settings * csa,const struct ieee80211_color_change_settings * cca,struct ieee80211_link_data * link)9485f9404abSJohn Crispin ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
949af296bdbSMichal Kazior 			 const u8 *resp, size_t resp_len,
9505f9404abSJohn Crispin 			 const struct ieee80211_csa_settings *csa,
951d9f83f22SShaul Triebitz 			 const struct ieee80211_color_change_settings *cca,
952d9f83f22SShaul Triebitz 			 struct ieee80211_link_data *link)
95302945821SArik Nemtsov {
954aa7a0080SEyal Shapira 	struct probe_resp *new, *old;
95502945821SArik Nemtsov 
95602945821SArik Nemtsov 	if (!resp || !resp_len)
957aba4e6ffSSujith Manoharan 		return 1;
95802945821SArik Nemtsov 
959d9f83f22SShaul Triebitz 	old = sdata_dereference(link->u.ap.probe_resp, sdata);
96002945821SArik Nemtsov 
961aa7a0080SEyal Shapira 	new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
96202945821SArik Nemtsov 	if (!new)
96302945821SArik Nemtsov 		return -ENOMEM;
96402945821SArik Nemtsov 
965aa7a0080SEyal Shapira 	new->len = resp_len;
966aa7a0080SEyal Shapira 	memcpy(new->data, resp, resp_len);
96702945821SArik Nemtsov 
968af296bdbSMichal Kazior 	if (csa)
9698552a434SJohn Crispin 		memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_presp,
970af296bdbSMichal Kazior 		       csa->n_counter_offsets_presp *
9718552a434SJohn Crispin 		       sizeof(new->cntdwn_counter_offsets[0]));
9725f9404abSJohn Crispin 	else if (cca)
9735f9404abSJohn Crispin 		new->cntdwn_counter_offsets[0] = cca->counter_offset_presp;
974af296bdbSMichal Kazior 
975d9f83f22SShaul Triebitz 	rcu_assign_pointer(link->u.ap.probe_resp, new);
976aa7a0080SEyal Shapira 	if (old)
977aa7a0080SEyal Shapira 		kfree_rcu(old, rcu_head);
97802945821SArik Nemtsov 
97902945821SArik Nemtsov 	return 0;
98002945821SArik Nemtsov }
98102945821SArik Nemtsov 
ieee80211_set_fils_discovery(struct ieee80211_sub_if_data * sdata,struct cfg80211_fils_discovery * params,struct ieee80211_link_data * link,struct ieee80211_bss_conf * link_conf)982295b02c4SAloka Dixit static int ieee80211_set_fils_discovery(struct ieee80211_sub_if_data *sdata,
983d9f83f22SShaul Triebitz 					struct cfg80211_fils_discovery *params,
984d9f83f22SShaul Triebitz 					struct ieee80211_link_data *link,
985d9f83f22SShaul Triebitz 					struct ieee80211_bss_conf *link_conf)
986295b02c4SAloka Dixit {
987295b02c4SAloka Dixit 	struct fils_discovery_data *new, *old = NULL;
988295b02c4SAloka Dixit 	struct ieee80211_fils_discovery *fd;
989295b02c4SAloka Dixit 
990295b02c4SAloka Dixit 	if (!params->tmpl || !params->tmpl_len)
991295b02c4SAloka Dixit 		return -EINVAL;
992295b02c4SAloka Dixit 
993d9f83f22SShaul Triebitz 	fd = &link_conf->fils_discovery;
994295b02c4SAloka Dixit 	fd->min_interval = params->min_interval;
995295b02c4SAloka Dixit 	fd->max_interval = params->max_interval;
996295b02c4SAloka Dixit 
997d9f83f22SShaul Triebitz 	old = sdata_dereference(link->u.ap.fils_discovery, sdata);
998295b02c4SAloka Dixit 	new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
999295b02c4SAloka Dixit 	if (!new)
1000295b02c4SAloka Dixit 		return -ENOMEM;
1001295b02c4SAloka Dixit 	new->len = params->tmpl_len;
1002295b02c4SAloka Dixit 	memcpy(new->data, params->tmpl, params->tmpl_len);
1003d9f83f22SShaul Triebitz 	rcu_assign_pointer(link->u.ap.fils_discovery, new);
1004295b02c4SAloka Dixit 
1005295b02c4SAloka Dixit 	if (old)
1006295b02c4SAloka Dixit 		kfree_rcu(old, rcu_head);
1007295b02c4SAloka Dixit 
1008295b02c4SAloka Dixit 	return 0;
1009295b02c4SAloka Dixit }
1010295b02c4SAloka Dixit 
1011632189a0SAloka Dixit static int
ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data * sdata,struct cfg80211_unsol_bcast_probe_resp * params,struct ieee80211_link_data * link,struct ieee80211_bss_conf * link_conf)1012632189a0SAloka Dixit ieee80211_set_unsol_bcast_probe_resp(struct ieee80211_sub_if_data *sdata,
1013d9f83f22SShaul Triebitz 				     struct cfg80211_unsol_bcast_probe_resp *params,
1014d9f83f22SShaul Triebitz 				     struct ieee80211_link_data *link,
1015d9f83f22SShaul Triebitz 				     struct ieee80211_bss_conf *link_conf)
1016632189a0SAloka Dixit {
1017632189a0SAloka Dixit 	struct unsol_bcast_probe_resp_data *new, *old = NULL;
1018632189a0SAloka Dixit 
1019632189a0SAloka Dixit 	if (!params->tmpl || !params->tmpl_len)
1020632189a0SAloka Dixit 		return -EINVAL;
1021632189a0SAloka Dixit 
1022d9f83f22SShaul Triebitz 	old = sdata_dereference(link->u.ap.unsol_bcast_probe_resp, sdata);
1023632189a0SAloka Dixit 	new = kzalloc(sizeof(*new) + params->tmpl_len, GFP_KERNEL);
1024632189a0SAloka Dixit 	if (!new)
1025632189a0SAloka Dixit 		return -ENOMEM;
1026632189a0SAloka Dixit 	new->len = params->tmpl_len;
1027632189a0SAloka Dixit 	memcpy(new->data, params->tmpl, params->tmpl_len);
1028d9f83f22SShaul Triebitz 	rcu_assign_pointer(link->u.ap.unsol_bcast_probe_resp, new);
1029632189a0SAloka Dixit 
1030632189a0SAloka Dixit 	if (old)
1031632189a0SAloka Dixit 		kfree_rcu(old, rcu_head);
1032632189a0SAloka Dixit 
1033d9f83f22SShaul Triebitz 	link_conf->unsol_bcast_probe_resp_interval = params->interval;
1034632189a0SAloka Dixit 
1035632189a0SAloka Dixit 	return 0;
1036632189a0SAloka Dixit }
1037632189a0SAloka Dixit 
ieee80211_set_ftm_responder_params(struct ieee80211_sub_if_data * sdata,const u8 * lci,size_t lci_len,const u8 * civicloc,size_t civicloc_len,struct ieee80211_bss_conf * link_conf)1038bc847970SPradeep Kumar Chitrapu static int ieee80211_set_ftm_responder_params(
1039bc847970SPradeep Kumar Chitrapu 				struct ieee80211_sub_if_data *sdata,
1040bc847970SPradeep Kumar Chitrapu 				const u8 *lci, size_t lci_len,
1041d9f83f22SShaul Triebitz 				const u8 *civicloc, size_t civicloc_len,
1042d9f83f22SShaul Triebitz 				struct ieee80211_bss_conf *link_conf)
1043bc847970SPradeep Kumar Chitrapu {
1044bc847970SPradeep Kumar Chitrapu 	struct ieee80211_ftm_responder_params *new, *old;
1045bc847970SPradeep Kumar Chitrapu 	u8 *pos;
1046bc847970SPradeep Kumar Chitrapu 	int len;
1047bc847970SPradeep Kumar Chitrapu 
1048554be833SJohannes Berg 	if (!lci_len && !civicloc_len)
1049554be833SJohannes Berg 		return 0;
1050bc847970SPradeep Kumar Chitrapu 
1051d9f83f22SShaul Triebitz 	old = link_conf->ftmr_params;
1052bc847970SPradeep Kumar Chitrapu 	len = lci_len + civicloc_len;
1053bc847970SPradeep Kumar Chitrapu 
1054bc847970SPradeep Kumar Chitrapu 	new = kzalloc(sizeof(*new) + len, GFP_KERNEL);
1055bc847970SPradeep Kumar Chitrapu 	if (!new)
1056bc847970SPradeep Kumar Chitrapu 		return -ENOMEM;
1057bc847970SPradeep Kumar Chitrapu 
1058bc847970SPradeep Kumar Chitrapu 	pos = (u8 *)(new + 1);
1059bc847970SPradeep Kumar Chitrapu 	if (lci_len) {
1060bc847970SPradeep Kumar Chitrapu 		new->lci_len = lci_len;
1061bc847970SPradeep Kumar Chitrapu 		new->lci = pos;
1062bc847970SPradeep Kumar Chitrapu 		memcpy(pos, lci, lci_len);
1063bc847970SPradeep Kumar Chitrapu 		pos += lci_len;
1064bc847970SPradeep Kumar Chitrapu 	}
1065bc847970SPradeep Kumar Chitrapu 
1066bc847970SPradeep Kumar Chitrapu 	if (civicloc_len) {
1067bc847970SPradeep Kumar Chitrapu 		new->civicloc_len = civicloc_len;
1068bc847970SPradeep Kumar Chitrapu 		new->civicloc = pos;
1069bc847970SPradeep Kumar Chitrapu 		memcpy(pos, civicloc, civicloc_len);
1070bc847970SPradeep Kumar Chitrapu 		pos += civicloc_len;
1071bc847970SPradeep Kumar Chitrapu 	}
1072bc847970SPradeep Kumar Chitrapu 
1073d9f83f22SShaul Triebitz 	link_conf->ftmr_params = new;
1074bc847970SPradeep Kumar Chitrapu 	kfree(old);
1075bc847970SPradeep Kumar Chitrapu 
1076bc847970SPradeep Kumar Chitrapu 	return 0;
1077bc847970SPradeep Kumar Chitrapu }
1078bc847970SPradeep Kumar Chitrapu 
10792b3171c6SLorenzo Bianconi static int
ieee80211_copy_mbssid_beacon(u8 * pos,struct cfg80211_mbssid_elems * dst,struct cfg80211_mbssid_elems * src)10802b3171c6SLorenzo Bianconi ieee80211_copy_mbssid_beacon(u8 *pos, struct cfg80211_mbssid_elems *dst,
10812b3171c6SLorenzo Bianconi 			     struct cfg80211_mbssid_elems *src)
10822b3171c6SLorenzo Bianconi {
10832b3171c6SLorenzo Bianconi 	int i, offset = 0;
10842b3171c6SLorenzo Bianconi 
1085c0792345SHaoyu Li 	dst->cnt = src->cnt;
10862b3171c6SLorenzo Bianconi 	for (i = 0; i < src->cnt; i++) {
10872b3171c6SLorenzo Bianconi 		memcpy(pos + offset, src->elem[i].data, src->elem[i].len);
10882b3171c6SLorenzo Bianconi 		dst->elem[i].len = src->elem[i].len;
10892b3171c6SLorenzo Bianconi 		dst->elem[i].data = pos + offset;
10902b3171c6SLorenzo Bianconi 		offset += dst->elem[i].len;
10912b3171c6SLorenzo Bianconi 	}
10922b3171c6SLorenzo Bianconi 
10932b3171c6SLorenzo Bianconi 	return offset;
10942b3171c6SLorenzo Bianconi }
10952b3171c6SLorenzo Bianconi 
109668b9bea2SAloka Dixit static int
ieee80211_copy_rnr_beacon(u8 * pos,struct cfg80211_rnr_elems * dst,struct cfg80211_rnr_elems * src)109768b9bea2SAloka Dixit ieee80211_copy_rnr_beacon(u8 *pos, struct cfg80211_rnr_elems *dst,
109868b9bea2SAloka Dixit 			  struct cfg80211_rnr_elems *src)
109968b9bea2SAloka Dixit {
110068b9bea2SAloka Dixit 	int i, offset = 0;
110168b9bea2SAloka Dixit 
110268b9bea2SAloka Dixit 	for (i = 0; i < src->cnt; i++) {
110368b9bea2SAloka Dixit 		memcpy(pos + offset, src->elem[i].data, src->elem[i].len);
110468b9bea2SAloka Dixit 		dst->elem[i].len = src->elem[i].len;
110568b9bea2SAloka Dixit 		dst->elem[i].data = pos + offset;
110668b9bea2SAloka Dixit 		offset += dst->elem[i].len;
110768b9bea2SAloka Dixit 	}
110868b9bea2SAloka Dixit 	dst->cnt = src->cnt;
110968b9bea2SAloka Dixit 
111068b9bea2SAloka Dixit 	return offset;
111168b9bea2SAloka Dixit }
111268b9bea2SAloka Dixit 
111315ddba5fSAnjaneyulu static int
ieee80211_assign_beacon(struct ieee80211_sub_if_data * sdata,struct ieee80211_link_data * link,struct cfg80211_beacon_data * params,const struct ieee80211_csa_settings * csa,const struct ieee80211_color_change_settings * cca,u64 * changed)111415ddba5fSAnjaneyulu ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
1115d8675a63SJohannes Berg 			struct ieee80211_link_data *link,
1116af296bdbSMichal Kazior 			struct cfg80211_beacon_data *params,
11175f9404abSJohn Crispin 			const struct ieee80211_csa_settings *csa,
111815ddba5fSAnjaneyulu 			const struct ieee80211_color_change_settings *cca,
111915ddba5fSAnjaneyulu 			u64 *changed)
11205dfdaf58SJohannes Berg {
11212b3171c6SLorenzo Bianconi 	struct cfg80211_mbssid_elems *mbssid = NULL;
112268b9bea2SAloka Dixit 	struct cfg80211_rnr_elems *rnr = NULL;
11235dfdaf58SJohannes Berg 	struct beacon_data *new, *old;
11245dfdaf58SJohannes Berg 	int new_head_len, new_tail_len;
11258860020eSJohannes Berg 	int size, err;
112615ddba5fSAnjaneyulu 	u64 _changed = BSS_CHANGED_BEACON;
1127d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
11285dfdaf58SJohannes Berg 
1129d9f83f22SShaul Triebitz 	old = sdata_dereference(link->u.ap.beacon, sdata);
11305dfdaf58SJohannes Berg 
11315dfdaf58SJohannes Berg 	/* Need to have a beacon head if we don't have one yet */
11325dfdaf58SJohannes Berg 	if (!params->head && !old)
11338860020eSJohannes Berg 		return -EINVAL;
11345dfdaf58SJohannes Berg 
11355dfdaf58SJohannes Berg 	/* new or old head? */
11365dfdaf58SJohannes Berg 	if (params->head)
11375dfdaf58SJohannes Berg 		new_head_len = params->head_len;
11385dfdaf58SJohannes Berg 	else
11395dfdaf58SJohannes Berg 		new_head_len = old->head_len;
11405dfdaf58SJohannes Berg 
11415dfdaf58SJohannes Berg 	/* new or old tail? */
11425dfdaf58SJohannes Berg 	if (params->tail || !old)
11435dfdaf58SJohannes Berg 		/* params->tail_len will be zero for !params->tail */
11445dfdaf58SJohannes Berg 		new_tail_len = params->tail_len;
11455dfdaf58SJohannes Berg 	else
11465dfdaf58SJohannes Berg 		new_tail_len = old->tail_len;
11475dfdaf58SJohannes Berg 
11485dfdaf58SJohannes Berg 	size = sizeof(*new) + new_head_len + new_tail_len;
11495dfdaf58SJohannes Berg 
11502b3171c6SLorenzo Bianconi 	/* new or old multiple BSSID elements? */
11512b3171c6SLorenzo Bianconi 	if (params->mbssid_ies) {
11522b3171c6SLorenzo Bianconi 		mbssid = params->mbssid_ies;
11532b3171c6SLorenzo Bianconi 		size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
115468b9bea2SAloka Dixit 		if (params->rnr_ies) {
115568b9bea2SAloka Dixit 			rnr = params->rnr_ies;
115668b9bea2SAloka Dixit 			size += struct_size(new->rnr_ies, elem, rnr->cnt);
115768b9bea2SAloka Dixit 		}
115868b9bea2SAloka Dixit 		size += ieee80211_get_mbssid_beacon_len(mbssid, rnr,
115968b9bea2SAloka Dixit 							mbssid->cnt);
11602b3171c6SLorenzo Bianconi 	} else if (old && old->mbssid_ies) {
11612b3171c6SLorenzo Bianconi 		mbssid = old->mbssid_ies;
11622b3171c6SLorenzo Bianconi 		size += struct_size(new->mbssid_ies, elem, mbssid->cnt);
116368b9bea2SAloka Dixit 		if (old && old->rnr_ies) {
116468b9bea2SAloka Dixit 			rnr = old->rnr_ies;
116568b9bea2SAloka Dixit 			size += struct_size(new->rnr_ies, elem, rnr->cnt);
116668b9bea2SAloka Dixit 		}
116768b9bea2SAloka Dixit 		size += ieee80211_get_mbssid_beacon_len(mbssid, rnr,
116868b9bea2SAloka Dixit 							mbssid->cnt);
11692b3171c6SLorenzo Bianconi 	}
11702b3171c6SLorenzo Bianconi 
11715dfdaf58SJohannes Berg 	new = kzalloc(size, GFP_KERNEL);
11725dfdaf58SJohannes Berg 	if (!new)
11735dfdaf58SJohannes Berg 		return -ENOMEM;
11745dfdaf58SJohannes Berg 
11755dfdaf58SJohannes Berg 	/* start filling the new info now */
11765dfdaf58SJohannes Berg 
11775dfdaf58SJohannes Berg 	/*
11785dfdaf58SJohannes Berg 	 * pointers go into the block we allocated,
117968b9bea2SAloka Dixit 	 * memory is | beacon_data | head | tail | mbssid_ies | rnr_ies
11805dfdaf58SJohannes Berg 	 */
11815dfdaf58SJohannes Berg 	new->head = ((u8 *) new) + sizeof(*new);
11825dfdaf58SJohannes Berg 	new->tail = new->head + new_head_len;
11835dfdaf58SJohannes Berg 	new->head_len = new_head_len;
11845dfdaf58SJohannes Berg 	new->tail_len = new_tail_len;
11852b3171c6SLorenzo Bianconi 	/* copy in optional mbssid_ies */
11862b3171c6SLorenzo Bianconi 	if (mbssid) {
11872b3171c6SLorenzo Bianconi 		u8 *pos = new->tail + new->tail_len;
11882b3171c6SLorenzo Bianconi 
11892b3171c6SLorenzo Bianconi 		new->mbssid_ies = (void *)pos;
11902b3171c6SLorenzo Bianconi 		pos += struct_size(new->mbssid_ies, elem, mbssid->cnt);
119168b9bea2SAloka Dixit 		pos += ieee80211_copy_mbssid_beacon(pos, new->mbssid_ies,
119268b9bea2SAloka Dixit 						    mbssid);
119368b9bea2SAloka Dixit 		if (rnr) {
119468b9bea2SAloka Dixit 			new->rnr_ies = (void *)pos;
119568b9bea2SAloka Dixit 			pos += struct_size(new->rnr_ies, elem, rnr->cnt);
119668b9bea2SAloka Dixit 			ieee80211_copy_rnr_beacon(pos, new->rnr_ies, rnr);
119768b9bea2SAloka Dixit 		}
1198dde78aa5SLorenzo Bianconi 		/* update bssid_indicator */
1199d9f83f22SShaul Triebitz 		link_conf->bssid_indicator =
1200dde78aa5SLorenzo Bianconi 			ilog2(__roundup_pow_of_two(mbssid->cnt + 1));
12012b3171c6SLorenzo Bianconi 	}
12025dfdaf58SJohannes Berg 
1203af296bdbSMichal Kazior 	if (csa) {
12048552a434SJohn Crispin 		new->cntdwn_current_counter = csa->count;
12058552a434SJohn Crispin 		memcpy(new->cntdwn_counter_offsets, csa->counter_offsets_beacon,
1206af296bdbSMichal Kazior 		       csa->n_counter_offsets_beacon *
12078552a434SJohn Crispin 		       sizeof(new->cntdwn_counter_offsets[0]));
12085f9404abSJohn Crispin 	} else if (cca) {
12095f9404abSJohn Crispin 		new->cntdwn_current_counter = cca->count;
12105f9404abSJohn Crispin 		new->cntdwn_counter_offsets[0] = cca->counter_offset_beacon;
1211af296bdbSMichal Kazior 	}
1212af296bdbSMichal Kazior 
12135dfdaf58SJohannes Berg 	/* copy in head */
12145dfdaf58SJohannes Berg 	if (params->head)
12155dfdaf58SJohannes Berg 		memcpy(new->head, params->head, new_head_len);
12165dfdaf58SJohannes Berg 	else
12175dfdaf58SJohannes Berg 		memcpy(new->head, old->head, new_head_len);
12185dfdaf58SJohannes Berg 
12195dfdaf58SJohannes Berg 	/* copy in optional tail */
12205dfdaf58SJohannes Berg 	if (params->tail)
12215dfdaf58SJohannes Berg 		memcpy(new->tail, params->tail, new_tail_len);
12225dfdaf58SJohannes Berg 	else
12235dfdaf58SJohannes Berg 		if (old)
12245dfdaf58SJohannes Berg 			memcpy(new->tail, old->tail, new_tail_len);
12255dfdaf58SJohannes Berg 
12268860020eSJohannes Berg 	err = ieee80211_set_probe_resp(sdata, params->probe_resp,
1227d9f83f22SShaul Triebitz 				       params->probe_resp_len, csa, cca, link);
1228bcc27fabSLorenzo Bianconi 	if (err < 0) {
1229bcc27fabSLorenzo Bianconi 		kfree(new);
12308860020eSJohannes Berg 		return err;
1231bcc27fabSLorenzo Bianconi 	}
12328860020eSJohannes Berg 	if (err == 0)
123315ddba5fSAnjaneyulu 		_changed |= BSS_CHANGED_AP_PROBE_RESP;
123419885c4fSJohannes Berg 
1235bc847970SPradeep Kumar Chitrapu 	if (params->ftm_responder != -1) {
1236d9f83f22SShaul Triebitz 		link_conf->ftm_responder = params->ftm_responder;
1237bc847970SPradeep Kumar Chitrapu 		err = ieee80211_set_ftm_responder_params(sdata,
1238bc847970SPradeep Kumar Chitrapu 							 params->lci,
1239bc847970SPradeep Kumar Chitrapu 							 params->lci_len,
1240bc847970SPradeep Kumar Chitrapu 							 params->civicloc,
1241d9f83f22SShaul Triebitz 							 params->civicloc_len,
1242d9f83f22SShaul Triebitz 							 link_conf);
1243bc847970SPradeep Kumar Chitrapu 
1244bcc27fabSLorenzo Bianconi 		if (err < 0) {
1245bcc27fabSLorenzo Bianconi 			kfree(new);
1246bc847970SPradeep Kumar Chitrapu 			return err;
1247bcc27fabSLorenzo Bianconi 		}
1248bc847970SPradeep Kumar Chitrapu 
124915ddba5fSAnjaneyulu 		_changed |= BSS_CHANGED_FTM_RESPONDER;
1250bc847970SPradeep Kumar Chitrapu 	}
1251bc847970SPradeep Kumar Chitrapu 
1252d9f83f22SShaul Triebitz 	rcu_assign_pointer(link->u.ap.beacon, new);
1253bfd8403aSJohannes Berg 	sdata->u.ap.active = true;
12545dfdaf58SJohannes Berg 
12558860020eSJohannes Berg 	if (old)
12568860020eSJohannes Berg 		kfree_rcu(old, rcu_head);
12575dfdaf58SJohannes Berg 
125815ddba5fSAnjaneyulu 	*changed |= _changed;
125915ddba5fSAnjaneyulu 	return 0;
12605dfdaf58SJohannes Berg }
12615dfdaf58SJohannes Berg 
ieee80211_start_ap(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_ap_settings * params)12628860020eSJohannes Berg static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
12638860020eSJohannes Berg 			      struct cfg80211_ap_settings *params)
12645dfdaf58SJohannes Berg {
12658860020eSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
126634a3740dSJohannes Berg 	struct ieee80211_local *local = sdata->local;
12675dfdaf58SJohannes Berg 	struct beacon_data *old;
1268665c93a9SJohannes Berg 	struct ieee80211_sub_if_data *vlan;
12692cc25e4bSAloka Dixit 	u64 changed = BSS_CHANGED_BEACON_INT |
12708860020eSJohannes Berg 		      BSS_CHANGED_BEACON_ENABLED |
12718860020eSJohannes Berg 		      BSS_CHANGED_BEACON |
12724bcc56bbSJohannes Berg 		      BSS_CHANGED_P2P_PS |
1273a0de1ca3SJohn Crispin 		      BSS_CHANGED_TXPOWER |
1274322cd27cSP Praneesh 		      BSS_CHANGED_TWT;
127508fad438SJouni Malinen 	int i, err;
127683e37e0bSRakesh Pillai 	int prev_beacon_int;
1277d9f83f22SShaul Triebitz 	unsigned int link_id = params->beacon.link_id;
1278d8675a63SJohannes Berg 	struct ieee80211_link_data *link;
1279d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf;
1280d8675a63SJohannes Berg 
1281d8675a63SJohannes Berg 	link = sdata_dereference(sdata->link[link_id], sdata);
1282d8675a63SJohannes Berg 	if (!link)
1283d8675a63SJohannes Berg 		return -ENOLINK;
1284d8675a63SJohannes Berg 
1285d8675a63SJohannes Berg 	link_conf = link->conf;
128614db74bcSJohannes Berg 
1287d9f83f22SShaul Triebitz 	old = sdata_dereference(link->u.ap.beacon, sdata);
12885dfdaf58SJohannes Berg 	if (old)
12895dfdaf58SJohannes Berg 		return -EALREADY;
12905dfdaf58SJohannes Berg 
129152b4810bSIlan Peer 	if (params->smps_mode != NL80211_SMPS_OFF)
129252b4810bSIlan Peer 		return -ENOTSUPP;
129352b4810bSIlan Peer 
1294d9f83f22SShaul Triebitz 	link->smps_mode = IEEE80211_SMPS_OFF;
1295b3dd8279SEmmanuel Grumbach 
1296d9f83f22SShaul Triebitz 	link->needed_rx_chains = sdata->local->rx_chains;
129704ecd257SJohannes Berg 
1298d9f83f22SShaul Triebitz 	prev_beacon_int = link_conf->beacon_int;
1299d9f83f22SShaul Triebitz 	link_conf->beacon_int = params->beacon_interval;
1300ac668afeSJohannes Berg 
13012ad7dd94SRyder Lee 	if (params->ht_cap)
13022ad7dd94SRyder Lee 		link_conf->ht_ldpc =
13032ad7dd94SRyder Lee 			params->ht_cap->cap_info &
13042ad7dd94SRyder Lee 				cpu_to_le16(IEEE80211_HT_CAP_LDPC_CODING);
13052ad7dd94SRyder Lee 
130642470fa0SMuna Sinada 	if (params->vht_cap) {
13072ad7dd94SRyder Lee 		link_conf->vht_ldpc =
13082ad7dd94SRyder Lee 			params->vht_cap->vht_cap_info &
13092ad7dd94SRyder Lee 				cpu_to_le32(IEEE80211_VHT_CAP_RXLDPC);
131042470fa0SMuna Sinada 		link_conf->vht_su_beamformer =
131142470fa0SMuna Sinada 			params->vht_cap->vht_cap_info &
131242470fa0SMuna Sinada 				cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
131342470fa0SMuna Sinada 		link_conf->vht_su_beamformee =
131442470fa0SMuna Sinada 			params->vht_cap->vht_cap_info &
131542470fa0SMuna Sinada 				cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
131642470fa0SMuna Sinada 		link_conf->vht_mu_beamformer =
131742470fa0SMuna Sinada 			params->vht_cap->vht_cap_info &
131842470fa0SMuna Sinada 				cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE);
131942470fa0SMuna Sinada 		link_conf->vht_mu_beamformee =
132042470fa0SMuna Sinada 			params->vht_cap->vht_cap_info &
132142470fa0SMuna Sinada 				cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
132242470fa0SMuna Sinada 	}
132342470fa0SMuna Sinada 
132403efb863SShaul Triebitz 	if (params->he_cap && params->he_oper) {
1325d9f83f22SShaul Triebitz 		link_conf->he_support = true;
1326d9f83f22SShaul Triebitz 		link_conf->htc_trig_based_pkt_ext =
132703efb863SShaul Triebitz 			le32_get_bits(params->he_oper->he_oper_params,
132803efb863SShaul Triebitz 			      IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK);
1329d9f83f22SShaul Triebitz 		link_conf->frame_time_rts_th =
133003efb863SShaul Triebitz 			le32_get_bits(params->he_oper->he_oper_params,
133103efb863SShaul Triebitz 			      IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
1332322cd27cSP Praneesh 		changed |= BSS_CHANGED_HE_OBSS_PD;
1333322cd27cSP Praneesh 
13343d48cb74SRameshkumar Sundaram 		if (params->beacon.he_bss_color.enabled)
1335322cd27cSP Praneesh 			changed |= BSS_CHANGED_HE_BSS_COLOR;
133603efb863SShaul Triebitz 	}
133734fb190eSShaul Triebitz 
1338b1b3297dSMuna Sinada 	if (params->he_cap) {
13392ad7dd94SRyder Lee 		link_conf->he_ldpc =
13402ad7dd94SRyder Lee 			params->he_cap->phy_cap_info[1] &
13412ad7dd94SRyder Lee 				IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
1342b1b3297dSMuna Sinada 		link_conf->he_su_beamformer =
1343b1b3297dSMuna Sinada 			params->he_cap->phy_cap_info[3] &
1344b1b3297dSMuna Sinada 				IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER;
1345b1b3297dSMuna Sinada 		link_conf->he_su_beamformee =
1346b1b3297dSMuna Sinada 			params->he_cap->phy_cap_info[4] &
1347b1b3297dSMuna Sinada 				IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
1348b1b3297dSMuna Sinada 		link_conf->he_mu_beamformer =
1349b1b3297dSMuna Sinada 			params->he_cap->phy_cap_info[4] &
1350b1b3297dSMuna Sinada 				IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER;
1351b1b3297dSMuna Sinada 		link_conf->he_full_ul_mumimo =
1352b1b3297dSMuna Sinada 			params->he_cap->phy_cap_info[2] &
1353b1b3297dSMuna Sinada 				IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO;
1354b1b3297dSMuna Sinada 	}
1355b1b3297dSMuna Sinada 
13562cc25e4bSAloka Dixit 	if (params->eht_cap) {
1357e3e0ca32SAloka Dixit 		if (!link_conf->he_support)
1358e3e0ca32SAloka Dixit 			return -EOPNOTSUPP;
1359e3e0ca32SAloka Dixit 
1360e3e0ca32SAloka Dixit 		link_conf->eht_support = true;
13612cc25e4bSAloka Dixit 		link_conf->eht_puncturing = params->punct_bitmap;
13622cc25e4bSAloka Dixit 		changed |= BSS_CHANGED_EHT_PUNCTURING;
1363f4d1181eSRyder Lee 
1364f4d1181eSRyder Lee 		link_conf->eht_su_beamformer =
1365f4d1181eSRyder Lee 			params->eht_cap->fixed.phy_cap_info[0] &
1366f4d1181eSRyder Lee 				IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER;
1367f4d1181eSRyder Lee 		link_conf->eht_su_beamformee =
1368f4d1181eSRyder Lee 			params->eht_cap->fixed.phy_cap_info[0] &
1369f4d1181eSRyder Lee 				IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE;
1370f4d1181eSRyder Lee 		link_conf->eht_mu_beamformer =
1371f4d1181eSRyder Lee 			params->eht_cap->fixed.phy_cap_info[7] &
1372f4d1181eSRyder Lee 				(IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ |
1373f4d1181eSRyder Lee 				 IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ |
1374f4d1181eSRyder Lee 				 IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ);
1375f4d1181eSRyder Lee 	} else {
1376f4d1181eSRyder Lee 		link_conf->eht_su_beamformer = false;
1377f4d1181eSRyder Lee 		link_conf->eht_su_beamformee = false;
1378f4d1181eSRyder Lee 		link_conf->eht_mu_beamformer = false;
13792cc25e4bSAloka Dixit 	}
13802cc25e4bSAloka Dixit 
138117196425SJohn Crispin 	if (sdata->vif.type == NL80211_IFTYPE_AP &&
138217196425SJohn Crispin 	    params->mbssid_config.tx_wdev) {
138317196425SJohn Crispin 		err = ieee80211_set_ap_mbssid_options(sdata,
1384d9f83f22SShaul Triebitz 						      params->mbssid_config,
1385d9f83f22SShaul Triebitz 						      link_conf);
138617196425SJohn Crispin 		if (err)
138717196425SJohn Crispin 			return err;
138817196425SJohn Crispin 	}
138917196425SJohn Crispin 
139034a3740dSJohannes Berg 	mutex_lock(&local->mtx);
1391d9f83f22SShaul Triebitz 	err = ieee80211_link_use_channel(link, &params->chandef,
139255de908aSJohannes Berg 					 IEEE80211_CHANCTX_SHARED);
13934e141dadSMichal Kazior 	if (!err)
1394d9f83f22SShaul Triebitz 		ieee80211_link_copy_chanctx_to_vlans(link, false);
139534a3740dSJohannes Berg 	mutex_unlock(&local->mtx);
139683e37e0bSRakesh Pillai 	if (err) {
1397d9f83f22SShaul Triebitz 		link_conf->beacon_int = prev_beacon_int;
1398aa430da4SJohannes Berg 		return err;
139983e37e0bSRakesh Pillai 	}
1400aa430da4SJohannes Berg 
1401665c93a9SJohannes Berg 	/*
1402665c93a9SJohannes Berg 	 * Apply control port protocol, this allows us to
1403665c93a9SJohannes Berg 	 * not encrypt dynamic WEP control frames.
1404665c93a9SJohannes Berg 	 */
1405665c93a9SJohannes Berg 	sdata->control_port_protocol = params->crypto.control_port_ethertype;
1406665c93a9SJohannes Berg 	sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
1407018f6fbfSDenis Kenzior 	sdata->control_port_over_nl80211 =
1408018f6fbfSDenis Kenzior 				params->crypto.control_port_over_nl80211;
14097f3f96ceSMarkus Theil 	sdata->control_port_no_preauth =
14107f3f96ceSMarkus Theil 				params->crypto.control_port_no_preauth;
14112475b1ccSMax Stepanov 
1412665c93a9SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
1413665c93a9SJohannes Berg 		vlan->control_port_protocol =
1414665c93a9SJohannes Berg 			params->crypto.control_port_ethertype;
1415665c93a9SJohannes Berg 		vlan->control_port_no_encrypt =
1416665c93a9SJohannes Berg 			params->crypto.control_port_no_encrypt;
1417018f6fbfSDenis Kenzior 		vlan->control_port_over_nl80211 =
1418018f6fbfSDenis Kenzior 			params->crypto.control_port_over_nl80211;
14197f3f96ceSMarkus Theil 		vlan->control_port_no_preauth =
14207f3f96ceSMarkus Theil 			params->crypto.control_port_no_preauth;
1421665c93a9SJohannes Berg 	}
1422665c93a9SJohannes Berg 
1423d9f83f22SShaul Triebitz 	link_conf->dtim_period = params->dtim_period;
1424d9f83f22SShaul Triebitz 	link_conf->enable_beacon = true;
1425d9f83f22SShaul Triebitz 	link_conf->allow_p2p_go_ps = sdata->vif.p2p;
1426d9f83f22SShaul Triebitz 	link_conf->twt_responder = params->twt_responder;
1427d9f83f22SShaul Triebitz 	link_conf->he_obss_pd = params->he_obss_pd;
1428d9f83f22SShaul Triebitz 	link_conf->he_bss_color = params->beacon.he_bss_color;
1429f276e20bSJohannes Berg 	sdata->vif.cfg.s1g = params->chandef.chan->band ==
14301d00ce80SThomas Pedersen 				  NL80211_BAND_S1GHZ;
14318860020eSJohannes Berg 
1432f276e20bSJohannes Berg 	sdata->vif.cfg.ssid_len = params->ssid_len;
14338860020eSJohannes Berg 	if (params->ssid_len)
1434f276e20bSJohannes Berg 		memcpy(sdata->vif.cfg.ssid, params->ssid,
14358860020eSJohannes Berg 		       params->ssid_len);
1436d9f83f22SShaul Triebitz 	link_conf->hidden_ssid =
14378860020eSJohannes Berg 		(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
14388860020eSJohannes Berg 
1439d9f83f22SShaul Triebitz 	memset(&link_conf->p2p_noa_attr, 0,
1440d9f83f22SShaul Triebitz 	       sizeof(link_conf->p2p_noa_attr));
1441d9f83f22SShaul Triebitz 	link_conf->p2p_noa_attr.oppps_ctwindow =
144267baf663SJanusz Dziedzic 		params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
144367baf663SJanusz Dziedzic 	if (params->p2p_opp_ps)
1444d9f83f22SShaul Triebitz 		link_conf->p2p_noa_attr.oppps_ctwindow |=
144567baf663SJanusz Dziedzic 					IEEE80211_P2P_OPPPS_ENABLE_BIT;
1446339afbf4SJohannes Berg 
144708fad438SJouni Malinen 	sdata->beacon_rate_set = false;
144808fad438SJouni Malinen 	if (wiphy_ext_feature_isset(local->hw.wiphy,
144908fad438SJouni Malinen 				    NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) {
145008fad438SJouni Malinen 		for (i = 0; i < NUM_NL80211_BANDS; i++) {
145108fad438SJouni Malinen 			sdata->beacon_rateidx_mask[i] =
145208fad438SJouni Malinen 				params->beacon_rate.control[i].legacy;
145308fad438SJouni Malinen 			if (sdata->beacon_rateidx_mask[i])
145408fad438SJouni Malinen 				sdata->beacon_rate_set = true;
145508fad438SJouni Malinen 		}
145608fad438SJouni Malinen 	}
145708fad438SJouni Malinen 
1458ba6ff70aSRajkumar Manoharan 	if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
1459d9f83f22SShaul Triebitz 		link_conf->beacon_tx_rate = params->beacon_rate;
1460ba6ff70aSRajkumar Manoharan 
146115ddba5fSAnjaneyulu 	err = ieee80211_assign_beacon(sdata, link, &params->beacon, NULL, NULL,
146215ddba5fSAnjaneyulu 				      &changed);
1463295b02c4SAloka Dixit 	if (err < 0)
1464295b02c4SAloka Dixit 		goto error;
14658860020eSJohannes Berg 
1466295b02c4SAloka Dixit 	if (params->fils_discovery.max_interval) {
1467295b02c4SAloka Dixit 		err = ieee80211_set_fils_discovery(sdata,
1468d9f83f22SShaul Triebitz 						   &params->fils_discovery,
1469d9f83f22SShaul Triebitz 						   link, link_conf);
1470295b02c4SAloka Dixit 		if (err < 0)
1471295b02c4SAloka Dixit 			goto error;
1472295b02c4SAloka Dixit 		changed |= BSS_CHANGED_FILS_DISCOVERY;
1473295b02c4SAloka Dixit 	}
1474295b02c4SAloka Dixit 
1475632189a0SAloka Dixit 	if (params->unsol_bcast_probe_resp.interval) {
1476632189a0SAloka Dixit 		err = ieee80211_set_unsol_bcast_probe_resp(sdata,
1477d9f83f22SShaul Triebitz 							   &params->unsol_bcast_probe_resp,
1478d9f83f22SShaul Triebitz 							   link, link_conf);
1479632189a0SAloka Dixit 		if (err < 0)
1480632189a0SAloka Dixit 			goto error;
1481632189a0SAloka Dixit 		changed |= BSS_CHANGED_UNSOL_BCAST_PROBE_RESP;
1482632189a0SAloka Dixit 	}
1483632189a0SAloka Dixit 
1484b327c84cSGregory Greenman 	err = drv_start_ap(sdata->local, sdata, link_conf);
14851041638fSJohannes Berg 	if (err) {
1486d9f83f22SShaul Triebitz 		old = sdata_dereference(link->u.ap.beacon, sdata);
14877ca133bcSSimon Wunderlich 
14881041638fSJohannes Berg 		if (old)
14891041638fSJohannes Berg 			kfree_rcu(old, rcu_head);
1490d9f83f22SShaul Triebitz 		RCU_INIT_POINTER(link->u.ap.beacon, NULL);
1491bfd8403aSJohannes Berg 		sdata->u.ap.active = false;
1492295b02c4SAloka Dixit 		goto error;
14931041638fSJohannes Berg 	}
14941041638fSJohannes Berg 
1495057d5f4bSThomas Pedersen 	ieee80211_recalc_dtim(local, sdata);
1496d9f83f22SShaul Triebitz 	ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_SSID);
1497d8675a63SJohannes Berg 	ieee80211_link_info_change_notify(sdata, link, changed);
14988860020eSJohannes Berg 
14993edaf3e6SJohannes Berg 	netif_carrier_on(dev);
15003edaf3e6SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
15013edaf3e6SJohannes Berg 		netif_carrier_on(vlan->dev);
15023edaf3e6SJohannes Berg 
1503665c93a9SJohannes Berg 	return 0;
1504295b02c4SAloka Dixit 
1505295b02c4SAloka Dixit error:
150687a27062SJohannes Berg 	mutex_lock(&local->mtx);
1507d9f83f22SShaul Triebitz 	ieee80211_link_release_channel(link);
150887a27062SJohannes Berg 	mutex_unlock(&local->mtx);
150987a27062SJohannes Berg 
1510295b02c4SAloka Dixit 	return err;
15115dfdaf58SJohannes Berg }
15125dfdaf58SJohannes Berg 
ieee80211_change_beacon(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_beacon_data * params)15138860020eSJohannes Berg static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
15148860020eSJohannes Berg 				   struct cfg80211_beacon_data *params)
15155dfdaf58SJohannes Berg {
1516d9f83f22SShaul Triebitz 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1517d8675a63SJohannes Berg 	struct ieee80211_link_data *link;
15185dfdaf58SJohannes Berg 	struct beacon_data *old;
15198860020eSJohannes Berg 	int err;
1520d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf;
152115ddba5fSAnjaneyulu 	u64 changed = 0;
15225dfdaf58SJohannes Berg 
1523dbd72850SMichal Kazior 	sdata_assert_lock(sdata);
152414db74bcSJohannes Berg 
1525d8675a63SJohannes Berg 	link = sdata_dereference(sdata->link[params->link_id], sdata);
1526d8675a63SJohannes Berg 	if (!link)
1527d8675a63SJohannes Berg 		return -ENOLINK;
1528d8675a63SJohannes Berg 
1529d8675a63SJohannes Berg 	link_conf = link->conf;
1530d8675a63SJohannes Berg 
15315f9404abSJohn Crispin 	/* don't allow changing the beacon while a countdown is in place - offset
153273da7d5bSSimon Wunderlich 	 * of channel switch counter may change
153373da7d5bSSimon Wunderlich 	 */
1534d9f83f22SShaul Triebitz 	if (link_conf->csa_active || link_conf->color_change_active)
153573da7d5bSSimon Wunderlich 		return -EBUSY;
153673da7d5bSSimon Wunderlich 
1537d8675a63SJohannes Berg 	old = sdata_dereference(link->u.ap.beacon, sdata);
15385dfdaf58SJohannes Berg 	if (!old)
15395dfdaf58SJohannes Berg 		return -ENOENT;
15405dfdaf58SJohannes Berg 
154115ddba5fSAnjaneyulu 	err = ieee80211_assign_beacon(sdata, link, params, NULL, NULL,
154215ddba5fSAnjaneyulu 				      &changed);
15438860020eSJohannes Berg 	if (err < 0)
15448860020eSJohannes Berg 		return err;
1545195b9a0fSLavanya Suresh 
1546195b9a0fSLavanya Suresh 	if (params->he_bss_color_valid &&
1547d9f83f22SShaul Triebitz 	    params->he_bss_color.enabled != link_conf->he_bss_color.enabled) {
1548d9f83f22SShaul Triebitz 		link_conf->he_bss_color.enabled = params->he_bss_color.enabled;
154915ddba5fSAnjaneyulu 		changed |= BSS_CHANGED_HE_BSS_COLOR;
1550195b9a0fSLavanya Suresh 	}
1551195b9a0fSLavanya Suresh 
155215ddba5fSAnjaneyulu 	ieee80211_link_info_change_notify(sdata, link, changed);
15538860020eSJohannes Berg 	return 0;
15545dfdaf58SJohannes Berg }
15555dfdaf58SJohannes Berg 
ieee80211_free_next_beacon(struct ieee80211_link_data * link)1556d9f83f22SShaul Triebitz static void ieee80211_free_next_beacon(struct ieee80211_link_data *link)
15570baef284SJohannes Berg {
1558d9f83f22SShaul Triebitz 	if (!link->u.ap.next_beacon)
15590baef284SJohannes Berg 		return;
15600baef284SJohannes Berg 
1561d9f83f22SShaul Triebitz 	kfree(link->u.ap.next_beacon->mbssid_ies);
156268b9bea2SAloka Dixit 	kfree(link->u.ap.next_beacon->rnr_ies);
1563d9f83f22SShaul Triebitz 	kfree(link->u.ap.next_beacon);
1564d9f83f22SShaul Triebitz 	link->u.ap.next_beacon = NULL;
15650baef284SJohannes Berg }
15660baef284SJohannes Berg 
ieee80211_stop_ap(struct wiphy * wiphy,struct net_device * dev,unsigned int link_id)15677b0a0e3cSJohannes Berg static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
15687b0a0e3cSJohannes Berg 			     unsigned int link_id)
15695dfdaf58SJohannes Berg {
15707b20b8e8SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
15717b20b8e8SJohannes Berg 	struct ieee80211_sub_if_data *vlan;
15727b20b8e8SJohannes Berg 	struct ieee80211_local *local = sdata->local;
15737b20b8e8SJohannes Berg 	struct beacon_data *old_beacon;
15747b20b8e8SJohannes Berg 	struct probe_resp *old_probe_resp;
1575295b02c4SAloka Dixit 	struct fils_discovery_data *old_fils_discovery;
1576632189a0SAloka Dixit 	struct unsol_bcast_probe_resp_data *old_unsol_bcast_probe_resp;
1577d2859df5SJanusz Dziedzic 	struct cfg80211_chan_def chandef;
1578d8675a63SJohannes Berg 	struct ieee80211_link_data *link =
1579d8675a63SJohannes Berg 		sdata_dereference(sdata->link[link_id], sdata);
1580d8675a63SJohannes Berg 	struct ieee80211_bss_conf *link_conf = link->conf;
15815dfdaf58SJohannes Berg 
1582dbd72850SMichal Kazior 	sdata_assert_lock(sdata);
1583dbd72850SMichal Kazior 
1584d9f83f22SShaul Triebitz 	old_beacon = sdata_dereference(link->u.ap.beacon, sdata);
15857b20b8e8SJohannes Berg 	if (!old_beacon)
15865dfdaf58SJohannes Berg 		return -ENOENT;
1587d9f83f22SShaul Triebitz 	old_probe_resp = sdata_dereference(link->u.ap.probe_resp,
1588bfd8403aSJohannes Berg 					   sdata);
1589d9f83f22SShaul Triebitz 	old_fils_discovery = sdata_dereference(link->u.ap.fils_discovery,
1590295b02c4SAloka Dixit 					       sdata);
1591632189a0SAloka Dixit 	old_unsol_bcast_probe_resp =
1592d9f83f22SShaul Triebitz 		sdata_dereference(link->u.ap.unsol_bcast_probe_resp,
1593632189a0SAloka Dixit 				  sdata);
15945dfdaf58SJohannes Berg 
1595a23d7f5bSMichael Lee 	/* abort any running channel switch or color change */
159659af6928SMichal Kazior 	mutex_lock(&local->mtx);
1597d9f83f22SShaul Triebitz 	link_conf->csa_active = false;
1598a23d7f5bSMichael Lee 	link_conf->color_change_active = false;
1599d9f83f22SShaul Triebitz 	if (link->csa_block_tx) {
1600a46992b4SLuciano Coelho 		ieee80211_wake_vif_queues(local, sdata,
1601a46992b4SLuciano Coelho 					  IEEE80211_QUEUE_STOP_REASON_CSA);
1602d9f83f22SShaul Triebitz 		link->csa_block_tx = false;
1603a46992b4SLuciano Coelho 	}
1604a46992b4SLuciano Coelho 
160559af6928SMichal Kazior 	mutex_unlock(&local->mtx);
160659af6928SMichal Kazior 
1607d9f83f22SShaul Triebitz 	ieee80211_free_next_beacon(link);
16081f3b8a2bSSimon Wunderlich 
16097b20b8e8SJohannes Berg 	/* turn off carrier for this interface and dependent VLANs */
16103edaf3e6SJohannes Berg 	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
16113edaf3e6SJohannes Berg 		netif_carrier_off(vlan->dev);
16123edaf3e6SJohannes Berg 	netif_carrier_off(dev);
16133edaf3e6SJohannes Berg 
16147b20b8e8SJohannes Berg 	/* remove beacon and probe response */
1615bfd8403aSJohannes Berg 	sdata->u.ap.active = false;
1616d9f83f22SShaul Triebitz 	RCU_INIT_POINTER(link->u.ap.beacon, NULL);
1617d9f83f22SShaul Triebitz 	RCU_INIT_POINTER(link->u.ap.probe_resp, NULL);
1618d9f83f22SShaul Triebitz 	RCU_INIT_POINTER(link->u.ap.fils_discovery, NULL);
1619d9f83f22SShaul Triebitz 	RCU_INIT_POINTER(link->u.ap.unsol_bcast_probe_resp, NULL);
16207b20b8e8SJohannes Berg 	kfree_rcu(old_beacon, rcu_head);
16217b20b8e8SJohannes Berg 	if (old_probe_resp)
16227b20b8e8SJohannes Berg 		kfree_rcu(old_probe_resp, rcu_head);
1623295b02c4SAloka Dixit 	if (old_fils_discovery)
1624295b02c4SAloka Dixit 		kfree_rcu(old_fils_discovery, rcu_head);
1625632189a0SAloka Dixit 	if (old_unsol_bcast_probe_resp)
1626632189a0SAloka Dixit 		kfree_rcu(old_unsol_bcast_probe_resp, rcu_head);
16278860020eSJohannes Berg 
1628d9f83f22SShaul Triebitz 	kfree(link_conf->ftmr_params);
1629d9f83f22SShaul Triebitz 	link_conf->ftmr_params = NULL;
1630bc847970SPradeep Kumar Chitrapu 
16310eb38842SAloka Dixit 	sdata->vif.mbssid_tx_vif = NULL;
16320eb38842SAloka Dixit 	link_conf->bssid_index = 0;
16330eb38842SAloka Dixit 	link_conf->nontransmitted = false;
16340eb38842SAloka Dixit 	link_conf->ema_ap = false;
16350eb38842SAloka Dixit 	link_conf->bssid_indicator = 0;
16360eb38842SAloka Dixit 
1637e716251dSJohannes Berg 	__sta_info_flush(sdata, true);
16387907c7d3SJohannes Berg 	ieee80211_free_keys(sdata, true);
163975de9113SJohannes Berg 
1640d9f83f22SShaul Triebitz 	link_conf->enable_beacon = false;
164108fad438SJouni Malinen 	sdata->beacon_rate_set = false;
1642f276e20bSJohannes Berg 	sdata->vif.cfg.ssid_len = 0;
1643d6a83228SJohannes Berg 	clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
1644d8675a63SJohannes Berg 	ieee80211_link_info_change_notify(sdata, link,
16457fc83a2bSJohannes Berg 					  BSS_CHANGED_BEACON_ENABLED);
16468860020eSJohannes Berg 
1647a6b368f6SSimon Wunderlich 	if (sdata->wdev.cac_started) {
1648d9f83f22SShaul Triebitz 		chandef = link_conf->chandef;
1649d9f83f22SShaul Triebitz 		cancel_delayed_work_sync(&link->dfs_cac_timer_work);
1650d2859df5SJanusz Dziedzic 		cfg80211_cac_event(sdata->dev, &chandef,
1651d2859df5SJanusz Dziedzic 				   NL80211_RADAR_CAC_ABORTED,
1652a6b368f6SSimon Wunderlich 				   GFP_KERNEL);
1653a6b368f6SSimon Wunderlich 	}
1654a6b368f6SSimon Wunderlich 
1655b327c84cSGregory Greenman 	drv_stop_ap(sdata->local, sdata, link_conf);
16561041638fSJohannes Berg 
16577b20b8e8SJohannes Berg 	/* free all potentially still buffered bcast frames */
16587b20b8e8SJohannes Berg 	local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
16596b07d9caSFelix Fietkau 	ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf);
16607b20b8e8SJohannes Berg 
166134a3740dSJohannes Berg 	mutex_lock(&local->mtx);
1662d9f83f22SShaul Triebitz 	ieee80211_link_copy_chanctx_to_vlans(link, true);
1663d9f83f22SShaul Triebitz 	ieee80211_link_release_channel(link);
166434a3740dSJohannes Berg 	mutex_unlock(&local->mtx);
166555de908aSJohannes Berg 
16662d0ddec5SJohannes Berg 	return 0;
16675dfdaf58SJohannes Berg }
16685dfdaf58SJohannes Berg 
sta_apply_auth_flags(struct ieee80211_local * local,struct sta_info * sta,u32 mask,u32 set)1669d582cffbSJohannes Berg static int sta_apply_auth_flags(struct ieee80211_local *local,
1670d582cffbSJohannes Berg 				struct sta_info *sta,
1671d582cffbSJohannes Berg 				u32 mask, u32 set)
1672d582cffbSJohannes Berg {
1673d582cffbSJohannes Berg 	int ret;
1674d582cffbSJohannes Berg 
1675d582cffbSJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
1676d582cffbSJohannes Berg 	    set & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
1677d582cffbSJohannes Berg 	    !test_sta_flag(sta, WLAN_STA_AUTH)) {
1678d582cffbSJohannes Berg 		ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
1679d582cffbSJohannes Berg 		if (ret)
1680d582cffbSJohannes Berg 			return ret;
1681d582cffbSJohannes Berg 	}
1682d582cffbSJohannes Berg 
1683d582cffbSJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
1684d582cffbSJohannes Berg 	    set & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
1685d582cffbSJohannes Berg 	    !test_sta_flag(sta, WLAN_STA_ASSOC)) {
1686c23e31cfSMarek Puzyniak 		/*
1687c23e31cfSMarek Puzyniak 		 * When peer becomes associated, init rate control as
1688c23e31cfSMarek Puzyniak 		 * well. Some drivers require rate control initialized
1689c23e31cfSMarek Puzyniak 		 * before drv_sta_state() is called.
1690c23e31cfSMarek Puzyniak 		 */
169144674d9cSAyala Beker 		if (!test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
1692c23e31cfSMarek Puzyniak 			rate_control_rate_init(sta);
1693c23e31cfSMarek Puzyniak 
1694d582cffbSJohannes Berg 		ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
1695d582cffbSJohannes Berg 		if (ret)
1696d582cffbSJohannes Berg 			return ret;
1697d582cffbSJohannes Berg 	}
1698d582cffbSJohannes Berg 
1699d582cffbSJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
1700d582cffbSJohannes Berg 		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED))
1701d582cffbSJohannes Berg 			ret = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
1702d582cffbSJohannes Berg 		else if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
1703d582cffbSJohannes Berg 			ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
1704d582cffbSJohannes Berg 		else
1705d582cffbSJohannes Berg 			ret = 0;
1706d582cffbSJohannes Berg 		if (ret)
1707d582cffbSJohannes Berg 			return ret;
1708d582cffbSJohannes Berg 	}
1709d582cffbSJohannes Berg 
1710d582cffbSJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) &&
1711d582cffbSJohannes Berg 	    !(set & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
1712d582cffbSJohannes Berg 	    test_sta_flag(sta, WLAN_STA_ASSOC)) {
1713d582cffbSJohannes Berg 		ret = sta_info_move_state(sta, IEEE80211_STA_AUTH);
1714d582cffbSJohannes Berg 		if (ret)
1715d582cffbSJohannes Berg 			return ret;
1716d582cffbSJohannes Berg 	}
1717d582cffbSJohannes Berg 
1718d582cffbSJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED) &&
1719d582cffbSJohannes Berg 	    !(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) &&
1720d582cffbSJohannes Berg 	    test_sta_flag(sta, WLAN_STA_AUTH)) {
1721d582cffbSJohannes Berg 		ret = sta_info_move_state(sta, IEEE80211_STA_NONE);
1722d582cffbSJohannes Berg 		if (ret)
1723d582cffbSJohannes Berg 			return ret;
1724d582cffbSJohannes Berg 	}
1725d582cffbSJohannes Berg 
1726d582cffbSJohannes Berg 	return 0;
1727d582cffbSJohannes Berg }
1728d582cffbSJohannes Berg 
sta_apply_mesh_params(struct ieee80211_local * local,struct sta_info * sta,struct station_parameters * params)172913657702SJohannes Berg static void sta_apply_mesh_params(struct ieee80211_local *local,
173013657702SJohannes Berg 				  struct sta_info *sta,
173113657702SJohannes Berg 				  struct station_parameters *params)
173213657702SJohannes Berg {
173313657702SJohannes Berg #ifdef CONFIG_MAC80211_MESH
173413657702SJohannes Berg 	struct ieee80211_sub_if_data *sdata = sta->sdata;
173515ddba5fSAnjaneyulu 	u64 changed = 0;
173613657702SJohannes Berg 
173713657702SJohannes Berg 	if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
173813657702SJohannes Berg 		switch (params->plink_state) {
173913657702SJohannes Berg 		case NL80211_PLINK_ESTAB:
174013657702SJohannes Berg 			if (sta->mesh->plink_state != NL80211_PLINK_ESTAB)
174113657702SJohannes Berg 				changed = mesh_plink_inc_estab_count(sdata);
174213657702SJohannes Berg 			sta->mesh->plink_state = params->plink_state;
17437d27a0baSMasashi Honma 			sta->mesh->aid = params->peer_aid;
174413657702SJohannes Berg 
174513657702SJohannes Berg 			ieee80211_mps_sta_status_update(sta);
174613657702SJohannes Berg 			changed |= ieee80211_mps_set_sta_local_pm(sta,
174713657702SJohannes Berg 				      sdata->u.mesh.mshcfg.power_mode);
174867fc0554SJulan Hsu 
174967fc0554SJulan Hsu 			ewma_mesh_tx_rate_avg_init(&sta->mesh->tx_rate_avg);
175067fc0554SJulan Hsu 			/* init at low value */
175167fc0554SJulan Hsu 			ewma_mesh_tx_rate_avg_add(&sta->mesh->tx_rate_avg, 10);
175267fc0554SJulan Hsu 
175313657702SJohannes Berg 			break;
175413657702SJohannes Berg 		case NL80211_PLINK_LISTEN:
175513657702SJohannes Berg 		case NL80211_PLINK_BLOCKED:
175613657702SJohannes Berg 		case NL80211_PLINK_OPN_SNT:
175713657702SJohannes Berg 		case NL80211_PLINK_OPN_RCVD:
175813657702SJohannes Berg 		case NL80211_PLINK_CNF_RCVD:
175913657702SJohannes Berg 		case NL80211_PLINK_HOLDING:
176013657702SJohannes Berg 			if (sta->mesh->plink_state == NL80211_PLINK_ESTAB)
176113657702SJohannes Berg 				changed = mesh_plink_dec_estab_count(sdata);
176213657702SJohannes Berg 			sta->mesh->plink_state = params->plink_state;
176313657702SJohannes Berg 
176413657702SJohannes Berg 			ieee80211_mps_sta_status_update(sta);
176513657702SJohannes Berg 			changed |= ieee80211_mps_set_sta_local_pm(sta,
176613657702SJohannes Berg 					NL80211_MESH_POWER_UNKNOWN);
176713657702SJohannes Berg 			break;
176813657702SJohannes Berg 		default:
176913657702SJohannes Berg 			/*  nothing  */
177013657702SJohannes Berg 			break;
177113657702SJohannes Berg 		}
177213657702SJohannes Berg 	}
177313657702SJohannes Berg 
177413657702SJohannes Berg 	switch (params->plink_action) {
177513657702SJohannes Berg 	case NL80211_PLINK_ACTION_NO_ACTION:
177613657702SJohannes Berg 		/* nothing */
177713657702SJohannes Berg 		break;
177813657702SJohannes Berg 	case NL80211_PLINK_ACTION_OPEN:
177913657702SJohannes Berg 		changed |= mesh_plink_open(sta);
178013657702SJohannes Berg 		break;
178113657702SJohannes Berg 	case NL80211_PLINK_ACTION_BLOCK:
178213657702SJohannes Berg 		changed |= mesh_plink_block(sta);
178313657702SJohannes Berg 		break;
178413657702SJohannes Berg 	}
178513657702SJohannes Berg 
178613657702SJohannes Berg 	if (params->local_pm)
178713657702SJohannes Berg 		changed |= ieee80211_mps_set_sta_local_pm(sta,
178813657702SJohannes Berg 							  params->local_pm);
178913657702SJohannes Berg 
179013657702SJohannes Berg 	ieee80211_mbss_info_change_notify(sdata, changed);
179113657702SJohannes Berg #endif
179213657702SJohannes Berg }
179313657702SJohannes Berg 
sta_link_apply_parameters(struct ieee80211_local * local,struct sta_info * sta,bool new_link,struct link_station_parameters * params)1794b95eb7f0SShaul Triebitz static int sta_link_apply_parameters(struct ieee80211_local *local,
17959aebce6cSJohannes Berg 				     struct sta_info *sta, bool new_link,
1796b95eb7f0SShaul Triebitz 				     struct link_station_parameters *params)
1797b95eb7f0SShaul Triebitz {
1798b95eb7f0SShaul Triebitz 	struct ieee80211_supported_band *sband;
1799b95eb7f0SShaul Triebitz 	struct ieee80211_sub_if_data *sdata = sta->sdata;
1800b95eb7f0SShaul Triebitz 	u32 link_id = params->link_id < 0 ? 0 : params->link_id;
1801d8675a63SJohannes Berg 	struct ieee80211_link_data *link =
1802d8675a63SJohannes Berg 		sdata_dereference(sdata->link[link_id], sdata);
1803b95eb7f0SShaul Triebitz 	struct link_sta_info *link_sta =
1804b95eb7f0SShaul Triebitz 		rcu_dereference_protected(sta->link[link_id],
1805b95eb7f0SShaul Triebitz 					  lockdep_is_held(&local->sta_mtx));
1806b95eb7f0SShaul Triebitz 
1807b303835dSJohannes Berg 	/*
1808b31a33adSEdward Adam Davis 	 * If there are no changes, then accept a link that exist,
1809b303835dSJohannes Berg 	 * unless it's a new link.
1810b303835dSJohannes Berg 	 */
1811b31a33adSEdward Adam Davis 	if (params->link_id >= 0 && !new_link &&
1812b303835dSJohannes Berg 	    !params->link_mac && !params->txpwr_set &&
1813b303835dSJohannes Berg 	    !params->supported_rates_len &&
1814b303835dSJohannes Berg 	    !params->ht_capa && !params->vht_capa &&
1815b303835dSJohannes Berg 	    !params->he_capa && !params->eht_capa &&
1816b303835dSJohannes Berg 	    !params->opmode_notif_used)
1817b303835dSJohannes Berg 		return 0;
1818b303835dSJohannes Berg 
1819d8675a63SJohannes Berg 	if (!link || !link_sta)
1820b95eb7f0SShaul Triebitz 		return -EINVAL;
1821b95eb7f0SShaul Triebitz 
1822d8675a63SJohannes Berg 	sband = ieee80211_get_link_sband(link);
1823b95eb7f0SShaul Triebitz 	if (!sband)
1824b95eb7f0SShaul Triebitz 		return -EINVAL;
1825b95eb7f0SShaul Triebitz 
1826b95eb7f0SShaul Triebitz 	if (params->link_mac) {
18279aebce6cSJohannes Berg 		if (new_link) {
1828b95eb7f0SShaul Triebitz 			memcpy(link_sta->addr, params->link_mac, ETH_ALEN);
1829b95eb7f0SShaul Triebitz 			memcpy(link_sta->pub->addr, params->link_mac, ETH_ALEN);
18309aebce6cSJohannes Berg 		} else if (!ether_addr_equal(link_sta->addr,
18319aebce6cSJohannes Berg 					     params->link_mac)) {
18329aebce6cSJohannes Berg 			return -EINVAL;
18339aebce6cSJohannes Berg 		}
1834b303835dSJohannes Berg 	} else if (new_link) {
1835b303835dSJohannes Berg 		return -EINVAL;
1836b95eb7f0SShaul Triebitz 	}
1837b95eb7f0SShaul Triebitz 
1838b95eb7f0SShaul Triebitz 	if (params->txpwr_set) {
1839c32db619SJohannes Berg 		int ret;
1840c32db619SJohannes Berg 
1841b95eb7f0SShaul Triebitz 		link_sta->pub->txpwr.type = params->txpwr.type;
1842b95eb7f0SShaul Triebitz 		if (params->txpwr.type == NL80211_TX_POWER_LIMITED)
1843b95eb7f0SShaul Triebitz 			link_sta->pub->txpwr.power = params->txpwr.power;
1844b95eb7f0SShaul Triebitz 		ret = drv_sta_set_txpwr(local, sdata, sta);
1845b95eb7f0SShaul Triebitz 		if (ret)
1846b95eb7f0SShaul Triebitz 			return ret;
1847b95eb7f0SShaul Triebitz 	}
1848b95eb7f0SShaul Triebitz 
1849b95eb7f0SShaul Triebitz 	if (params->supported_rates &&
1850b95eb7f0SShaul Triebitz 	    params->supported_rates_len) {
18513dc05935SJohannes Berg 		ieee80211_parse_bitrates(link->conf->chandef.width,
1852b95eb7f0SShaul Triebitz 					 sband, params->supported_rates,
1853b95eb7f0SShaul Triebitz 					 params->supported_rates_len,
1854b95eb7f0SShaul Triebitz 					 &link_sta->pub->supp_rates[sband->band]);
1855b95eb7f0SShaul Triebitz 	}
1856b95eb7f0SShaul Triebitz 
1857b95eb7f0SShaul Triebitz 	if (params->ht_capa)
1858b95eb7f0SShaul Triebitz 		ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
1859b95eb7f0SShaul Triebitz 						  params->ht_capa, link_sta);
1860b95eb7f0SShaul Triebitz 
1861b95eb7f0SShaul Triebitz 	/* VHT can override some HT caps such as the A-MSDU max length */
1862b95eb7f0SShaul Triebitz 	if (params->vht_capa)
1863b95eb7f0SShaul Triebitz 		ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
1864084cf2aeSJohannes Berg 						    params->vht_capa, NULL,
1865084cf2aeSJohannes Berg 						    link_sta);
1866b95eb7f0SShaul Triebitz 
1867b95eb7f0SShaul Triebitz 	if (params->he_capa)
1868b95eb7f0SShaul Triebitz 		ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
1869b95eb7f0SShaul Triebitz 						  (void *)params->he_capa,
1870b95eb7f0SShaul Triebitz 						  params->he_capa_len,
1871b95eb7f0SShaul Triebitz 						  (void *)params->he_6ghz_capa,
1872b95eb7f0SShaul Triebitz 						  link_sta);
1873b95eb7f0SShaul Triebitz 
1874e8edb346SJohannes Berg 	if (params->he_capa && params->eht_capa)
1875b95eb7f0SShaul Triebitz 		ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband,
1876b95eb7f0SShaul Triebitz 						    (u8 *)params->he_capa,
1877b95eb7f0SShaul Triebitz 						    params->he_capa_len,
1878b95eb7f0SShaul Triebitz 						    params->eht_capa,
1879b95eb7f0SShaul Triebitz 						    params->eht_capa_len,
1880b95eb7f0SShaul Triebitz 						    link_sta);
1881b95eb7f0SShaul Triebitz 
1882*567df47cSBenjamin Lin 	ieee80211_sta_init_nss(link_sta);
1883*567df47cSBenjamin Lin 
1884b95eb7f0SShaul Triebitz 	if (params->opmode_notif_used) {
1885b95eb7f0SShaul Triebitz 		/* returned value is only needed for rc update, but the
1886b95eb7f0SShaul Triebitz 		 * rc isn't initialized here yet, so ignore it
1887b95eb7f0SShaul Triebitz 		 */
1888b95eb7f0SShaul Triebitz 		__ieee80211_vht_handle_opmode(sdata, link_sta,
1889b95eb7f0SShaul Triebitz 					      params->opmode_notif,
1890b95eb7f0SShaul Triebitz 					      sband->band);
1891b95eb7f0SShaul Triebitz 	}
1892b95eb7f0SShaul Triebitz 
1893c32db619SJohannes Berg 	return 0;
1894b95eb7f0SShaul Triebitz }
1895b95eb7f0SShaul Triebitz 
sta_apply_parameters(struct ieee80211_local * local,struct sta_info * sta,struct station_parameters * params)1896d9a7ddb0SJohannes Berg static int sta_apply_parameters(struct ieee80211_local *local,
18974fd6931eSJohannes Berg 				struct sta_info *sta,
18984fd6931eSJohannes Berg 				struct station_parameters *params)
18994fd6931eSJohannes Berg {
1900d0709a65SJohannes Berg 	struct ieee80211_sub_if_data *sdata = sta->sdata;
1901eccb8e8fSJohannes Berg 	u32 mask, set;
190245b12570SJohannes Berg 	int ret = 0;
1903ae5eb026SJohannes Berg 
1904eccb8e8fSJohannes Berg 	mask = params->sta_flags_mask;
1905eccb8e8fSJohannes Berg 	set = params->sta_flags_set;
1906eccb8e8fSJohannes Berg 
1907d582cffbSJohannes Berg 	if (ieee80211_vif_is_mesh(&sdata->vif)) {
1908d9a7ddb0SJohannes Berg 		/*
1909d582cffbSJohannes Berg 		 * In mesh mode, ASSOCIATED isn't part of the nl80211
1910d582cffbSJohannes Berg 		 * API but must follow AUTHENTICATED for driver state.
1911d9a7ddb0SJohannes Berg 		 */
1912d582cffbSJohannes Berg 		if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED))
1913d582cffbSJohannes Berg 			mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
1914d582cffbSJohannes Berg 		if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED))
1915d582cffbSJohannes Berg 			set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
191677ee7c89SJohannes Berg 	} else if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
191777ee7c89SJohannes Berg 		/*
191877ee7c89SJohannes Berg 		 * TDLS -- everything follows authorized, but
191977ee7c89SJohannes Berg 		 * only becoming authorized is possible, not
192077ee7c89SJohannes Berg 		 * going back
192177ee7c89SJohannes Berg 		 */
192277ee7c89SJohannes Berg 		if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
192377ee7c89SJohannes Berg 			set |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
192477ee7c89SJohannes Berg 			       BIT(NL80211_STA_FLAG_ASSOCIATED);
192577ee7c89SJohannes Berg 			mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED) |
192677ee7c89SJohannes Berg 				BIT(NL80211_STA_FLAG_ASSOCIATED);
192777ee7c89SJohannes Berg 		}
1928d9a7ddb0SJohannes Berg 	}
1929d9a7ddb0SJohannes Berg 
19302c44be81SJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_WME) &&
19312c44be81SJohannes Berg 	    local->hw.queues >= IEEE80211_NUM_ACS)
19322c44be81SJohannes Berg 		sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME);
19332c44be81SJohannes Berg 
193444674d9cSAyala Beker 	/* auth flags will be set later for TDLS,
1935b18dacabSBhaskar Chowdhury 	 * and for unassociated stations that move to associated */
193644674d9cSAyala Beker 	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
193744674d9cSAyala Beker 	    !((mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) &&
193844674d9cSAyala Beker 	      (set & BIT(NL80211_STA_FLAG_ASSOCIATED)))) {
1939d582cffbSJohannes Berg 		ret = sta_apply_auth_flags(local, sta, mask, set);
1940d9a7ddb0SJohannes Berg 		if (ret)
1941d9a7ddb0SJohannes Berg 			return ret;
194268885a54SArik Nemtsov 	}
1943d9a7ddb0SJohannes Berg 
1944eccb8e8fSJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) {
1945eccb8e8fSJohannes Berg 		if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE))
1946c2c98fdeSJohannes Berg 			set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
1947c2c98fdeSJohannes Berg 		else
1948c2c98fdeSJohannes Berg 			clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE);
1949eccb8e8fSJohannes Berg 	}
1950eccb8e8fSJohannes Berg 
1951eccb8e8fSJohannes Berg 	if (mask & BIT(NL80211_STA_FLAG_MFP)) {
195293f0490eSTamizh chelvam 		sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP));
1953eccb8e8fSJohannes Berg 		if (set & BIT(NL80211_STA_FLAG_MFP))
1954c2c98fdeSJohannes Berg 			set_sta_flag(sta, WLAN_STA_MFP);
1955c2c98fdeSJohannes Berg 		else
1956c2c98fdeSJohannes Berg 			clear_sta_flag(sta, WLAN_STA_MFP);
1957eccb8e8fSJohannes Berg 	}
1958b39c48faSJavier Cardona 
195907ba55d7SArik Nemtsov 	if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
196007ba55d7SArik Nemtsov 		if (set & BIT(NL80211_STA_FLAG_TDLS_PEER))
1961c2c98fdeSJohannes Berg 			set_sta_flag(sta, WLAN_STA_TDLS_PEER);
1962c2c98fdeSJohannes Berg 		else
1963c2c98fdeSJohannes Berg 			clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
196407ba55d7SArik Nemtsov 	}
1965eccb8e8fSJohannes Berg 
19669041c1faSArik Nemtsov 	/* mark TDLS channel switch support, if the AP allows it */
19679041c1faSArik Nemtsov 	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
1968ab3a830dSJohannes Berg 	    !sdata->deflink.u.mgd.tdls_chan_switch_prohibited &&
19699041c1faSArik Nemtsov 	    params->ext_capab_len >= 4 &&
19709041c1faSArik Nemtsov 	    params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
19719041c1faSArik Nemtsov 		set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
19729041c1faSArik Nemtsov 
1973b98fb44fSArik Nemtsov 	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
197482c0cc90SArik Nemtsov 	    !sdata->u.mgd.tdls_wider_bw_prohibited &&
1975b98fb44fSArik Nemtsov 	    ieee80211_hw_check(&local->hw, TDLS_WIDER_BW) &&
1976b98fb44fSArik Nemtsov 	    params->ext_capab_len >= 8 &&
1977b98fb44fSArik Nemtsov 	    params->ext_capab[7] & WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED)
1978b98fb44fSArik Nemtsov 		set_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW);
1979b98fb44fSArik Nemtsov 
19803b9ce80cSJohannes Berg 	if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
19819533b4acSEliad Peller 		sta->sta.uapsd_queues = params->uapsd_queues;
19829533b4acSEliad Peller 		sta->sta.max_sp = params->max_sp;
19833b9ce80cSJohannes Berg 	}
19849533b4acSEliad Peller 
1985175ad2ecSJohannes Berg 	ieee80211_sta_set_max_amsdu_subframes(sta, params->ext_capab,
1986175ad2ecSJohannes Berg 					      params->ext_capab_len);
1987506bcfa8SEmmanuel Grumbach 
198873651ee6SJohannes Berg 	/*
198951b50fbeSJohannes Berg 	 * cfg80211 validates this (1-2007) and allows setting the AID
199051b50fbeSJohannes Berg 	 * only when creating a new station entry
199151b50fbeSJohannes Berg 	 */
199251b50fbeSJohannes Berg 	if (params->aid)
199351b50fbeSJohannes Berg 		sta->sta.aid = params->aid;
199451b50fbeSJohannes Berg 
199551b50fbeSJohannes Berg 	/*
1996ba23d206SJohannes Berg 	 * Some of the following updates would be racy if called on an
1997ba23d206SJohannes Berg 	 * existing station, via ieee80211_change_station(). However,
1998ba23d206SJohannes Berg 	 * all such changes are rejected by cfg80211 except for updates
1999ba23d206SJohannes Berg 	 * changing the supported rates on an existing but not yet used
2000ba23d206SJohannes Berg 	 * TDLS peer.
200173651ee6SJohannes Berg 	 */
200273651ee6SJohannes Berg 
20034fd6931eSJohannes Berg 	if (params->listen_interval >= 0)
20044fd6931eSJohannes Berg 		sta->listen_interval = params->listen_interval;
20054fd6931eSJohannes Berg 
20069aebce6cSJohannes Berg 	ret = sta_link_apply_parameters(local, sta, false,
20079aebce6cSJohannes Berg 					&params->link_sta_params);
2008ba905bf4SAshok Raj Nagarajan 	if (ret)
2009ba905bf4SAshok Raj Nagarajan 		return ret;
2010b1bce14aSMarek Kwaczynski 
201152cfa1d6SAyala Beker 	if (params->support_p2p_ps >= 0)
201252cfa1d6SAyala Beker 		sta->sta.support_p2p_ps = params->support_p2p_ps;
201352cfa1d6SAyala Beker 
201413657702SJohannes Berg 	if (ieee80211_vif_is_mesh(&sdata->vif))
201513657702SJohannes Berg 		sta_apply_mesh_params(local, sta, params);
2016d9a7ddb0SJohannes Berg 
2017b4809e94SToke Høiland-Jørgensen 	if (params->airtime_weight)
2018942741daSFelix Fietkau 		sta->airtime_weight = params->airtime_weight;
2019b4809e94SToke Høiland-Jørgensen 
202068885a54SArik Nemtsov 	/* set the STA state after all sta info from usermode has been set */
202144674d9cSAyala Beker 	if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
202244674d9cSAyala Beker 	    set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
202368885a54SArik Nemtsov 		ret = sta_apply_auth_flags(local, sta, mask, set);
202468885a54SArik Nemtsov 		if (ret)
202568885a54SArik Nemtsov 			return ret;
202668885a54SArik Nemtsov 	}
202768885a54SArik Nemtsov 
20283e0278b7SAndrei Otcheretianski 	/* Mark the STA as MLO if MLD MAC address is available */
20293e0278b7SAndrei Otcheretianski 	if (params->link_sta_params.mld_mac)
20303e0278b7SAndrei Otcheretianski 		sta->sta.mlo = true;
20313e0278b7SAndrei Otcheretianski 
2032d9a7ddb0SJohannes Berg 	return 0;
20334fd6931eSJohannes Berg }
20344fd6931eSJohannes Berg 
ieee80211_add_station(struct wiphy * wiphy,struct net_device * dev,const u8 * mac,struct station_parameters * params)20354fd6931eSJohannes Berg static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
20363b3a0162SJohannes Berg 				 const u8 *mac,
20373b3a0162SJohannes Berg 				 struct station_parameters *params)
20384fd6931eSJohannes Berg {
203914db74bcSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
20404fd6931eSJohannes Berg 	struct sta_info *sta;
20414fd6931eSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
204273651ee6SJohannes Berg 	int err;
20434fd6931eSJohannes Berg 
20444fd6931eSJohannes Berg 	if (params->vlan) {
20454fd6931eSJohannes Berg 		sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
20464fd6931eSJohannes Berg 
204705c914feSJohannes Berg 		if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
204805c914feSJohannes Berg 		    sdata->vif.type != NL80211_IFTYPE_AP)
20494fd6931eSJohannes Berg 			return -EINVAL;
20504fd6931eSJohannes Berg 	} else
20514fd6931eSJohannes Berg 		sdata = IEEE80211_DEV_TO_SUB_IF(dev);
20524fd6931eSJohannes Berg 
2053b203ca39SJoe Perches 	if (ether_addr_equal(mac, sdata->vif.addr))
205403e4497eSJohannes Berg 		return -EINVAL;
205503e4497eSJohannes Berg 
205652dba8d7SKarthikeyan Periyasamy 	if (!is_valid_ether_addr(mac))
205703e4497eSJohannes Berg 		return -EINVAL;
205803e4497eSJohannes Berg 
20595fd2f91aSJohannes Berg 	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER) &&
20605fd2f91aSJohannes Berg 	    sdata->vif.type == NL80211_IFTYPE_STATION &&
20615fd2f91aSJohannes Berg 	    !sdata->u.mgd.associated)
20625fd2f91aSJohannes Berg 		return -EINVAL;
20635fd2f91aSJohannes Berg 
2064206c8c06SJohannes Berg 	/*
2065206c8c06SJohannes Berg 	 * If we have a link ID, it can be a non-MLO station on an AP MLD,
2066206c8c06SJohannes Berg 	 * but we need to have a link_mac in that case as well, so use the
2067206c8c06SJohannes Berg 	 * STA's MAC address in that case.
2068206c8c06SJohannes Berg 	 */
2069f36fe0a2SJohannes Berg 	if (params->link_sta_params.link_id >= 0)
2070f36fe0a2SJohannes Berg 		sta = sta_info_alloc_with_link(sdata, mac,
2071f36fe0a2SJohannes Berg 					       params->link_sta_params.link_id,
2072206c8c06SJohannes Berg 					       params->link_sta_params.link_mac ?: mac,
2073b95eb7f0SShaul Triebitz 					       GFP_KERNEL);
2074f36fe0a2SJohannes Berg 	else
2075f36fe0a2SJohannes Berg 		sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
2076f36fe0a2SJohannes Berg 
207773651ee6SJohannes Berg 	if (!sta)
207873651ee6SJohannes Berg 		return -ENOMEM;
20794fd6931eSJohannes Berg 
208044674d9cSAyala Beker 	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
208144674d9cSAyala Beker 		sta->sta.tdls = true;
20824fd6931eSJohannes Berg 
2083b95eb7f0SShaul Triebitz 	/* Though the mutex is not needed here (since the station is not
2084b95eb7f0SShaul Triebitz 	 * visible yet), sta_apply_parameters (and inner functions) require
2085b95eb7f0SShaul Triebitz 	 * the mutex due to other paths.
2086b95eb7f0SShaul Triebitz 	 */
2087b95eb7f0SShaul Triebitz 	mutex_lock(&local->sta_mtx);
2088d9a7ddb0SJohannes Berg 	err = sta_apply_parameters(local, sta, params);
2089b95eb7f0SShaul Triebitz 	mutex_unlock(&local->sta_mtx);
2090d9a7ddb0SJohannes Berg 	if (err) {
2091d9a7ddb0SJohannes Berg 		sta_info_free(local, sta);
2092d9a7ddb0SJohannes Berg 		return err;
2093d9a7ddb0SJohannes Berg 	}
20944fd6931eSJohannes Berg 
2095d64cf63eSArik Nemtsov 	/*
209644674d9cSAyala Beker 	 * for TDLS and for unassociated station, rate control should be
209744674d9cSAyala Beker 	 * initialized only when rates are known and station is marked
209844674d9cSAyala Beker 	 * authorized/associated
2099d64cf63eSArik Nemtsov 	 */
210044674d9cSAyala Beker 	if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
210144674d9cSAyala Beker 	    test_sta_flag(sta, WLAN_STA_ASSOC))
21024b7679a5SJohannes Berg 		rate_control_rate_init(sta);
21034fd6931eSJohannes Berg 
21044ebdce1dSJohannes Berg 	return sta_info_insert(sta);
21054fd6931eSJohannes Berg }
21064fd6931eSJohannes Berg 
ieee80211_del_station(struct wiphy * wiphy,struct net_device * dev,struct station_del_parameters * params)21074fd6931eSJohannes Berg static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
210889c771e5SJouni Malinen 				 struct station_del_parameters *params)
21094fd6931eSJohannes Berg {
211014db74bcSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
21114fd6931eSJohannes Berg 
211214db74bcSJohannes Berg 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
211314db74bcSJohannes Berg 
211489c771e5SJouni Malinen 	if (params->mac)
211589c771e5SJouni Malinen 		return sta_info_destroy_addr_bss(sdata, params->mac);
211698dd6a57SJohannes Berg 
2117b998e8bbSJohannes Berg 	sta_info_flush(sdata);
21184fd6931eSJohannes Berg 	return 0;
21194fd6931eSJohannes Berg }
21204fd6931eSJohannes Berg 
ieee80211_change_station(struct wiphy * wiphy,struct net_device * dev,const u8 * mac,struct station_parameters * params)21214fd6931eSJohannes Berg static int ieee80211_change_station(struct wiphy *wiphy,
21223b3a0162SJohannes Berg 				    struct net_device *dev, const u8 *mac,
21234fd6931eSJohannes Berg 				    struct station_parameters *params)
21244fd6931eSJohannes Berg {
2125abe60632SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
212614db74bcSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
21274fd6931eSJohannes Berg 	struct sta_info *sta;
21284fd6931eSJohannes Berg 	struct ieee80211_sub_if_data *vlansdata;
212977ee7c89SJohannes Berg 	enum cfg80211_station_type statype;
213035b88623SEliad Peller 	int err;
21314fd6931eSJohannes Berg 
213287be1e1eSJohannes Berg 	mutex_lock(&local->sta_mtx);
213398dd6a57SJohannes Berg 
21340e5ded5aSFelix Fietkau 	sta = sta_info_get_bss(sdata, mac);
213598dd6a57SJohannes Berg 	if (!sta) {
213677ee7c89SJohannes Berg 		err = -ENOENT;
213777ee7c89SJohannes Berg 		goto out_err;
213898dd6a57SJohannes Berg 	}
21394fd6931eSJohannes Berg 
214077ee7c89SJohannes Berg 	switch (sdata->vif.type) {
214177ee7c89SJohannes Berg 	case NL80211_IFTYPE_MESH_POINT:
2142a6dad6a2SThomas Pedersen 		if (sdata->u.mesh.user_mpm)
2143eef941e6SThomas Pedersen 			statype = CFG80211_STA_MESH_PEER_USER;
214477ee7c89SJohannes Berg 		else
2145eef941e6SThomas Pedersen 			statype = CFG80211_STA_MESH_PEER_KERNEL;
214677ee7c89SJohannes Berg 		break;
214777ee7c89SJohannes Berg 	case NL80211_IFTYPE_ADHOC:
214877ee7c89SJohannes Berg 		statype = CFG80211_STA_IBSS;
214977ee7c89SJohannes Berg 		break;
215077ee7c89SJohannes Berg 	case NL80211_IFTYPE_STATION:
215177ee7c89SJohannes Berg 		if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
215277ee7c89SJohannes Berg 			statype = CFG80211_STA_AP_STA;
215377ee7c89SJohannes Berg 			break;
2154bdd90d5eSJohannes Berg 		}
215577ee7c89SJohannes Berg 		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
215677ee7c89SJohannes Berg 			statype = CFG80211_STA_TDLS_PEER_ACTIVE;
215777ee7c89SJohannes Berg 		else
215877ee7c89SJohannes Berg 			statype = CFG80211_STA_TDLS_PEER_SETUP;
215977ee7c89SJohannes Berg 		break;
216077ee7c89SJohannes Berg 	case NL80211_IFTYPE_AP:
216177ee7c89SJohannes Berg 	case NL80211_IFTYPE_AP_VLAN:
216244674d9cSAyala Beker 		if (test_sta_flag(sta, WLAN_STA_ASSOC))
216377ee7c89SJohannes Berg 			statype = CFG80211_STA_AP_CLIENT;
216444674d9cSAyala Beker 		else
216544674d9cSAyala Beker 			statype = CFG80211_STA_AP_CLIENT_UNASSOC;
216677ee7c89SJohannes Berg 		break;
216777ee7c89SJohannes Berg 	default:
216877ee7c89SJohannes Berg 		err = -EOPNOTSUPP;
216977ee7c89SJohannes Berg 		goto out_err;
217077ee7c89SJohannes Berg 	}
217177ee7c89SJohannes Berg 
217277ee7c89SJohannes Berg 	err = cfg80211_check_station_change(wiphy, params, statype);
217377ee7c89SJohannes Berg 	if (err)
217477ee7c89SJohannes Berg 		goto out_err;
2175bdd90d5eSJohannes Berg 
2176d0709a65SJohannes Berg 	if (params->vlan && params->vlan != sta->sdata->dev) {
21774fd6931eSJohannes Berg 		vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
21784fd6931eSJohannes Berg 
21799bc383deSJohannes Berg 		if (params->vlan->ieee80211_ptr->use_4addr) {
21803305443cSJohannes Berg 			if (vlansdata->u.vlan.sta) {
218177ee7c89SJohannes Berg 				err = -EBUSY;
218277ee7c89SJohannes Berg 				goto out_err;
21833305443cSJohannes Berg 			}
2184f14543eeSFelix Fietkau 
2185cf778b00SEric Dumazet 			rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
218649ddf8e6SJohannes Berg 			__ieee80211_check_fast_rx_iface(vlansdata);
21871ff4e8f2SFelix Fietkau 			drv_sta_set_4addr(local, sta->sdata, &sta->sta, true);
21887e3ed02cSFelix Fietkau 		}
21897e3ed02cSFelix Fietkau 
21907e3ed02cSFelix Fietkau 		if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
21916b948b54SFelix Fietkau 		    sta->sdata->u.vlan.sta)
21920c2bef46SMonam Agarwal 			RCU_INIT_POINTER(sta->sdata->u.vlan.sta, NULL);
219372f15d53SMichael Braun 
219472f15d53SMichael Braun 		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED))
219572f15d53SMichael Braun 			ieee80211_vif_dec_num_mcast(sta->sdata);
2196f14543eeSFelix Fietkau 
219714db74bcSJohannes Berg 		sta->sdata = vlansdata;
21986b948b54SFelix Fietkau 		ieee80211_check_fast_rx(sta);
2199464daaf0SMichal Kazior 		ieee80211_check_fast_xmit(sta);
22007e3ed02cSFelix Fietkau 
22013e493173SJouni Malinen 		if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
220272f15d53SMichael Braun 			ieee80211_vif_inc_num_mcast(sta->sdata);
22033e493173SJouni Malinen 			cfg80211_send_layer2_update(sta->sdata->dev,
22043e493173SJouni Malinen 						    sta->sta.addr);
22053e493173SJouni Malinen 		}
22064fd6931eSJohannes Berg 	}
22074fd6931eSJohannes Berg 
2208d8675a63SJohannes Berg 	/* we use sta_info_get_bss() so this might be different */
2209d8675a63SJohannes Berg 	if (sdata != sta->sdata) {
2210d8675a63SJohannes Berg 		mutex_lock_nested(&sta->sdata->wdev.mtx, 1);
221135b88623SEliad Peller 		err = sta_apply_parameters(local, sta, params);
2212d8675a63SJohannes Berg 		mutex_unlock(&sta->sdata->wdev.mtx);
2213d8675a63SJohannes Berg 	} else {
2214d8675a63SJohannes Berg 		err = sta_apply_parameters(local, sta, params);
2215d8675a63SJohannes Berg 	}
221677ee7c89SJohannes Berg 	if (err)
221777ee7c89SJohannes Berg 		goto out_err;
22184fd6931eSJohannes Berg 
221987be1e1eSJohannes Berg 	mutex_unlock(&local->sta_mtx);
222098dd6a57SJohannes Berg 
2221808118cbSJason Young 	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
2222ab095877SEliad Peller 	    params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) {
22234a733ef1SJohannes Berg 		ieee80211_recalc_ps(local);
2224ab095877SEliad Peller 		ieee80211_recalc_ps_vif(sdata);
2225ab095877SEliad Peller 	}
222677ee7c89SJohannes Berg 
22274fd6931eSJohannes Berg 	return 0;
222877ee7c89SJohannes Berg out_err:
222977ee7c89SJohannes Berg 	mutex_unlock(&local->sta_mtx);
223077ee7c89SJohannes Berg 	return err;
22314fd6931eSJohannes Berg }
22324fd6931eSJohannes Berg 
2233c5dd9c2bSLuis Carlos Cobo #ifdef CONFIG_MAC80211_MESH
ieee80211_add_mpath(struct wiphy * wiphy,struct net_device * dev,const u8 * dst,const u8 * next_hop)2234c5dd9c2bSLuis Carlos Cobo static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
22353b3a0162SJohannes Berg 			       const u8 *dst, const u8 *next_hop)
2236c5dd9c2bSLuis Carlos Cobo {
223714db74bcSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
2238c5dd9c2bSLuis Carlos Cobo 	struct mesh_path *mpath;
2239c5dd9c2bSLuis Carlos Cobo 	struct sta_info *sta;
2240c5dd9c2bSLuis Carlos Cobo 
224114db74bcSJohannes Berg 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
224214db74bcSJohannes Berg 
2243d0709a65SJohannes Berg 	rcu_read_lock();
2244abe60632SJohannes Berg 	sta = sta_info_get(sdata, next_hop);
2245d0709a65SJohannes Berg 	if (!sta) {
2246d0709a65SJohannes Berg 		rcu_read_unlock();
2247c5dd9c2bSLuis Carlos Cobo 		return -ENOENT;
2248d0709a65SJohannes Berg 	}
2249c5dd9c2bSLuis Carlos Cobo 
2250ae76eef0SBob Copeland 	mpath = mesh_path_add(sdata, dst);
2251ae76eef0SBob Copeland 	if (IS_ERR(mpath)) {
2252d0709a65SJohannes Berg 		rcu_read_unlock();
2253ae76eef0SBob Copeland 		return PTR_ERR(mpath);
2254d0709a65SJohannes Berg 	}
2255c5dd9c2bSLuis Carlos Cobo 
2256c5dd9c2bSLuis Carlos Cobo 	mesh_path_fix_nexthop(mpath, sta);
2257d0709a65SJohannes Berg 
2258c5dd9c2bSLuis Carlos Cobo 	rcu_read_unlock();
2259c5dd9c2bSLuis Carlos Cobo 	return 0;
2260c5dd9c2bSLuis Carlos Cobo }
2261c5dd9c2bSLuis Carlos Cobo 
ieee80211_del_mpath(struct wiphy * wiphy,struct net_device * dev,const u8 * dst)2262c5dd9c2bSLuis Carlos Cobo static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
22633b3a0162SJohannes Berg 			       const u8 *dst)
2264c5dd9c2bSLuis Carlos Cobo {
2265f698d856SJasper Bryant-Greene 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2266c5dd9c2bSLuis Carlos Cobo 
2267f698d856SJasper Bryant-Greene 	if (dst)
2268bf7cd94dSJohannes Berg 		return mesh_path_del(sdata, dst);
2269f698d856SJasper Bryant-Greene 
2270ece1a2e7SJavier Cardona 	mesh_path_flush_by_iface(sdata);
2271c5dd9c2bSLuis Carlos Cobo 	return 0;
2272c5dd9c2bSLuis Carlos Cobo }
2273c5dd9c2bSLuis Carlos Cobo 
ieee80211_change_mpath(struct wiphy * wiphy,struct net_device * dev,const u8 * dst,const u8 * next_hop)22743b3a0162SJohannes Berg static int ieee80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
22753b3a0162SJohannes Berg 				  const u8 *dst, const u8 *next_hop)
2276c5dd9c2bSLuis Carlos Cobo {
227714db74bcSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
2278c5dd9c2bSLuis Carlos Cobo 	struct mesh_path *mpath;
2279c5dd9c2bSLuis Carlos Cobo 	struct sta_info *sta;
2280c5dd9c2bSLuis Carlos Cobo 
228114db74bcSJohannes Berg 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
228214db74bcSJohannes Berg 
2283c5dd9c2bSLuis Carlos Cobo 	rcu_read_lock();
2284d0709a65SJohannes Berg 
2285abe60632SJohannes Berg 	sta = sta_info_get(sdata, next_hop);
2286d0709a65SJohannes Berg 	if (!sta) {
2287d0709a65SJohannes Berg 		rcu_read_unlock();
2288d0709a65SJohannes Berg 		return -ENOENT;
2289d0709a65SJohannes Berg 	}
2290d0709a65SJohannes Berg 
2291bf7cd94dSJohannes Berg 	mpath = mesh_path_lookup(sdata, dst);
2292c5dd9c2bSLuis Carlos Cobo 	if (!mpath) {
2293c5dd9c2bSLuis Carlos Cobo 		rcu_read_unlock();
2294c5dd9c2bSLuis Carlos Cobo 		return -ENOENT;
2295c5dd9c2bSLuis Carlos Cobo 	}
2296c5dd9c2bSLuis Carlos Cobo 
2297c5dd9c2bSLuis Carlos Cobo 	mesh_path_fix_nexthop(mpath, sta);
2298d0709a65SJohannes Berg 
2299c5dd9c2bSLuis Carlos Cobo 	rcu_read_unlock();
2300c5dd9c2bSLuis Carlos Cobo 	return 0;
2301c5dd9c2bSLuis Carlos Cobo }
2302c5dd9c2bSLuis Carlos Cobo 
mpath_set_pinfo(struct mesh_path * mpath,u8 * next_hop,struct mpath_info * pinfo)2303c5dd9c2bSLuis Carlos Cobo static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
2304c5dd9c2bSLuis Carlos Cobo 			    struct mpath_info *pinfo)
2305c5dd9c2bSLuis Carlos Cobo {
2306a3836e02SJohannes Berg 	struct sta_info *next_hop_sta = rcu_dereference(mpath->next_hop);
2307a3836e02SJohannes Berg 
2308a3836e02SJohannes Berg 	if (next_hop_sta)
2309a3836e02SJohannes Berg 		memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN);
2310c5dd9c2bSLuis Carlos Cobo 	else
2311c84a67a2SJoe Perches 		eth_zero_addr(next_hop);
2312c5dd9c2bSLuis Carlos Cobo 
23137ce8c7a3SLEO Airwarosu Yoichi Shinoda 	memset(pinfo, 0, sizeof(*pinfo));
23147ce8c7a3SLEO Airwarosu Yoichi Shinoda 
23152bdaf386SBob Copeland 	pinfo->generation = mpath->sdata->u.mesh.mesh_paths_generation;
2316f5ea9120SJohannes Berg 
2317c5dd9c2bSLuis Carlos Cobo 	pinfo->filled = MPATH_INFO_FRAME_QLEN |
2318d19b3bf6SRui Paulo 			MPATH_INFO_SN |
2319c5dd9c2bSLuis Carlos Cobo 			MPATH_INFO_METRIC |
2320c5dd9c2bSLuis Carlos Cobo 			MPATH_INFO_EXPTIME |
2321c5dd9c2bSLuis Carlos Cobo 			MPATH_INFO_DISCOVERY_TIMEOUT |
2322c5dd9c2bSLuis Carlos Cobo 			MPATH_INFO_DISCOVERY_RETRIES |
2323cc241636SJulan Hsu 			MPATH_INFO_FLAGS |
2324540bbcb9SJulan Hsu 			MPATH_INFO_HOP_COUNT |
2325540bbcb9SJulan Hsu 			MPATH_INFO_PATH_CHANGE;
2326c5dd9c2bSLuis Carlos Cobo 
2327c5dd9c2bSLuis Carlos Cobo 	pinfo->frame_qlen = mpath->frame_queue.qlen;
2328d19b3bf6SRui Paulo 	pinfo->sn = mpath->sn;
2329c5dd9c2bSLuis Carlos Cobo 	pinfo->metric = mpath->metric;
2330c5dd9c2bSLuis Carlos Cobo 	if (time_before(jiffies, mpath->exp_time))
2331c5dd9c2bSLuis Carlos Cobo 		pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
2332c5dd9c2bSLuis Carlos Cobo 	pinfo->discovery_timeout =
2333c5dd9c2bSLuis Carlos Cobo 			jiffies_to_msecs(mpath->discovery_timeout);
2334c5dd9c2bSLuis Carlos Cobo 	pinfo->discovery_retries = mpath->discovery_retries;
2335c5dd9c2bSLuis Carlos Cobo 	if (mpath->flags & MESH_PATH_ACTIVE)
2336c5dd9c2bSLuis Carlos Cobo 		pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
2337c5dd9c2bSLuis Carlos Cobo 	if (mpath->flags & MESH_PATH_RESOLVING)
2338c5dd9c2bSLuis Carlos Cobo 		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
2339d19b3bf6SRui Paulo 	if (mpath->flags & MESH_PATH_SN_VALID)
2340d19b3bf6SRui Paulo 		pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID;
2341c5dd9c2bSLuis Carlos Cobo 	if (mpath->flags & MESH_PATH_FIXED)
2342c5dd9c2bSLuis Carlos Cobo 		pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
23437ce8c7a3SLEO Airwarosu Yoichi Shinoda 	if (mpath->flags & MESH_PATH_RESOLVED)
23447ce8c7a3SLEO Airwarosu Yoichi Shinoda 		pinfo->flags |= NL80211_MPATH_FLAG_RESOLVED;
2345cc241636SJulan Hsu 	pinfo->hop_count = mpath->hop_count;
2346540bbcb9SJulan Hsu 	pinfo->path_change_count = mpath->path_change_count;
2347c5dd9c2bSLuis Carlos Cobo }
2348c5dd9c2bSLuis Carlos Cobo 
ieee80211_get_mpath(struct wiphy * wiphy,struct net_device * dev,u8 * dst,u8 * next_hop,struct mpath_info * pinfo)2349c5dd9c2bSLuis Carlos Cobo static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
2350c5dd9c2bSLuis Carlos Cobo 			       u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
2351c5dd9c2bSLuis Carlos Cobo 
2352c5dd9c2bSLuis Carlos Cobo {
235314db74bcSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
2354c5dd9c2bSLuis Carlos Cobo 	struct mesh_path *mpath;
2355c5dd9c2bSLuis Carlos Cobo 
235614db74bcSJohannes Berg 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
235714db74bcSJohannes Berg 
2358c5dd9c2bSLuis Carlos Cobo 	rcu_read_lock();
2359bf7cd94dSJohannes Berg 	mpath = mesh_path_lookup(sdata, dst);
2360c5dd9c2bSLuis Carlos Cobo 	if (!mpath) {
2361c5dd9c2bSLuis Carlos Cobo 		rcu_read_unlock();
2362c5dd9c2bSLuis Carlos Cobo 		return -ENOENT;
2363c5dd9c2bSLuis Carlos Cobo 	}
2364c5dd9c2bSLuis Carlos Cobo 	memcpy(dst, mpath->dst, ETH_ALEN);
2365c5dd9c2bSLuis Carlos Cobo 	mpath_set_pinfo(mpath, next_hop, pinfo);
2366c5dd9c2bSLuis Carlos Cobo 	rcu_read_unlock();
2367c5dd9c2bSLuis Carlos Cobo 	return 0;
2368c5dd9c2bSLuis Carlos Cobo }
2369c5dd9c2bSLuis Carlos Cobo 
ieee80211_dump_mpath(struct wiphy * wiphy,struct net_device * dev,int idx,u8 * dst,u8 * next_hop,struct mpath_info * pinfo)2370c5dd9c2bSLuis Carlos Cobo static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
2371c5dd9c2bSLuis Carlos Cobo 				int idx, u8 *dst, u8 *next_hop,
2372c5dd9c2bSLuis Carlos Cobo 				struct mpath_info *pinfo)
2373c5dd9c2bSLuis Carlos Cobo {
237414db74bcSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
2375c5dd9c2bSLuis Carlos Cobo 	struct mesh_path *mpath;
2376c5dd9c2bSLuis Carlos Cobo 
237714db74bcSJohannes Berg 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
237814db74bcSJohannes Berg 
2379c5dd9c2bSLuis Carlos Cobo 	rcu_read_lock();
2380bf7cd94dSJohannes Berg 	mpath = mesh_path_lookup_by_idx(sdata, idx);
2381c5dd9c2bSLuis Carlos Cobo 	if (!mpath) {
2382c5dd9c2bSLuis Carlos Cobo 		rcu_read_unlock();
2383c5dd9c2bSLuis Carlos Cobo 		return -ENOENT;
2384c5dd9c2bSLuis Carlos Cobo 	}
2385c5dd9c2bSLuis Carlos Cobo 	memcpy(dst, mpath->dst, ETH_ALEN);
2386c5dd9c2bSLuis Carlos Cobo 	mpath_set_pinfo(mpath, next_hop, pinfo);
2387c5dd9c2bSLuis Carlos Cobo 	rcu_read_unlock();
2388c5dd9c2bSLuis Carlos Cobo 	return 0;
2389c5dd9c2bSLuis Carlos Cobo }
239093da9cc1Scolin@cozybit.com 
mpp_set_pinfo(struct mesh_path * mpath,u8 * mpp,struct mpath_info * pinfo)2391a2db2ed3SHenning Rogge static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
2392a2db2ed3SHenning Rogge 			  struct mpath_info *pinfo)
2393a2db2ed3SHenning Rogge {
2394a2db2ed3SHenning Rogge 	memset(pinfo, 0, sizeof(*pinfo));
2395a2db2ed3SHenning Rogge 	memcpy(mpp, mpath->mpp, ETH_ALEN);
2396a2db2ed3SHenning Rogge 
23972bdaf386SBob Copeland 	pinfo->generation = mpath->sdata->u.mesh.mpp_paths_generation;
2398a2db2ed3SHenning Rogge }
2399a2db2ed3SHenning Rogge 
ieee80211_get_mpp(struct wiphy * wiphy,struct net_device * dev,u8 * dst,u8 * mpp,struct mpath_info * pinfo)2400a2db2ed3SHenning Rogge static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
2401a2db2ed3SHenning Rogge 			     u8 *dst, u8 *mpp, struct mpath_info *pinfo)
2402a2db2ed3SHenning Rogge 
2403a2db2ed3SHenning Rogge {
2404a2db2ed3SHenning Rogge 	struct ieee80211_sub_if_data *sdata;
2405a2db2ed3SHenning Rogge 	struct mesh_path *mpath;
2406a2db2ed3SHenning Rogge 
2407a2db2ed3SHenning Rogge 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2408a2db2ed3SHenning Rogge 
2409a2db2ed3SHenning Rogge 	rcu_read_lock();
2410a2db2ed3SHenning Rogge 	mpath = mpp_path_lookup(sdata, dst);
2411a2db2ed3SHenning Rogge 	if (!mpath) {
2412a2db2ed3SHenning Rogge 		rcu_read_unlock();
2413a2db2ed3SHenning Rogge 		return -ENOENT;
2414a2db2ed3SHenning Rogge 	}
2415a2db2ed3SHenning Rogge 	memcpy(dst, mpath->dst, ETH_ALEN);
2416a2db2ed3SHenning Rogge 	mpp_set_pinfo(mpath, mpp, pinfo);
2417a2db2ed3SHenning Rogge 	rcu_read_unlock();
2418a2db2ed3SHenning Rogge 	return 0;
2419a2db2ed3SHenning Rogge }
2420a2db2ed3SHenning Rogge 
ieee80211_dump_mpp(struct wiphy * wiphy,struct net_device * dev,int idx,u8 * dst,u8 * mpp,struct mpath_info * pinfo)2421a2db2ed3SHenning Rogge static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
2422a2db2ed3SHenning Rogge 			      int idx, u8 *dst, u8 *mpp,
2423a2db2ed3SHenning Rogge 			      struct mpath_info *pinfo)
2424a2db2ed3SHenning Rogge {
2425a2db2ed3SHenning Rogge 	struct ieee80211_sub_if_data *sdata;
2426a2db2ed3SHenning Rogge 	struct mesh_path *mpath;
2427a2db2ed3SHenning Rogge 
2428a2db2ed3SHenning Rogge 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2429a2db2ed3SHenning Rogge 
2430a2db2ed3SHenning Rogge 	rcu_read_lock();
2431a2db2ed3SHenning Rogge 	mpath = mpp_path_lookup_by_idx(sdata, idx);
2432a2db2ed3SHenning Rogge 	if (!mpath) {
2433a2db2ed3SHenning Rogge 		rcu_read_unlock();
2434a2db2ed3SHenning Rogge 		return -ENOENT;
2435a2db2ed3SHenning Rogge 	}
2436a2db2ed3SHenning Rogge 	memcpy(dst, mpath->dst, ETH_ALEN);
2437a2db2ed3SHenning Rogge 	mpp_set_pinfo(mpath, mpp, pinfo);
2438a2db2ed3SHenning Rogge 	rcu_read_unlock();
2439a2db2ed3SHenning Rogge 	return 0;
2440a2db2ed3SHenning Rogge }
2441a2db2ed3SHenning Rogge 
ieee80211_get_mesh_config(struct wiphy * wiphy,struct net_device * dev,struct mesh_config * conf)244224bdd9f4SJavier Cardona static int ieee80211_get_mesh_config(struct wiphy *wiphy,
244393da9cc1Scolin@cozybit.com 				struct net_device *dev,
244493da9cc1Scolin@cozybit.com 				struct mesh_config *conf)
244593da9cc1Scolin@cozybit.com {
244693da9cc1Scolin@cozybit.com 	struct ieee80211_sub_if_data *sdata;
244793da9cc1Scolin@cozybit.com 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
244893da9cc1Scolin@cozybit.com 
244993da9cc1Scolin@cozybit.com 	memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
245093da9cc1Scolin@cozybit.com 	return 0;
245193da9cc1Scolin@cozybit.com }
245293da9cc1Scolin@cozybit.com 
_chg_mesh_attr(enum nl80211_meshconf_params parm,u32 mask)245393da9cc1Scolin@cozybit.com static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
245493da9cc1Scolin@cozybit.com {
245593da9cc1Scolin@cozybit.com 	return (mask >> (parm-1)) & 0x1;
245693da9cc1Scolin@cozybit.com }
245793da9cc1Scolin@cozybit.com 
copy_mesh_setup(struct ieee80211_if_mesh * ifmsh,const struct mesh_setup * setup)2458c80d545dSJavier Cardona static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
2459c80d545dSJavier Cardona 		const struct mesh_setup *setup)
2460c80d545dSJavier Cardona {
2461c80d545dSJavier Cardona 	u8 *new_ie;
24624bb62344SChun-Yeow Yeoh 	struct ieee80211_sub_if_data *sdata = container_of(ifmsh,
24634bb62344SChun-Yeow Yeoh 					struct ieee80211_sub_if_data, u.mesh);
246408fad438SJouni Malinen 	int i;
2465c80d545dSJavier Cardona 
2466581a8b0fSJavier Cardona 	/* allocate information elements */
2467c80d545dSJavier Cardona 	new_ie = NULL;
2468c80d545dSJavier Cardona 
2469581a8b0fSJavier Cardona 	if (setup->ie_len) {
2470581a8b0fSJavier Cardona 		new_ie = kmemdup(setup->ie, setup->ie_len,
2471c80d545dSJavier Cardona 				GFP_KERNEL);
2472c80d545dSJavier Cardona 		if (!new_ie)
2473c80d545dSJavier Cardona 			return -ENOMEM;
2474c80d545dSJavier Cardona 	}
2475581a8b0fSJavier Cardona 	ifmsh->ie_len = setup->ie_len;
2476581a8b0fSJavier Cardona 	ifmsh->ie = new_ie;
2477c80d545dSJavier Cardona 
2478c80d545dSJavier Cardona 	/* now copy the rest of the setup parameters */
2479c80d545dSJavier Cardona 	ifmsh->mesh_id_len = setup->mesh_id_len;
2480c80d545dSJavier Cardona 	memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
2481d299a1f2SJavier Cardona 	ifmsh->mesh_sp_id = setup->sync_method;
2482c80d545dSJavier Cardona 	ifmsh->mesh_pp_id = setup->path_sel_proto;
2483c80d545dSJavier Cardona 	ifmsh->mesh_pm_id = setup->path_metric;
2484a6dad6a2SThomas Pedersen 	ifmsh->user_mpm = setup->user_mpm;
24850d4261adSColleen Twitty 	ifmsh->mesh_auth_id = setup->auth_id;
2486b130e5ceSJavier Cardona 	ifmsh->security = IEEE80211_MESH_SEC_NONE;
24870ab2e55dSBenjamin Berg 	ifmsh->userspace_handles_dfs = setup->userspace_handles_dfs;
2488b130e5ceSJavier Cardona 	if (setup->is_authenticated)
2489b130e5ceSJavier Cardona 		ifmsh->security |= IEEE80211_MESH_SEC_AUTHED;
2490b130e5ceSJavier Cardona 	if (setup->is_secure)
2491b130e5ceSJavier Cardona 		ifmsh->security |= IEEE80211_MESH_SEC_SECURED;
2492c80d545dSJavier Cardona 
24934bb62344SChun-Yeow Yeoh 	/* mcast rate setting in Mesh Node */
24944bb62344SChun-Yeow Yeoh 	memcpy(sdata->vif.bss_conf.mcast_rate, setup->mcast_rate,
24954bb62344SChun-Yeow Yeoh 						sizeof(setup->mcast_rate));
2496ffb3cf30SAshok Nagarajan 	sdata->vif.bss_conf.basic_rates = setup->basic_rates;
24974bb62344SChun-Yeow Yeoh 
24989bdbf04dSMarco Porsch 	sdata->vif.bss_conf.beacon_int = setup->beacon_interval;
24999bdbf04dSMarco Porsch 	sdata->vif.bss_conf.dtim_period = setup->dtim_period;
25009bdbf04dSMarco Porsch 
250108fad438SJouni Malinen 	sdata->beacon_rate_set = false;
250208fad438SJouni Malinen 	if (wiphy_ext_feature_isset(sdata->local->hw.wiphy,
250308fad438SJouni Malinen 				    NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) {
250408fad438SJouni Malinen 		for (i = 0; i < NUM_NL80211_BANDS; i++) {
250508fad438SJouni Malinen 			sdata->beacon_rateidx_mask[i] =
250608fad438SJouni Malinen 				setup->beacon_rate.control[i].legacy;
250708fad438SJouni Malinen 			if (sdata->beacon_rateidx_mask[i])
250808fad438SJouni Malinen 				sdata->beacon_rate_set = true;
250908fad438SJouni Malinen 		}
251008fad438SJouni Malinen 	}
251108fad438SJouni Malinen 
2512c80d545dSJavier Cardona 	return 0;
2513c80d545dSJavier Cardona }
2514c80d545dSJavier Cardona 
ieee80211_update_mesh_config(struct wiphy * wiphy,struct net_device * dev,u32 mask,const struct mesh_config * nconf)251524bdd9f4SJavier Cardona static int ieee80211_update_mesh_config(struct wiphy *wiphy,
251629cbe68cSJohannes Berg 					struct net_device *dev, u32 mask,
251729cbe68cSJohannes Berg 					const struct mesh_config *nconf)
251893da9cc1Scolin@cozybit.com {
251993da9cc1Scolin@cozybit.com 	struct mesh_config *conf;
252093da9cc1Scolin@cozybit.com 	struct ieee80211_sub_if_data *sdata;
252163c5723bSRui Paulo 	struct ieee80211_if_mesh *ifmsh;
252263c5723bSRui Paulo 
252393da9cc1Scolin@cozybit.com 	sdata = IEEE80211_DEV_TO_SUB_IF(dev);
252463c5723bSRui Paulo 	ifmsh = &sdata->u.mesh;
252593da9cc1Scolin@cozybit.com 
252693da9cc1Scolin@cozybit.com 	/* Set the config options which we are interested in setting */
252793da9cc1Scolin@cozybit.com 	conf = &(sdata->u.mesh.mshcfg);
252893da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
252993da9cc1Scolin@cozybit.com 		conf->dot11MeshRetryTimeout = nconf->dot11MeshRetryTimeout;
253093da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_CONFIRM_TIMEOUT, mask))
253193da9cc1Scolin@cozybit.com 		conf->dot11MeshConfirmTimeout = nconf->dot11MeshConfirmTimeout;
253293da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_HOLDING_TIMEOUT, mask))
253393da9cc1Scolin@cozybit.com 		conf->dot11MeshHoldingTimeout = nconf->dot11MeshHoldingTimeout;
253493da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_MAX_PEER_LINKS, mask))
253593da9cc1Scolin@cozybit.com 		conf->dot11MeshMaxPeerLinks = nconf->dot11MeshMaxPeerLinks;
253693da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_MAX_RETRIES, mask))
253793da9cc1Scolin@cozybit.com 		conf->dot11MeshMaxRetries = nconf->dot11MeshMaxRetries;
253893da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_TTL, mask))
253993da9cc1Scolin@cozybit.com 		conf->dot11MeshTTL = nconf->dot11MeshTTL;
254045904f21SJavier Cardona 	if (_chg_mesh_attr(NL80211_MESHCONF_ELEMENT_TTL, mask))
254158886a90SChun-Yeow Yeoh 		conf->element_ttl = nconf->element_ttl;
2542146bb483SThomas Pedersen 	if (_chg_mesh_attr(NL80211_MESHCONF_AUTO_OPEN_PLINKS, mask)) {
2543146bb483SThomas Pedersen 		if (ifmsh->user_mpm)
2544146bb483SThomas Pedersen 			return -EBUSY;
254593da9cc1Scolin@cozybit.com 		conf->auto_open_plinks = nconf->auto_open_plinks;
2546146bb483SThomas Pedersen 	}
2547d299a1f2SJavier Cardona 	if (_chg_mesh_attr(NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, mask))
2548d299a1f2SJavier Cardona 		conf->dot11MeshNbrOffsetMaxNeighbor =
2549d299a1f2SJavier Cardona 			nconf->dot11MeshNbrOffsetMaxNeighbor;
255093da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, mask))
255193da9cc1Scolin@cozybit.com 		conf->dot11MeshHWMPmaxPREQretries =
255293da9cc1Scolin@cozybit.com 			nconf->dot11MeshHWMPmaxPREQretries;
255393da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_PATH_REFRESH_TIME, mask))
255493da9cc1Scolin@cozybit.com 		conf->path_refresh_time = nconf->path_refresh_time;
255593da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, mask))
255693da9cc1Scolin@cozybit.com 		conf->min_discovery_timeout = nconf->min_discovery_timeout;
255793da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, mask))
255893da9cc1Scolin@cozybit.com 		conf->dot11MeshHWMPactivePathTimeout =
255993da9cc1Scolin@cozybit.com 			nconf->dot11MeshHWMPactivePathTimeout;
256093da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, mask))
256193da9cc1Scolin@cozybit.com 		conf->dot11MeshHWMPpreqMinInterval =
256293da9cc1Scolin@cozybit.com 			nconf->dot11MeshHWMPpreqMinInterval;
2563dca7e943SThomas Pedersen 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, mask))
2564dca7e943SThomas Pedersen 		conf->dot11MeshHWMPperrMinInterval =
2565dca7e943SThomas Pedersen 			nconf->dot11MeshHWMPperrMinInterval;
256693da9cc1Scolin@cozybit.com 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
256793da9cc1Scolin@cozybit.com 			   mask))
256893da9cc1Scolin@cozybit.com 		conf->dot11MeshHWMPnetDiameterTraversalTime =
256993da9cc1Scolin@cozybit.com 			nconf->dot11MeshHWMPnetDiameterTraversalTime;
257063c5723bSRui Paulo 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOTMODE, mask)) {
257163c5723bSRui Paulo 		conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode;
257263c5723bSRui Paulo 		ieee80211_mesh_root_setup(ifmsh);
257363c5723bSRui Paulo 	}
257416dd7267SJavier Cardona 	if (_chg_mesh_attr(NL80211_MESHCONF_GATE_ANNOUNCEMENTS, mask)) {
2575c6133661SThomas Pedersen 		/* our current gate announcement implementation rides on root
2576c6133661SThomas Pedersen 		 * announcements, so require this ifmsh to also be a root node
2577c6133661SThomas Pedersen 		 * */
2578c6133661SThomas Pedersen 		if (nconf->dot11MeshGateAnnouncementProtocol &&
2579dbb912cdSChun-Yeow Yeoh 		    !(conf->dot11MeshHWMPRootMode > IEEE80211_ROOTMODE_ROOT)) {
2580dbb912cdSChun-Yeow Yeoh 			conf->dot11MeshHWMPRootMode = IEEE80211_PROACTIVE_RANN;
2581c6133661SThomas Pedersen 			ieee80211_mesh_root_setup(ifmsh);
2582c6133661SThomas Pedersen 		}
258316dd7267SJavier Cardona 		conf->dot11MeshGateAnnouncementProtocol =
258416dd7267SJavier Cardona 			nconf->dot11MeshGateAnnouncementProtocol;
258516dd7267SJavier Cardona 	}
2586a4f606eaSChun-Yeow Yeoh 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_RANN_INTERVAL, mask))
25870507e159SJavier Cardona 		conf->dot11MeshHWMPRannInterval =
25880507e159SJavier Cardona 			nconf->dot11MeshHWMPRannInterval;
258994f90656SChun-Yeow Yeoh 	if (_chg_mesh_attr(NL80211_MESHCONF_FORWARDING, mask))
259094f90656SChun-Yeow Yeoh 		conf->dot11MeshForwarding = nconf->dot11MeshForwarding;
259155335137SAshok Nagarajan 	if (_chg_mesh_attr(NL80211_MESHCONF_RSSI_THRESHOLD, mask)) {
259255335137SAshok Nagarajan 		/* our RSSI threshold implementation is supported only for
259355335137SAshok Nagarajan 		 * devices that report signal in dBm.
259455335137SAshok Nagarajan 		 */
259530686bf7SJohannes Berg 		if (!ieee80211_hw_check(&sdata->local->hw, SIGNAL_DBM))
259655335137SAshok Nagarajan 			return -ENOTSUPP;
259755335137SAshok Nagarajan 		conf->rssi_threshold = nconf->rssi_threshold;
259855335137SAshok Nagarajan 	}
259970c33eaaSAshok Nagarajan 	if (_chg_mesh_attr(NL80211_MESHCONF_HT_OPMODE, mask)) {
260070c33eaaSAshok Nagarajan 		conf->ht_opmode = nconf->ht_opmode;
260170c33eaaSAshok Nagarajan 		sdata->vif.bss_conf.ht_operation_mode = nconf->ht_opmode;
2602d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
2603d8675a63SJohannes Berg 						  BSS_CHANGED_HT);
260470c33eaaSAshok Nagarajan 	}
2605ac1073a6SChun-Yeow Yeoh 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, mask))
2606ac1073a6SChun-Yeow Yeoh 		conf->dot11MeshHWMPactivePathToRootTimeout =
2607ac1073a6SChun-Yeow Yeoh 			nconf->dot11MeshHWMPactivePathToRootTimeout;
2608ac1073a6SChun-Yeow Yeoh 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOT_INTERVAL, mask))
2609ac1073a6SChun-Yeow Yeoh 		conf->dot11MeshHWMProotInterval =
2610ac1073a6SChun-Yeow Yeoh 			nconf->dot11MeshHWMProotInterval;
2611728b19e5SChun-Yeow Yeoh 	if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, mask))
2612728b19e5SChun-Yeow Yeoh 		conf->dot11MeshHWMPconfirmationInterval =
2613728b19e5SChun-Yeow Yeoh 			nconf->dot11MeshHWMPconfirmationInterval;
26143f52b7e3SMarco Porsch 	if (_chg_mesh_attr(NL80211_MESHCONF_POWER_MODE, mask)) {
26153f52b7e3SMarco Porsch 		conf->power_mode = nconf->power_mode;
26163f52b7e3SMarco Porsch 		ieee80211_mps_local_status_update(sdata);
26173f52b7e3SMarco Porsch 	}
26182b5e1967SThomas Pedersen 	if (_chg_mesh_attr(NL80211_MESHCONF_AWAKE_WINDOW, mask))
26193f52b7e3SMarco Porsch 		conf->dot11MeshAwakeWindowDuration =
26203f52b7e3SMarco Porsch 			nconf->dot11MeshAwakeWindowDuration;
262166de6713SColleen Twitty 	if (_chg_mesh_attr(NL80211_MESHCONF_PLINK_TIMEOUT, mask))
262266de6713SColleen Twitty 		conf->plink_timeout = nconf->plink_timeout;
262301d66fbdSBob Copeland 	if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_GATE, mask))
262401d66fbdSBob Copeland 		conf->dot11MeshConnectedToMeshGate =
262501d66fbdSBob Copeland 			nconf->dot11MeshConnectedToMeshGate;
2626e3718a61SLinus Lüssing 	if (_chg_mesh_attr(NL80211_MESHCONF_NOLEARN, mask))
2627e3718a61SLinus Lüssing 		conf->dot11MeshNolearn = nconf->dot11MeshNolearn;
2628184eebe6SMarkus Theil 	if (_chg_mesh_attr(NL80211_MESHCONF_CONNECTED_TO_AS, mask))
2629184eebe6SMarkus Theil 		conf->dot11MeshConnectedToAuthServer =
2630184eebe6SMarkus Theil 			nconf->dot11MeshConnectedToAuthServer;
26312b5e1967SThomas Pedersen 	ieee80211_mbss_info_change_notify(sdata, BSS_CHANGED_BEACON);
263293da9cc1Scolin@cozybit.com 	return 0;
263393da9cc1Scolin@cozybit.com }
263493da9cc1Scolin@cozybit.com 
ieee80211_join_mesh(struct wiphy * wiphy,struct net_device * dev,const struct mesh_config * conf,const struct mesh_setup * setup)263529cbe68cSJohannes Berg static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
263629cbe68cSJohannes Berg 			       const struct mesh_config *conf,
263729cbe68cSJohannes Berg 			       const struct mesh_setup *setup)
263829cbe68cSJohannes Berg {
263929cbe68cSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
264029cbe68cSJohannes Berg 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
2641c80d545dSJavier Cardona 	int err;
264229cbe68cSJohannes Berg 
2643c80d545dSJavier Cardona 	memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
2644c80d545dSJavier Cardona 	err = copy_mesh_setup(ifmsh, setup);
2645c80d545dSJavier Cardona 	if (err)
2646c80d545dSJavier Cardona 		return err;
2647cc1d2806SJohannes Berg 
2648018f6fbfSDenis Kenzior 	sdata->control_port_over_nl80211 = setup->control_port_over_nl80211;
2649018f6fbfSDenis Kenzior 
265004ecd257SJohannes Berg 	/* can mesh use other SMPS modes? */
2651bfd8403aSJohannes Berg 	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
2652bfd8403aSJohannes Berg 	sdata->deflink.needed_rx_chains = sdata->local->rx_chains;
265304ecd257SJohannes Berg 
265434a3740dSJohannes Berg 	mutex_lock(&sdata->local->mtx);
2655d8675a63SJohannes Berg 	err = ieee80211_link_use_channel(&sdata->deflink, &setup->chandef,
265655de908aSJohannes Berg 					 IEEE80211_CHANCTX_SHARED);
265734a3740dSJohannes Berg 	mutex_unlock(&sdata->local->mtx);
2658cc1d2806SJohannes Berg 	if (err)
2659cc1d2806SJohannes Berg 		return err;
2660cc1d2806SJohannes Berg 
26612b5e1967SThomas Pedersen 	return ieee80211_start_mesh(sdata);
266229cbe68cSJohannes Berg }
266329cbe68cSJohannes Berg 
ieee80211_leave_mesh(struct wiphy * wiphy,struct net_device * dev)266429cbe68cSJohannes Berg static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
266529cbe68cSJohannes Berg {
266629cbe68cSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
266729cbe68cSJohannes Berg 
266829cbe68cSJohannes Berg 	ieee80211_stop_mesh(sdata);
266934a3740dSJohannes Berg 	mutex_lock(&sdata->local->mtx);
2670d8675a63SJohannes Berg 	ieee80211_link_release_channel(&sdata->deflink);
26716a01afcfSRemi Pommarel 	kfree(sdata->u.mesh.ie);
267234a3740dSJohannes Berg 	mutex_unlock(&sdata->local->mtx);
267329cbe68cSJohannes Berg 
267429cbe68cSJohannes Berg 	return 0;
267529cbe68cSJohannes Berg }
2676c5dd9c2bSLuis Carlos Cobo #endif
2677c5dd9c2bSLuis Carlos Cobo 
ieee80211_change_bss(struct wiphy * wiphy,struct net_device * dev,struct bss_parameters * params)26789f1ba906SJouni Malinen static int ieee80211_change_bss(struct wiphy *wiphy,
26799f1ba906SJouni Malinen 				struct net_device *dev,
26809f1ba906SJouni Malinen 				struct bss_parameters *params)
26819f1ba906SJouni Malinen {
268255de908aSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
26839a886df0SJohannes Berg 	struct ieee80211_link_data *link;
268421a8e9ddSMohammed Shafi Shajakhan 	struct ieee80211_supported_band *sband;
268515ddba5fSAnjaneyulu 	u64 changed = 0;
26869f1ba906SJouni Malinen 
26879a886df0SJohannes Berg 	link = ieee80211_link_or_deflink(sdata, params->link_id, true);
26889a886df0SJohannes Berg 	if (IS_ERR(link))
26899a886df0SJohannes Berg 		return PTR_ERR(link);
26909a886df0SJohannes Berg 
26919a886df0SJohannes Berg 	if (!sdata_dereference(link->u.ap.beacon, sdata))
269255de908aSJohannes Berg 		return -ENOENT;
269355de908aSJohannes Berg 
26949a886df0SJohannes Berg 	sband = ieee80211_get_link_sband(link);
269521a8e9ddSMohammed Shafi Shajakhan 	if (!sband)
269621a8e9ddSMohammed Shafi Shajakhan 		return -EINVAL;
26979f1ba906SJouni Malinen 
2698ce04abc3SJohannes Berg 	if (params->basic_rates) {
2699ce04abc3SJohannes Berg 		if (!ieee80211_parse_bitrates(link->conf->chandef.width,
2700ce04abc3SJohannes Berg 					      wiphy->bands[sband->band],
2701ce04abc3SJohannes Berg 					      params->basic_rates,
2702ce04abc3SJohannes Berg 					      params->basic_rates_len,
2703ce04abc3SJohannes Berg 					      &link->conf->basic_rates))
2704ce04abc3SJohannes Berg 			return -EINVAL;
2705ce04abc3SJohannes Berg 		changed |= BSS_CHANGED_BASIC_RATES;
2706ce04abc3SJohannes Berg 		ieee80211_check_rate_mask(link);
2707ce04abc3SJohannes Berg 	}
2708ce04abc3SJohannes Berg 
27099f1ba906SJouni Malinen 	if (params->use_cts_prot >= 0) {
27109a886df0SJohannes Berg 		link->conf->use_cts_prot = params->use_cts_prot;
27119f1ba906SJouni Malinen 		changed |= BSS_CHANGED_ERP_CTS_PROT;
27129f1ba906SJouni Malinen 	}
27139f1ba906SJouni Malinen 	if (params->use_short_preamble >= 0) {
27149a886df0SJohannes Berg 		link->conf->use_short_preamble = params->use_short_preamble;
27159f1ba906SJouni Malinen 		changed |= BSS_CHANGED_ERP_PREAMBLE;
27169f1ba906SJouni Malinen 	}
271743d35343SFelix Fietkau 
27189a886df0SJohannes Berg 	if (!link->conf->use_short_slot &&
271907c12d61STova Mussai 	    (sband->band == NL80211_BAND_5GHZ ||
272007c12d61STova Mussai 	     sband->band == NL80211_BAND_6GHZ)) {
27219a886df0SJohannes Berg 		link->conf->use_short_slot = true;
272243d35343SFelix Fietkau 		changed |= BSS_CHANGED_ERP_SLOT;
272343d35343SFelix Fietkau 	}
272443d35343SFelix Fietkau 
27259f1ba906SJouni Malinen 	if (params->use_short_slot_time >= 0) {
27269a886df0SJohannes Berg 		link->conf->use_short_slot = params->use_short_slot_time;
27279f1ba906SJouni Malinen 		changed |= BSS_CHANGED_ERP_SLOT;
27289f1ba906SJouni Malinen 	}
27299f1ba906SJouni Malinen 
27307b7b5e56SFelix Fietkau 	if (params->ap_isolate >= 0) {
27317b7b5e56SFelix Fietkau 		if (params->ap_isolate)
27327b7b5e56SFelix Fietkau 			sdata->flags |= IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
27337b7b5e56SFelix Fietkau 		else
27347b7b5e56SFelix Fietkau 			sdata->flags &= ~IEEE80211_SDATA_DONT_BRIDGE_PACKETS;
273549ddf8e6SJohannes Berg 		ieee80211_check_fast_rx_iface(sdata);
27367b7b5e56SFelix Fietkau 	}
27377b7b5e56SFelix Fietkau 
273880d7e403SHelmut Schaa 	if (params->ht_opmode >= 0) {
27399a886df0SJohannes Berg 		link->conf->ht_operation_mode = (u16)params->ht_opmode;
274080d7e403SHelmut Schaa 		changed |= BSS_CHANGED_HT;
274180d7e403SHelmut Schaa 	}
274280d7e403SHelmut Schaa 
2743339afbf4SJohannes Berg 	if (params->p2p_ctwindow >= 0) {
27449a886df0SJohannes Berg 		link->conf->p2p_noa_attr.oppps_ctwindow &=
274567baf663SJanusz Dziedzic 					~IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
27469a886df0SJohannes Berg 		link->conf->p2p_noa_attr.oppps_ctwindow |=
274767baf663SJanusz Dziedzic 			params->p2p_ctwindow & IEEE80211_P2P_OPPPS_CTWINDOW_MASK;
2748339afbf4SJohannes Berg 		changed |= BSS_CHANGED_P2P_PS;
2749339afbf4SJohannes Berg 	}
2750339afbf4SJohannes Berg 
275167baf663SJanusz Dziedzic 	if (params->p2p_opp_ps > 0) {
27529a886df0SJohannes Berg 		link->conf->p2p_noa_attr.oppps_ctwindow |=
275367baf663SJanusz Dziedzic 					IEEE80211_P2P_OPPPS_ENABLE_BIT;
275467baf663SJanusz Dziedzic 		changed |= BSS_CHANGED_P2P_PS;
275567baf663SJanusz Dziedzic 	} else if (params->p2p_opp_ps == 0) {
27569a886df0SJohannes Berg 		link->conf->p2p_noa_attr.oppps_ctwindow &=
275767baf663SJanusz Dziedzic 					~IEEE80211_P2P_OPPPS_ENABLE_BIT;
2758339afbf4SJohannes Berg 		changed |= BSS_CHANGED_P2P_PS;
2759339afbf4SJohannes Berg 	}
2760339afbf4SJohannes Berg 
27619a886df0SJohannes Berg 	ieee80211_link_info_change_notify(sdata, link, changed);
27629f1ba906SJouni Malinen 
27639f1ba906SJouni Malinen 	return 0;
27649f1ba906SJouni Malinen }
27659f1ba906SJouni Malinen 
ieee80211_set_txq_params(struct wiphy * wiphy,struct net_device * dev,struct ieee80211_txq_params * params)276631888487SJouni Malinen static int ieee80211_set_txq_params(struct wiphy *wiphy,
2767f70f01c2SEliad Peller 				    struct net_device *dev,
276831888487SJouni Malinen 				    struct ieee80211_txq_params *params)
276931888487SJouni Malinen {
277031888487SJouni Malinen 	struct ieee80211_local *local = wiphy_priv(wiphy);
2771f6f3def3SEliad Peller 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2772c88f1542SShaul Triebitz 	struct ieee80211_link_data *link =
2773ccdde7c7SJohannes Berg 		ieee80211_link_or_deflink(sdata, params->link_id, true);
277431888487SJouni Malinen 	struct ieee80211_tx_queue_params p;
277531888487SJouni Malinen 
277631888487SJouni Malinen 	if (!local->ops->conf_tx)
277731888487SJouni Malinen 		return -EOPNOTSUPP;
277831888487SJouni Malinen 
277954bcbc69SJohannes Berg 	if (local->hw.queues < IEEE80211_NUM_ACS)
278054bcbc69SJohannes Berg 		return -EOPNOTSUPP;
278154bcbc69SJohannes Berg 
2782c88f1542SShaul Triebitz 	if (IS_ERR(link))
2783c88f1542SShaul Triebitz 		return PTR_ERR(link);
2784c88f1542SShaul Triebitz 
278531888487SJouni Malinen 	memset(&p, 0, sizeof(p));
278631888487SJouni Malinen 	p.aifs = params->aifs;
278731888487SJouni Malinen 	p.cw_max = params->cwmax;
278831888487SJouni Malinen 	p.cw_min = params->cwmin;
278931888487SJouni Malinen 	p.txop = params->txop;
2790ab13315aSKalle Valo 
2791ab13315aSKalle Valo 	/*
2792ab13315aSKalle Valo 	 * Setting tx queue params disables u-apsd because it's only
2793ab13315aSKalle Valo 	 * called in master mode.
2794ab13315aSKalle Valo 	 */
2795ab13315aSKalle Valo 	p.uapsd = false;
2796ab13315aSKalle Valo 
2797e552af05SHaim Dreyfuss 	ieee80211_regulatory_limit_wmm_params(sdata, &p, params->ac);
2798e552af05SHaim Dreyfuss 
2799b3e2130bSJohannes Berg 	link->tx_conf[params->ac] = p;
2800b3e2130bSJohannes Berg 	if (drv_conf_tx(local, link, params->ac, &p)) {
28010fb9a9ecSJoe Perches 		wiphy_debug(local->hw.wiphy,
2802a3304b0aSJohannes Berg 			    "failed to set TX queue parameters for AC %d\n",
2803a3304b0aSJohannes Berg 			    params->ac);
280431888487SJouni Malinen 		return -EINVAL;
280531888487SJouni Malinen 	}
280631888487SJouni Malinen 
2807b3e2130bSJohannes Berg 	ieee80211_link_info_change_notify(sdata, link,
2808d8675a63SJohannes Berg 					  BSS_CHANGED_QOS);
28097d25745dSJohannes Berg 
281031888487SJouni Malinen 	return 0;
281131888487SJouni Malinen }
281231888487SJouni Malinen 
2813665af4fcSBob Copeland #ifdef CONFIG_PM
ieee80211_suspend(struct wiphy * wiphy,struct cfg80211_wowlan * wowlan)2814ff1b6e69SJohannes Berg static int ieee80211_suspend(struct wiphy *wiphy,
2815ff1b6e69SJohannes Berg 			     struct cfg80211_wowlan *wowlan)
2816665af4fcSBob Copeland {
2817eecc4800SJohannes Berg 	return __ieee80211_suspend(wiphy_priv(wiphy), wowlan);
2818665af4fcSBob Copeland }
2819665af4fcSBob Copeland 
ieee80211_resume(struct wiphy * wiphy)2820665af4fcSBob Copeland static int ieee80211_resume(struct wiphy *wiphy)
2821665af4fcSBob Copeland {
2822665af4fcSBob Copeland 	return __ieee80211_resume(wiphy_priv(wiphy));
2823665af4fcSBob Copeland }
2824665af4fcSBob Copeland #else
2825665af4fcSBob Copeland #define ieee80211_suspend NULL
2826665af4fcSBob Copeland #define ieee80211_resume NULL
2827665af4fcSBob Copeland #endif
2828665af4fcSBob Copeland 
ieee80211_scan(struct wiphy * wiphy,struct cfg80211_scan_request * req)28292a519311SJohannes Berg static int ieee80211_scan(struct wiphy *wiphy,
28302a519311SJohannes Berg 			  struct cfg80211_scan_request *req)
28312a519311SJohannes Berg {
2832fd014284SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
2833fd014284SJohannes Berg 
2834fd014284SJohannes Berg 	sdata = IEEE80211_WDEV_TO_SUB_IF(req->wdev);
28352a519311SJohannes Berg 
28362ca27bcfSJohannes Berg 	switch (ieee80211_vif_type_p2p(&sdata->vif)) {
28372ca27bcfSJohannes Berg 	case NL80211_IFTYPE_STATION:
28382ca27bcfSJohannes Berg 	case NL80211_IFTYPE_ADHOC:
28392ca27bcfSJohannes Berg 	case NL80211_IFTYPE_MESH_POINT:
28402ca27bcfSJohannes Berg 	case NL80211_IFTYPE_P2P_CLIENT:
2841f142c6b9SJohannes Berg 	case NL80211_IFTYPE_P2P_DEVICE:
28422ca27bcfSJohannes Berg 		break;
28432ca27bcfSJohannes Berg 	case NL80211_IFTYPE_P2P_GO:
28442ca27bcfSJohannes Berg 		if (sdata->local->ops->hw_scan)
28452ca27bcfSJohannes Berg 			break;
2846e9d7732eSJohannes Berg 		/*
2847e9d7732eSJohannes Berg 		 * FIXME: implement NoA while scanning in software,
2848e9d7732eSJohannes Berg 		 * for now fall through to allow scanning only when
2849e9d7732eSJohannes Berg 		 * beaconing hasn't been configured yet
2850e9d7732eSJohannes Berg 		 */
2851fc0561dcSGustavo A. R. Silva 		fallthrough;
28522ca27bcfSJohannes Berg 	case NL80211_IFTYPE_AP:
28535c95b940SAntonio Quartulli 		/*
28545c95b940SAntonio Quartulli 		 * If the scan has been forced (and the driver supports
28555c95b940SAntonio Quartulli 		 * forcing), don't care about being beaconing already.
28565c95b940SAntonio Quartulli 		 * This will create problems to the attached stations (e.g. all
28575c95b940SAntonio Quartulli 		 * the frames sent while scanning on other channel will be
28585c95b940SAntonio Quartulli 		 * lost)
28595c95b940SAntonio Quartulli 		 */
2860bfd8403aSJohannes Berg 		if (sdata->deflink.u.ap.beacon &&
28615c95b940SAntonio Quartulli 		    (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
28625c95b940SAntonio Quartulli 		     !(req->flags & NL80211_SCAN_FLAG_AP)))
28632ca27bcfSJohannes Berg 			return -EOPNOTSUPP;
28642ca27bcfSJohannes Berg 		break;
2865cb3b7d87SAyala Beker 	case NL80211_IFTYPE_NAN:
28662ca27bcfSJohannes Berg 	default:
28672ca27bcfSJohannes Berg 		return -EOPNOTSUPP;
28682ca27bcfSJohannes Berg 	}
28692a519311SJohannes Berg 
28702a519311SJohannes Berg 	return ieee80211_request_scan(sdata, req);
28712a519311SJohannes Berg }
28722a519311SJohannes Berg 
ieee80211_abort_scan(struct wiphy * wiphy,struct wireless_dev * wdev)287391f123f2SVidyullatha Kanchanapally static void ieee80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
287491f123f2SVidyullatha Kanchanapally {
287591f123f2SVidyullatha Kanchanapally 	ieee80211_scan_cancel(wiphy_priv(wiphy));
287691f123f2SVidyullatha Kanchanapally }
287791f123f2SVidyullatha Kanchanapally 
287879f460caSLuciano Coelho static int
ieee80211_sched_scan_start(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_sched_scan_request * req)287979f460caSLuciano Coelho ieee80211_sched_scan_start(struct wiphy *wiphy,
288079f460caSLuciano Coelho 			   struct net_device *dev,
288179f460caSLuciano Coelho 			   struct cfg80211_sched_scan_request *req)
288279f460caSLuciano Coelho {
288379f460caSLuciano Coelho 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
288479f460caSLuciano Coelho 
288579f460caSLuciano Coelho 	if (!sdata->local->ops->sched_scan_start)
288679f460caSLuciano Coelho 		return -EOPNOTSUPP;
288779f460caSLuciano Coelho 
288879f460caSLuciano Coelho 	return ieee80211_request_sched_scan_start(sdata, req);
288979f460caSLuciano Coelho }
289079f460caSLuciano Coelho 
289179f460caSLuciano Coelho static int
ieee80211_sched_scan_stop(struct wiphy * wiphy,struct net_device * dev,u64 reqid)28923a3ecf1dSArend Van Spriel ieee80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev,
28933a3ecf1dSArend Van Spriel 			  u64 reqid)
289479f460caSLuciano Coelho {
28950d440ea2SEliad Peller 	struct ieee80211_local *local = wiphy_priv(wiphy);
289679f460caSLuciano Coelho 
28970d440ea2SEliad Peller 	if (!local->ops->sched_scan_stop)
289879f460caSLuciano Coelho 		return -EOPNOTSUPP;
289979f460caSLuciano Coelho 
29000d440ea2SEliad Peller 	return ieee80211_request_sched_scan_stop(local);
290179f460caSLuciano Coelho }
290279f460caSLuciano Coelho 
ieee80211_auth(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_auth_request * req)2903636a5d36SJouni Malinen static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
2904636a5d36SJouni Malinen 			  struct cfg80211_auth_request *req)
2905636a5d36SJouni Malinen {
290677fdaa12SJohannes Berg 	return ieee80211_mgd_auth(IEEE80211_DEV_TO_SUB_IF(dev), req);
2907636a5d36SJouni Malinen }
2908636a5d36SJouni Malinen 
ieee80211_assoc(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_assoc_request * req)2909636a5d36SJouni Malinen static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
2910636a5d36SJouni Malinen 			   struct cfg80211_assoc_request *req)
2911636a5d36SJouni Malinen {
291277fdaa12SJohannes Berg 	return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
2913636a5d36SJouni Malinen }
2914636a5d36SJouni Malinen 
ieee80211_deauth(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_deauth_request * req)2915636a5d36SJouni Malinen static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
291663c9c5e7SJohannes Berg 			    struct cfg80211_deauth_request *req)
2917636a5d36SJouni Malinen {
291863c9c5e7SJohannes Berg 	return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req);
2919636a5d36SJouni Malinen }
2920636a5d36SJouni Malinen 
ieee80211_disassoc(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_disassoc_request * req)2921636a5d36SJouni Malinen static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
292263c9c5e7SJohannes Berg 			      struct cfg80211_disassoc_request *req)
2923636a5d36SJouni Malinen {
292463c9c5e7SJohannes Berg 	return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
2925636a5d36SJouni Malinen }
2926636a5d36SJouni Malinen 
ieee80211_join_ibss(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_ibss_params * params)2927af8cdcd8SJohannes Berg static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
2928af8cdcd8SJohannes Berg 			       struct cfg80211_ibss_params *params)
2929af8cdcd8SJohannes Berg {
293055de908aSJohannes Berg 	return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);
2931af8cdcd8SJohannes Berg }
2932af8cdcd8SJohannes Berg 
ieee80211_leave_ibss(struct wiphy * wiphy,struct net_device * dev)2933af8cdcd8SJohannes Berg static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
2934af8cdcd8SJohannes Berg {
293555de908aSJohannes Berg 	return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
2936af8cdcd8SJohannes Berg }
2937af8cdcd8SJohannes Berg 
ieee80211_join_ocb(struct wiphy * wiphy,struct net_device * dev,struct ocb_setup * setup)2938239281f8SRostislav Lisovy static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
2939239281f8SRostislav Lisovy 			      struct ocb_setup *setup)
2940239281f8SRostislav Lisovy {
2941239281f8SRostislav Lisovy 	return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
2942239281f8SRostislav Lisovy }
2943239281f8SRostislav Lisovy 
ieee80211_leave_ocb(struct wiphy * wiphy,struct net_device * dev)2944239281f8SRostislav Lisovy static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
2945239281f8SRostislav Lisovy {
2946239281f8SRostislav Lisovy 	return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
2947239281f8SRostislav Lisovy }
2948239281f8SRostislav Lisovy 
ieee80211_set_mcast_rate(struct wiphy * wiphy,struct net_device * dev,int rate[NUM_NL80211_BANDS])2949391e53e3SAntonio Quartulli static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
295057fbcce3SJohannes Berg 				    int rate[NUM_NL80211_BANDS])
2951391e53e3SAntonio Quartulli {
2952391e53e3SAntonio Quartulli 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2953391e53e3SAntonio Quartulli 
29549887dbf5SCong Ding 	memcpy(sdata->vif.bss_conf.mcast_rate, rate,
295557fbcce3SJohannes Berg 	       sizeof(int) * NUM_NL80211_BANDS);
2956391e53e3SAntonio Quartulli 
29578936ae49SJohannes Berg 	if (ieee80211_sdata_running(sdata))
2958d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
2959d8675a63SJohannes Berg 						  BSS_CHANGED_MCAST_RATE);
2960dcbe73caSPradeep Kumar Chitrapu 
2961391e53e3SAntonio Quartulli 	return 0;
2962391e53e3SAntonio Quartulli }
2963391e53e3SAntonio Quartulli 
ieee80211_set_wiphy_params(struct wiphy * wiphy,u32 changed)2964b9a5f8caSJouni Malinen static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
2965b9a5f8caSJouni Malinen {
2966b9a5f8caSJouni Malinen 	struct ieee80211_local *local = wiphy_priv(wiphy);
2967b9a5f8caSJouni Malinen 	int err;
2968b9a5f8caSJouni Malinen 
2969f23a4780SArik Nemtsov 	if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
297017c18bf8SJohannes Berg 		ieee80211_check_fast_xmit_all(local);
297117c18bf8SJohannes Berg 
2972f23a4780SArik Nemtsov 		err = drv_set_frag_threshold(local, wiphy->frag_threshold);
2973f23a4780SArik Nemtsov 
297417c18bf8SJohannes Berg 		if (err) {
297517c18bf8SJohannes Berg 			ieee80211_check_fast_xmit_all(local);
2976f23a4780SArik Nemtsov 			return err;
2977f23a4780SArik Nemtsov 		}
297817c18bf8SJohannes Berg 	}
2979f23a4780SArik Nemtsov 
2980a4bcaf55SLorenzo Bianconi 	if ((changed & WIPHY_PARAM_COVERAGE_CLASS) ||
2981a4bcaf55SLorenzo Bianconi 	    (changed & WIPHY_PARAM_DYN_ACK)) {
2982a4bcaf55SLorenzo Bianconi 		s16 coverage_class;
2983a4bcaf55SLorenzo Bianconi 
2984a4bcaf55SLorenzo Bianconi 		coverage_class = changed & WIPHY_PARAM_COVERAGE_CLASS ?
2985a4bcaf55SLorenzo Bianconi 					wiphy->coverage_class : -1;
2986a4bcaf55SLorenzo Bianconi 		err = drv_set_coverage_class(local, coverage_class);
2987310bc676SLukáš Turek 
2988310bc676SLukáš Turek 		if (err)
2989310bc676SLukáš Turek 			return err;
2990310bc676SLukáš Turek 	}
2991310bc676SLukáš Turek 
299224487981SJohannes Berg 	if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
299324487981SJohannes Berg 		err = drv_set_rts_threshold(local, wiphy->rts_threshold);
299424487981SJohannes Berg 
2995b9a5f8caSJouni Malinen 		if (err)
2996b9a5f8caSJouni Malinen 			return err;
2997b9a5f8caSJouni Malinen 	}
2998b9a5f8caSJouni Malinen 
29998bc83c24SJohannes Berg 	if (changed & WIPHY_PARAM_RETRY_SHORT) {
30008bc83c24SJohannes Berg 		if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY)
30018bc83c24SJohannes Berg 			return -EINVAL;
3002b9a5f8caSJouni Malinen 		local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
30038bc83c24SJohannes Berg 	}
30048bc83c24SJohannes Berg 	if (changed & WIPHY_PARAM_RETRY_LONG) {
30058bc83c24SJohannes Berg 		if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY)
30068bc83c24SJohannes Berg 			return -EINVAL;
3007b9a5f8caSJouni Malinen 		local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
30088bc83c24SJohannes Berg 	}
3009b9a5f8caSJouni Malinen 	if (changed &
3010b9a5f8caSJouni Malinen 	    (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
3011b9a5f8caSJouni Malinen 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
3012b9a5f8caSJouni Malinen 
30132fe4a29aSToke Høiland-Jørgensen 	if (changed & (WIPHY_PARAM_TXQ_LIMIT |
30142fe4a29aSToke Høiland-Jørgensen 		       WIPHY_PARAM_TXQ_MEMORY_LIMIT |
30152fe4a29aSToke Høiland-Jørgensen 		       WIPHY_PARAM_TXQ_QUANTUM))
30162fe4a29aSToke Høiland-Jørgensen 		ieee80211_txq_set_params(local);
30172fe4a29aSToke Høiland-Jørgensen 
3018b9a5f8caSJouni Malinen 	return 0;
3019b9a5f8caSJouni Malinen }
3020b9a5f8caSJouni Malinen 
ieee80211_set_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,enum nl80211_tx_power_setting type,int mbm)30217643a2c3SJohannes Berg static int ieee80211_set_tx_power(struct wiphy *wiphy,
3022c8442118SJohannes Berg 				  struct wireless_dev *wdev,
3023fa61cf70SJuuso Oikarinen 				  enum nl80211_tx_power_setting type, int mbm)
30247643a2c3SJohannes Berg {
30257643a2c3SJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
30261ea6f9c0SJohannes Berg 	struct ieee80211_sub_if_data *sdata;
3027db82d8a9SLorenzo Bianconi 	enum nl80211_tx_power_setting txp_type = type;
3028db82d8a9SLorenzo Bianconi 	bool update_txp_type = false;
30293a3713ecSPeter Große 	bool has_monitor = false;
30307643a2c3SJohannes Berg 
30311ea6f9c0SJohannes Berg 	if (wdev) {
30321ea6f9c0SJohannes Berg 		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
303355de908aSJohannes Berg 
30343a3713ecSPeter Große 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
30356dd23603SJohannes Berg 			sdata = wiphy_dereference(local->hw.wiphy,
30366dd23603SJohannes Berg 						  local->monitor_sdata);
30373a3713ecSPeter Große 			if (!sdata)
30383a3713ecSPeter Große 				return -EOPNOTSUPP;
30393a3713ecSPeter Große 		}
30403a3713ecSPeter Große 
30417643a2c3SJohannes Berg 		switch (type) {
3042fa61cf70SJuuso Oikarinen 		case NL80211_TX_POWER_AUTOMATIC:
3043bfd8403aSJohannes Berg 			sdata->deflink.user_power_level =
3044bfd8403aSJohannes Berg 				IEEE80211_UNSET_POWER_LEVEL;
3045db82d8a9SLorenzo Bianconi 			txp_type = NL80211_TX_POWER_LIMITED;
30467643a2c3SJohannes Berg 			break;
3047fa61cf70SJuuso Oikarinen 		case NL80211_TX_POWER_LIMITED:
3048fa61cf70SJuuso Oikarinen 		case NL80211_TX_POWER_FIXED:
3049fa61cf70SJuuso Oikarinen 			if (mbm < 0 || (mbm % 100))
3050fa61cf70SJuuso Oikarinen 				return -EOPNOTSUPP;
3051bfd8403aSJohannes Berg 			sdata->deflink.user_power_level = MBM_TO_DBM(mbm);
30521ea6f9c0SJohannes Berg 			break;
30531ea6f9c0SJohannes Berg 		}
30541ea6f9c0SJohannes Berg 
3055db82d8a9SLorenzo Bianconi 		if (txp_type != sdata->vif.bss_conf.txpower_type) {
3056db82d8a9SLorenzo Bianconi 			update_txp_type = true;
3057db82d8a9SLorenzo Bianconi 			sdata->vif.bss_conf.txpower_type = txp_type;
3058db82d8a9SLorenzo Bianconi 		}
3059db82d8a9SLorenzo Bianconi 
3060db82d8a9SLorenzo Bianconi 		ieee80211_recalc_txpower(sdata, update_txp_type);
30611ea6f9c0SJohannes Berg 
30621ea6f9c0SJohannes Berg 		return 0;
30631ea6f9c0SJohannes Berg 	}
30641ea6f9c0SJohannes Berg 
30651ea6f9c0SJohannes Berg 	switch (type) {
30661ea6f9c0SJohannes Berg 	case NL80211_TX_POWER_AUTOMATIC:
30671ea6f9c0SJohannes Berg 		local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
3068db82d8a9SLorenzo Bianconi 		txp_type = NL80211_TX_POWER_LIMITED;
30691ea6f9c0SJohannes Berg 		break;
30701ea6f9c0SJohannes Berg 	case NL80211_TX_POWER_LIMITED:
30711ea6f9c0SJohannes Berg 	case NL80211_TX_POWER_FIXED:
30721ea6f9c0SJohannes Berg 		if (mbm < 0 || (mbm % 100))
30731ea6f9c0SJohannes Berg 			return -EOPNOTSUPP;
3074fa61cf70SJuuso Oikarinen 		local->user_power_level = MBM_TO_DBM(mbm);
30757643a2c3SJohannes Berg 		break;
30767643a2c3SJohannes Berg 	}
30777643a2c3SJohannes Berg 
30781ea6f9c0SJohannes Berg 	mutex_lock(&local->iflist_mtx);
3079db82d8a9SLorenzo Bianconi 	list_for_each_entry(sdata, &local->interfaces, list) {
30803a3713ecSPeter Große 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
30813a3713ecSPeter Große 			has_monitor = true;
30823a3713ecSPeter Große 			continue;
30833a3713ecSPeter Große 		}
3084bfd8403aSJohannes Berg 		sdata->deflink.user_power_level = local->user_power_level;
3085db82d8a9SLorenzo Bianconi 		if (txp_type != sdata->vif.bss_conf.txpower_type)
3086db82d8a9SLorenzo Bianconi 			update_txp_type = true;
3087db82d8a9SLorenzo Bianconi 		sdata->vif.bss_conf.txpower_type = txp_type;
3088db82d8a9SLorenzo Bianconi 	}
30893a3713ecSPeter Große 	list_for_each_entry(sdata, &local->interfaces, list) {
30903a3713ecSPeter Große 		if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
30913a3713ecSPeter Große 			continue;
3092db82d8a9SLorenzo Bianconi 		ieee80211_recalc_txpower(sdata, update_txp_type);
30933a3713ecSPeter Große 	}
30941ea6f9c0SJohannes Berg 	mutex_unlock(&local->iflist_mtx);
30957643a2c3SJohannes Berg 
30963a3713ecSPeter Große 	if (has_monitor) {
30976dd23603SJohannes Berg 		sdata = wiphy_dereference(local->hw.wiphy,
30986dd23603SJohannes Berg 					  local->monitor_sdata);
30993a3713ecSPeter Große 		if (sdata) {
3100bfd8403aSJohannes Berg 			sdata->deflink.user_power_level = local->user_power_level;
31013a3713ecSPeter Große 			if (txp_type != sdata->vif.bss_conf.txpower_type)
31023a3713ecSPeter Große 				update_txp_type = true;
31033a3713ecSPeter Große 			sdata->vif.bss_conf.txpower_type = txp_type;
31043a3713ecSPeter Große 
31053a3713ecSPeter Große 			ieee80211_recalc_txpower(sdata, update_txp_type);
31063a3713ecSPeter Große 		}
31073a3713ecSPeter Große 	}
31083a3713ecSPeter Große 
31097643a2c3SJohannes Berg 	return 0;
31107643a2c3SJohannes Berg }
31117643a2c3SJohannes Berg 
ieee80211_get_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,int * dbm)3112c8442118SJohannes Berg static int ieee80211_get_tx_power(struct wiphy *wiphy,
3113c8442118SJohannes Berg 				  struct wireless_dev *wdev,
3114c8442118SJohannes Berg 				  int *dbm)
31157643a2c3SJohannes Berg {
31167643a2c3SJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
31171ea6f9c0SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
31187643a2c3SJohannes Berg 
3119ee35c423SFelix Fietkau 	if (local->ops->get_txpower &&
3120ee35c423SFelix Fietkau 	    (sdata->flags & IEEE80211_SDATA_IN_DRIVER))
31215b3dc42bSFelix Fietkau 		return drv_get_txpower(local, sdata, dbm);
31225b3dc42bSFelix Fietkau 
31231ea6f9c0SJohannes Berg 	if (!local->use_chanctx)
31247643a2c3SJohannes Berg 		*dbm = local->hw.conf.power_level;
31251ea6f9c0SJohannes Berg 	else
31261ea6f9c0SJohannes Berg 		*dbm = sdata->vif.bss_conf.txpower;
31277643a2c3SJohannes Berg 
31285a94cffeSPing-Ke Shih 	/* INT_MIN indicates no power level was set yet */
31295a94cffeSPing-Ke Shih 	if (*dbm == INT_MIN)
31305a94cffeSPing-Ke Shih 		return -EINVAL;
31315a94cffeSPing-Ke Shih 
31327643a2c3SJohannes Berg 	return 0;
31337643a2c3SJohannes Berg }
31347643a2c3SJohannes Berg 
ieee80211_rfkill_poll(struct wiphy * wiphy)31351f87f7d3SJohannes Berg static void ieee80211_rfkill_poll(struct wiphy *wiphy)
31361f87f7d3SJohannes Berg {
31371f87f7d3SJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
31381f87f7d3SJohannes Berg 
31391f87f7d3SJohannes Berg 	drv_rfkill_poll(local);
31401f87f7d3SJohannes Berg }
31411f87f7d3SJohannes Berg 
3142aff89a9bSJohannes Berg #ifdef CONFIG_NL80211_TESTMODE
ieee80211_testmode_cmd(struct wiphy * wiphy,struct wireless_dev * wdev,void * data,int len)3143fc73f11fSDavid Spinadel static int ieee80211_testmode_cmd(struct wiphy *wiphy,
3144fc73f11fSDavid Spinadel 				  struct wireless_dev *wdev,
3145fc73f11fSDavid Spinadel 				  void *data, int len)
3146aff89a9bSJohannes Berg {
3147aff89a9bSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
314852981cd7SDavid Spinadel 	struct ieee80211_vif *vif = NULL;
3149aff89a9bSJohannes Berg 
3150aff89a9bSJohannes Berg 	if (!local->ops->testmode_cmd)
3151aff89a9bSJohannes Berg 		return -EOPNOTSUPP;
3152aff89a9bSJohannes Berg 
315352981cd7SDavid Spinadel 	if (wdev) {
315452981cd7SDavid Spinadel 		struct ieee80211_sub_if_data *sdata;
315552981cd7SDavid Spinadel 
315652981cd7SDavid Spinadel 		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
315752981cd7SDavid Spinadel 		if (sdata->flags & IEEE80211_SDATA_IN_DRIVER)
315852981cd7SDavid Spinadel 			vif = &sdata->vif;
315952981cd7SDavid Spinadel 	}
316052981cd7SDavid Spinadel 
316152981cd7SDavid Spinadel 	return local->ops->testmode_cmd(&local->hw, vif, data, len);
3162aff89a9bSJohannes Berg }
316371063f0eSWey-Yi Guy 
ieee80211_testmode_dump(struct wiphy * wiphy,struct sk_buff * skb,struct netlink_callback * cb,void * data,int len)316471063f0eSWey-Yi Guy static int ieee80211_testmode_dump(struct wiphy *wiphy,
316571063f0eSWey-Yi Guy 				   struct sk_buff *skb,
316671063f0eSWey-Yi Guy 				   struct netlink_callback *cb,
316771063f0eSWey-Yi Guy 				   void *data, int len)
316871063f0eSWey-Yi Guy {
316971063f0eSWey-Yi Guy 	struct ieee80211_local *local = wiphy_priv(wiphy);
317071063f0eSWey-Yi Guy 
317171063f0eSWey-Yi Guy 	if (!local->ops->testmode_dump)
317271063f0eSWey-Yi Guy 		return -EOPNOTSUPP;
317371063f0eSWey-Yi Guy 
317471063f0eSWey-Yi Guy 	return local->ops->testmode_dump(&local->hw, skb, cb, data, len);
317571063f0eSWey-Yi Guy }
3176aff89a9bSJohannes Berg #endif
3177aff89a9bSJohannes Berg 
__ieee80211_request_smps_mgd(struct ieee80211_sub_if_data * sdata,struct ieee80211_link_data * link,enum ieee80211_smps_mode smps_mode)3178687da132SEmmanuel Grumbach int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
3179d8675a63SJohannes Berg 				 struct ieee80211_link_data *link,
31800f78231bSJohannes Berg 				 enum ieee80211_smps_mode smps_mode)
31810f78231bSJohannes Berg {
31820f78231bSJohannes Berg 	const u8 *ap;
31830f78231bSJohannes Berg 	enum ieee80211_smps_mode old_req;
31840f78231bSJohannes Berg 	int err;
3185d51c2ea3SArik Nemtsov 	struct sta_info *sta;
3186d51c2ea3SArik Nemtsov 	bool tdls_peer_found = false;
31870f78231bSJohannes Berg 
31888d61ffa5SJohannes Berg 	lockdep_assert_held(&sdata->wdev.mtx);
3189243e6df4SJohannes Berg 
3190687da132SEmmanuel Grumbach 	if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION))
3191687da132SEmmanuel Grumbach 		return -EINVAL;
3192687da132SEmmanuel Grumbach 
3193d8675a63SJohannes Berg 	old_req = link->u.mgd.req_smps;
3194d8675a63SJohannes Berg 	link->u.mgd.req_smps = smps_mode;
31950f78231bSJohannes Berg 
31960f78231bSJohannes Berg 	if (old_req == smps_mode &&
31970f78231bSJohannes Berg 	    smps_mode != IEEE80211_SMPS_AUTOMATIC)
31980f78231bSJohannes Berg 		return 0;
31990f78231bSJohannes Berg 
32000f78231bSJohannes Berg 	/*
32010f78231bSJohannes Berg 	 * If not associated, or current association is not an HT
320204ecd257SJohannes Berg 	 * association, there's no need to do anything, just store
320304ecd257SJohannes Berg 	 * the new value until we associate.
32040f78231bSJohannes Berg 	 */
32050f78231bSJohannes Berg 	if (!sdata->u.mgd.associated ||
3206d8675a63SJohannes Berg 	    link->conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
32070f78231bSJohannes Berg 		return 0;
32080f78231bSJohannes Berg 
3209d8675a63SJohannes Berg 	ap = link->u.mgd.bssid;
32100f78231bSJohannes Berg 
3211d51c2ea3SArik Nemtsov 	rcu_read_lock();
3212d51c2ea3SArik Nemtsov 	list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
3213d51c2ea3SArik Nemtsov 		if (!sta->sta.tdls || sta->sdata != sdata || !sta->uploaded ||
3214d51c2ea3SArik Nemtsov 		    !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
3215d51c2ea3SArik Nemtsov 			continue;
3216d51c2ea3SArik Nemtsov 
3217d51c2ea3SArik Nemtsov 		tdls_peer_found = true;
3218d51c2ea3SArik Nemtsov 		break;
3219d51c2ea3SArik Nemtsov 	}
3220d51c2ea3SArik Nemtsov 	rcu_read_unlock();
3221d51c2ea3SArik Nemtsov 
32220f78231bSJohannes Berg 	if (smps_mode == IEEE80211_SMPS_AUTOMATIC) {
3223d51c2ea3SArik Nemtsov 		if (tdls_peer_found || !sdata->u.mgd.powersave)
32240f78231bSJohannes Berg 			smps_mode = IEEE80211_SMPS_OFF;
3225d51c2ea3SArik Nemtsov 		else
3226d51c2ea3SArik Nemtsov 			smps_mode = IEEE80211_SMPS_DYNAMIC;
32270f78231bSJohannes Berg 	}
32280f78231bSJohannes Berg 
32290f78231bSJohannes Berg 	/* send SM PS frame to AP */
32300f78231bSJohannes Berg 	err = ieee80211_send_smps_action(sdata, smps_mode,
32310f78231bSJohannes Berg 					 ap, ap);
32320f78231bSJohannes Berg 	if (err)
3233d8675a63SJohannes Berg 		link->u.mgd.req_smps = old_req;
3234d51c2ea3SArik Nemtsov 	else if (smps_mode != IEEE80211_SMPS_OFF && tdls_peer_found)
3235d51c2ea3SArik Nemtsov 		ieee80211_teardown_tdls_peers(sdata);
32360f78231bSJohannes Berg 
32370f78231bSJohannes Berg 	return err;
32380f78231bSJohannes Berg }
32390f78231bSJohannes Berg 
ieee80211_set_power_mgmt(struct wiphy * wiphy,struct net_device * dev,bool enabled,int timeout)3240bc92afd9SJohannes Berg static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
3241bc92afd9SJohannes Berg 				    bool enabled, int timeout)
3242bc92afd9SJohannes Berg {
3243bc92afd9SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
3244bc92afd9SJohannes Berg 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
3245e9aac179SJohannes Berg 	unsigned int link_id;
3246bc92afd9SJohannes Berg 
32472d3db210SBob Copeland 	if (sdata->vif.type != NL80211_IFTYPE_STATION)
3248e5de30c9SBenoit Papillault 		return -EOPNOTSUPP;
3249e5de30c9SBenoit Papillault 
325030686bf7SJohannes Berg 	if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS))
3251bc92afd9SJohannes Berg 		return -EOPNOTSUPP;
3252bc92afd9SJohannes Berg 
3253bc92afd9SJohannes Berg 	if (enabled == sdata->u.mgd.powersave &&
3254ff616381SJuuso Oikarinen 	    timeout == local->dynamic_ps_forced_timeout)
3255bc92afd9SJohannes Berg 		return 0;
3256bc92afd9SJohannes Berg 
3257bc92afd9SJohannes Berg 	sdata->u.mgd.powersave = enabled;
3258ff616381SJuuso Oikarinen 	local->dynamic_ps_forced_timeout = timeout;
3259bc92afd9SJohannes Berg 
32600f78231bSJohannes Berg 	/* no change, but if automatic follow powersave */
3261ed405be5SJohannes Berg 	sdata_lock(sdata);
3262e9aac179SJohannes Berg 	for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
3263d8675a63SJohannes Berg 		struct ieee80211_link_data *link;
3264d8675a63SJohannes Berg 
3265d8675a63SJohannes Berg 		link = sdata_dereference(sdata->link[link_id], sdata);
3266d8675a63SJohannes Berg 
3267d8675a63SJohannes Berg 		if (!link)
3268e9aac179SJohannes Berg 			continue;
3269d8675a63SJohannes Berg 		__ieee80211_request_smps_mgd(sdata, link,
3270d8675a63SJohannes Berg 					     link->u.mgd.req_smps);
3271e9aac179SJohannes Berg 	}
3272ed405be5SJohannes Berg 	sdata_unlock(sdata);
32730f78231bSJohannes Berg 
327430686bf7SJohannes Berg 	if (ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS))
3275bc92afd9SJohannes Berg 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
3276bc92afd9SJohannes Berg 
32774a733ef1SJohannes Berg 	ieee80211_recalc_ps(local);
3278ab095877SEliad Peller 	ieee80211_recalc_ps_vif(sdata);
32791d870162SFelix Fietkau 	ieee80211_check_fast_rx_iface(sdata);
3280bc92afd9SJohannes Berg 
3281bc92afd9SJohannes Berg 	return 0;
3282bc92afd9SJohannes Berg }
3283bc92afd9SJohannes Berg 
ieee80211_set_cqm_rssi_config(struct wiphy * wiphy,struct net_device * dev,s32 rssi_thold,u32 rssi_hyst)3284a97c13c3SJuuso Oikarinen static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
3285a97c13c3SJuuso Oikarinen 					 struct net_device *dev,
3286a97c13c3SJuuso Oikarinen 					 s32 rssi_thold, u32 rssi_hyst)
3287a97c13c3SJuuso Oikarinen {
3288a97c13c3SJuuso Oikarinen 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
3289a97c13c3SJuuso Oikarinen 	struct ieee80211_vif *vif = &sdata->vif;
3290a97c13c3SJuuso Oikarinen 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
3291a97c13c3SJuuso Oikarinen 
3292a97c13c3SJuuso Oikarinen 	if (rssi_thold == bss_conf->cqm_rssi_thold &&
3293a97c13c3SJuuso Oikarinen 	    rssi_hyst == bss_conf->cqm_rssi_hyst)
3294a97c13c3SJuuso Oikarinen 		return 0;
3295a97c13c3SJuuso Oikarinen 
3296ef9be10cSJohannes Berg 	if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER &&
3297ef9be10cSJohannes Berg 	    !(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
3298ef9be10cSJohannes Berg 		return -EOPNOTSUPP;
3299ef9be10cSJohannes Berg 
3300a97c13c3SJuuso Oikarinen 	bss_conf->cqm_rssi_thold = rssi_thold;
3301a97c13c3SJuuso Oikarinen 	bss_conf->cqm_rssi_hyst = rssi_hyst;
33022c3c5f8cSAndrew Zaborowski 	bss_conf->cqm_rssi_low = 0;
33032c3c5f8cSAndrew Zaborowski 	bss_conf->cqm_rssi_high = 0;
3304bfd8403aSJohannes Berg 	sdata->deflink.u.mgd.last_cqm_event_signal = 0;
33052c3c5f8cSAndrew Zaborowski 
33062c3c5f8cSAndrew Zaborowski 	/* tell the driver upon association, unless already associated */
33072c3c5f8cSAndrew Zaborowski 	if (sdata->u.mgd.associated &&
33082c3c5f8cSAndrew Zaborowski 	    sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
3309d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
3310d8675a63SJohannes Berg 						  BSS_CHANGED_CQM);
33112c3c5f8cSAndrew Zaborowski 
33122c3c5f8cSAndrew Zaborowski 	return 0;
33132c3c5f8cSAndrew Zaborowski }
33142c3c5f8cSAndrew Zaborowski 
ieee80211_set_cqm_rssi_range_config(struct wiphy * wiphy,struct net_device * dev,s32 rssi_low,s32 rssi_high)33152c3c5f8cSAndrew Zaborowski static int ieee80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
33162c3c5f8cSAndrew Zaborowski 					       struct net_device *dev,
33172c3c5f8cSAndrew Zaborowski 					       s32 rssi_low, s32 rssi_high)
33182c3c5f8cSAndrew Zaborowski {
33192c3c5f8cSAndrew Zaborowski 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
33202c3c5f8cSAndrew Zaborowski 	struct ieee80211_vif *vif = &sdata->vif;
33212c3c5f8cSAndrew Zaborowski 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
33222c3c5f8cSAndrew Zaborowski 
33232c3c5f8cSAndrew Zaborowski 	if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
33242c3c5f8cSAndrew Zaborowski 		return -EOPNOTSUPP;
33252c3c5f8cSAndrew Zaborowski 
33262c3c5f8cSAndrew Zaborowski 	bss_conf->cqm_rssi_low = rssi_low;
33272c3c5f8cSAndrew Zaborowski 	bss_conf->cqm_rssi_high = rssi_high;
33282c3c5f8cSAndrew Zaborowski 	bss_conf->cqm_rssi_thold = 0;
33292c3c5f8cSAndrew Zaborowski 	bss_conf->cqm_rssi_hyst = 0;
3330bfd8403aSJohannes Berg 	sdata->deflink.u.mgd.last_cqm_event_signal = 0;
3331a97c13c3SJuuso Oikarinen 
3332a97c13c3SJuuso Oikarinen 	/* tell the driver upon association, unless already associated */
3333ea086359SJohannes Berg 	if (sdata->u.mgd.associated &&
3334ea086359SJohannes Berg 	    sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)
3335d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
3336d8675a63SJohannes Berg 						  BSS_CHANGED_CQM);
3337a97c13c3SJuuso Oikarinen 
3338a97c13c3SJuuso Oikarinen 	return 0;
3339a97c13c3SJuuso Oikarinen }
3340a97c13c3SJuuso Oikarinen 
ieee80211_set_bitrate_mask(struct wiphy * wiphy,struct net_device * dev,unsigned int link_id,const u8 * addr,const struct cfg80211_bitrate_mask * mask)33419930380fSJohannes Berg static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
33429930380fSJohannes Berg 				      struct net_device *dev,
33437b0a0e3cSJohannes Berg 				      unsigned int link_id,
33449930380fSJohannes Berg 				      const u8 *addr,
33459930380fSJohannes Berg 				      const struct cfg80211_bitrate_mask *mask)
33469930380fSJohannes Berg {
33479930380fSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
33489930380fSJohannes Berg 	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
3349bdbfd6b5SSujith Manoharan 	int i, ret;
33509930380fSJohannes Berg 
3351554a43d5SEliad Peller 	if (!ieee80211_sdata_running(sdata))
3352554a43d5SEliad Peller 		return -ENETDOWN;
3353554a43d5SEliad Peller 
3354e8e4f528SJohannes Berg 	/*
3355e8e4f528SJohannes Berg 	 * If active validate the setting and reject it if it doesn't leave
3356e8e4f528SJohannes Berg 	 * at least one basic rate usable, since we really have to be able
3357e8e4f528SJohannes Berg 	 * to send something, and if we're an AP we have to be able to do
3358e8e4f528SJohannes Berg 	 * so at a basic rate so that all clients can receive it.
3359e8e4f528SJohannes Berg 	 */
3360d0a9123eSJohannes Berg 	if (rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) &&
3361e8e4f528SJohannes Berg 	    sdata->vif.bss_conf.chandef.chan) {
3362e8e4f528SJohannes Berg 		u32 basic_rates = sdata->vif.bss_conf.basic_rates;
3363e8e4f528SJohannes Berg 		enum nl80211_band band = sdata->vif.bss_conf.chandef.chan->band;
3364e8e4f528SJohannes Berg 
3365e8e4f528SJohannes Berg 		if (!(mask->control[band].legacy & basic_rates))
3366e8e4f528SJohannes Berg 			return -EINVAL;
3367e8e4f528SJohannes Berg 	}
3368e8e4f528SJohannes Berg 
3369e5f5ce37SJohannes Berg 	if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
3370e5f5ce37SJohannes Berg 		ret = drv_set_bitrate_mask(local, sdata, mask);
3371e5f5ce37SJohannes Berg 		if (ret)
3372e5f5ce37SJohannes Berg 			return ret;
3373e5f5ce37SJohannes Berg 	}
3374e5f5ce37SJohannes Berg 
337557fbcce3SJohannes Berg 	for (i = 0; i < NUM_NL80211_BANDS; i++) {
33762ffbe6d3SFelix Fietkau 		struct ieee80211_supported_band *sband = wiphy->bands[i];
33772ffbe6d3SFelix Fietkau 		int j;
33782ffbe6d3SFelix Fietkau 
337937eb0b16SJouni Malinen 		sdata->rc_rateidx_mask[i] = mask->control[i].legacy;
3380d1e33e65SJanusz Dziedzic 		memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs,
3381d1e33e65SJanusz Dziedzic 		       sizeof(mask->control[i].ht_mcs));
3382b119ad6eSLorenzo Bianconi 		memcpy(sdata->rc_rateidx_vht_mcs_mask[i],
3383b119ad6eSLorenzo Bianconi 		       mask->control[i].vht_mcs,
3384b119ad6eSLorenzo Bianconi 		       sizeof(mask->control[i].vht_mcs));
33852ffbe6d3SFelix Fietkau 
33862ffbe6d3SFelix Fietkau 		sdata->rc_has_mcs_mask[i] = false;
3387b119ad6eSLorenzo Bianconi 		sdata->rc_has_vht_mcs_mask[i] = false;
33882ffbe6d3SFelix Fietkau 		if (!sband)
33892ffbe6d3SFelix Fietkau 			continue;
33902ffbe6d3SFelix Fietkau 
3391b119ad6eSLorenzo Bianconi 		for (j = 0; j < IEEE80211_HT_MCS_MASK_LEN; j++) {
33921944015fSJohannes Berg 			if (sdata->rc_rateidx_mcs_mask[i][j] != 0xff) {
33932ffbe6d3SFelix Fietkau 				sdata->rc_has_mcs_mask[i] = true;
33942ffbe6d3SFelix Fietkau 				break;
33952ffbe6d3SFelix Fietkau 			}
339619468413SSimon Wunderlich 		}
33979930380fSJohannes Berg 
33982df1b131SJohannes Berg 		for (j = 0; j < NL80211_VHT_NSS_MAX; j++) {
33991944015fSJohannes Berg 			if (sdata->rc_rateidx_vht_mcs_mask[i][j] != 0xffff) {
34002df1b131SJohannes Berg 				sdata->rc_has_vht_mcs_mask[i] = true;
34012df1b131SJohannes Berg 				break;
34022df1b131SJohannes Berg 			}
34032df1b131SJohannes Berg 		}
34042df1b131SJohannes Berg 	}
34052df1b131SJohannes Berg 
34069930380fSJohannes Berg 	return 0;
34079930380fSJohannes Berg }
34089930380fSJohannes Berg 
ieee80211_start_radar_detection(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_chan_def * chandef,u32 cac_time_ms)3409164eb02dSSimon Wunderlich static int ieee80211_start_radar_detection(struct wiphy *wiphy,
3410164eb02dSSimon Wunderlich 					   struct net_device *dev,
341131559f35SJanusz Dziedzic 					   struct cfg80211_chan_def *chandef,
341231559f35SJanusz Dziedzic 					   u32 cac_time_ms)
3413164eb02dSSimon Wunderlich {
3414164eb02dSSimon Wunderlich 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
3415164eb02dSSimon Wunderlich 	struct ieee80211_local *local = sdata->local;
3416164eb02dSSimon Wunderlich 	int err;
3417164eb02dSSimon Wunderlich 
341834a3740dSJohannes Berg 	mutex_lock(&local->mtx);
341934a3740dSJohannes Berg 	if (!list_empty(&local->roc_list) || local->scanning) {
342034a3740dSJohannes Berg 		err = -EBUSY;
342134a3740dSJohannes Berg 		goto out_unlock;
342234a3740dSJohannes Berg 	}
3423164eb02dSSimon Wunderlich 
3424164eb02dSSimon Wunderlich 	/* whatever, but channel contexts should not complain about that one */
3425bfd8403aSJohannes Berg 	sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
3426bfd8403aSJohannes Berg 	sdata->deflink.needed_rx_chains = local->rx_chains;
3427164eb02dSSimon Wunderlich 
3428d8675a63SJohannes Berg 	err = ieee80211_link_use_channel(&sdata->deflink, chandef,
3429164eb02dSSimon Wunderlich 					 IEEE80211_CHANCTX_SHARED);
3430164eb02dSSimon Wunderlich 	if (err)
343134a3740dSJohannes Berg 		goto out_unlock;
3432164eb02dSSimon Wunderlich 
3433164eb02dSSimon Wunderlich 	ieee80211_queue_delayed_work(&sdata->local->hw,
3434bfd8403aSJohannes Berg 				     &sdata->deflink.dfs_cac_timer_work,
343531559f35SJanusz Dziedzic 				     msecs_to_jiffies(cac_time_ms));
3436164eb02dSSimon Wunderlich 
343734a3740dSJohannes Berg  out_unlock:
343834a3740dSJohannes Berg 	mutex_unlock(&local->mtx);
343934a3740dSJohannes Berg 	return err;
3440164eb02dSSimon Wunderlich }
3441164eb02dSSimon Wunderlich 
ieee80211_end_cac(struct wiphy * wiphy,struct net_device * dev)344226ec17a1SOrr Mazor static void ieee80211_end_cac(struct wiphy *wiphy,
344326ec17a1SOrr Mazor 			      struct net_device *dev)
344426ec17a1SOrr Mazor {
344526ec17a1SOrr Mazor 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
344626ec17a1SOrr Mazor 	struct ieee80211_local *local = sdata->local;
344726ec17a1SOrr Mazor 
344826ec17a1SOrr Mazor 	mutex_lock(&local->mtx);
344926ec17a1SOrr Mazor 	list_for_each_entry(sdata, &local->interfaces, list) {
345026ec17a1SOrr Mazor 		/* it might be waiting for the local->mtx, but then
345126ec17a1SOrr Mazor 		 * by the time it gets it, sdata->wdev.cac_started
345226ec17a1SOrr Mazor 		 * will no longer be true
345326ec17a1SOrr Mazor 		 */
3454bfd8403aSJohannes Berg 		cancel_delayed_work(&sdata->deflink.dfs_cac_timer_work);
345526ec17a1SOrr Mazor 
345626ec17a1SOrr Mazor 		if (sdata->wdev.cac_started) {
3457d8675a63SJohannes Berg 			ieee80211_link_release_channel(&sdata->deflink);
345826ec17a1SOrr Mazor 			sdata->wdev.cac_started = false;
345926ec17a1SOrr Mazor 		}
346026ec17a1SOrr Mazor 	}
346126ec17a1SOrr Mazor 	mutex_unlock(&local->mtx);
346226ec17a1SOrr Mazor }
346326ec17a1SOrr Mazor 
346473da7d5bSSimon Wunderlich static struct cfg80211_beacon_data *
cfg80211_beacon_dup(struct cfg80211_beacon_data * beacon)346573da7d5bSSimon Wunderlich cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
346673da7d5bSSimon Wunderlich {
346773da7d5bSSimon Wunderlich 	struct cfg80211_beacon_data *new_beacon;
346873da7d5bSSimon Wunderlich 	u8 *pos;
346973da7d5bSSimon Wunderlich 	int len;
347073da7d5bSSimon Wunderlich 
347173da7d5bSSimon Wunderlich 	len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
347273da7d5bSSimon Wunderlich 	      beacon->proberesp_ies_len + beacon->assocresp_ies_len +
3473bd54f3c2SAloka Dixit 	      beacon->probe_resp_len + beacon->lci_len + beacon->civicloc_len;
3474bd54f3c2SAloka Dixit 
3475bd54f3c2SAloka Dixit 	if (beacon->mbssid_ies)
3476bd54f3c2SAloka Dixit 		len += ieee80211_get_mbssid_beacon_len(beacon->mbssid_ies,
347768b9bea2SAloka Dixit 						       beacon->rnr_ies,
3478bd54f3c2SAloka Dixit 						       beacon->mbssid_ies->cnt);
347973da7d5bSSimon Wunderlich 
348073da7d5bSSimon Wunderlich 	new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
348173da7d5bSSimon Wunderlich 	if (!new_beacon)
348273da7d5bSSimon Wunderlich 		return NULL;
348373da7d5bSSimon Wunderlich 
34842b3171c6SLorenzo Bianconi 	if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
34852b3171c6SLorenzo Bianconi 		new_beacon->mbssid_ies =
34862b3171c6SLorenzo Bianconi 			kzalloc(struct_size(new_beacon->mbssid_ies,
34872b3171c6SLorenzo Bianconi 					    elem, beacon->mbssid_ies->cnt),
34882b3171c6SLorenzo Bianconi 				GFP_KERNEL);
34892b3171c6SLorenzo Bianconi 		if (!new_beacon->mbssid_ies) {
34902b3171c6SLorenzo Bianconi 			kfree(new_beacon);
34912b3171c6SLorenzo Bianconi 			return NULL;
34922b3171c6SLorenzo Bianconi 		}
349368b9bea2SAloka Dixit 
349468b9bea2SAloka Dixit 		if (beacon->rnr_ies && beacon->rnr_ies->cnt) {
349568b9bea2SAloka Dixit 			new_beacon->rnr_ies =
349668b9bea2SAloka Dixit 				kzalloc(struct_size(new_beacon->rnr_ies,
349768b9bea2SAloka Dixit 						    elem, beacon->rnr_ies->cnt),
349868b9bea2SAloka Dixit 					GFP_KERNEL);
349968b9bea2SAloka Dixit 			if (!new_beacon->rnr_ies) {
350068b9bea2SAloka Dixit 				kfree(new_beacon->mbssid_ies);
350168b9bea2SAloka Dixit 				kfree(new_beacon);
350268b9bea2SAloka Dixit 				return NULL;
350368b9bea2SAloka Dixit 			}
350468b9bea2SAloka Dixit 		}
35052b3171c6SLorenzo Bianconi 	}
35062b3171c6SLorenzo Bianconi 
350773da7d5bSSimon Wunderlich 	pos = (u8 *)(new_beacon + 1);
350873da7d5bSSimon Wunderlich 	if (beacon->head_len) {
350973da7d5bSSimon Wunderlich 		new_beacon->head_len = beacon->head_len;
351073da7d5bSSimon Wunderlich 		new_beacon->head = pos;
351173da7d5bSSimon Wunderlich 		memcpy(pos, beacon->head, beacon->head_len);
351273da7d5bSSimon Wunderlich 		pos += beacon->head_len;
351373da7d5bSSimon Wunderlich 	}
351473da7d5bSSimon Wunderlich 	if (beacon->tail_len) {
351573da7d5bSSimon Wunderlich 		new_beacon->tail_len = beacon->tail_len;
351673da7d5bSSimon Wunderlich 		new_beacon->tail = pos;
351773da7d5bSSimon Wunderlich 		memcpy(pos, beacon->tail, beacon->tail_len);
351873da7d5bSSimon Wunderlich 		pos += beacon->tail_len;
351973da7d5bSSimon Wunderlich 	}
352073da7d5bSSimon Wunderlich 	if (beacon->beacon_ies_len) {
352173da7d5bSSimon Wunderlich 		new_beacon->beacon_ies_len = beacon->beacon_ies_len;
352273da7d5bSSimon Wunderlich 		new_beacon->beacon_ies = pos;
352373da7d5bSSimon Wunderlich 		memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
352473da7d5bSSimon Wunderlich 		pos += beacon->beacon_ies_len;
352573da7d5bSSimon Wunderlich 	}
352673da7d5bSSimon Wunderlich 	if (beacon->proberesp_ies_len) {
352773da7d5bSSimon Wunderlich 		new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
352873da7d5bSSimon Wunderlich 		new_beacon->proberesp_ies = pos;
352973da7d5bSSimon Wunderlich 		memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
353073da7d5bSSimon Wunderlich 		pos += beacon->proberesp_ies_len;
353173da7d5bSSimon Wunderlich 	}
353273da7d5bSSimon Wunderlich 	if (beacon->assocresp_ies_len) {
353373da7d5bSSimon Wunderlich 		new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
353473da7d5bSSimon Wunderlich 		new_beacon->assocresp_ies = pos;
353573da7d5bSSimon Wunderlich 		memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
353673da7d5bSSimon Wunderlich 		pos += beacon->assocresp_ies_len;
353773da7d5bSSimon Wunderlich 	}
353873da7d5bSSimon Wunderlich 	if (beacon->probe_resp_len) {
353973da7d5bSSimon Wunderlich 		new_beacon->probe_resp_len = beacon->probe_resp_len;
3540bee92d06SArnd Bergmann 		new_beacon->probe_resp = pos;
354173da7d5bSSimon Wunderlich 		memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
354273da7d5bSSimon Wunderlich 		pos += beacon->probe_resp_len;
354373da7d5bSSimon Wunderlich 	}
354468b9bea2SAloka Dixit 	if (beacon->mbssid_ies && beacon->mbssid_ies->cnt) {
35452b3171c6SLorenzo Bianconi 		pos += ieee80211_copy_mbssid_beacon(pos,
35462b3171c6SLorenzo Bianconi 						    new_beacon->mbssid_ies,
35472b3171c6SLorenzo Bianconi 						    beacon->mbssid_ies);
354868b9bea2SAloka Dixit 		if (beacon->rnr_ies && beacon->rnr_ies->cnt)
354968b9bea2SAloka Dixit 			pos += ieee80211_copy_rnr_beacon(pos,
355068b9bea2SAloka Dixit 							 new_beacon->rnr_ies,
355168b9bea2SAloka Dixit 							 beacon->rnr_ies);
355268b9bea2SAloka Dixit 	}
355303b73862SJohannes Berg 
355403b73862SJohannes Berg 	/* might copy -1, meaning no changes requested */
3555bc847970SPradeep Kumar Chitrapu 	new_beacon->ftm_responder = beacon->ftm_responder;
3556bc847970SPradeep Kumar Chitrapu 	if (beacon->lci) {
3557bc847970SPradeep Kumar Chitrapu 		new_beacon->lci_len = beacon->lci_len;
3558bc847970SPradeep Kumar Chitrapu 		new_beacon->lci = pos;
3559bc847970SPradeep Kumar Chitrapu 		memcpy(pos, beacon->lci, beacon->lci_len);
3560bc847970SPradeep Kumar Chitrapu 		pos += beacon->lci_len;
3561bc847970SPradeep Kumar Chitrapu 	}
3562bc847970SPradeep Kumar Chitrapu 	if (beacon->civicloc) {
3563bc847970SPradeep Kumar Chitrapu 		new_beacon->civicloc_len = beacon->civicloc_len;
3564bc847970SPradeep Kumar Chitrapu 		new_beacon->civicloc = pos;
3565bc847970SPradeep Kumar Chitrapu 		memcpy(pos, beacon->civicloc, beacon->civicloc_len);
3566bc847970SPradeep Kumar Chitrapu 		pos += beacon->civicloc_len;
3567bc847970SPradeep Kumar Chitrapu 	}
356873da7d5bSSimon Wunderlich 
356973da7d5bSSimon Wunderlich 	return new_beacon;
357073da7d5bSSimon Wunderlich }
357173da7d5bSSimon Wunderlich 
ieee80211_csa_finish(struct ieee80211_vif * vif)357266e01cf9SLuciano Coelho void ieee80211_csa_finish(struct ieee80211_vif *vif)
357373da7d5bSSimon Wunderlich {
357466e01cf9SLuciano Coelho 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
3575c9eb90a5SJohn Crispin 	struct ieee80211_local *local = sdata->local;
357666e01cf9SLuciano Coelho 
3577c9eb90a5SJohn Crispin 	rcu_read_lock();
3578c9eb90a5SJohn Crispin 
3579c9eb90a5SJohn Crispin 	if (vif->mbssid_tx_vif == vif) {
3580c9eb90a5SJohn Crispin 		/* Trigger ieee80211_csa_finish() on the non-transmitting
3581c9eb90a5SJohn Crispin 		 * interfaces when channel switch is received on
3582c9eb90a5SJohn Crispin 		 * transmitting interface
3583c9eb90a5SJohn Crispin 		 */
3584c9eb90a5SJohn Crispin 		struct ieee80211_sub_if_data *iter;
3585c9eb90a5SJohn Crispin 
3586c9eb90a5SJohn Crispin 		list_for_each_entry_rcu(iter, &local->interfaces, list) {
3587c9eb90a5SJohn Crispin 			if (!ieee80211_sdata_running(iter))
3588c9eb90a5SJohn Crispin 				continue;
3589c9eb90a5SJohn Crispin 
3590c9eb90a5SJohn Crispin 			if (iter == sdata || iter->vif.mbssid_tx_vif != vif)
3591c9eb90a5SJohn Crispin 				continue;
3592c9eb90a5SJohn Crispin 
3593c9eb90a5SJohn Crispin 			ieee80211_queue_work(&iter->local->hw,
3594bfd8403aSJohannes Berg 					     &iter->deflink.csa_finalize_work);
3595c9eb90a5SJohn Crispin 		}
3596c9eb90a5SJohn Crispin 	}
3597bfd8403aSJohannes Berg 	ieee80211_queue_work(&local->hw, &sdata->deflink.csa_finalize_work);
3598c9eb90a5SJohn Crispin 
3599c9eb90a5SJohn Crispin 	rcu_read_unlock();
360066e01cf9SLuciano Coelho }
360166e01cf9SLuciano Coelho EXPORT_SYMBOL(ieee80211_csa_finish);
360266e01cf9SLuciano Coelho 
ieee80211_channel_switch_disconnect(struct ieee80211_vif * vif,bool block_tx)36036d501764SNathan Errera void ieee80211_channel_switch_disconnect(struct ieee80211_vif *vif, bool block_tx)
36046d501764SNathan Errera {
36056d501764SNathan Errera 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
36066d501764SNathan Errera 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
36076d501764SNathan Errera 	struct ieee80211_local *local = sdata->local;
36086d501764SNathan Errera 
3609bfd8403aSJohannes Berg 	sdata->deflink.csa_block_tx = block_tx;
36106d501764SNathan Errera 	sdata_info(sdata, "channel switch failed, disconnecting\n");
36114b8d43f1SJohannes Berg 	wiphy_work_queue(local->hw.wiphy, &ifmgd->csa_connection_drop_work);
36126d501764SNathan Errera }
36136d501764SNathan Errera EXPORT_SYMBOL(ieee80211_channel_switch_disconnect);
36146d501764SNathan Errera 
ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data * sdata,u64 * changed)361566199506SMichal Kazior static int ieee80211_set_after_csa_beacon(struct ieee80211_sub_if_data *sdata,
361615ddba5fSAnjaneyulu 					  u64 *changed)
361766199506SMichal Kazior {
361866199506SMichal Kazior 	int err;
361966199506SMichal Kazior 
362066199506SMichal Kazior 	switch (sdata->vif.type) {
362166199506SMichal Kazior 	case NL80211_IFTYPE_AP:
3622bfd8403aSJohannes Berg 		if (!sdata->deflink.u.ap.next_beacon)
3623450c271dSLorenzo Bianconi 			return -EINVAL;
3624450c271dSLorenzo Bianconi 
3625d8675a63SJohannes Berg 		err = ieee80211_assign_beacon(sdata, &sdata->deflink,
3626bfd8403aSJohannes Berg 					      sdata->deflink.u.ap.next_beacon,
362715ddba5fSAnjaneyulu 					      NULL, NULL, changed);
3628d9f83f22SShaul Triebitz 		ieee80211_free_next_beacon(&sdata->deflink);
362966199506SMichal Kazior 
363066199506SMichal Kazior 		if (err < 0)
363166199506SMichal Kazior 			return err;
363266199506SMichal Kazior 		break;
363366199506SMichal Kazior 	case NL80211_IFTYPE_ADHOC:
363415ddba5fSAnjaneyulu 		err = ieee80211_ibss_finish_csa(sdata, changed);
363566199506SMichal Kazior 		if (err < 0)
363666199506SMichal Kazior 			return err;
363766199506SMichal Kazior 		break;
363866199506SMichal Kazior #ifdef CONFIG_MAC80211_MESH
363966199506SMichal Kazior 	case NL80211_IFTYPE_MESH_POINT:
364015ddba5fSAnjaneyulu 		err = ieee80211_mesh_finish_csa(sdata, changed);
364166199506SMichal Kazior 		if (err < 0)
364266199506SMichal Kazior 			return err;
364366199506SMichal Kazior 		break;
364466199506SMichal Kazior #endif
364566199506SMichal Kazior 	default:
364666199506SMichal Kazior 		WARN_ON(1);
364766199506SMichal Kazior 		return -EINVAL;
364866199506SMichal Kazior 	}
364966199506SMichal Kazior 
365066199506SMichal Kazior 	return 0;
365166199506SMichal Kazior }
365266199506SMichal Kazior 
__ieee80211_csa_finalize(struct ieee80211_sub_if_data * sdata)3653cf8767ddSMichal Kazior static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
365466e01cf9SLuciano Coelho {
365573da7d5bSSimon Wunderlich 	struct ieee80211_local *local = sdata->local;
365615ddba5fSAnjaneyulu 	u64 changed = 0;
365766199506SMichal Kazior 	int err;
365873da7d5bSSimon Wunderlich 
3659dbd72850SMichal Kazior 	sdata_assert_lock(sdata);
366059af6928SMichal Kazior 	lockdep_assert_held(&local->mtx);
366103078de4SMichal Kazior 	lockdep_assert_held(&local->chanctx_mtx);
3662dbd72850SMichal Kazior 
366303078de4SMichal Kazior 	/*
366403078de4SMichal Kazior 	 * using reservation isn't immediate as it may be deferred until later
366503078de4SMichal Kazior 	 * with multi-vif. once reservation is complete it will re-schedule the
366603078de4SMichal Kazior 	 * work with no reserved_chanctx so verify chandef to check if it
366703078de4SMichal Kazior 	 * completed successfully
366803078de4SMichal Kazior 	 */
366903078de4SMichal Kazior 
3670bfd8403aSJohannes Berg 	if (sdata->deflink.reserved_chanctx) {
367103078de4SMichal Kazior 		/*
367203078de4SMichal Kazior 		 * with multi-vif csa driver may call ieee80211_csa_finish()
367303078de4SMichal Kazior 		 * many times while waiting for other interfaces to use their
367403078de4SMichal Kazior 		 * reservations
367503078de4SMichal Kazior 		 */
3676bfd8403aSJohannes Berg 		if (sdata->deflink.reserved_ready)
367703078de4SMichal Kazior 			return 0;
367803078de4SMichal Kazior 
3679d8675a63SJohannes Berg 		return ieee80211_link_use_reserved_context(&sdata->deflink);
36807578d575SArik Nemtsov 	}
36817578d575SArik Nemtsov 
368203078de4SMichal Kazior 	if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
3683bfd8403aSJohannes Berg 					&sdata->deflink.csa_chandef))
368403078de4SMichal Kazior 		return -EINVAL;
368503078de4SMichal Kazior 
3686d0a9123eSJohannes Berg 	sdata->vif.bss_conf.csa_active = false;
368797518af1SMichal Kazior 
368866199506SMichal Kazior 	err = ieee80211_set_after_csa_beacon(sdata, &changed);
368966199506SMichal Kazior 	if (err)
3690cf8767ddSMichal Kazior 		return err;
369173da7d5bSSimon Wunderlich 
369292752117SJohannes Berg 	if (sdata->vif.bss_conf.eht_puncturing != sdata->vif.bss_conf.csa_punct_bitmap) {
369392752117SJohannes Berg 		sdata->vif.bss_conf.eht_puncturing =
369492752117SJohannes Berg 					sdata->vif.bss_conf.csa_punct_bitmap;
369592752117SJohannes Berg 		changed |= BSS_CHANGED_EHT_PUNCTURING;
369692752117SJohannes Berg 	}
369792752117SJohannes Berg 
3698d8675a63SJohannes Berg 	ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
369959af6928SMichal Kazior 
3700bfd8403aSJohannes Berg 	if (sdata->deflink.csa_block_tx) {
3701a46992b4SLuciano Coelho 		ieee80211_wake_vif_queues(local, sdata,
3702a46992b4SLuciano Coelho 					  IEEE80211_QUEUE_STOP_REASON_CSA);
3703bfd8403aSJohannes Berg 		sdata->deflink.csa_block_tx = false;
3704a46992b4SLuciano Coelho 	}
3705cf8767ddSMichal Kazior 
3706f1d65583SLuciano Coelho 	err = drv_post_channel_switch(sdata);
3707f1d65583SLuciano Coelho 	if (err)
3708f1d65583SLuciano Coelho 		return err;
3709f1d65583SLuciano Coelho 
3710b345f063SAloka Dixit 	cfg80211_ch_switch_notify(sdata->dev, &sdata->deflink.csa_chandef, 0,
37112cc25e4bSAloka Dixit 				  sdata->vif.bss_conf.eht_puncturing);
3712f1d65583SLuciano Coelho 
3713cf8767ddSMichal Kazior 	return 0;
3714cf8767ddSMichal Kazior }
3715cf8767ddSMichal Kazior 
ieee80211_csa_finalize(struct ieee80211_sub_if_data * sdata)3716cf8767ddSMichal Kazior static void ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
3717cf8767ddSMichal Kazior {
3718cf8767ddSMichal Kazior 	if (__ieee80211_csa_finalize(sdata)) {
3719cf8767ddSMichal Kazior 		sdata_info(sdata, "failed to finalize CSA, disconnecting\n");
3720cf8767ddSMichal Kazior 		cfg80211_stop_iface(sdata->local->hw.wiphy, &sdata->wdev,
3721cf8767ddSMichal Kazior 				    GFP_KERNEL);
3722cf8767ddSMichal Kazior 	}
372366e01cf9SLuciano Coelho }
372466e01cf9SLuciano Coelho 
ieee80211_csa_finalize_work(struct work_struct * work)372566e01cf9SLuciano Coelho void ieee80211_csa_finalize_work(struct work_struct *work)
372666e01cf9SLuciano Coelho {
372766e01cf9SLuciano Coelho 	struct ieee80211_sub_if_data *sdata =
372866e01cf9SLuciano Coelho 		container_of(work, struct ieee80211_sub_if_data,
3729bfd8403aSJohannes Berg 			     deflink.csa_finalize_work);
373059af6928SMichal Kazior 	struct ieee80211_local *local = sdata->local;
373166e01cf9SLuciano Coelho 
373266e01cf9SLuciano Coelho 	sdata_lock(sdata);
373359af6928SMichal Kazior 	mutex_lock(&local->mtx);
373403078de4SMichal Kazior 	mutex_lock(&local->chanctx_mtx);
373559af6928SMichal Kazior 
373666e01cf9SLuciano Coelho 	/* AP might have been stopped while waiting for the lock. */
3737d0a9123eSJohannes Berg 	if (!sdata->vif.bss_conf.csa_active)
373866e01cf9SLuciano Coelho 		goto unlock;
373966e01cf9SLuciano Coelho 
374066e01cf9SLuciano Coelho 	if (!ieee80211_sdata_running(sdata))
374166e01cf9SLuciano Coelho 		goto unlock;
374266e01cf9SLuciano Coelho 
374366e01cf9SLuciano Coelho 	ieee80211_csa_finalize(sdata);
3744e487eaebSSimon Wunderlich 
3745e487eaebSSimon Wunderlich unlock:
374603078de4SMichal Kazior 	mutex_unlock(&local->chanctx_mtx);
374759af6928SMichal Kazior 	mutex_unlock(&local->mtx);
3748e487eaebSSimon Wunderlich 	sdata_unlock(sdata);
374973da7d5bSSimon Wunderlich }
375073da7d5bSSimon Wunderlich 
ieee80211_set_csa_beacon(struct ieee80211_sub_if_data * sdata,struct cfg80211_csa_settings * params,u64 * changed)375137fa2bddSMichal Kazior static int ieee80211_set_csa_beacon(struct ieee80211_sub_if_data *sdata,
375237fa2bddSMichal Kazior 				    struct cfg80211_csa_settings *params,
375315ddba5fSAnjaneyulu 				    u64 *changed)
375437fa2bddSMichal Kazior {
3755af296bdbSMichal Kazior 	struct ieee80211_csa_settings csa = {};
375637fa2bddSMichal Kazior 	int err;
375737fa2bddSMichal Kazior 
375837fa2bddSMichal Kazior 	switch (sdata->vif.type) {
375937fa2bddSMichal Kazior 	case NL80211_IFTYPE_AP:
3760bfd8403aSJohannes Berg 		sdata->deflink.u.ap.next_beacon =
376137fa2bddSMichal Kazior 			cfg80211_beacon_dup(&params->beacon_after);
3762bfd8403aSJohannes Berg 		if (!sdata->deflink.u.ap.next_beacon)
376337fa2bddSMichal Kazior 			return -ENOMEM;
376437fa2bddSMichal Kazior 
376537fa2bddSMichal Kazior 		/*
376637fa2bddSMichal Kazior 		 * With a count of 0, we don't have to wait for any
376737fa2bddSMichal Kazior 		 * TBTT before switching, so complete the CSA
376837fa2bddSMichal Kazior 		 * immediately.  In theory, with a count == 1 we
376937fa2bddSMichal Kazior 		 * should delay the switch until just before the next
377037fa2bddSMichal Kazior 		 * TBTT, but that would complicate things so we switch
377137fa2bddSMichal Kazior 		 * immediately too.  If we would delay the switch
377237fa2bddSMichal Kazior 		 * until the next TBTT, we would have to set the probe
377337fa2bddSMichal Kazior 		 * response here.
377437fa2bddSMichal Kazior 		 *
377537fa2bddSMichal Kazior 		 * TODO: A channel switch with count <= 1 without
377637fa2bddSMichal Kazior 		 * sending a CSA action frame is kind of useless,
377737fa2bddSMichal Kazior 		 * because the clients won't know we're changing
377837fa2bddSMichal Kazior 		 * channels.  The action frame must be implemented
377937fa2bddSMichal Kazior 		 * either here or in the userspace.
378037fa2bddSMichal Kazior 		 */
378137fa2bddSMichal Kazior 		if (params->count <= 1)
378237fa2bddSMichal Kazior 			break;
378337fa2bddSMichal Kazior 
37840d06d9baSAndrei Otcheretianski 		if ((params->n_counter_offsets_beacon >
37858552a434SJohn Crispin 		     IEEE80211_MAX_CNTDWN_COUNTERS_NUM) ||
37860d06d9baSAndrei Otcheretianski 		    (params->n_counter_offsets_presp >
37872b3171c6SLorenzo Bianconi 		     IEEE80211_MAX_CNTDWN_COUNTERS_NUM)) {
3788d9f83f22SShaul Triebitz 			ieee80211_free_next_beacon(&sdata->deflink);
37890d06d9baSAndrei Otcheretianski 			return -EINVAL;
37902b3171c6SLorenzo Bianconi 		}
37919a774c78SAndrei Otcheretianski 
3792af296bdbSMichal Kazior 		csa.counter_offsets_beacon = params->counter_offsets_beacon;
3793af296bdbSMichal Kazior 		csa.counter_offsets_presp = params->counter_offsets_presp;
3794af296bdbSMichal Kazior 		csa.n_counter_offsets_beacon = params->n_counter_offsets_beacon;
3795af296bdbSMichal Kazior 		csa.n_counter_offsets_presp = params->n_counter_offsets_presp;
3796af296bdbSMichal Kazior 		csa.count = params->count;
37970d06d9baSAndrei Otcheretianski 
3798d8675a63SJohannes Berg 		err = ieee80211_assign_beacon(sdata, &sdata->deflink,
3799d8675a63SJohannes Berg 					      &params->beacon_csa, &csa,
380015ddba5fSAnjaneyulu 					      NULL, changed);
380137fa2bddSMichal Kazior 		if (err < 0) {
3802d9f83f22SShaul Triebitz 			ieee80211_free_next_beacon(&sdata->deflink);
380337fa2bddSMichal Kazior 			return err;
380437fa2bddSMichal Kazior 		}
380537fa2bddSMichal Kazior 
380637fa2bddSMichal Kazior 		break;
380737fa2bddSMichal Kazior 	case NL80211_IFTYPE_ADHOC:
3808f276e20bSJohannes Berg 		if (!sdata->vif.cfg.ibss_joined)
380937fa2bddSMichal Kazior 			return -EINVAL;
381037fa2bddSMichal Kazior 
381137fa2bddSMichal Kazior 		if (params->chandef.width != sdata->u.ibss.chandef.width)
381237fa2bddSMichal Kazior 			return -EINVAL;
381337fa2bddSMichal Kazior 
381437fa2bddSMichal Kazior 		switch (params->chandef.width) {
381537fa2bddSMichal Kazior 		case NL80211_CHAN_WIDTH_40:
381637fa2bddSMichal Kazior 			if (cfg80211_get_chandef_type(&params->chandef) !=
381737fa2bddSMichal Kazior 			    cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
381837fa2bddSMichal Kazior 				return -EINVAL;
3819aaaee2d6SGustavo A. R. Silva 			break;
382037fa2bddSMichal Kazior 		case NL80211_CHAN_WIDTH_5:
382137fa2bddSMichal Kazior 		case NL80211_CHAN_WIDTH_10:
382237fa2bddSMichal Kazior 		case NL80211_CHAN_WIDTH_20_NOHT:
382337fa2bddSMichal Kazior 		case NL80211_CHAN_WIDTH_20:
382437fa2bddSMichal Kazior 			break;
382537fa2bddSMichal Kazior 		default:
382637fa2bddSMichal Kazior 			return -EINVAL;
382737fa2bddSMichal Kazior 		}
382837fa2bddSMichal Kazior 
382937fa2bddSMichal Kazior 		/* changes into another band are not supported */
383037fa2bddSMichal Kazior 		if (sdata->u.ibss.chandef.chan->band !=
383137fa2bddSMichal Kazior 		    params->chandef.chan->band)
383237fa2bddSMichal Kazior 			return -EINVAL;
383337fa2bddSMichal Kazior 
383437fa2bddSMichal Kazior 		/* see comments in the NL80211_IFTYPE_AP block */
383537fa2bddSMichal Kazior 		if (params->count > 1) {
383615ddba5fSAnjaneyulu 			err = ieee80211_ibss_csa_beacon(sdata, params, changed);
383737fa2bddSMichal Kazior 			if (err < 0)
383837fa2bddSMichal Kazior 				return err;
383937fa2bddSMichal Kazior 		}
384037fa2bddSMichal Kazior 
384137fa2bddSMichal Kazior 		ieee80211_send_action_csa(sdata, params);
384237fa2bddSMichal Kazior 
384337fa2bddSMichal Kazior 		break;
384437fa2bddSMichal Kazior #ifdef CONFIG_MAC80211_MESH
384537fa2bddSMichal Kazior 	case NL80211_IFTYPE_MESH_POINT: {
384637fa2bddSMichal Kazior 		struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
384737fa2bddSMichal Kazior 
384837fa2bddSMichal Kazior 		/* changes into another band are not supported */
384937fa2bddSMichal Kazior 		if (sdata->vif.bss_conf.chandef.chan->band !=
385037fa2bddSMichal Kazior 		    params->chandef.chan->band)
385137fa2bddSMichal Kazior 			return -EINVAL;
385237fa2bddSMichal Kazior 
385337fa2bddSMichal Kazior 		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_NONE) {
385437fa2bddSMichal Kazior 			ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_INIT;
385537fa2bddSMichal Kazior 			if (!ifmsh->pre_value)
385637fa2bddSMichal Kazior 				ifmsh->pre_value = 1;
385737fa2bddSMichal Kazior 			else
385837fa2bddSMichal Kazior 				ifmsh->pre_value++;
385937fa2bddSMichal Kazior 		}
386037fa2bddSMichal Kazior 
386137fa2bddSMichal Kazior 		/* see comments in the NL80211_IFTYPE_AP block */
386237fa2bddSMichal Kazior 		if (params->count > 1) {
386315ddba5fSAnjaneyulu 			err = ieee80211_mesh_csa_beacon(sdata, params, changed);
386437fa2bddSMichal Kazior 			if (err < 0) {
386537fa2bddSMichal Kazior 				ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE;
386637fa2bddSMichal Kazior 				return err;
386737fa2bddSMichal Kazior 			}
386837fa2bddSMichal Kazior 		}
386937fa2bddSMichal Kazior 
387037fa2bddSMichal Kazior 		if (ifmsh->csa_role == IEEE80211_MESH_CSA_ROLE_INIT)
387137fa2bddSMichal Kazior 			ieee80211_send_action_csa(sdata, params);
387237fa2bddSMichal Kazior 
387337fa2bddSMichal Kazior 		break;
387437fa2bddSMichal Kazior 		}
387537fa2bddSMichal Kazior #endif
387637fa2bddSMichal Kazior 	default:
387737fa2bddSMichal Kazior 		return -EOPNOTSUPP;
387837fa2bddSMichal Kazior 	}
387937fa2bddSMichal Kazior 
388037fa2bddSMichal Kazior 	return 0;
388137fa2bddSMichal Kazior }
388237fa2bddSMichal Kazior 
ieee80211_color_change_abort(struct ieee80211_sub_if_data * sdata)38835f9404abSJohn Crispin static void ieee80211_color_change_abort(struct ieee80211_sub_if_data  *sdata)
38845f9404abSJohn Crispin {
3885d0a9123eSJohannes Berg 	sdata->vif.bss_conf.color_change_active = false;
38860baef284SJohannes Berg 
3887d9f83f22SShaul Triebitz 	ieee80211_free_next_beacon(&sdata->deflink);
38885f9404abSJohn Crispin 
38895f9404abSJohn Crispin 	cfg80211_color_change_aborted_notify(sdata->dev);
38905f9404abSJohn Crispin }
38915f9404abSJohn Crispin 
3892f29f58a9SLuciano Coelho static int
__ieee80211_channel_switch(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_csa_settings * params)3893f29f58a9SLuciano Coelho __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
389473da7d5bSSimon Wunderlich 			   struct cfg80211_csa_settings *params)
389573da7d5bSSimon Wunderlich {
389673da7d5bSSimon Wunderlich 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
389773da7d5bSSimon Wunderlich 	struct ieee80211_local *local = sdata->local;
38986d027bccSLuciano Coelho 	struct ieee80211_channel_switch ch_switch;
38992b32713dSMichal Kazior 	struct ieee80211_chanctx_conf *conf;
390073da7d5bSSimon Wunderlich 	struct ieee80211_chanctx *chanctx;
390115ddba5fSAnjaneyulu 	u64 changed = 0;
3902b08cc24eSJohannes Berg 	int err;
390373da7d5bSSimon Wunderlich 
3904dbd72850SMichal Kazior 	sdata_assert_lock(sdata);
390559af6928SMichal Kazior 	lockdep_assert_held(&local->mtx);
39067ca133bcSSimon Wunderlich 
390773da7d5bSSimon Wunderlich 	if (!list_empty(&local->roc_list) || local->scanning)
390873da7d5bSSimon Wunderlich 		return -EBUSY;
390973da7d5bSSimon Wunderlich 
391073da7d5bSSimon Wunderlich 	if (sdata->wdev.cac_started)
391173da7d5bSSimon Wunderlich 		return -EBUSY;
391273da7d5bSSimon Wunderlich 
391373da7d5bSSimon Wunderlich 	if (cfg80211_chandef_identical(&params->chandef,
391473da7d5bSSimon Wunderlich 				       &sdata->vif.bss_conf.chandef))
391573da7d5bSSimon Wunderlich 		return -EINVAL;
391673da7d5bSSimon Wunderlich 
391773da7d5bSSimon Wunderlich 	/* don't allow another channel switch if one is already active. */
3918d0a9123eSJohannes Berg 	if (sdata->vif.bss_conf.csa_active)
391973da7d5bSSimon Wunderlich 		return -EBUSY;
392073da7d5bSSimon Wunderlich 
392103078de4SMichal Kazior 	mutex_lock(&local->chanctx_mtx);
3922d0a9123eSJohannes Berg 	conf = rcu_dereference_protected(sdata->vif.bss_conf.chanctx_conf,
392303078de4SMichal Kazior 					 lockdep_is_held(&local->chanctx_mtx));
392403078de4SMichal Kazior 	if (!conf) {
392503078de4SMichal Kazior 		err = -EBUSY;
392603078de4SMichal Kazior 		goto out;
392703078de4SMichal Kazior 	}
392873da7d5bSSimon Wunderlich 
3929b6011960SThomas Pedersen 	if (params->chandef.chan->freq_offset) {
3930b6011960SThomas Pedersen 		/* this may work, but is untested */
3931b6011960SThomas Pedersen 		err = -EOPNOTSUPP;
3932b6011960SThomas Pedersen 		goto out;
3933b6011960SThomas Pedersen 	}
3934b6011960SThomas Pedersen 
393503078de4SMichal Kazior 	chanctx = container_of(conf, struct ieee80211_chanctx, conf);
393603078de4SMichal Kazior 
3937000baa5dSLuciano Coelho 	ch_switch.timestamp = 0;
3938000baa5dSLuciano Coelho 	ch_switch.device_timestamp = 0;
3939000baa5dSLuciano Coelho 	ch_switch.block_tx = params->block_tx;
3940000baa5dSLuciano Coelho 	ch_switch.chandef = params->chandef;
3941000baa5dSLuciano Coelho 	ch_switch.count = params->count;
3942000baa5dSLuciano Coelho 
39436d027bccSLuciano Coelho 	err = drv_pre_channel_switch(sdata, &ch_switch);
39446d027bccSLuciano Coelho 	if (err)
39456d027bccSLuciano Coelho 		goto out;
39466d027bccSLuciano Coelho 
3947d8675a63SJohannes Berg 	err = ieee80211_link_reserve_chanctx(&sdata->deflink, &params->chandef,
394803078de4SMichal Kazior 					     chanctx->mode,
394903078de4SMichal Kazior 					     params->radar_required);
395003078de4SMichal Kazior 	if (err)
395103078de4SMichal Kazior 		goto out;
395203078de4SMichal Kazior 
395303078de4SMichal Kazior 	/* if reservation is invalid then this will fail */
395403078de4SMichal Kazior 	err = ieee80211_check_combinations(sdata, NULL, chanctx->mode, 0);
395503078de4SMichal Kazior 	if (err) {
3956d8675a63SJohannes Berg 		ieee80211_link_unreserve_chanctx(&sdata->deflink);
395703078de4SMichal Kazior 		goto out;
395803078de4SMichal Kazior 	}
395903078de4SMichal Kazior 
39605f9404abSJohn Crispin 	/* if there is a color change in progress, abort it */
3961d0a9123eSJohannes Berg 	if (sdata->vif.bss_conf.color_change_active)
39625f9404abSJohn Crispin 		ieee80211_color_change_abort(sdata);
39635f9404abSJohn Crispin 
396403078de4SMichal Kazior 	err = ieee80211_set_csa_beacon(sdata, params, &changed);
396503078de4SMichal Kazior 	if (err) {
3966d8675a63SJohannes Berg 		ieee80211_link_unreserve_chanctx(&sdata->deflink);
396703078de4SMichal Kazior 		goto out;
396803078de4SMichal Kazior 	}
396903078de4SMichal Kazior 
39702cc25e4bSAloka Dixit 	if (params->punct_bitmap && !sdata->vif.bss_conf.eht_support)
39712cc25e4bSAloka Dixit 		goto out;
39722cc25e4bSAloka Dixit 
3973bfd8403aSJohannes Berg 	sdata->deflink.csa_chandef = params->chandef;
3974bfd8403aSJohannes Berg 	sdata->deflink.csa_block_tx = params->block_tx;
3975d0a9123eSJohannes Berg 	sdata->vif.bss_conf.csa_active = true;
39762cc25e4bSAloka Dixit 	sdata->vif.bss_conf.csa_punct_bitmap = params->punct_bitmap;
397773da7d5bSSimon Wunderlich 
3978bfd8403aSJohannes Berg 	if (sdata->deflink.csa_block_tx)
3979a46992b4SLuciano Coelho 		ieee80211_stop_vif_queues(local, sdata,
3980a46992b4SLuciano Coelho 					  IEEE80211_QUEUE_STOP_REASON_CSA);
398173da7d5bSSimon Wunderlich 
3982bfd8403aSJohannes Berg 	cfg80211_ch_switch_started_notify(sdata->dev,
3983b8c9024eSVeerendranath Jakkam 					  &sdata->deflink.csa_chandef, 0,
39842cc25e4bSAloka Dixit 					  params->count, params->block_tx,
39852cc25e4bSAloka Dixit 					  sdata->vif.bss_conf.csa_punct_bitmap);
39862f457293SLuciano Coelho 
398766e01cf9SLuciano Coelho 	if (changed) {
3988d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, &sdata->deflink,
3989d8675a63SJohannes Berg 						  changed);
399073da7d5bSSimon Wunderlich 		drv_channel_switch_beacon(sdata, &params->chandef);
399166e01cf9SLuciano Coelho 	} else {
399266e01cf9SLuciano Coelho 		/* if the beacon didn't change, we can finalize immediately */
399366e01cf9SLuciano Coelho 		ieee80211_csa_finalize(sdata);
399466e01cf9SLuciano Coelho 	}
399573da7d5bSSimon Wunderlich 
399603078de4SMichal Kazior out:
399703078de4SMichal Kazior 	mutex_unlock(&local->chanctx_mtx);
399803078de4SMichal Kazior 	return err;
399973da7d5bSSimon Wunderlich }
400073da7d5bSSimon Wunderlich 
ieee80211_channel_switch(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_csa_settings * params)400159af6928SMichal Kazior int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
400259af6928SMichal Kazior 			     struct cfg80211_csa_settings *params)
400359af6928SMichal Kazior {
400459af6928SMichal Kazior 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
400559af6928SMichal Kazior 	struct ieee80211_local *local = sdata->local;
400659af6928SMichal Kazior 	int err;
400759af6928SMichal Kazior 
400859af6928SMichal Kazior 	mutex_lock(&local->mtx);
400959af6928SMichal Kazior 	err = __ieee80211_channel_switch(wiphy, dev, params);
401059af6928SMichal Kazior 	mutex_unlock(&local->mtx);
401159af6928SMichal Kazior 
401259af6928SMichal Kazior 	return err;
401359af6928SMichal Kazior }
401459af6928SMichal Kazior 
ieee80211_mgmt_tx_cookie(struct ieee80211_local * local)4015a2fcfccbSJohannes Berg u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
4016a2fcfccbSJohannes Berg {
4017a2fcfccbSJohannes Berg 	lockdep_assert_held(&local->mtx);
4018a2fcfccbSJohannes Berg 
4019a2fcfccbSJohannes Berg 	local->roc_cookie_counter++;
4020a2fcfccbSJohannes Berg 
4021a2fcfccbSJohannes Berg 	/* wow, you wrapped 64 bits ... more likely a bug */
4022a2fcfccbSJohannes Berg 	if (WARN_ON(local->roc_cookie_counter == 0))
4023a2fcfccbSJohannes Berg 		local->roc_cookie_counter++;
4024a2fcfccbSJohannes Berg 
4025a2fcfccbSJohannes Berg 	return local->roc_cookie_counter;
4026a2fcfccbSJohannes Berg }
4027a2fcfccbSJohannes Berg 
ieee80211_attach_ack_skb(struct ieee80211_local * local,struct sk_buff * skb,u64 * cookie,gfp_t gfp)40285ee00dbdSJohannes Berg int ieee80211_attach_ack_skb(struct ieee80211_local *local, struct sk_buff *skb,
40295ee00dbdSJohannes Berg 			     u64 *cookie, gfp_t gfp)
40303b79af97SJohannes Berg {
40313b79af97SJohannes Berg 	unsigned long spin_flags;
40323b79af97SJohannes Berg 	struct sk_buff *ack_skb;
40333b79af97SJohannes Berg 	int id;
40343b79af97SJohannes Berg 
40353b79af97SJohannes Berg 	ack_skb = skb_copy(skb, gfp);
40363b79af97SJohannes Berg 	if (!ack_skb)
40375ee00dbdSJohannes Berg 		return -ENOMEM;
40383b79af97SJohannes Berg 
40393b79af97SJohannes Berg 	spin_lock_irqsave(&local->ack_status_lock, spin_flags);
40403b79af97SJohannes Berg 	id = idr_alloc(&local->ack_status_frames, ack_skb,
4041f2b18bacSJohannes Berg 		       1, 0x2000, GFP_ATOMIC);
40423b79af97SJohannes Berg 	spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
40433b79af97SJohannes Berg 
40443b79af97SJohannes Berg 	if (id < 0) {
40453b79af97SJohannes Berg 		kfree_skb(ack_skb);
40465ee00dbdSJohannes Berg 		return -ENOMEM;
40473b79af97SJohannes Berg 	}
40483b79af97SJohannes Berg 
40493b79af97SJohannes Berg 	IEEE80211_SKB_CB(skb)->ack_frame_id = id;
40503b79af97SJohannes Berg 
40513b79af97SJohannes Berg 	*cookie = ieee80211_mgmt_tx_cookie(local);
40523b79af97SJohannes Berg 	IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
40533b79af97SJohannes Berg 
40545ee00dbdSJohannes Berg 	return 0;
40553b79af97SJohannes Berg }
40563b79af97SJohannes Berg 
40576cd536feSJohannes Berg static void
ieee80211_update_mgmt_frame_registrations(struct wiphy * wiphy,struct wireless_dev * wdev,struct mgmt_frame_regs * upd)40586cd536feSJohannes Berg ieee80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
405971bbc994SJohannes Berg 					  struct wireless_dev *wdev,
40606cd536feSJohannes Berg 					  struct mgmt_frame_regs *upd)
40617be5086dSJohannes Berg {
40627be5086dSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
40631b09b556SAndrei Otcheretianski 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
40646cd536feSJohannes Berg 	u32 preq_mask = BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
4065873b1cf6SJouni Malinen 	u32 action_mask = BIT(IEEE80211_STYPE_ACTION >> 4);
40666cd536feSJohannes Berg 	bool global_change, intf_change;
40677be5086dSJohannes Berg 
40686cd536feSJohannes Berg 	global_change =
4069873b1cf6SJouni Malinen 		(local->probe_req_reg != !!(upd->global_stypes & preq_mask)) ||
4070873b1cf6SJouni Malinen 		(local->rx_mcast_action_reg !=
4071873b1cf6SJouni Malinen 		 !!(upd->global_mcast_stypes & action_mask));
40726cd536feSJohannes Berg 	local->probe_req_reg = upd->global_stypes & preq_mask;
4073873b1cf6SJouni Malinen 	local->rx_mcast_action_reg = upd->global_mcast_stypes & action_mask;
40747be5086dSJohannes Berg 
4075873b1cf6SJouni Malinen 	intf_change = (sdata->vif.probe_req_reg !=
4076873b1cf6SJouni Malinen 		       !!(upd->interface_stypes & preq_mask)) ||
4077873b1cf6SJouni Malinen 		(sdata->vif.rx_mcast_action_reg !=
4078873b1cf6SJouni Malinen 		 !!(upd->interface_mcast_stypes & action_mask));
40796cd536feSJohannes Berg 	sdata->vif.probe_req_reg = upd->interface_stypes & preq_mask;
4080873b1cf6SJouni Malinen 	sdata->vif.rx_mcast_action_reg =
4081873b1cf6SJouni Malinen 		upd->interface_mcast_stypes & action_mask;
40821b09b556SAndrei Otcheretianski 
408335f5149eSFelix Fietkau 	if (!local->open_count)
40846cd536feSJohannes Berg 		return;
408535f5149eSFelix Fietkau 
40866cd536feSJohannes Berg 	if (intf_change && ieee80211_sdata_running(sdata))
408790e8f58dSJohannes Berg 		drv_config_iface_filter(local, sdata,
40886cd536feSJohannes Berg 					sdata->vif.probe_req_reg ?
40896cd536feSJohannes Berg 						FIF_PROBE_REQ : 0,
40901b09b556SAndrei Otcheretianski 					FIF_PROBE_REQ);
40911b09b556SAndrei Otcheretianski 
40926cd536feSJohannes Berg 	if (global_change)
40931b09b556SAndrei Otcheretianski 		ieee80211_configure_filter(local);
40947be5086dSJohannes Berg }
40957be5086dSJohannes Berg 
ieee80211_set_antenna(struct wiphy * wiphy,u32 tx_ant,u32 rx_ant)409615d96753SBruno Randolf static int ieee80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant)
409715d96753SBruno Randolf {
409815d96753SBruno Randolf 	struct ieee80211_local *local = wiphy_priv(wiphy);
409915d96753SBruno Randolf 
410015d96753SBruno Randolf 	if (local->started)
410115d96753SBruno Randolf 		return -EOPNOTSUPP;
410215d96753SBruno Randolf 
410315d96753SBruno Randolf 	return drv_set_antenna(local, tx_ant, rx_ant);
410415d96753SBruno Randolf }
410515d96753SBruno Randolf 
ieee80211_get_antenna(struct wiphy * wiphy,u32 * tx_ant,u32 * rx_ant)410615d96753SBruno Randolf static int ieee80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant)
410715d96753SBruno Randolf {
410815d96753SBruno Randolf 	struct ieee80211_local *local = wiphy_priv(wiphy);
410915d96753SBruno Randolf 
411015d96753SBruno Randolf 	return drv_get_antenna(local, tx_ant, rx_ant);
411115d96753SBruno Randolf }
411215d96753SBruno Randolf 
ieee80211_set_rekey_data(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_gtk_rekey_data * data)4113c68f4b89SJohannes Berg static int ieee80211_set_rekey_data(struct wiphy *wiphy,
4114c68f4b89SJohannes Berg 				    struct net_device *dev,
4115c68f4b89SJohannes Berg 				    struct cfg80211_gtk_rekey_data *data)
4116c68f4b89SJohannes Berg {
4117c68f4b89SJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
4118c68f4b89SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
4119c68f4b89SJohannes Berg 
4120c68f4b89SJohannes Berg 	if (!local->ops->set_rekey_data)
4121c68f4b89SJohannes Berg 		return -EOPNOTSUPP;
4122c68f4b89SJohannes Berg 
4123c68f4b89SJohannes Berg 	drv_set_rekey_data(local, sdata, data);
4124c68f4b89SJohannes Berg 
4125c68f4b89SJohannes Berg 	return 0;
4126c68f4b89SJohannes Berg }
4127c68f4b89SJohannes Berg 
ieee80211_probe_client(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,u64 * cookie)412806500736SJohannes Berg static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
412906500736SJohannes Berg 				  const u8 *peer, u64 *cookie)
413006500736SJohannes Berg {
413106500736SJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
413206500736SJohannes Berg 	struct ieee80211_local *local = sdata->local;
413306500736SJohannes Berg 	struct ieee80211_qos_hdr *nullfunc;
41345ee00dbdSJohannes Berg 	struct sk_buff *skb;
413506500736SJohannes Berg 	int size = sizeof(*nullfunc);
413606500736SJohannes Berg 	__le16 fc;
413706500736SJohannes Berg 	bool qos;
413806500736SJohannes Berg 	struct ieee80211_tx_info *info;
413906500736SJohannes Berg 	struct sta_info *sta;
414055de908aSJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
414157fbcce3SJohannes Berg 	enum nl80211_band band;
41423b79af97SJohannes Berg 	int ret;
41433b79af97SJohannes Berg 
41443b79af97SJohannes Berg 	/* the lock is needed to assign the cookie later */
41453b79af97SJohannes Berg 	mutex_lock(&local->mtx);
414606500736SJohannes Berg 
414706500736SJohannes Berg 	rcu_read_lock();
414867dfa589SJohannes Berg 	sta = sta_info_get_bss(sdata, peer);
414967dfa589SJohannes Berg 	if (!sta) {
415067dfa589SJohannes Berg 		ret = -ENOLINK;
415167dfa589SJohannes Berg 		goto unlock;
415267dfa589SJohannes Berg 	}
415367dfa589SJohannes Berg 
415467dfa589SJohannes Berg 	qos = sta->sta.wme;
415567dfa589SJohannes Berg 
4156d0a9123eSJohannes Berg 	chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
415755de908aSJohannes Berg 	if (WARN_ON(!chanctx_conf)) {
41583b79af97SJohannes Berg 		ret = -EINVAL;
41593b79af97SJohannes Berg 		goto unlock;
416055de908aSJohannes Berg 	}
41614bf88530SJohannes Berg 	band = chanctx_conf->def.chan->band;
416206500736SJohannes Berg 
416306500736SJohannes Berg 	if (qos) {
416406500736SJohannes Berg 		fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
416506500736SJohannes Berg 				 IEEE80211_STYPE_QOS_NULLFUNC |
416606500736SJohannes Berg 				 IEEE80211_FCTL_FROMDS);
416706500736SJohannes Berg 	} else {
416806500736SJohannes Berg 		size -= 2;
416906500736SJohannes Berg 		fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
417006500736SJohannes Berg 				 IEEE80211_STYPE_NULLFUNC |
417106500736SJohannes Berg 				 IEEE80211_FCTL_FROMDS);
417206500736SJohannes Berg 	}
417306500736SJohannes Berg 
417406500736SJohannes Berg 	skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
417555de908aSJohannes Berg 	if (!skb) {
41763b79af97SJohannes Berg 		ret = -ENOMEM;
41773b79af97SJohannes Berg 		goto unlock;
417855de908aSJohannes Berg 	}
417906500736SJohannes Berg 
418006500736SJohannes Berg 	skb->dev = dev;
418106500736SJohannes Berg 
418206500736SJohannes Berg 	skb_reserve(skb, local->hw.extra_tx_headroom);
418306500736SJohannes Berg 
41844df864c1SJohannes Berg 	nullfunc = skb_put(skb, size);
418506500736SJohannes Berg 	nullfunc->frame_control = fc;
418606500736SJohannes Berg 	nullfunc->duration_id = 0;
418706500736SJohannes Berg 	memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN);
418806500736SJohannes Berg 	memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
418906500736SJohannes Berg 	memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN);
419006500736SJohannes Berg 	nullfunc->seq_ctrl = 0;
419106500736SJohannes Berg 
419206500736SJohannes Berg 	info = IEEE80211_SKB_CB(skb);
419306500736SJohannes Berg 
419406500736SJohannes Berg 	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
419506500736SJohannes Berg 		       IEEE80211_TX_INTFL_NL80211_FRAME_TX;
419673c4e195SJohannes Berg 	info->band = band;
419706500736SJohannes Berg 
419806500736SJohannes Berg 	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
419906500736SJohannes Berg 	skb->priority = 7;
420006500736SJohannes Berg 	if (qos)
420106500736SJohannes Berg 		nullfunc->qos_ctrl = cpu_to_le16(7);
420206500736SJohannes Berg 
42035ee00dbdSJohannes Berg 	ret = ieee80211_attach_ack_skb(local, skb, cookie, GFP_ATOMIC);
42045ee00dbdSJohannes Berg 	if (ret) {
42053b79af97SJohannes Berg 		kfree_skb(skb);
42063b79af97SJohannes Berg 		goto unlock;
42073b79af97SJohannes Berg 	}
42083b79af97SJohannes Berg 
420906500736SJohannes Berg 	local_bh_disable();
421008aca29aSMathy Vanhoef 	ieee80211_xmit(sdata, sta, skb);
421106500736SJohannes Berg 	local_bh_enable();
421206500736SJohannes Berg 
42133b79af97SJohannes Berg 	ret = 0;
42143b79af97SJohannes Berg unlock:
42153b79af97SJohannes Berg 	rcu_read_unlock();
42163b79af97SJohannes Berg 	mutex_unlock(&local->mtx);
42173b79af97SJohannes Berg 
42183b79af97SJohannes Berg 	return ret;
421906500736SJohannes Berg }
422006500736SJohannes Berg 
ieee80211_cfg_get_channel(struct wiphy * wiphy,struct wireless_dev * wdev,unsigned int link_id,struct cfg80211_chan_def * chandef)4221683b6d3bSJohannes Berg static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
4222683b6d3bSJohannes Berg 				     struct wireless_dev *wdev,
42237b0a0e3cSJohannes Berg 				     unsigned int link_id,
4224683b6d3bSJohannes Berg 				     struct cfg80211_chan_def *chandef)
42255b7ccaf3SJohannes Berg {
422655de908aSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
4227cb601ffaSFelix Fietkau 	struct ieee80211_local *local = wiphy_priv(wiphy);
422855de908aSJohannes Berg 	struct ieee80211_chanctx_conf *chanctx_conf;
4229d8675a63SJohannes Berg 	struct ieee80211_link_data *link;
4230683b6d3bSJohannes Berg 	int ret = -ENODATA;
42315b7ccaf3SJohannes Berg 
423255de908aSJohannes Berg 	rcu_read_lock();
4233d8675a63SJohannes Berg 	link = rcu_dereference(sdata->link[link_id]);
4234d8675a63SJohannes Berg 	if (!link) {
4235d8675a63SJohannes Berg 		ret = -ENOLINK;
4236d8675a63SJohannes Berg 		goto out;
4237d8675a63SJohannes Berg 	}
4238d8675a63SJohannes Berg 
4239d8675a63SJohannes Berg 	chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
424055de908aSJohannes Berg 	if (chanctx_conf) {
4241d8675a63SJohannes Berg 		*chandef = link->conf->chandef;
4242683b6d3bSJohannes Berg 		ret = 0;
4243feda3027SJohannes Berg 	} else if (local->open_count > 0 &&
4244feda3027SJohannes Berg 		   local->open_count == local->monitors &&
4245feda3027SJohannes Berg 		   sdata->vif.type == NL80211_IFTYPE_MONITOR) {
4246feda3027SJohannes Berg 		if (local->use_chanctx)
4247cb601ffaSFelix Fietkau 			*chandef = local->monitor_chandef;
4248feda3027SJohannes Berg 		else
4249675a0b04SKarl Beldan 			*chandef = local->_oper_chandef;
4250cb601ffaSFelix Fietkau 		ret = 0;
4251cb601ffaSFelix Fietkau 	}
4252d8675a63SJohannes Berg out:
425355de908aSJohannes Berg 	rcu_read_unlock();
425455de908aSJohannes Berg 
4255683b6d3bSJohannes Berg 	return ret;
42565b7ccaf3SJohannes Berg }
42575b7ccaf3SJohannes Berg 
42586d52563fSJohannes Berg #ifdef CONFIG_PM
ieee80211_set_wakeup(struct wiphy * wiphy,bool enabled)42596d52563fSJohannes Berg static void ieee80211_set_wakeup(struct wiphy *wiphy, bool enabled)
42606d52563fSJohannes Berg {
42616d52563fSJohannes Berg 	drv_set_wakeup(wiphy_priv(wiphy), enabled);
42626d52563fSJohannes Berg }
42636d52563fSJohannes Berg #endif
42646d52563fSJohannes Berg 
ieee80211_set_qos_map(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_qos_map * qos_map)426532db6b54SKyeyoon Park static int ieee80211_set_qos_map(struct wiphy *wiphy,
426632db6b54SKyeyoon Park 				 struct net_device *dev,
426732db6b54SKyeyoon Park 				 struct cfg80211_qos_map *qos_map)
426832db6b54SKyeyoon Park {
426932db6b54SKyeyoon Park 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
427032db6b54SKyeyoon Park 	struct mac80211_qos_map *new_qos_map, *old_qos_map;
427132db6b54SKyeyoon Park 
427232db6b54SKyeyoon Park 	if (qos_map) {
427332db6b54SKyeyoon Park 		new_qos_map = kzalloc(sizeof(*new_qos_map), GFP_KERNEL);
427432db6b54SKyeyoon Park 		if (!new_qos_map)
427532db6b54SKyeyoon Park 			return -ENOMEM;
427632db6b54SKyeyoon Park 		memcpy(&new_qos_map->qos_map, qos_map, sizeof(*qos_map));
427732db6b54SKyeyoon Park 	} else {
427832db6b54SKyeyoon Park 		/* A NULL qos_map was passed to disable QoS mapping */
427932db6b54SKyeyoon Park 		new_qos_map = NULL;
428032db6b54SKyeyoon Park 	}
428132db6b54SKyeyoon Park 
4282194ff52dSJohannes Berg 	old_qos_map = sdata_dereference(sdata->qos_map, sdata);
428332db6b54SKyeyoon Park 	rcu_assign_pointer(sdata->qos_map, new_qos_map);
428432db6b54SKyeyoon Park 	if (old_qos_map)
428532db6b54SKyeyoon Park 		kfree_rcu(old_qos_map, rcu_head);
428632db6b54SKyeyoon Park 
428732db6b54SKyeyoon Park 	return 0;
428832db6b54SKyeyoon Park }
428932db6b54SKyeyoon Park 
ieee80211_set_ap_chanwidth(struct wiphy * wiphy,struct net_device * dev,unsigned int link_id,struct cfg80211_chan_def * chandef)42903b1700bdSJouni Malinen static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
42913b1700bdSJouni Malinen 				      struct net_device *dev,
42927b0a0e3cSJohannes Berg 				      unsigned int link_id,
42933b1700bdSJouni Malinen 				      struct cfg80211_chan_def *chandef)
42943b1700bdSJouni Malinen {
42953b1700bdSJouni Malinen 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
4296d8675a63SJohannes Berg 	struct ieee80211_link_data *link;
42973b1700bdSJouni Malinen 	int ret;
4298aa87cd8bSJohannes Berg 	u64 changed = 0;
42993b1700bdSJouni Malinen 
4300d8675a63SJohannes Berg 	link = sdata_dereference(sdata->link[link_id], sdata);
4301d8675a63SJohannes Berg 
4302d8675a63SJohannes Berg 	ret = ieee80211_link_change_bandwidth(link, chandef, &changed);
43033b1700bdSJouni Malinen 	if (ret == 0)
4304d8675a63SJohannes Berg 		ieee80211_link_info_change_notify(sdata, link, changed);
43053b1700bdSJouni Malinen 
43063b1700bdSJouni Malinen 	return ret;
43073b1700bdSJouni Malinen }
43083b1700bdSJouni Malinen 
ieee80211_add_tx_ts(struct wiphy * wiphy,struct net_device * dev,u8 tsid,const u8 * peer,u8 up,u16 admitted_time)430902219b3aSJohannes Berg static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev,
431002219b3aSJohannes Berg 			       u8 tsid, const u8 *peer, u8 up,
431102219b3aSJohannes Berg 			       u16 admitted_time)
431202219b3aSJohannes Berg {
431302219b3aSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
431402219b3aSJohannes Berg 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
431502219b3aSJohannes Berg 	int ac = ieee802_1d_to_ac[up];
431602219b3aSJohannes Berg 
431702219b3aSJohannes Berg 	if (sdata->vif.type != NL80211_IFTYPE_STATION)
431802219b3aSJohannes Berg 		return -EOPNOTSUPP;
431902219b3aSJohannes Berg 
432002219b3aSJohannes Berg 	if (!(sdata->wmm_acm & BIT(up)))
432102219b3aSJohannes Berg 		return -EINVAL;
432202219b3aSJohannes Berg 
432302219b3aSJohannes Berg 	if (ifmgd->tx_tspec[ac].admitted_time)
432402219b3aSJohannes Berg 		return -EBUSY;
432502219b3aSJohannes Berg 
432602219b3aSJohannes Berg 	if (admitted_time) {
432702219b3aSJohannes Berg 		ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time;
432802219b3aSJohannes Berg 		ifmgd->tx_tspec[ac].tsid = tsid;
432902219b3aSJohannes Berg 		ifmgd->tx_tspec[ac].up = up;
433002219b3aSJohannes Berg 	}
433102219b3aSJohannes Berg 
433202219b3aSJohannes Berg 	return 0;
433302219b3aSJohannes Berg }
433402219b3aSJohannes Berg 
ieee80211_del_tx_ts(struct wiphy * wiphy,struct net_device * dev,u8 tsid,const u8 * peer)433502219b3aSJohannes Berg static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
433602219b3aSJohannes Berg 			       u8 tsid, const u8 *peer)
433702219b3aSJohannes Berg {
433802219b3aSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
433902219b3aSJohannes Berg 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
434002219b3aSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
434102219b3aSJohannes Berg 	int ac;
434202219b3aSJohannes Berg 
434302219b3aSJohannes Berg 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
434402219b3aSJohannes Berg 		struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
434502219b3aSJohannes Berg 
434602219b3aSJohannes Berg 		/* skip unused entries */
434702219b3aSJohannes Berg 		if (!tx_tspec->admitted_time)
434802219b3aSJohannes Berg 			continue;
434902219b3aSJohannes Berg 
435002219b3aSJohannes Berg 		if (tx_tspec->tsid != tsid)
435102219b3aSJohannes Berg 			continue;
435202219b3aSJohannes Berg 
435302219b3aSJohannes Berg 		/* due to this new packets will be reassigned to non-ACM ACs */
435402219b3aSJohannes Berg 		tx_tspec->up = -1;
435502219b3aSJohannes Berg 
435602219b3aSJohannes Berg 		/* Make sure that all packets have been sent to avoid to
435702219b3aSJohannes Berg 		 * restore the QoS params on packets that are still on the
435802219b3aSJohannes Berg 		 * queues.
435902219b3aSJohannes Berg 		 */
436002219b3aSJohannes Berg 		synchronize_net();
43613b24f4c6SEmmanuel Grumbach 		ieee80211_flush_queues(local, sdata, false);
436202219b3aSJohannes Berg 
436302219b3aSJohannes Berg 		/* restore the normal QoS parameters
436402219b3aSJohannes Berg 		 * (unconditionally to avoid races)
436502219b3aSJohannes Berg 		 */
436602219b3aSJohannes Berg 		tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
436702219b3aSJohannes Berg 		tx_tspec->downgraded = false;
436802219b3aSJohannes Berg 		ieee80211_sta_handle_tspec_ac_params(sdata);
436902219b3aSJohannes Berg 
437002219b3aSJohannes Berg 		/* finally clear all the data */
437102219b3aSJohannes Berg 		memset(tx_tspec, 0, sizeof(*tx_tspec));
437202219b3aSJohannes Berg 
437302219b3aSJohannes Berg 		return 0;
437402219b3aSJohannes Berg 	}
437502219b3aSJohannes Berg 
437602219b3aSJohannes Berg 	return -ENOENT;
437702219b3aSJohannes Berg }
437802219b3aSJohannes Berg 
ieee80211_nan_func_terminated(struct ieee80211_vif * vif,u8 inst_id,enum nl80211_nan_func_term_reason reason,gfp_t gfp)4379167e33f4SAyala Beker void ieee80211_nan_func_terminated(struct ieee80211_vif *vif,
4380167e33f4SAyala Beker 				   u8 inst_id,
4381167e33f4SAyala Beker 				   enum nl80211_nan_func_term_reason reason,
4382167e33f4SAyala Beker 				   gfp_t gfp)
4383167e33f4SAyala Beker {
4384167e33f4SAyala Beker 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
4385167e33f4SAyala Beker 	struct cfg80211_nan_func *func;
4386167e33f4SAyala Beker 	u64 cookie;
4387167e33f4SAyala Beker 
4388167e33f4SAyala Beker 	if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
4389167e33f4SAyala Beker 		return;
4390167e33f4SAyala Beker 
4391167e33f4SAyala Beker 	spin_lock_bh(&sdata->u.nan.func_lock);
4392167e33f4SAyala Beker 
4393167e33f4SAyala Beker 	func = idr_find(&sdata->u.nan.function_inst_ids, inst_id);
4394167e33f4SAyala Beker 	if (WARN_ON(!func)) {
4395167e33f4SAyala Beker 		spin_unlock_bh(&sdata->u.nan.func_lock);
4396167e33f4SAyala Beker 		return;
4397167e33f4SAyala Beker 	}
4398167e33f4SAyala Beker 
4399167e33f4SAyala Beker 	cookie = func->cookie;
4400167e33f4SAyala Beker 	idr_remove(&sdata->u.nan.function_inst_ids, inst_id);
4401167e33f4SAyala Beker 
4402167e33f4SAyala Beker 	spin_unlock_bh(&sdata->u.nan.func_lock);
4403167e33f4SAyala Beker 
4404167e33f4SAyala Beker 	cfg80211_free_nan_func(func);
4405167e33f4SAyala Beker 
4406167e33f4SAyala Beker 	cfg80211_nan_func_terminated(ieee80211_vif_to_wdev(vif), inst_id,
4407167e33f4SAyala Beker 				     reason, cookie, gfp);
4408167e33f4SAyala Beker }
4409167e33f4SAyala Beker EXPORT_SYMBOL(ieee80211_nan_func_terminated);
4410167e33f4SAyala Beker 
ieee80211_nan_func_match(struct ieee80211_vif * vif,struct cfg80211_nan_match_params * match,gfp_t gfp)441192bc43bcSAyala Beker void ieee80211_nan_func_match(struct ieee80211_vif *vif,
441292bc43bcSAyala Beker 			      struct cfg80211_nan_match_params *match,
441392bc43bcSAyala Beker 			      gfp_t gfp)
441492bc43bcSAyala Beker {
441592bc43bcSAyala Beker 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
441692bc43bcSAyala Beker 	struct cfg80211_nan_func *func;
441792bc43bcSAyala Beker 
441892bc43bcSAyala Beker 	if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
441992bc43bcSAyala Beker 		return;
442092bc43bcSAyala Beker 
442192bc43bcSAyala Beker 	spin_lock_bh(&sdata->u.nan.func_lock);
442292bc43bcSAyala Beker 
442392bc43bcSAyala Beker 	func = idr_find(&sdata->u.nan.function_inst_ids,  match->inst_id);
442492bc43bcSAyala Beker 	if (WARN_ON(!func)) {
442592bc43bcSAyala Beker 		spin_unlock_bh(&sdata->u.nan.func_lock);
442692bc43bcSAyala Beker 		return;
442792bc43bcSAyala Beker 	}
442892bc43bcSAyala Beker 	match->cookie = func->cookie;
442992bc43bcSAyala Beker 
443092bc43bcSAyala Beker 	spin_unlock_bh(&sdata->u.nan.func_lock);
443192bc43bcSAyala Beker 
443292bc43bcSAyala Beker 	cfg80211_nan_match(ieee80211_vif_to_wdev(vif), match, gfp);
443392bc43bcSAyala Beker }
443492bc43bcSAyala Beker EXPORT_SYMBOL(ieee80211_nan_func_match);
443592bc43bcSAyala Beker 
ieee80211_set_multicast_to_unicast(struct wiphy * wiphy,struct net_device * dev,const bool enabled)4436ebceec86SMichael Braun static int ieee80211_set_multicast_to_unicast(struct wiphy *wiphy,
4437ebceec86SMichael Braun 					      struct net_device *dev,
4438ebceec86SMichael Braun 					      const bool enabled)
4439ebceec86SMichael Braun {
4440ebceec86SMichael Braun 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
4441ebceec86SMichael Braun 
4442ebceec86SMichael Braun 	sdata->u.ap.multicast_to_unicast = enabled;
4443ebceec86SMichael Braun 
4444ebceec86SMichael Braun 	return 0;
4445ebceec86SMichael Braun }
4446ebceec86SMichael Braun 
ieee80211_fill_txq_stats(struct cfg80211_txq_stats * txqstats,struct txq_info * txqi)44472fe4a29aSToke Høiland-Jørgensen void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats,
44482fe4a29aSToke Høiland-Jørgensen 			      struct txq_info *txqi)
44492fe4a29aSToke Høiland-Jørgensen {
44502fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_BYTES))) {
44512fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_BYTES);
44522fe4a29aSToke Høiland-Jørgensen 		txqstats->backlog_bytes = txqi->tin.backlog_bytes;
44532fe4a29aSToke Høiland-Jørgensen 	}
44542fe4a29aSToke Høiland-Jørgensen 
44552fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS))) {
44562fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS);
44572fe4a29aSToke Høiland-Jørgensen 		txqstats->backlog_packets = txqi->tin.backlog_packets;
44582fe4a29aSToke Høiland-Jørgensen 	}
44592fe4a29aSToke Høiland-Jørgensen 
44602fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_FLOWS))) {
44612fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_FLOWS);
44622fe4a29aSToke Høiland-Jørgensen 		txqstats->flows = txqi->tin.flows;
44632fe4a29aSToke Høiland-Jørgensen 	}
44642fe4a29aSToke Høiland-Jørgensen 
44652fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_DROPS))) {
44662fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_DROPS);
44672fe4a29aSToke Høiland-Jørgensen 		txqstats->drops = txqi->cstats.drop_count;
44682fe4a29aSToke Høiland-Jørgensen 	}
44692fe4a29aSToke Høiland-Jørgensen 
44702fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_ECN_MARKS))) {
44712fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_ECN_MARKS);
44722fe4a29aSToke Høiland-Jørgensen 		txqstats->ecn_marks = txqi->cstats.ecn_mark;
44732fe4a29aSToke Høiland-Jørgensen 	}
44742fe4a29aSToke Høiland-Jørgensen 
44752fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_OVERLIMIT))) {
44762fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_OVERLIMIT);
44772fe4a29aSToke Høiland-Jørgensen 		txqstats->overlimit = txqi->tin.overlimit;
44782fe4a29aSToke Høiland-Jørgensen 	}
44792fe4a29aSToke Høiland-Jørgensen 
44802fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_COLLISIONS))) {
44812fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_COLLISIONS);
44822fe4a29aSToke Høiland-Jørgensen 		txqstats->collisions = txqi->tin.collisions;
44832fe4a29aSToke Høiland-Jørgensen 	}
44842fe4a29aSToke Høiland-Jørgensen 
44852fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_BYTES))) {
44862fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_BYTES);
44872fe4a29aSToke Høiland-Jørgensen 		txqstats->tx_bytes = txqi->tin.tx_bytes;
44882fe4a29aSToke Høiland-Jørgensen 	}
44892fe4a29aSToke Høiland-Jørgensen 
44902fe4a29aSToke Høiland-Jørgensen 	if (!(txqstats->filled & BIT(NL80211_TXQ_STATS_TX_PACKETS))) {
44912fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_TX_PACKETS);
44922fe4a29aSToke Høiland-Jørgensen 		txqstats->tx_packets = txqi->tin.tx_packets;
44932fe4a29aSToke Høiland-Jørgensen 	}
44942fe4a29aSToke Høiland-Jørgensen }
44952fe4a29aSToke Høiland-Jørgensen 
ieee80211_get_txq_stats(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_txq_stats * txqstats)44962fe4a29aSToke Høiland-Jørgensen static int ieee80211_get_txq_stats(struct wiphy *wiphy,
44972fe4a29aSToke Høiland-Jørgensen 				   struct wireless_dev *wdev,
44982fe4a29aSToke Høiland-Jørgensen 				   struct cfg80211_txq_stats *txqstats)
44992fe4a29aSToke Høiland-Jørgensen {
45002fe4a29aSToke Høiland-Jørgensen 	struct ieee80211_local *local = wiphy_priv(wiphy);
45012fe4a29aSToke Høiland-Jørgensen 	struct ieee80211_sub_if_data *sdata;
45022fe4a29aSToke Høiland-Jørgensen 	int ret = 0;
45032fe4a29aSToke Høiland-Jørgensen 
45042fe4a29aSToke Høiland-Jørgensen 	spin_lock_bh(&local->fq.lock);
45052fe4a29aSToke Høiland-Jørgensen 	rcu_read_lock();
45062fe4a29aSToke Høiland-Jørgensen 
45072fe4a29aSToke Høiland-Jørgensen 	if (wdev) {
45082fe4a29aSToke Høiland-Jørgensen 		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
45092fe4a29aSToke Høiland-Jørgensen 		if (!sdata->vif.txq) {
45102fe4a29aSToke Høiland-Jørgensen 			ret = 1;
45112fe4a29aSToke Høiland-Jørgensen 			goto out;
45122fe4a29aSToke Høiland-Jørgensen 		}
45132fe4a29aSToke Høiland-Jørgensen 		ieee80211_fill_txq_stats(txqstats, to_txq_info(sdata->vif.txq));
45142fe4a29aSToke Høiland-Jørgensen 	} else {
45152fe4a29aSToke Høiland-Jørgensen 		/* phy stats */
45162fe4a29aSToke Høiland-Jørgensen 		txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS) |
45172fe4a29aSToke Høiland-Jørgensen 				    BIT(NL80211_TXQ_STATS_BACKLOG_BYTES) |
45182fe4a29aSToke Høiland-Jørgensen 				    BIT(NL80211_TXQ_STATS_OVERLIMIT) |
45192fe4a29aSToke Høiland-Jørgensen 				    BIT(NL80211_TXQ_STATS_OVERMEMORY) |
45202fe4a29aSToke Høiland-Jørgensen 				    BIT(NL80211_TXQ_STATS_COLLISIONS) |
45212fe4a29aSToke Høiland-Jørgensen 				    BIT(NL80211_TXQ_STATS_MAX_FLOWS);
45222fe4a29aSToke Høiland-Jørgensen 		txqstats->backlog_packets = local->fq.backlog;
45232fe4a29aSToke Høiland-Jørgensen 		txqstats->backlog_bytes = local->fq.memory_usage;
45242fe4a29aSToke Høiland-Jørgensen 		txqstats->overlimit = local->fq.overlimit;
45252fe4a29aSToke Høiland-Jørgensen 		txqstats->overmemory = local->fq.overmemory;
45262fe4a29aSToke Høiland-Jørgensen 		txqstats->collisions = local->fq.collisions;
45272fe4a29aSToke Høiland-Jørgensen 		txqstats->max_flows = local->fq.flows_cnt;
45282fe4a29aSToke Høiland-Jørgensen 	}
45292fe4a29aSToke Høiland-Jørgensen 
45302fe4a29aSToke Høiland-Jørgensen out:
45312fe4a29aSToke Høiland-Jørgensen 	rcu_read_unlock();
45322fe4a29aSToke Høiland-Jørgensen 	spin_unlock_bh(&local->fq.lock);
45332fe4a29aSToke Høiland-Jørgensen 
45342fe4a29aSToke Høiland-Jørgensen 	return ret;
45352fe4a29aSToke Høiland-Jørgensen }
45362fe4a29aSToke Høiland-Jørgensen 
4537bc847970SPradeep Kumar Chitrapu static int
ieee80211_get_ftm_responder_stats(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_ftm_responder_stats * ftm_stats)4538bc847970SPradeep Kumar Chitrapu ieee80211_get_ftm_responder_stats(struct wiphy *wiphy,
4539bc847970SPradeep Kumar Chitrapu 				  struct net_device *dev,
4540bc847970SPradeep Kumar Chitrapu 				  struct cfg80211_ftm_responder_stats *ftm_stats)
4541bc847970SPradeep Kumar Chitrapu {
4542bc847970SPradeep Kumar Chitrapu 	struct ieee80211_local *local = wiphy_priv(wiphy);
4543bc847970SPradeep Kumar Chitrapu 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
4544bc847970SPradeep Kumar Chitrapu 
4545bc847970SPradeep Kumar Chitrapu 	return drv_get_ftm_responder_stats(local, sdata, ftm_stats);
4546bc847970SPradeep Kumar Chitrapu }
4547bc847970SPradeep Kumar Chitrapu 
4548cee7013bSJohannes Berg static int
ieee80211_start_pmsr(struct wiphy * wiphy,struct wireless_dev * dev,struct cfg80211_pmsr_request * request)4549cee7013bSJohannes Berg ieee80211_start_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
4550cee7013bSJohannes Berg 		     struct cfg80211_pmsr_request *request)
4551cee7013bSJohannes Berg {
4552cee7013bSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
4553cee7013bSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
4554cee7013bSJohannes Berg 
4555cee7013bSJohannes Berg 	return drv_start_pmsr(local, sdata, request);
4556cee7013bSJohannes Berg }
4557cee7013bSJohannes Berg 
4558cee7013bSJohannes Berg static void
ieee80211_abort_pmsr(struct wiphy * wiphy,struct wireless_dev * dev,struct cfg80211_pmsr_request * request)4559cee7013bSJohannes Berg ieee80211_abort_pmsr(struct wiphy *wiphy, struct wireless_dev *dev,
4560cee7013bSJohannes Berg 		     struct cfg80211_pmsr_request *request)
4561cee7013bSJohannes Berg {
4562cee7013bSJohannes Berg 	struct ieee80211_local *local = wiphy_priv(wiphy);
4563cee7013bSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(dev);
4564cee7013bSJohannes Berg 
4565cee7013bSJohannes Berg 	return drv_abort_pmsr(local, sdata, request);
4566cee7013bSJohannes Berg }
4567cee7013bSJohannes Berg 
ieee80211_set_tid_config(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_tid_config * tid_conf)4568370f51d5STamizh chelvam static int ieee80211_set_tid_config(struct wiphy *wiphy,
4569370f51d5STamizh chelvam 				    struct net_device *dev,
4570370f51d5STamizh chelvam 				    struct cfg80211_tid_config *tid_conf)
4571370f51d5STamizh chelvam {
4572370f51d5STamizh chelvam 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
4573370f51d5STamizh chelvam 	struct sta_info *sta;
4574370f51d5STamizh chelvam 	int ret;
4575370f51d5STamizh chelvam 
4576370f51d5STamizh chelvam 	if (!sdata->local->ops->set_tid_config)
4577370f51d5STamizh chelvam 		return -EOPNOTSUPP;
4578370f51d5STamizh chelvam 
4579370f51d5STamizh chelvam 	if (!tid_conf->peer)
4580370f51d5STamizh chelvam 		return drv_set_tid_config(sdata->local, sdata, NULL, tid_conf);
4581370f51d5STamizh chelvam 
4582370f51d5STamizh chelvam 	mutex_lock(&sdata->local->sta_mtx);
4583370f51d5STamizh chelvam 	sta = sta_info_get_bss(sdata, tid_conf->peer);
4584370f51d5STamizh chelvam 	if (!sta) {
4585370f51d5STamizh chelvam 		mutex_unlock(&sdata->local->sta_mtx);
4586370f51d5STamizh chelvam 		return -ENOENT;
4587370f51d5STamizh chelvam 	}
4588370f51d5STamizh chelvam 
4589370f51d5STamizh chelvam 	ret = drv_set_tid_config(sdata->local, sdata, &sta->sta, tid_conf);
4590370f51d5STamizh chelvam 	mutex_unlock(&sdata->local->sta_mtx);
4591370f51d5STamizh chelvam 
4592370f51d5STamizh chelvam 	return ret;
4593370f51d5STamizh chelvam }
4594370f51d5STamizh chelvam 
ieee80211_reset_tid_config(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,u8 tids)4595370f51d5STamizh chelvam static int ieee80211_reset_tid_config(struct wiphy *wiphy,
4596370f51d5STamizh chelvam 				      struct net_device *dev,
459760c2ef0eSSergey Matyukevich 				      const u8 *peer, u8 tids)
4598370f51d5STamizh chelvam {
4599370f51d5STamizh chelvam 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
4600370f51d5STamizh chelvam 	struct sta_info *sta;
4601370f51d5STamizh chelvam 	int ret;
4602370f51d5STamizh chelvam 
4603370f51d5STamizh chelvam 	if (!sdata->local->ops->reset_tid_config)
4604370f51d5STamizh chelvam 		return -EOPNOTSUPP;
4605370f51d5STamizh chelvam 
4606370f51d5STamizh chelvam 	if (!peer)
460760c2ef0eSSergey Matyukevich 		return drv_reset_tid_config(sdata->local, sdata, NULL, tids);
4608370f51d5STamizh chelvam 
4609370f51d5STamizh chelvam 	mutex_lock(&sdata->local->sta_mtx);
4610370f51d5STamizh chelvam 	sta = sta_info_get_bss(sdata, peer);
4611370f51d5STamizh chelvam 	if (!sta) {
4612370f51d5STamizh chelvam 		mutex_unlock(&sdata->local->sta_mtx);
4613370f51d5STamizh chelvam 		return -ENOENT;
4614370f51d5STamizh chelvam 	}
4615370f51d5STamizh chelvam 
461660c2ef0eSSergey Matyukevich 	ret = drv_reset_tid_config(sdata->local, sdata, &sta->sta, tids);
4617370f51d5STamizh chelvam 	mutex_unlock(&sdata->local->sta_mtx);
4618370f51d5STamizh chelvam 
4619370f51d5STamizh chelvam 	return ret;
4620370f51d5STamizh chelvam }
4621370f51d5STamizh chelvam 
ieee80211_set_sar_specs(struct wiphy * wiphy,struct cfg80211_sar_specs * sar)4622c534e093SCarl Huang static int ieee80211_set_sar_specs(struct wiphy *wiphy,
4623c534e093SCarl Huang 				   struct cfg80211_sar_specs *sar)
4624c534e093SCarl Huang {
4625c534e093SCarl Huang 	struct ieee80211_local *local = wiphy_priv(wiphy);
4626c534e093SCarl Huang 
4627c534e093SCarl Huang 	if (!local->ops->set_sar_specs)
4628c534e093SCarl Huang 		return -EOPNOTSUPP;
4629c534e093SCarl Huang 
4630c534e093SCarl Huang 	return local->ops->set_sar_specs(&local->hw, sar);
4631c534e093SCarl Huang }
4632c534e093SCarl Huang 
46335f9404abSJohn Crispin static int
ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data * sdata,u64 * changed)46345f9404abSJohn Crispin ieee80211_set_after_color_change_beacon(struct ieee80211_sub_if_data *sdata,
463515ddba5fSAnjaneyulu 					u64 *changed)
46365f9404abSJohn Crispin {
46375f9404abSJohn Crispin 	switch (sdata->vif.type) {
46385f9404abSJohn Crispin 	case NL80211_IFTYPE_AP: {
46395f9404abSJohn Crispin 		int ret;
46405f9404abSJohn Crispin 
4641bfd8403aSJohannes Berg 		if (!sdata->deflink.u.ap.next_beacon)
4642450c271dSLorenzo Bianconi 			return -EINVAL;
4643450c271dSLorenzo Bianconi 
4644d8675a63SJohannes Berg 		ret = ieee80211_assign_beacon(sdata, &sdata->deflink,
4645bfd8403aSJohannes Berg 					      sdata->deflink.u.ap.next_beacon,
464615ddba5fSAnjaneyulu 					      NULL, NULL, changed);
4647d9f83f22SShaul Triebitz 		ieee80211_free_next_beacon(&sdata->deflink);
46485f9404abSJohn Crispin 
46495f9404abSJohn Crispin 		if (ret < 0)
46505f9404abSJohn Crispin 			return ret;
46515f9404abSJohn Crispin 
46525f9404abSJohn Crispin 		break;
46535f9404abSJohn Crispin 	}
46545f9404abSJohn Crispin 	default:
46555f9404abSJohn Crispin 		WARN_ON_ONCE(1);
46565f9404abSJohn Crispin 		return -EINVAL;
46575f9404abSJohn Crispin 	}
46585f9404abSJohn Crispin 
46595f9404abSJohn Crispin 	return 0;
46605f9404abSJohn Crispin }
46615f9404abSJohn Crispin 
46625f9404abSJohn Crispin static int
ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data * sdata,struct cfg80211_color_change_settings * params,u64 * changed)46635f9404abSJohn Crispin ieee80211_set_color_change_beacon(struct ieee80211_sub_if_data *sdata,
46645f9404abSJohn Crispin 				  struct cfg80211_color_change_settings *params,
466515ddba5fSAnjaneyulu 				  u64 *changed)
46665f9404abSJohn Crispin {
46675f9404abSJohn Crispin 	struct ieee80211_color_change_settings color_change = {};
46685f9404abSJohn Crispin 	int err;
46695f9404abSJohn Crispin 
46705f9404abSJohn Crispin 	switch (sdata->vif.type) {
46715f9404abSJohn Crispin 	case NL80211_IFTYPE_AP:
4672bfd8403aSJohannes Berg 		sdata->deflink.u.ap.next_beacon =
46735f9404abSJohn Crispin 			cfg80211_beacon_dup(&params->beacon_next);
4674bfd8403aSJohannes Berg 		if (!sdata->deflink.u.ap.next_beacon)
46755f9404abSJohn Crispin 			return -ENOMEM;
46765f9404abSJohn Crispin 
46775f9404abSJohn Crispin 		if (params->count <= 1)
46785f9404abSJohn Crispin 			break;
46795f9404abSJohn Crispin 
46805f9404abSJohn Crispin 		color_change.counter_offset_beacon =
46815f9404abSJohn Crispin 			params->counter_offset_beacon;
46825f9404abSJohn Crispin 		color_change.counter_offset_presp =
46835f9404abSJohn Crispin 			params->counter_offset_presp;
46845f9404abSJohn Crispin 		color_change.count = params->count;
46855f9404abSJohn Crispin 
4686d8675a63SJohannes Berg 		err = ieee80211_assign_beacon(sdata, &sdata->deflink,
4687d8675a63SJohannes Berg 					      &params->beacon_color_change,
468815ddba5fSAnjaneyulu 					      NULL, &color_change, changed);
46895f9404abSJohn Crispin 		if (err < 0) {
4690d9f83f22SShaul Triebitz 			ieee80211_free_next_beacon(&sdata->deflink);
46915f9404abSJohn Crispin 			return err;
46925f9404abSJohn Crispin 		}
46935f9404abSJohn Crispin 		break;
46945f9404abSJohn Crispin 	default:
46955f9404abSJohn Crispin 		return -EOPNOTSUPP;
46965f9404abSJohn Crispin 	}
46975f9404abSJohn Crispin 
46985f9404abSJohn Crispin 	return 0;
46995f9404abSJohn Crispin }
47005f9404abSJohn Crispin 
47015f9404abSJohn Crispin static void
ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data * sdata,u8 color,int enable,u64 changed)47025f9404abSJohn Crispin ieee80211_color_change_bss_config_notify(struct ieee80211_sub_if_data *sdata,
470315ddba5fSAnjaneyulu 					 u8 color, int enable, u64 changed)
47045f9404abSJohn Crispin {
47055f9404abSJohn Crispin 	sdata->vif.bss_conf.he_bss_color.color = color;
47065f9404abSJohn Crispin 	sdata->vif.bss_conf.he_bss_color.enabled = enable;
47075f9404abSJohn Crispin 	changed |= BSS_CHANGED_HE_BSS_COLOR;
47085f9404abSJohn Crispin 
4709d8675a63SJohannes Berg 	ieee80211_link_info_change_notify(sdata, &sdata->deflink, changed);
4710eb87d3e0SJohn Crispin 
4711eb87d3e0SJohn Crispin 	if (!sdata->vif.bss_conf.nontransmitted && sdata->vif.mbssid_tx_vif) {
4712eb87d3e0SJohn Crispin 		struct ieee80211_sub_if_data *child;
4713eb87d3e0SJohn Crispin 
4714eb87d3e0SJohn Crispin 		mutex_lock(&sdata->local->iflist_mtx);
4715eb87d3e0SJohn Crispin 		list_for_each_entry(child, &sdata->local->interfaces, list) {
4716eb87d3e0SJohn Crispin 			if (child != sdata && child->vif.mbssid_tx_vif == &sdata->vif) {
4717eb87d3e0SJohn Crispin 				child->vif.bss_conf.he_bss_color.color = color;
4718eb87d3e0SJohn Crispin 				child->vif.bss_conf.he_bss_color.enabled = enable;
4719d8675a63SJohannes Berg 				ieee80211_link_info_change_notify(child,
4720d8675a63SJohannes Berg 								  &child->deflink,
4721eb87d3e0SJohn Crispin 								  BSS_CHANGED_HE_BSS_COLOR);
4722eb87d3e0SJohn Crispin 			}
4723eb87d3e0SJohn Crispin 		}
4724eb87d3e0SJohn Crispin 		mutex_unlock(&sdata->local->iflist_mtx);
4725eb87d3e0SJohn Crispin 	}
47265f9404abSJohn Crispin }
47275f9404abSJohn Crispin 
ieee80211_color_change_finalize(struct ieee80211_sub_if_data * sdata)47285f9404abSJohn Crispin static int ieee80211_color_change_finalize(struct ieee80211_sub_if_data *sdata)
47295f9404abSJohn Crispin {
47305f9404abSJohn Crispin 	struct ieee80211_local *local = sdata->local;
473115ddba5fSAnjaneyulu 	u64 changed = 0;
47325f9404abSJohn Crispin 	int err;
47335f9404abSJohn Crispin 
47345f9404abSJohn Crispin 	sdata_assert_lock(sdata);
47355f9404abSJohn Crispin 	lockdep_assert_held(&local->mtx);
47365f9404abSJohn Crispin 
4737d0a9123eSJohannes Berg 	sdata->vif.bss_conf.color_change_active = false;
47385f9404abSJohn Crispin 
47395f9404abSJohn Crispin 	err = ieee80211_set_after_color_change_beacon(sdata, &changed);
47405f9404abSJohn Crispin 	if (err) {
47415f9404abSJohn Crispin 		cfg80211_color_change_aborted_notify(sdata->dev);
47425f9404abSJohn Crispin 		return err;
47435f9404abSJohn Crispin 	}
47445f9404abSJohn Crispin 
47455f9404abSJohn Crispin 	ieee80211_color_change_bss_config_notify(sdata,
4746d0a9123eSJohannes Berg 						 sdata->vif.bss_conf.color_change_color,
47475f9404abSJohn Crispin 						 1, changed);
47485f9404abSJohn Crispin 	cfg80211_color_change_notify(sdata->dev);
47495f9404abSJohn Crispin 
47505f9404abSJohn Crispin 	return 0;
47515f9404abSJohn Crispin }
47525f9404abSJohn Crispin 
ieee80211_color_change_finalize_work(struct work_struct * work)47535f9404abSJohn Crispin void ieee80211_color_change_finalize_work(struct work_struct *work)
47545f9404abSJohn Crispin {
47555f9404abSJohn Crispin 	struct ieee80211_sub_if_data *sdata =
47565f9404abSJohn Crispin 		container_of(work, struct ieee80211_sub_if_data,
4757bfd8403aSJohannes Berg 			     deflink.color_change_finalize_work);
47585f9404abSJohn Crispin 	struct ieee80211_local *local = sdata->local;
47595f9404abSJohn Crispin 
47605f9404abSJohn Crispin 	sdata_lock(sdata);
47615f9404abSJohn Crispin 	mutex_lock(&local->mtx);
47625f9404abSJohn Crispin 
47635f9404abSJohn Crispin 	/* AP might have been stopped while waiting for the lock. */
4764d0a9123eSJohannes Berg 	if (!sdata->vif.bss_conf.color_change_active)
47655f9404abSJohn Crispin 		goto unlock;
47665f9404abSJohn Crispin 
47675f9404abSJohn Crispin 	if (!ieee80211_sdata_running(sdata))
47685f9404abSJohn Crispin 		goto unlock;
47695f9404abSJohn Crispin 
47705f9404abSJohn Crispin 	ieee80211_color_change_finalize(sdata);
47715f9404abSJohn Crispin 
47725f9404abSJohn Crispin unlock:
47735f9404abSJohn Crispin 	mutex_unlock(&local->mtx);
47745f9404abSJohn Crispin 	sdata_unlock(sdata);
47755f9404abSJohn Crispin }
47765f9404abSJohn Crispin 
ieee80211_color_collision_detection_work(struct work_struct * work)477792881884SLorenzo Bianconi void ieee80211_color_collision_detection_work(struct work_struct *work)
477892881884SLorenzo Bianconi {
477992881884SLorenzo Bianconi 	struct delayed_work *delayed_work = to_delayed_work(work);
478092881884SLorenzo Bianconi 	struct ieee80211_link_data *link =
478192881884SLorenzo Bianconi 		container_of(delayed_work, struct ieee80211_link_data,
478292881884SLorenzo Bianconi 			     color_collision_detect_work);
478392881884SLorenzo Bianconi 	struct ieee80211_sub_if_data *sdata = link->sdata;
478492881884SLorenzo Bianconi 
478592881884SLorenzo Bianconi 	sdata_lock(sdata);
4786935ef47bSLorenzo Bianconi 	cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap);
478792881884SLorenzo Bianconi 	sdata_unlock(sdata);
478892881884SLorenzo Bianconi }
478992881884SLorenzo Bianconi 
ieee80211_color_change_finish(struct ieee80211_vif * vif)47905f9404abSJohn Crispin void ieee80211_color_change_finish(struct ieee80211_vif *vif)
47915f9404abSJohn Crispin {
47925f9404abSJohn Crispin 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
47935f9404abSJohn Crispin 
47945f9404abSJohn Crispin 	ieee80211_queue_work(&sdata->local->hw,
4795bfd8403aSJohannes Berg 			     &sdata->deflink.color_change_finalize_work);
47965f9404abSJohn Crispin }
47975f9404abSJohn Crispin EXPORT_SYMBOL_GPL(ieee80211_color_change_finish);
47985f9404abSJohn Crispin 
47995f9404abSJohn Crispin void
ieee80211_obss_color_collision_notify(struct ieee80211_vif * vif,u64 color_bitmap,gfp_t gfp)480082253ddaSJohannes Berg ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
480103895c84SLorenzo Bianconi 				       u64 color_bitmap, gfp_t gfp)
48025f9404abSJohn Crispin {
48035f9404abSJohn Crispin 	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
480492881884SLorenzo Bianconi 	struct ieee80211_link_data *link = &sdata->deflink;
48055f9404abSJohn Crispin 
4806d0a9123eSJohannes Berg 	if (sdata->vif.bss_conf.color_change_active || sdata->vif.bss_conf.csa_active)
48075f9404abSJohn Crispin 		return;
48085f9404abSJohn Crispin 
480992881884SLorenzo Bianconi 	if (delayed_work_pending(&link->color_collision_detect_work))
481092881884SLorenzo Bianconi 		return;
481192881884SLorenzo Bianconi 
481292881884SLorenzo Bianconi 	link->color_bitmap = color_bitmap;
481392881884SLorenzo Bianconi 	/* queue the color collision detection event every 500 ms in order to
481492881884SLorenzo Bianconi 	 * avoid sending too much netlink messages to userspace.
481592881884SLorenzo Bianconi 	 */
481692881884SLorenzo Bianconi 	ieee80211_queue_delayed_work(&sdata->local->hw,
481792881884SLorenzo Bianconi 				     &link->color_collision_detect_work,
481892881884SLorenzo Bianconi 				     msecs_to_jiffies(500));
48195f9404abSJohn Crispin }
482082253ddaSJohannes Berg EXPORT_SYMBOL_GPL(ieee80211_obss_color_collision_notify);
48215f9404abSJohn Crispin 
48225f9404abSJohn Crispin static int
ieee80211_color_change(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_color_change_settings * params)48235f9404abSJohn Crispin ieee80211_color_change(struct wiphy *wiphy, struct net_device *dev,
48245f9404abSJohn Crispin 		       struct cfg80211_color_change_settings *params)
48255f9404abSJohn Crispin {
48265f9404abSJohn Crispin 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
48275f9404abSJohn Crispin 	struct ieee80211_local *local = sdata->local;
482815ddba5fSAnjaneyulu 	u64 changed = 0;
48295f9404abSJohn Crispin 	int err;
48305f9404abSJohn Crispin 
48315f9404abSJohn Crispin 	sdata_assert_lock(sdata);
48325f9404abSJohn Crispin 
4833eb87d3e0SJohn Crispin 	if (sdata->vif.bss_conf.nontransmitted)
4834eb87d3e0SJohn Crispin 		return -EINVAL;
4835eb87d3e0SJohn Crispin 
48365f9404abSJohn Crispin 	mutex_lock(&local->mtx);
48375f9404abSJohn Crispin 
48385f9404abSJohn Crispin 	/* don't allow another color change if one is already active or if csa
48395f9404abSJohn Crispin 	 * is active
48405f9404abSJohn Crispin 	 */
4841d0a9123eSJohannes Berg 	if (sdata->vif.bss_conf.color_change_active || sdata->vif.bss_conf.csa_active) {
48425f9404abSJohn Crispin 		err = -EBUSY;
48435f9404abSJohn Crispin 		goto out;
48445f9404abSJohn Crispin 	}
48455f9404abSJohn Crispin 
48465f9404abSJohn Crispin 	err = ieee80211_set_color_change_beacon(sdata, params, &changed);
48475f9404abSJohn Crispin 	if (err)
48485f9404abSJohn Crispin 		goto out;
48495f9404abSJohn Crispin 
4850d0a9123eSJohannes Berg 	sdata->vif.bss_conf.color_change_active = true;
4851d0a9123eSJohannes Berg 	sdata->vif.bss_conf.color_change_color = params->color;
48525f9404abSJohn Crispin 
48535f9404abSJohn Crispin 	cfg80211_color_change_started_notify(sdata->dev, params->count);
48545f9404abSJohn Crispin 
48555f9404abSJohn Crispin 	if (changed)
48565f9404abSJohn Crispin 		ieee80211_color_change_bss_config_notify(sdata, 0, 0, changed);
48575f9404abSJohn Crispin 	else
48585f9404abSJohn Crispin 		/* if the beacon didn't change, we can finalize immediately */
48595f9404abSJohn Crispin 		ieee80211_color_change_finalize(sdata);
48605f9404abSJohn Crispin 
48615f9404abSJohn Crispin out:
48625f9404abSJohn Crispin 	mutex_unlock(&local->mtx);
48635f9404abSJohn Crispin 
48645f9404abSJohn Crispin 	return err;
48655f9404abSJohn Crispin }
48665f9404abSJohn Crispin 
4867237337c2SLorenzo Bianconi static int
ieee80211_set_radar_background(struct wiphy * wiphy,struct cfg80211_chan_def * chandef)4868a95bfb87SLorenzo Bianconi ieee80211_set_radar_background(struct wiphy *wiphy,
4869237337c2SLorenzo Bianconi 			       struct cfg80211_chan_def *chandef)
4870237337c2SLorenzo Bianconi {
4871237337c2SLorenzo Bianconi 	struct ieee80211_local *local = wiphy_priv(wiphy);
4872237337c2SLorenzo Bianconi 
4873a95bfb87SLorenzo Bianconi 	if (!local->ops->set_radar_background)
4874237337c2SLorenzo Bianconi 		return -EOPNOTSUPP;
4875237337c2SLorenzo Bianconi 
4876a95bfb87SLorenzo Bianconi 	return local->ops->set_radar_background(&local->hw, chandef);
4877237337c2SLorenzo Bianconi }
4878237337c2SLorenzo Bianconi 
ieee80211_add_intf_link(struct wiphy * wiphy,struct wireless_dev * wdev,unsigned int link_id)48790d8c4a3cSJohannes Berg static int ieee80211_add_intf_link(struct wiphy *wiphy,
48800d8c4a3cSJohannes Berg 				   struct wireless_dev *wdev,
48810d8c4a3cSJohannes Berg 				   unsigned int link_id)
48820d8c4a3cSJohannes Berg {
48830d8c4a3cSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
488415846f95SBenjamin Berg 	int res;
48850d8c4a3cSJohannes Berg 
488690703ba9SJohannes Berg 	if (wdev->use_4addr)
488790703ba9SJohannes Berg 		return -EOPNOTSUPP;
488890703ba9SJohannes Berg 
488915846f95SBenjamin Berg 	mutex_lock(&sdata->local->mtx);
48906d543b34SIlan Peer 	res = ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
489115846f95SBenjamin Berg 	mutex_unlock(&sdata->local->mtx);
489215846f95SBenjamin Berg 
489315846f95SBenjamin Berg 	return res;
48940d8c4a3cSJohannes Berg }
48950d8c4a3cSJohannes Berg 
ieee80211_del_intf_link(struct wiphy * wiphy,struct wireless_dev * wdev,unsigned int link_id)48960d8c4a3cSJohannes Berg static void ieee80211_del_intf_link(struct wiphy *wiphy,
48970d8c4a3cSJohannes Berg 				    struct wireless_dev *wdev,
48980d8c4a3cSJohannes Berg 				    unsigned int link_id)
48990d8c4a3cSJohannes Berg {
49000d8c4a3cSJohannes Berg 	struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
49010d8c4a3cSJohannes Berg 
490215846f95SBenjamin Berg 	mutex_lock(&sdata->local->mtx);
49036d543b34SIlan Peer 	ieee80211_vif_set_links(sdata, wdev->valid_links, 0);
490415846f95SBenjamin Berg 	mutex_unlock(&sdata->local->mtx);
49050d8c4a3cSJohannes Berg }
49060d8c4a3cSJohannes Berg 
sta_add_link_station(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct link_station_parameters * params)490721476ad1SShaul Triebitz static int sta_add_link_station(struct ieee80211_local *local,
490821476ad1SShaul Triebitz 				struct ieee80211_sub_if_data *sdata,
490921476ad1SShaul Triebitz 				struct link_station_parameters *params)
491021476ad1SShaul Triebitz {
491121476ad1SShaul Triebitz 	struct sta_info *sta;
491221476ad1SShaul Triebitz 	int ret;
491321476ad1SShaul Triebitz 
491421476ad1SShaul Triebitz 	sta = sta_info_get_bss(sdata, params->mld_mac);
491521476ad1SShaul Triebitz 	if (!sta)
491621476ad1SShaul Triebitz 		return -ENOENT;
491721476ad1SShaul Triebitz 
4918956b9613SJohannes Berg 	if (!sta->sta.valid_links)
4919956b9613SJohannes Berg 		return -EINVAL;
4920956b9613SJohannes Berg 
492121476ad1SShaul Triebitz 	if (sta->sta.valid_links & BIT(params->link_id))
492221476ad1SShaul Triebitz 		return -EALREADY;
492321476ad1SShaul Triebitz 
492421476ad1SShaul Triebitz 	ret = ieee80211_sta_allocate_link(sta, params->link_id);
492521476ad1SShaul Triebitz 	if (ret)
492621476ad1SShaul Triebitz 		return ret;
492721476ad1SShaul Triebitz 
49289aebce6cSJohannes Berg 	ret = sta_link_apply_parameters(local, sta, true, params);
492921476ad1SShaul Triebitz 	if (ret) {
493021476ad1SShaul Triebitz 		ieee80211_sta_free_link(sta, params->link_id);
493121476ad1SShaul Triebitz 		return ret;
493221476ad1SShaul Triebitz 	}
493321476ad1SShaul Triebitz 
493421476ad1SShaul Triebitz 	/* ieee80211_sta_activate_link frees the link upon failure */
493521476ad1SShaul Triebitz 	return ieee80211_sta_activate_link(sta, params->link_id);
493621476ad1SShaul Triebitz }
493721476ad1SShaul Triebitz 
493821476ad1SShaul Triebitz static int
ieee80211_add_link_station(struct wiphy * wiphy,struct net_device * dev,struct link_station_parameters * params)493921476ad1SShaul Triebitz ieee80211_add_link_station(struct wiphy *wiphy, struct net_device *dev,
494021476ad1SShaul Triebitz 			   struct link_station_parameters *params)
494121476ad1SShaul Triebitz {
494221476ad1SShaul Triebitz 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
494321476ad1SShaul Triebitz 	struct ieee80211_local *local = wiphy_priv(wiphy);
494421476ad1SShaul Triebitz 	int ret;
494521476ad1SShaul Triebitz 
494621476ad1SShaul Triebitz 	mutex_lock(&sdata->local->sta_mtx);
494721476ad1SShaul Triebitz 	ret = sta_add_link_station(local, sdata, params);
494821476ad1SShaul Triebitz 	mutex_unlock(&sdata->local->sta_mtx);
494921476ad1SShaul Triebitz 
495021476ad1SShaul Triebitz 	return ret;
495121476ad1SShaul Triebitz }
495221476ad1SShaul Triebitz 
sta_mod_link_station(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct link_station_parameters * params)495321476ad1SShaul Triebitz static int sta_mod_link_station(struct ieee80211_local *local,
495421476ad1SShaul Triebitz 				struct ieee80211_sub_if_data *sdata,
495521476ad1SShaul Triebitz 				struct link_station_parameters *params)
495621476ad1SShaul Triebitz {
495721476ad1SShaul Triebitz 	struct sta_info *sta;
495821476ad1SShaul Triebitz 
495921476ad1SShaul Triebitz 	sta = sta_info_get_bss(sdata, params->mld_mac);
496021476ad1SShaul Triebitz 	if (!sta)
496121476ad1SShaul Triebitz 		return -ENOENT;
496221476ad1SShaul Triebitz 
496321476ad1SShaul Triebitz 	if (!(sta->sta.valid_links & BIT(params->link_id)))
496421476ad1SShaul Triebitz 		return -EINVAL;
496521476ad1SShaul Triebitz 
49669aebce6cSJohannes Berg 	return sta_link_apply_parameters(local, sta, false, params);
496721476ad1SShaul Triebitz }
496821476ad1SShaul Triebitz 
496921476ad1SShaul Triebitz static int
ieee80211_mod_link_station(struct wiphy * wiphy,struct net_device * dev,struct link_station_parameters * params)497021476ad1SShaul Triebitz ieee80211_mod_link_station(struct wiphy *wiphy, struct net_device *dev,
497121476ad1SShaul Triebitz 			   struct link_station_parameters *params)
497221476ad1SShaul Triebitz {
497321476ad1SShaul Triebitz 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
497421476ad1SShaul Triebitz 	struct ieee80211_local *local = wiphy_priv(wiphy);
497521476ad1SShaul Triebitz 	int ret;
497621476ad1SShaul Triebitz 
497721476ad1SShaul Triebitz 	mutex_lock(&sdata->local->sta_mtx);
497821476ad1SShaul Triebitz 	ret = sta_mod_link_station(local, sdata, params);
497921476ad1SShaul Triebitz 	mutex_unlock(&sdata->local->sta_mtx);
498021476ad1SShaul Triebitz 
498121476ad1SShaul Triebitz 	return ret;
498221476ad1SShaul Triebitz }
498321476ad1SShaul Triebitz 
sta_del_link_station(struct ieee80211_sub_if_data * sdata,struct link_station_del_parameters * params)498421476ad1SShaul Triebitz static int sta_del_link_station(struct ieee80211_sub_if_data *sdata,
498521476ad1SShaul Triebitz 				struct link_station_del_parameters *params)
498621476ad1SShaul Triebitz {
498721476ad1SShaul Triebitz 	struct sta_info *sta;
498821476ad1SShaul Triebitz 
498921476ad1SShaul Triebitz 	sta = sta_info_get_bss(sdata, params->mld_mac);
499021476ad1SShaul Triebitz 	if (!sta)
499121476ad1SShaul Triebitz 		return -ENOENT;
499221476ad1SShaul Triebitz 
499321476ad1SShaul Triebitz 	if (!(sta->sta.valid_links & BIT(params->link_id)))
499421476ad1SShaul Triebitz 		return -EINVAL;
499521476ad1SShaul Triebitz 
4996956b9613SJohannes Berg 	/* must not create a STA without links */
4997956b9613SJohannes Berg 	if (sta->sta.valid_links == BIT(params->link_id))
4998956b9613SJohannes Berg 		return -EINVAL;
4999956b9613SJohannes Berg 
500021476ad1SShaul Triebitz 	ieee80211_sta_remove_link(sta, params->link_id);
500121476ad1SShaul Triebitz 
500221476ad1SShaul Triebitz 	return 0;
500321476ad1SShaul Triebitz }
500421476ad1SShaul Triebitz 
500521476ad1SShaul Triebitz static int
ieee80211_del_link_station(struct wiphy * wiphy,struct net_device * dev,struct link_station_del_parameters * params)500621476ad1SShaul Triebitz ieee80211_del_link_station(struct wiphy *wiphy, struct net_device *dev,
500721476ad1SShaul Triebitz 			   struct link_station_del_parameters *params)
500821476ad1SShaul Triebitz {
500921476ad1SShaul Triebitz 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
501021476ad1SShaul Triebitz 	int ret;
501121476ad1SShaul Triebitz 
501221476ad1SShaul Triebitz 	mutex_lock(&sdata->local->sta_mtx);
501321476ad1SShaul Triebitz 	ret = sta_del_link_station(sdata, params);
501421476ad1SShaul Triebitz 	mutex_unlock(&sdata->local->sta_mtx);
501521476ad1SShaul Triebitz 
501621476ad1SShaul Triebitz 	return ret;
501721476ad1SShaul Triebitz }
501821476ad1SShaul Triebitz 
ieee80211_set_hw_timestamp(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_set_hw_timestamp * hwts)501981202305SAvraham Stern static int ieee80211_set_hw_timestamp(struct wiphy *wiphy,
502081202305SAvraham Stern 				      struct net_device *dev,
502181202305SAvraham Stern 				      struct cfg80211_set_hw_timestamp *hwts)
502281202305SAvraham Stern {
502381202305SAvraham Stern 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
502481202305SAvraham Stern 	struct ieee80211_local *local = sdata->local;
502581202305SAvraham Stern 
502681202305SAvraham Stern 	if (!local->ops->set_hw_timestamp)
502781202305SAvraham Stern 		return -EOPNOTSUPP;
502881202305SAvraham Stern 
502981202305SAvraham Stern 	if (!check_sdata_in_driver(sdata))
503081202305SAvraham Stern 		return -EIO;
503181202305SAvraham Stern 
503281202305SAvraham Stern 	return local->ops->set_hw_timestamp(&local->hw, &sdata->vif, hwts);
503381202305SAvraham Stern }
503481202305SAvraham Stern 
50358a47cea7SJohannes Berg const struct cfg80211_ops mac80211_config_ops = {
5036fa5fea71SJohannes Berg 	.add_virtual_intf = ieee80211_add_iface,
5037fa5fea71SJohannes Berg 	.del_virtual_intf = ieee80211_del_iface,
503842613db7SJohannes Berg 	.change_virtual_intf = ieee80211_change_iface,
5039f142c6b9SJohannes Berg 	.start_p2p_device = ieee80211_start_p2p_device,
5040f142c6b9SJohannes Berg 	.stop_p2p_device = ieee80211_stop_p2p_device,
5041e8cbb4cbSJohannes Berg 	.add_key = ieee80211_add_key,
5042e8cbb4cbSJohannes Berg 	.del_key = ieee80211_del_key,
504362da92fbSJohannes Berg 	.get_key = ieee80211_get_key,
5044e8cbb4cbSJohannes Berg 	.set_default_key = ieee80211_config_default_key,
50453cfcf6acSJouni Malinen 	.set_default_mgmt_key = ieee80211_config_default_mgmt_key,
5046e5473e80SJouni Malinen 	.set_default_beacon_key = ieee80211_config_default_beacon_key,
50478860020eSJohannes Berg 	.start_ap = ieee80211_start_ap,
50488860020eSJohannes Berg 	.change_beacon = ieee80211_change_beacon,
50498860020eSJohannes Berg 	.stop_ap = ieee80211_stop_ap,
50504fd6931eSJohannes Berg 	.add_station = ieee80211_add_station,
50514fd6931eSJohannes Berg 	.del_station = ieee80211_del_station,
50524fd6931eSJohannes Berg 	.change_station = ieee80211_change_station,
50537bbdd2d9SJohannes Berg 	.get_station = ieee80211_get_station,
5054c5dd9c2bSLuis Carlos Cobo 	.dump_station = ieee80211_dump_station,
50551289723eSHolger Schurig 	.dump_survey = ieee80211_dump_survey,
5056c5dd9c2bSLuis Carlos Cobo #ifdef CONFIG_MAC80211_MESH
5057c5dd9c2bSLuis Carlos Cobo 	.add_mpath = ieee80211_add_mpath,
5058c5dd9c2bSLuis Carlos Cobo 	.del_mpath = ieee80211_del_mpath,
5059c5dd9c2bSLuis Carlos Cobo 	.change_mpath = ieee80211_change_mpath,
5060c5dd9c2bSLuis Carlos Cobo 	.get_mpath = ieee80211_get_mpath,
5061c5dd9c2bSLuis Carlos Cobo 	.dump_mpath = ieee80211_dump_mpath,
5062a2db2ed3SHenning Rogge 	.get_mpp = ieee80211_get_mpp,
5063a2db2ed3SHenning Rogge 	.dump_mpp = ieee80211_dump_mpp,
506424bdd9f4SJavier Cardona 	.update_mesh_config = ieee80211_update_mesh_config,
506524bdd9f4SJavier Cardona 	.get_mesh_config = ieee80211_get_mesh_config,
506629cbe68cSJohannes Berg 	.join_mesh = ieee80211_join_mesh,
506729cbe68cSJohannes Berg 	.leave_mesh = ieee80211_leave_mesh,
5068c5dd9c2bSLuis Carlos Cobo #endif
5069239281f8SRostislav Lisovy 	.join_ocb = ieee80211_join_ocb,
5070239281f8SRostislav Lisovy 	.leave_ocb = ieee80211_leave_ocb,
50719f1ba906SJouni Malinen 	.change_bss = ieee80211_change_bss,
5072108d2022SBenjamin Berg 	.inform_bss = ieee80211_inform_bss,
507331888487SJouni Malinen 	.set_txq_params = ieee80211_set_txq_params,
5074e8c9bd5bSJohannes Berg 	.set_monitor_channel = ieee80211_set_monitor_channel,
5075665af4fcSBob Copeland 	.suspend = ieee80211_suspend,
5076665af4fcSBob Copeland 	.resume = ieee80211_resume,
50772a519311SJohannes Berg 	.scan = ieee80211_scan,
507891f123f2SVidyullatha Kanchanapally 	.abort_scan = ieee80211_abort_scan,
507979f460caSLuciano Coelho 	.sched_scan_start = ieee80211_sched_scan_start,
508079f460caSLuciano Coelho 	.sched_scan_stop = ieee80211_sched_scan_stop,
5081636a5d36SJouni Malinen 	.auth = ieee80211_auth,
5082636a5d36SJouni Malinen 	.assoc = ieee80211_assoc,
5083636a5d36SJouni Malinen 	.deauth = ieee80211_deauth,
5084636a5d36SJouni Malinen 	.disassoc = ieee80211_disassoc,
5085af8cdcd8SJohannes Berg 	.join_ibss = ieee80211_join_ibss,
5086af8cdcd8SJohannes Berg 	.leave_ibss = ieee80211_leave_ibss,
5087391e53e3SAntonio Quartulli 	.set_mcast_rate = ieee80211_set_mcast_rate,
5088b9a5f8caSJouni Malinen 	.set_wiphy_params = ieee80211_set_wiphy_params,
50897643a2c3SJohannes Berg 	.set_tx_power = ieee80211_set_tx_power,
50907643a2c3SJohannes Berg 	.get_tx_power = ieee80211_get_tx_power,
50911f87f7d3SJohannes Berg 	.rfkill_poll = ieee80211_rfkill_poll,
5092aff89a9bSJohannes Berg 	CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
509371063f0eSWey-Yi Guy 	CFG80211_TESTMODE_DUMP(ieee80211_testmode_dump)
5094bc92afd9SJohannes Berg 	.set_power_mgmt = ieee80211_set_power_mgmt,
50959930380fSJohannes Berg 	.set_bitrate_mask = ieee80211_set_bitrate_mask,
5096b8bc4b0aSJohannes Berg 	.remain_on_channel = ieee80211_remain_on_channel,
5097b8bc4b0aSJohannes Berg 	.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
50982e161f78SJohannes Berg 	.mgmt_tx = ieee80211_mgmt_tx,
5099f30221e4SJohannes Berg 	.mgmt_tx_cancel_wait = ieee80211_mgmt_tx_cancel_wait,
5100a97c13c3SJuuso Oikarinen 	.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
51012c3c5f8cSAndrew Zaborowski 	.set_cqm_rssi_range_config = ieee80211_set_cqm_rssi_range_config,
51026cd536feSJohannes Berg 	.update_mgmt_frame_registrations =
51036cd536feSJohannes Berg 		ieee80211_update_mgmt_frame_registrations,
510415d96753SBruno Randolf 	.set_antenna = ieee80211_set_antenna,
510515d96753SBruno Randolf 	.get_antenna = ieee80211_get_antenna,
5106c68f4b89SJohannes Berg 	.set_rekey_data = ieee80211_set_rekey_data,
5107dfe018bfSArik Nemtsov 	.tdls_oper = ieee80211_tdls_oper,
5108dfe018bfSArik Nemtsov 	.tdls_mgmt = ieee80211_tdls_mgmt,
5109a7a6bdd0SArik Nemtsov 	.tdls_channel_switch = ieee80211_tdls_channel_switch,
5110a7a6bdd0SArik Nemtsov 	.tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
511106500736SJohannes Berg 	.probe_client = ieee80211_probe_client,
5112b53be792SSimon Wunderlich 	.set_noack_map = ieee80211_set_noack_map,
51136d52563fSJohannes Berg #ifdef CONFIG_PM
51146d52563fSJohannes Berg 	.set_wakeup = ieee80211_set_wakeup,
51156d52563fSJohannes Berg #endif
51165b7ccaf3SJohannes Berg 	.get_channel = ieee80211_cfg_get_channel,
5117164eb02dSSimon Wunderlich 	.start_radar_detection = ieee80211_start_radar_detection,
511826ec17a1SOrr Mazor 	.end_cac = ieee80211_end_cac,
511973da7d5bSSimon Wunderlich 	.channel_switch = ieee80211_channel_switch,
512032db6b54SKyeyoon Park 	.set_qos_map = ieee80211_set_qos_map,
51213b1700bdSJouni Malinen 	.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
512202219b3aSJohannes Berg 	.add_tx_ts = ieee80211_add_tx_ts,
512302219b3aSJohannes Berg 	.del_tx_ts = ieee80211_del_tx_ts,
5124708d50edSAyala Beker 	.start_nan = ieee80211_start_nan,
5125708d50edSAyala Beker 	.stop_nan = ieee80211_stop_nan,
51265953ff6dSAyala Beker 	.nan_change_conf = ieee80211_nan_change_conf,
5127167e33f4SAyala Beker 	.add_nan_func = ieee80211_add_nan_func,
5128167e33f4SAyala Beker 	.del_nan_func = ieee80211_del_nan_func,
5129ebceec86SMichael Braun 	.set_multicast_to_unicast = ieee80211_set_multicast_to_unicast,
513091180649SDenis Kenzior 	.tx_control_port = ieee80211_tx_control_port,
51312fe4a29aSToke Høiland-Jørgensen 	.get_txq_stats = ieee80211_get_txq_stats,
5132bc847970SPradeep Kumar Chitrapu 	.get_ftm_responder_stats = ieee80211_get_ftm_responder_stats,
5133cee7013bSJohannes Berg 	.start_pmsr = ieee80211_start_pmsr,
5134cee7013bSJohannes Berg 	.abort_pmsr = ieee80211_abort_pmsr,
51358828f81aSRajkumar Manoharan 	.probe_mesh_link = ieee80211_probe_mesh_link,
5136370f51d5STamizh chelvam 	.set_tid_config = ieee80211_set_tid_config,
5137370f51d5STamizh chelvam 	.reset_tid_config = ieee80211_reset_tid_config,
5138c534e093SCarl Huang 	.set_sar_specs = ieee80211_set_sar_specs,
51395f9404abSJohn Crispin 	.color_change = ieee80211_color_change,
5140a95bfb87SLorenzo Bianconi 	.set_radar_background = ieee80211_set_radar_background,
51410d8c4a3cSJohannes Berg 	.add_intf_link = ieee80211_add_intf_link,
51420d8c4a3cSJohannes Berg 	.del_intf_link = ieee80211_del_intf_link,
514321476ad1SShaul Triebitz 	.add_link_station = ieee80211_add_link_station,
514421476ad1SShaul Triebitz 	.mod_link_station = ieee80211_mod_link_station,
514521476ad1SShaul Triebitz 	.del_link_station = ieee80211_del_link_station,
514681202305SAvraham Stern 	.set_hw_timestamp = ieee80211_set_hw_timestamp,
5147fa5fea71SJohannes Berg };
5148