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(¶ms, 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, ¶ms);
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, ¶ms->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, ¶ms->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 ¶ms->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 ¶ms->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 ¶ms->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(¶ms->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 ¶ms->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(¶ms->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(¶ms->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, ¶ms->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, ¶ms->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(¶ms->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 ¶ms->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