xref: /openbmc/linux/net/mac80211/driver-ops.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2727da60bSDenys Vlasenko /*
3968a76ceSEliad Peller  * Copyright 2015 Intel Deutschland GmbH
4*35de90d7SJohannes Berg  * Copyright (C) 2022-2023 Intel Corporation
5727da60bSDenys Vlasenko  */
6727da60bSDenys Vlasenko #include <net/mac80211.h>
7727da60bSDenys Vlasenko #include "ieee80211_i.h"
8727da60bSDenys Vlasenko #include "trace.h"
9727da60bSDenys Vlasenko #include "driver-ops.h"
10d2caad52SBenjamin Berg #include "debugfs_sta.h"
11170cd6a6SBenjamin Berg #include "debugfs_netdev.h"
12727da60bSDenys Vlasenko 
drv_start(struct ieee80211_local * local)13968a76ceSEliad Peller int drv_start(struct ieee80211_local *local)
14968a76ceSEliad Peller {
15968a76ceSEliad Peller 	int ret;
16968a76ceSEliad Peller 
17968a76ceSEliad Peller 	might_sleep();
18968a76ceSEliad Peller 
19968a76ceSEliad Peller 	if (WARN_ON(local->started))
20968a76ceSEliad Peller 		return -EALREADY;
21968a76ceSEliad Peller 
22968a76ceSEliad Peller 	trace_drv_start(local);
23968a76ceSEliad Peller 	local->started = true;
24968a76ceSEliad Peller 	/* allow rx frames */
25968a76ceSEliad Peller 	smp_mb();
26968a76ceSEliad Peller 	ret = local->ops->start(&local->hw);
27968a76ceSEliad Peller 	trace_drv_return_int(local, ret);
28968a76ceSEliad Peller 
29968a76ceSEliad Peller 	if (ret)
30968a76ceSEliad Peller 		local->started = false;
31968a76ceSEliad Peller 
32968a76ceSEliad Peller 	return ret;
33968a76ceSEliad Peller }
34968a76ceSEliad Peller 
drv_stop(struct ieee80211_local * local)35968a76ceSEliad Peller void drv_stop(struct ieee80211_local *local)
36968a76ceSEliad Peller {
37968a76ceSEliad Peller 	might_sleep();
38968a76ceSEliad Peller 
39968a76ceSEliad Peller 	if (WARN_ON(!local->started))
40968a76ceSEliad Peller 		return;
41968a76ceSEliad Peller 
42968a76ceSEliad Peller 	trace_drv_stop(local);
43968a76ceSEliad Peller 	local->ops->stop(&local->hw);
44968a76ceSEliad Peller 	trace_drv_return_void(local);
45968a76ceSEliad Peller 
46968a76ceSEliad Peller 	/* sync away all work on the tasklet before clearing started */
47968a76ceSEliad Peller 	tasklet_disable(&local->tasklet);
48968a76ceSEliad Peller 	tasklet_enable(&local->tasklet);
49968a76ceSEliad Peller 
50968a76ceSEliad Peller 	barrier();
51968a76ceSEliad Peller 
52968a76ceSEliad Peller 	local->started = false;
53968a76ceSEliad Peller }
54968a76ceSEliad Peller 
drv_add_interface(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata)559aae296aSDenys Vlasenko int drv_add_interface(struct ieee80211_local *local,
569aae296aSDenys Vlasenko 		      struct ieee80211_sub_if_data *sdata)
579aae296aSDenys Vlasenko {
589aae296aSDenys Vlasenko 	int ret;
599aae296aSDenys Vlasenko 
609aae296aSDenys Vlasenko 	might_sleep();
619aae296aSDenys Vlasenko 
629aae296aSDenys Vlasenko 	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
639aae296aSDenys Vlasenko 		    (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
649aae296aSDenys Vlasenko 		     !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
65d8212184SAviya Erenfeld 		     !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
669aae296aSDenys Vlasenko 		return -EINVAL;
679aae296aSDenys Vlasenko 
689aae296aSDenys Vlasenko 	trace_drv_add_interface(local, sdata);
699aae296aSDenys Vlasenko 	ret = local->ops->add_interface(&local->hw, &sdata->vif);
709aae296aSDenys Vlasenko 	trace_drv_return_int(local, ret);
719aae296aSDenys Vlasenko 
729aae296aSDenys Vlasenko 	if (ret == 0)
739aae296aSDenys Vlasenko 		sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
749aae296aSDenys Vlasenko 
759aae296aSDenys Vlasenko 	return ret;
769aae296aSDenys Vlasenko }
779aae296aSDenys Vlasenko 
drv_change_interface(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,enum nl80211_iftype type,bool p2p)789aae296aSDenys Vlasenko int drv_change_interface(struct ieee80211_local *local,
799aae296aSDenys Vlasenko 			 struct ieee80211_sub_if_data *sdata,
809aae296aSDenys Vlasenko 			 enum nl80211_iftype type, bool p2p)
819aae296aSDenys Vlasenko {
829aae296aSDenys Vlasenko 	int ret;
839aae296aSDenys Vlasenko 
849aae296aSDenys Vlasenko 	might_sleep();
859aae296aSDenys Vlasenko 
869aae296aSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
879aae296aSDenys Vlasenko 		return -EIO;
889aae296aSDenys Vlasenko 
899aae296aSDenys Vlasenko 	trace_drv_change_interface(local, sdata, type, p2p);
909aae296aSDenys Vlasenko 	ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
919aae296aSDenys Vlasenko 	trace_drv_return_int(local, ret);
929aae296aSDenys Vlasenko 	return ret;
939aae296aSDenys Vlasenko }
949aae296aSDenys Vlasenko 
drv_remove_interface(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata)959aae296aSDenys Vlasenko void drv_remove_interface(struct ieee80211_local *local,
969aae296aSDenys Vlasenko 			  struct ieee80211_sub_if_data *sdata)
979aae296aSDenys Vlasenko {
989aae296aSDenys Vlasenko 	might_sleep();
999aae296aSDenys Vlasenko 
1009aae296aSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
1019aae296aSDenys Vlasenko 		return;
1029aae296aSDenys Vlasenko 
1039aae296aSDenys Vlasenko 	trace_drv_remove_interface(local, sdata);
1049aae296aSDenys Vlasenko 	local->ops->remove_interface(&local->hw, &sdata->vif);
1059aae296aSDenys Vlasenko 	sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
1069aae296aSDenys Vlasenko 	trace_drv_return_void(local);
1079aae296aSDenys Vlasenko }
1089aae296aSDenys Vlasenko 
109727da60bSDenys Vlasenko __must_check
drv_sta_state(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct sta_info * sta,enum ieee80211_sta_state old_state,enum ieee80211_sta_state new_state)110727da60bSDenys Vlasenko int drv_sta_state(struct ieee80211_local *local,
111727da60bSDenys Vlasenko 		  struct ieee80211_sub_if_data *sdata,
112727da60bSDenys Vlasenko 		  struct sta_info *sta,
113727da60bSDenys Vlasenko 		  enum ieee80211_sta_state old_state,
114727da60bSDenys Vlasenko 		  enum ieee80211_sta_state new_state)
115727da60bSDenys Vlasenko {
116727da60bSDenys Vlasenko 	int ret = 0;
117727da60bSDenys Vlasenko 
118727da60bSDenys Vlasenko 	might_sleep();
119727da60bSDenys Vlasenko 
120727da60bSDenys Vlasenko 	sdata = get_bss_sdata(sdata);
121727da60bSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
122727da60bSDenys Vlasenko 		return -EIO;
123727da60bSDenys Vlasenko 
124727da60bSDenys Vlasenko 	trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
125727da60bSDenys Vlasenko 	if (local->ops->sta_state) {
126727da60bSDenys Vlasenko 		ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
127727da60bSDenys Vlasenko 					    old_state, new_state);
128727da60bSDenys Vlasenko 	} else if (old_state == IEEE80211_STA_AUTH &&
129727da60bSDenys Vlasenko 		   new_state == IEEE80211_STA_ASSOC) {
130727da60bSDenys Vlasenko 		ret = drv_sta_add(local, sdata, &sta->sta);
13118fe0faeSFelix Fietkau 		if (ret == 0) {
132727da60bSDenys Vlasenko 			sta->uploaded = true;
13318fe0faeSFelix Fietkau 			if (rcu_access_pointer(sta->sta.rates))
13418fe0faeSFelix Fietkau 				drv_sta_rate_tbl_update(local, sdata, &sta->sta);
13518fe0faeSFelix Fietkau 		}
136727da60bSDenys Vlasenko 	} else if (old_state == IEEE80211_STA_ASSOC &&
137727da60bSDenys Vlasenko 		   new_state == IEEE80211_STA_AUTH) {
138727da60bSDenys Vlasenko 		drv_sta_remove(local, sdata, &sta->sta);
139727da60bSDenys Vlasenko 	}
140727da60bSDenys Vlasenko 	trace_drv_return_int(local, ret);
141727da60bSDenys Vlasenko 	return ret;
142727da60bSDenys Vlasenko }
143b23dcd4aSDenys Vlasenko 
144ba905bf4SAshok Raj Nagarajan __must_check
drv_sta_set_txpwr(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct sta_info * sta)145ba905bf4SAshok Raj Nagarajan int drv_sta_set_txpwr(struct ieee80211_local *local,
146ba905bf4SAshok Raj Nagarajan 		      struct ieee80211_sub_if_data *sdata,
147ba905bf4SAshok Raj Nagarajan 		      struct sta_info *sta)
148ba905bf4SAshok Raj Nagarajan {
149ba905bf4SAshok Raj Nagarajan 	int ret = -EOPNOTSUPP;
150ba905bf4SAshok Raj Nagarajan 
151ba905bf4SAshok Raj Nagarajan 	might_sleep();
152ba905bf4SAshok Raj Nagarajan 
153ba905bf4SAshok Raj Nagarajan 	sdata = get_bss_sdata(sdata);
154ba905bf4SAshok Raj Nagarajan 	if (!check_sdata_in_driver(sdata))
155ba905bf4SAshok Raj Nagarajan 		return -EIO;
156ba905bf4SAshok Raj Nagarajan 
157ba905bf4SAshok Raj Nagarajan 	trace_drv_sta_set_txpwr(local, sdata, &sta->sta);
158ba905bf4SAshok Raj Nagarajan 	if (local->ops->sta_set_txpwr)
159ba905bf4SAshok Raj Nagarajan 		ret = local->ops->sta_set_txpwr(&local->hw, &sdata->vif,
160ba905bf4SAshok Raj Nagarajan 						&sta->sta);
161ba905bf4SAshok Raj Nagarajan 	trace_drv_return_int(local, ret);
162ba905bf4SAshok Raj Nagarajan 	return ret;
163ba905bf4SAshok Raj Nagarajan }
164ba905bf4SAshok Raj Nagarajan 
drv_sta_rc_update(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_sta * sta,u32 changed)1654fbd572cSDenys Vlasenko void drv_sta_rc_update(struct ieee80211_local *local,
1664fbd572cSDenys Vlasenko 		       struct ieee80211_sub_if_data *sdata,
1674fbd572cSDenys Vlasenko 		       struct ieee80211_sta *sta, u32 changed)
1684fbd572cSDenys Vlasenko {
1694fbd572cSDenys Vlasenko 	sdata = get_bss_sdata(sdata);
1704fbd572cSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
1714fbd572cSDenys Vlasenko 		return;
1724fbd572cSDenys Vlasenko 
1734fbd572cSDenys Vlasenko 	WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
1744fbd572cSDenys Vlasenko 		(sdata->vif.type != NL80211_IFTYPE_ADHOC &&
1754fbd572cSDenys Vlasenko 		 sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
1764fbd572cSDenys Vlasenko 
1774fbd572cSDenys Vlasenko 	trace_drv_sta_rc_update(local, sdata, sta, changed);
1784fbd572cSDenys Vlasenko 	if (local->ops->sta_rc_update)
1794fbd572cSDenys Vlasenko 		local->ops->sta_rc_update(&local->hw, &sdata->vif,
1804fbd572cSDenys Vlasenko 					  sta, changed);
1814fbd572cSDenys Vlasenko 
1824fbd572cSDenys Vlasenko 	trace_drv_return_void(local);
1834fbd572cSDenys Vlasenko }
1844fbd572cSDenys Vlasenko 
drv_conf_tx(struct ieee80211_local * local,struct ieee80211_link_data * link,u16 ac,const struct ieee80211_tx_queue_params * params)185b23dcd4aSDenys Vlasenko int drv_conf_tx(struct ieee80211_local *local,
186b3e2130bSJohannes Berg 		struct ieee80211_link_data *link, u16 ac,
187b23dcd4aSDenys Vlasenko 		const struct ieee80211_tx_queue_params *params)
188b23dcd4aSDenys Vlasenko {
189b3e2130bSJohannes Berg 	struct ieee80211_sub_if_data *sdata = link->sdata;
190b23dcd4aSDenys Vlasenko 	int ret = -EOPNOTSUPP;
191b23dcd4aSDenys Vlasenko 
192b23dcd4aSDenys Vlasenko 	might_sleep();
193b23dcd4aSDenys Vlasenko 
194b23dcd4aSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
195b23dcd4aSDenys Vlasenko 		return -EIO;
196b23dcd4aSDenys Vlasenko 
197efe9c2bfSJohannes Berg 	if (sdata->vif.active_links &&
198efe9c2bfSJohannes Berg 	    !(sdata->vif.active_links & BIT(link->link_id)))
199efe9c2bfSJohannes Berg 		return 0;
200efe9c2bfSJohannes Berg 
201d2b3fe42SBrian Norris 	if (params->cw_min == 0 || params->cw_min > params->cw_max) {
202d2b3fe42SBrian Norris 		/*
203d2b3fe42SBrian Norris 		 * If we can't configure hardware anyway, don't warn. We may
204d2b3fe42SBrian Norris 		 * never have initialized the CW parameters.
205d2b3fe42SBrian Norris 		 */
206d2b3fe42SBrian Norris 		WARN_ONCE(local->ops->conf_tx,
207b23dcd4aSDenys Vlasenko 			  "%s: invalid CW_min/CW_max: %d/%d\n",
208d2b3fe42SBrian Norris 			  sdata->name, params->cw_min, params->cw_max);
209b23dcd4aSDenys Vlasenko 		return -EINVAL;
210d2b3fe42SBrian Norris 	}
211b23dcd4aSDenys Vlasenko 
212b3e2130bSJohannes Berg 	trace_drv_conf_tx(local, sdata, link->link_id, ac, params);
213b23dcd4aSDenys Vlasenko 	if (local->ops->conf_tx)
214b23dcd4aSDenys Vlasenko 		ret = local->ops->conf_tx(&local->hw, &sdata->vif,
215b3e2130bSJohannes Berg 					  link->link_id, ac, params);
216b23dcd4aSDenys Vlasenko 	trace_drv_return_int(local, ret);
217b23dcd4aSDenys Vlasenko 	return ret;
218b23dcd4aSDenys Vlasenko }
21942677ed3SDenys Vlasenko 
drv_get_tsf(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata)220416eb9fcSDenys Vlasenko u64 drv_get_tsf(struct ieee80211_local *local,
221416eb9fcSDenys Vlasenko 		struct ieee80211_sub_if_data *sdata)
222416eb9fcSDenys Vlasenko {
223416eb9fcSDenys Vlasenko 	u64 ret = -1ULL;
224416eb9fcSDenys Vlasenko 
225416eb9fcSDenys Vlasenko 	might_sleep();
226416eb9fcSDenys Vlasenko 
227416eb9fcSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
228416eb9fcSDenys Vlasenko 		return ret;
229416eb9fcSDenys Vlasenko 
230416eb9fcSDenys Vlasenko 	trace_drv_get_tsf(local, sdata);
231416eb9fcSDenys Vlasenko 	if (local->ops->get_tsf)
232416eb9fcSDenys Vlasenko 		ret = local->ops->get_tsf(&local->hw, &sdata->vif);
233416eb9fcSDenys Vlasenko 	trace_drv_return_u64(local, ret);
234416eb9fcSDenys Vlasenko 	return ret;
235416eb9fcSDenys Vlasenko }
236416eb9fcSDenys Vlasenko 
drv_set_tsf(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,u64 tsf)237416eb9fcSDenys Vlasenko void drv_set_tsf(struct ieee80211_local *local,
238416eb9fcSDenys Vlasenko 		 struct ieee80211_sub_if_data *sdata,
239416eb9fcSDenys Vlasenko 		 u64 tsf)
240416eb9fcSDenys Vlasenko {
241416eb9fcSDenys Vlasenko 	might_sleep();
242416eb9fcSDenys Vlasenko 
243416eb9fcSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
244416eb9fcSDenys Vlasenko 		return;
245416eb9fcSDenys Vlasenko 
246416eb9fcSDenys Vlasenko 	trace_drv_set_tsf(local, sdata, tsf);
247416eb9fcSDenys Vlasenko 	if (local->ops->set_tsf)
248416eb9fcSDenys Vlasenko 		local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
249416eb9fcSDenys Vlasenko 	trace_drv_return_void(local);
250416eb9fcSDenys Vlasenko }
251416eb9fcSDenys Vlasenko 
drv_offset_tsf(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,s64 offset)252354d381bSPedersen, Thomas void drv_offset_tsf(struct ieee80211_local *local,
253354d381bSPedersen, Thomas 		    struct ieee80211_sub_if_data *sdata,
254354d381bSPedersen, Thomas 		    s64 offset)
255354d381bSPedersen, Thomas {
256354d381bSPedersen, Thomas 	might_sleep();
257354d381bSPedersen, Thomas 
258354d381bSPedersen, Thomas 	if (!check_sdata_in_driver(sdata))
259354d381bSPedersen, Thomas 		return;
260354d381bSPedersen, Thomas 
261354d381bSPedersen, Thomas 	trace_drv_offset_tsf(local, sdata, offset);
262354d381bSPedersen, Thomas 	if (local->ops->offset_tsf)
263354d381bSPedersen, Thomas 		local->ops->offset_tsf(&local->hw, &sdata->vif, offset);
264354d381bSPedersen, Thomas 	trace_drv_return_void(local);
265354d381bSPedersen, Thomas }
266354d381bSPedersen, Thomas 
drv_reset_tsf(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata)267416eb9fcSDenys Vlasenko void drv_reset_tsf(struct ieee80211_local *local,
268416eb9fcSDenys Vlasenko 		   struct ieee80211_sub_if_data *sdata)
269416eb9fcSDenys Vlasenko {
270416eb9fcSDenys Vlasenko 	might_sleep();
271416eb9fcSDenys Vlasenko 
272416eb9fcSDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
273416eb9fcSDenys Vlasenko 		return;
274416eb9fcSDenys Vlasenko 
275416eb9fcSDenys Vlasenko 	trace_drv_reset_tsf(local, sdata);
276416eb9fcSDenys Vlasenko 	if (local->ops->reset_tsf)
277416eb9fcSDenys Vlasenko 		local->ops->reset_tsf(&local->hw, &sdata->vif);
278416eb9fcSDenys Vlasenko 	trace_drv_return_void(local);
279416eb9fcSDenys Vlasenko }
280416eb9fcSDenys Vlasenko 
drv_assign_vif_chanctx(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_bss_conf * link_conf,struct ieee80211_chanctx * ctx)281efe9c2bfSJohannes Berg int drv_assign_vif_chanctx(struct ieee80211_local *local,
282efe9c2bfSJohannes Berg 			   struct ieee80211_sub_if_data *sdata,
283efe9c2bfSJohannes Berg 			   struct ieee80211_bss_conf *link_conf,
284efe9c2bfSJohannes Berg 			   struct ieee80211_chanctx *ctx)
285efe9c2bfSJohannes Berg {
286efe9c2bfSJohannes Berg 	int ret = 0;
287efe9c2bfSJohannes Berg 
288efe9c2bfSJohannes Berg 	drv_verify_link_exists(sdata, link_conf);
289efe9c2bfSJohannes Berg 	if (!check_sdata_in_driver(sdata))
290efe9c2bfSJohannes Berg 		return -EIO;
291efe9c2bfSJohannes Berg 
292efe9c2bfSJohannes Berg 	if (sdata->vif.active_links &&
293efe9c2bfSJohannes Berg 	    !(sdata->vif.active_links & BIT(link_conf->link_id)))
294efe9c2bfSJohannes Berg 		return 0;
295efe9c2bfSJohannes Berg 
296efe9c2bfSJohannes Berg 	trace_drv_assign_vif_chanctx(local, sdata, link_conf, ctx);
297efe9c2bfSJohannes Berg 	if (local->ops->assign_vif_chanctx) {
298efe9c2bfSJohannes Berg 		WARN_ON_ONCE(!ctx->driver_present);
299efe9c2bfSJohannes Berg 		ret = local->ops->assign_vif_chanctx(&local->hw,
300efe9c2bfSJohannes Berg 						     &sdata->vif,
301efe9c2bfSJohannes Berg 						     link_conf,
302efe9c2bfSJohannes Berg 						     &ctx->conf);
303efe9c2bfSJohannes Berg 	}
304efe9c2bfSJohannes Berg 	trace_drv_return_int(local, ret);
305efe9c2bfSJohannes Berg 
306efe9c2bfSJohannes Berg 	return ret;
307efe9c2bfSJohannes Berg }
308efe9c2bfSJohannes Berg 
drv_unassign_vif_chanctx(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_bss_conf * link_conf,struct ieee80211_chanctx * ctx)309efe9c2bfSJohannes Berg void drv_unassign_vif_chanctx(struct ieee80211_local *local,
310efe9c2bfSJohannes Berg 			      struct ieee80211_sub_if_data *sdata,
311efe9c2bfSJohannes Berg 			      struct ieee80211_bss_conf *link_conf,
312efe9c2bfSJohannes Berg 			      struct ieee80211_chanctx *ctx)
313efe9c2bfSJohannes Berg {
314efe9c2bfSJohannes Berg 	might_sleep();
315efe9c2bfSJohannes Berg 
316efe9c2bfSJohannes Berg 	drv_verify_link_exists(sdata, link_conf);
317efe9c2bfSJohannes Berg 	if (!check_sdata_in_driver(sdata))
318efe9c2bfSJohannes Berg 		return;
319efe9c2bfSJohannes Berg 
320efe9c2bfSJohannes Berg 	if (sdata->vif.active_links &&
321efe9c2bfSJohannes Berg 	    !(sdata->vif.active_links & BIT(link_conf->link_id)))
322efe9c2bfSJohannes Berg 		return;
323efe9c2bfSJohannes Berg 
324efe9c2bfSJohannes Berg 	trace_drv_unassign_vif_chanctx(local, sdata, link_conf, ctx);
325efe9c2bfSJohannes Berg 	if (local->ops->unassign_vif_chanctx) {
326efe9c2bfSJohannes Berg 		WARN_ON_ONCE(!ctx->driver_present);
327efe9c2bfSJohannes Berg 		local->ops->unassign_vif_chanctx(&local->hw,
328efe9c2bfSJohannes Berg 						 &sdata->vif,
329efe9c2bfSJohannes Berg 						 link_conf,
330efe9c2bfSJohannes Berg 						 &ctx->conf);
331efe9c2bfSJohannes Berg 	}
332efe9c2bfSJohannes Berg 	trace_drv_return_void(local);
333efe9c2bfSJohannes Berg }
334efe9c2bfSJohannes Berg 
drv_switch_vif_chanctx(struct ieee80211_local * local,struct ieee80211_vif_chanctx_switch * vifs,int n_vifs,enum ieee80211_chanctx_switch_mode mode)33542677ed3SDenys Vlasenko int drv_switch_vif_chanctx(struct ieee80211_local *local,
33642677ed3SDenys Vlasenko 			   struct ieee80211_vif_chanctx_switch *vifs,
33742677ed3SDenys Vlasenko 			   int n_vifs, enum ieee80211_chanctx_switch_mode mode)
33842677ed3SDenys Vlasenko {
33942677ed3SDenys Vlasenko 	int ret = 0;
34042677ed3SDenys Vlasenko 	int i;
34142677ed3SDenys Vlasenko 
342dcae9e02SChaitanya T K 	might_sleep();
343dcae9e02SChaitanya T K 
34442677ed3SDenys Vlasenko 	if (!local->ops->switch_vif_chanctx)
34542677ed3SDenys Vlasenko 		return -EOPNOTSUPP;
34642677ed3SDenys Vlasenko 
34742677ed3SDenys Vlasenko 	for (i = 0; i < n_vifs; i++) {
34842677ed3SDenys Vlasenko 		struct ieee80211_chanctx *new_ctx =
34942677ed3SDenys Vlasenko 			container_of(vifs[i].new_ctx,
35042677ed3SDenys Vlasenko 				     struct ieee80211_chanctx,
35142677ed3SDenys Vlasenko 				     conf);
35242677ed3SDenys Vlasenko 		struct ieee80211_chanctx *old_ctx =
35342677ed3SDenys Vlasenko 			container_of(vifs[i].old_ctx,
35442677ed3SDenys Vlasenko 				     struct ieee80211_chanctx,
35542677ed3SDenys Vlasenko 				     conf);
35642677ed3SDenys Vlasenko 
35742677ed3SDenys Vlasenko 		WARN_ON_ONCE(!old_ctx->driver_present);
35842677ed3SDenys Vlasenko 		WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
35942677ed3SDenys Vlasenko 			      new_ctx->driver_present) ||
36042677ed3SDenys Vlasenko 			     (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
36142677ed3SDenys Vlasenko 			      !new_ctx->driver_present));
36242677ed3SDenys Vlasenko 	}
36342677ed3SDenys Vlasenko 
36442677ed3SDenys Vlasenko 	trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
36542677ed3SDenys Vlasenko 	ret = local->ops->switch_vif_chanctx(&local->hw,
36642677ed3SDenys Vlasenko 					     vifs, n_vifs, mode);
36742677ed3SDenys Vlasenko 	trace_drv_return_int(local, ret);
36842677ed3SDenys Vlasenko 
36942677ed3SDenys Vlasenko 	if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
37042677ed3SDenys Vlasenko 		for (i = 0; i < n_vifs; i++) {
37142677ed3SDenys Vlasenko 			struct ieee80211_chanctx *new_ctx =
37242677ed3SDenys Vlasenko 				container_of(vifs[i].new_ctx,
37342677ed3SDenys Vlasenko 					     struct ieee80211_chanctx,
37442677ed3SDenys Vlasenko 					     conf);
37542677ed3SDenys Vlasenko 			struct ieee80211_chanctx *old_ctx =
37642677ed3SDenys Vlasenko 				container_of(vifs[i].old_ctx,
37742677ed3SDenys Vlasenko 					     struct ieee80211_chanctx,
37842677ed3SDenys Vlasenko 					     conf);
37942677ed3SDenys Vlasenko 
38042677ed3SDenys Vlasenko 			new_ctx->driver_present = true;
38142677ed3SDenys Vlasenko 			old_ctx->driver_present = false;
38242677ed3SDenys Vlasenko 		}
38342677ed3SDenys Vlasenko 	}
38442677ed3SDenys Vlasenko 
38542677ed3SDenys Vlasenko 	return ret;
38642677ed3SDenys Vlasenko }
3876db96838SDenys Vlasenko 
drv_ampdu_action(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_ampdu_params * params)3886db96838SDenys Vlasenko int drv_ampdu_action(struct ieee80211_local *local,
3896db96838SDenys Vlasenko 		     struct ieee80211_sub_if_data *sdata,
39050ea05efSSara Sharon 		     struct ieee80211_ampdu_params *params)
3916db96838SDenys Vlasenko {
3926db96838SDenys Vlasenko 	int ret = -EOPNOTSUPP;
3936db96838SDenys Vlasenko 
3946db96838SDenys Vlasenko 	might_sleep();
3956db96838SDenys Vlasenko 
3966db96838SDenys Vlasenko 	sdata = get_bss_sdata(sdata);
3976db96838SDenys Vlasenko 	if (!check_sdata_in_driver(sdata))
3986db96838SDenys Vlasenko 		return -EIO;
3996db96838SDenys Vlasenko 
40050ea05efSSara Sharon 	trace_drv_ampdu_action(local, sdata, params);
4016db96838SDenys Vlasenko 
4026db96838SDenys Vlasenko 	if (local->ops->ampdu_action)
40350ea05efSSara Sharon 		ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params);
4046db96838SDenys Vlasenko 
4056db96838SDenys Vlasenko 	trace_drv_return_int(local, ret);
4066db96838SDenys Vlasenko 
4076db96838SDenys Vlasenko 	return ret;
4086db96838SDenys Vlasenko }
409efe9c2bfSJohannes Berg 
drv_link_info_changed(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_bss_conf * info,int link_id,u64 changed)410efe9c2bfSJohannes Berg void drv_link_info_changed(struct ieee80211_local *local,
411efe9c2bfSJohannes Berg 			   struct ieee80211_sub_if_data *sdata,
412efe9c2bfSJohannes Berg 			   struct ieee80211_bss_conf *info,
413efe9c2bfSJohannes Berg 			   int link_id, u64 changed)
414efe9c2bfSJohannes Berg {
415efe9c2bfSJohannes Berg 	might_sleep();
416efe9c2bfSJohannes Berg 
417efe9c2bfSJohannes Berg 	if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
418efe9c2bfSJohannes Berg 				    BSS_CHANGED_BEACON_ENABLED) &&
419efe9c2bfSJohannes Berg 			 sdata->vif.type != NL80211_IFTYPE_AP &&
420efe9c2bfSJohannes Berg 			 sdata->vif.type != NL80211_IFTYPE_ADHOC &&
421efe9c2bfSJohannes Berg 			 sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
422efe9c2bfSJohannes Berg 			 sdata->vif.type != NL80211_IFTYPE_OCB))
423efe9c2bfSJohannes Berg 		return;
424efe9c2bfSJohannes Berg 
425efe9c2bfSJohannes Berg 	if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
426efe9c2bfSJohannes Berg 			 sdata->vif.type == NL80211_IFTYPE_NAN ||
427efe9c2bfSJohannes Berg 			 (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
428efe9c2bfSJohannes Berg 			  !sdata->vif.bss_conf.mu_mimo_owner &&
429efe9c2bfSJohannes Berg 			  !(changed & BSS_CHANGED_TXPOWER))))
430efe9c2bfSJohannes Berg 		return;
431efe9c2bfSJohannes Berg 
432efe9c2bfSJohannes Berg 	if (!check_sdata_in_driver(sdata))
433efe9c2bfSJohannes Berg 		return;
434efe9c2bfSJohannes Berg 
435efe9c2bfSJohannes Berg 	if (sdata->vif.active_links &&
436efe9c2bfSJohannes Berg 	    !(sdata->vif.active_links & BIT(link_id)))
437efe9c2bfSJohannes Berg 		return;
438efe9c2bfSJohannes Berg 
439efe9c2bfSJohannes Berg 	trace_drv_link_info_changed(local, sdata, info, changed);
440efe9c2bfSJohannes Berg 	if (local->ops->link_info_changed)
441efe9c2bfSJohannes Berg 		local->ops->link_info_changed(&local->hw, &sdata->vif,
442efe9c2bfSJohannes Berg 					      info, changed);
443efe9c2bfSJohannes Berg 	else if (local->ops->bss_info_changed)
444efe9c2bfSJohannes Berg 		local->ops->bss_info_changed(&local->hw, &sdata->vif,
445efe9c2bfSJohannes Berg 					     info, changed);
446efe9c2bfSJohannes Berg 	trace_drv_return_void(local);
447efe9c2bfSJohannes Berg }
448efe9c2bfSJohannes Berg 
drv_set_key(struct ieee80211_local * local,enum set_key_cmd cmd,struct ieee80211_sub_if_data * sdata,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)449efe9c2bfSJohannes Berg int drv_set_key(struct ieee80211_local *local,
450efe9c2bfSJohannes Berg 		enum set_key_cmd cmd,
451efe9c2bfSJohannes Berg 		struct ieee80211_sub_if_data *sdata,
452efe9c2bfSJohannes Berg 		struct ieee80211_sta *sta,
453efe9c2bfSJohannes Berg 		struct ieee80211_key_conf *key)
454efe9c2bfSJohannes Berg {
455efe9c2bfSJohannes Berg 	int ret;
456efe9c2bfSJohannes Berg 
457efe9c2bfSJohannes Berg 	might_sleep();
458efe9c2bfSJohannes Berg 
459efe9c2bfSJohannes Berg 	sdata = get_bss_sdata(sdata);
460efe9c2bfSJohannes Berg 	if (!check_sdata_in_driver(sdata))
461efe9c2bfSJohannes Berg 		return -EIO;
462efe9c2bfSJohannes Berg 
463efe9c2bfSJohannes Berg 	if (WARN_ON(key->link_id >= 0 && sdata->vif.active_links &&
464efe9c2bfSJohannes Berg 		    !(sdata->vif.active_links & BIT(key->link_id))))
465efe9c2bfSJohannes Berg 		return -ENOLINK;
466efe9c2bfSJohannes Berg 
467efe9c2bfSJohannes Berg 	trace_drv_set_key(local, cmd, sdata, sta, key);
468efe9c2bfSJohannes Berg 	ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
469efe9c2bfSJohannes Berg 	trace_drv_return_int(local, ret);
470efe9c2bfSJohannes Berg 	return ret;
471efe9c2bfSJohannes Berg }
472efe9c2bfSJohannes Berg 
drv_change_vif_links(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,u16 old_links,u16 new_links,struct ieee80211_bss_conf * old[IEEE80211_MLD_MAX_NUM_LINKS])473efe9c2bfSJohannes Berg int drv_change_vif_links(struct ieee80211_local *local,
474efe9c2bfSJohannes Berg 			 struct ieee80211_sub_if_data *sdata,
475efe9c2bfSJohannes Berg 			 u16 old_links, u16 new_links,
476efe9c2bfSJohannes Berg 			 struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
477efe9c2bfSJohannes Berg {
478170cd6a6SBenjamin Berg 	struct ieee80211_link_data *link;
479170cd6a6SBenjamin Berg 	unsigned long links_to_add;
480170cd6a6SBenjamin Berg 	unsigned long links_to_rem;
481170cd6a6SBenjamin Berg 	unsigned int link_id;
482efe9c2bfSJohannes Berg 	int ret = -EOPNOTSUPP;
483efe9c2bfSJohannes Berg 
484efe9c2bfSJohannes Berg 	might_sleep();
485efe9c2bfSJohannes Berg 
486efe9c2bfSJohannes Berg 	if (!check_sdata_in_driver(sdata))
487efe9c2bfSJohannes Berg 		return -EIO;
488efe9c2bfSJohannes Berg 
489efe9c2bfSJohannes Berg 	if (old_links == new_links)
490efe9c2bfSJohannes Berg 		return 0;
491efe9c2bfSJohannes Berg 
492170cd6a6SBenjamin Berg 	links_to_add = ~old_links & new_links;
493170cd6a6SBenjamin Berg 	links_to_rem = old_links & ~new_links;
494170cd6a6SBenjamin Berg 
495170cd6a6SBenjamin Berg 	for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
496170cd6a6SBenjamin Berg 		link = rcu_access_pointer(sdata->link[link_id]);
497170cd6a6SBenjamin Berg 
498170cd6a6SBenjamin Berg 		ieee80211_link_debugfs_drv_remove(link);
499170cd6a6SBenjamin Berg 	}
500170cd6a6SBenjamin Berg 
501efe9c2bfSJohannes Berg 	trace_drv_change_vif_links(local, sdata, old_links, new_links);
502efe9c2bfSJohannes Berg 	if (local->ops->change_vif_links)
503efe9c2bfSJohannes Berg 		ret = local->ops->change_vif_links(&local->hw, &sdata->vif,
504efe9c2bfSJohannes Berg 						   old_links, new_links, old);
505efe9c2bfSJohannes Berg 	trace_drv_return_int(local, ret);
506efe9c2bfSJohannes Berg 
507170cd6a6SBenjamin Berg 	if (ret)
508efe9c2bfSJohannes Berg 		return ret;
509170cd6a6SBenjamin Berg 
510d0803ca6SJohannes Berg 	if (!local->in_reconfig) {
511d0803ca6SJohannes Berg 		for_each_set_bit(link_id, &links_to_add,
512d0803ca6SJohannes Berg 				 IEEE80211_MLD_MAX_NUM_LINKS) {
513170cd6a6SBenjamin Berg 			link = rcu_access_pointer(sdata->link[link_id]);
514170cd6a6SBenjamin Berg 
515170cd6a6SBenjamin Berg 			ieee80211_link_debugfs_drv_add(link);
516170cd6a6SBenjamin Berg 		}
517d0803ca6SJohannes Berg 	}
518170cd6a6SBenjamin Berg 
519170cd6a6SBenjamin Berg 	return 0;
520efe9c2bfSJohannes Berg }
521efe9c2bfSJohannes Berg 
drv_change_sta_links(struct ieee80211_local * local,struct ieee80211_sub_if_data * sdata,struct ieee80211_sta * sta,u16 old_links,u16 new_links)522efe9c2bfSJohannes Berg int drv_change_sta_links(struct ieee80211_local *local,
523efe9c2bfSJohannes Berg 			 struct ieee80211_sub_if_data *sdata,
524efe9c2bfSJohannes Berg 			 struct ieee80211_sta *sta,
525efe9c2bfSJohannes Berg 			 u16 old_links, u16 new_links)
526efe9c2bfSJohannes Berg {
527d2caad52SBenjamin Berg 	struct sta_info *info = container_of(sta, struct sta_info, sta);
528d2caad52SBenjamin Berg 	struct link_sta_info *link_sta;
529d2caad52SBenjamin Berg 	unsigned long links_to_add;
530d2caad52SBenjamin Berg 	unsigned long links_to_rem;
531d2caad52SBenjamin Berg 	unsigned int link_id;
532efe9c2bfSJohannes Berg 	int ret = -EOPNOTSUPP;
533efe9c2bfSJohannes Berg 
534efe9c2bfSJohannes Berg 	might_sleep();
535efe9c2bfSJohannes Berg 
536efe9c2bfSJohannes Berg 	if (!check_sdata_in_driver(sdata))
537efe9c2bfSJohannes Berg 		return -EIO;
538efe9c2bfSJohannes Berg 
539efe9c2bfSJohannes Berg 	old_links &= sdata->vif.active_links;
540efe9c2bfSJohannes Berg 	new_links &= sdata->vif.active_links;
541efe9c2bfSJohannes Berg 
542efe9c2bfSJohannes Berg 	if (old_links == new_links)
543efe9c2bfSJohannes Berg 		return 0;
544efe9c2bfSJohannes Berg 
545d2caad52SBenjamin Berg 	links_to_add = ~old_links & new_links;
546d2caad52SBenjamin Berg 	links_to_rem = old_links & ~new_links;
547d2caad52SBenjamin Berg 
548d2caad52SBenjamin Berg 	for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
549d2caad52SBenjamin Berg 		link_sta = rcu_dereference_protected(info->link[link_id],
550d2caad52SBenjamin Berg 						     lockdep_is_held(&local->sta_mtx));
551d2caad52SBenjamin Berg 
552d2caad52SBenjamin Berg 		ieee80211_link_sta_debugfs_drv_remove(link_sta);
553d2caad52SBenjamin Berg 	}
554d2caad52SBenjamin Berg 
555efe9c2bfSJohannes Berg 	trace_drv_change_sta_links(local, sdata, sta, old_links, new_links);
556efe9c2bfSJohannes Berg 	if (local->ops->change_sta_links)
557efe9c2bfSJohannes Berg 		ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta,
558efe9c2bfSJohannes Berg 						   old_links, new_links);
559efe9c2bfSJohannes Berg 	trace_drv_return_int(local, ret);
560efe9c2bfSJohannes Berg 
561d2caad52SBenjamin Berg 	if (ret)
562efe9c2bfSJohannes Berg 		return ret;
563d2caad52SBenjamin Berg 
564*35de90d7SJohannes Berg 	/* during reconfig don't add it to debugfs again */
565*35de90d7SJohannes Berg 	if (local->in_reconfig)
566*35de90d7SJohannes Berg 		return 0;
567*35de90d7SJohannes Berg 
568d2caad52SBenjamin Berg 	for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
569d2caad52SBenjamin Berg 		link_sta = rcu_dereference_protected(info->link[link_id],
570d2caad52SBenjamin Berg 						     lockdep_is_held(&local->sta_mtx));
571d2caad52SBenjamin Berg 		ieee80211_link_sta_debugfs_drv_add(link_sta);
572d2caad52SBenjamin Berg 	}
573d2caad52SBenjamin Berg 
574d2caad52SBenjamin Berg 	return 0;
575efe9c2bfSJohannes Berg }
576