xref: /openbmc/linux/net/mac80211/driver-ops.c (revision 2a9eb57e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2015 Intel Deutschland GmbH
4  * Copyright (C) 2022 Intel Corporation
5  */
6 #include <net/mac80211.h>
7 #include "ieee80211_i.h"
8 #include "trace.h"
9 #include "driver-ops.h"
10 
11 int drv_start(struct ieee80211_local *local)
12 {
13 	int ret;
14 
15 	might_sleep();
16 
17 	if (WARN_ON(local->started))
18 		return -EALREADY;
19 
20 	trace_drv_start(local);
21 	local->started = true;
22 	/* allow rx frames */
23 	smp_mb();
24 	ret = local->ops->start(&local->hw);
25 	trace_drv_return_int(local, ret);
26 
27 	if (ret)
28 		local->started = false;
29 
30 	return ret;
31 }
32 
33 void drv_stop(struct ieee80211_local *local)
34 {
35 	might_sleep();
36 
37 	if (WARN_ON(!local->started))
38 		return;
39 
40 	trace_drv_stop(local);
41 	local->ops->stop(&local->hw);
42 	trace_drv_return_void(local);
43 
44 	/* sync away all work on the tasklet before clearing started */
45 	tasklet_disable(&local->tasklet);
46 	tasklet_enable(&local->tasklet);
47 
48 	barrier();
49 
50 	local->started = false;
51 }
52 
53 int drv_add_interface(struct ieee80211_local *local,
54 		      struct ieee80211_sub_if_data *sdata)
55 {
56 	int ret;
57 
58 	might_sleep();
59 
60 	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
61 		    (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
62 		     !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
63 		     !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
64 		return -EINVAL;
65 
66 	trace_drv_add_interface(local, sdata);
67 	ret = local->ops->add_interface(&local->hw, &sdata->vif);
68 	trace_drv_return_int(local, ret);
69 
70 	if (ret == 0)
71 		sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
72 
73 	return ret;
74 }
75 
76 int drv_change_interface(struct ieee80211_local *local,
77 			 struct ieee80211_sub_if_data *sdata,
78 			 enum nl80211_iftype type, bool p2p)
79 {
80 	int ret;
81 
82 	might_sleep();
83 
84 	if (!check_sdata_in_driver(sdata))
85 		return -EIO;
86 
87 	trace_drv_change_interface(local, sdata, type, p2p);
88 	ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
89 	trace_drv_return_int(local, ret);
90 	return ret;
91 }
92 
93 void drv_remove_interface(struct ieee80211_local *local,
94 			  struct ieee80211_sub_if_data *sdata)
95 {
96 	might_sleep();
97 
98 	if (!check_sdata_in_driver(sdata))
99 		return;
100 
101 	trace_drv_remove_interface(local, sdata);
102 	local->ops->remove_interface(&local->hw, &sdata->vif);
103 	sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
104 	trace_drv_return_void(local);
105 }
106 
107 __must_check
108 int drv_sta_state(struct ieee80211_local *local,
109 		  struct ieee80211_sub_if_data *sdata,
110 		  struct sta_info *sta,
111 		  enum ieee80211_sta_state old_state,
112 		  enum ieee80211_sta_state new_state)
113 {
114 	int ret = 0;
115 
116 	might_sleep();
117 
118 	sdata = get_bss_sdata(sdata);
119 	if (!check_sdata_in_driver(sdata))
120 		return -EIO;
121 
122 	trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
123 	if (local->ops->sta_state) {
124 		ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
125 					    old_state, new_state);
126 	} else if (old_state == IEEE80211_STA_AUTH &&
127 		   new_state == IEEE80211_STA_ASSOC) {
128 		ret = drv_sta_add(local, sdata, &sta->sta);
129 		if (ret == 0) {
130 			sta->uploaded = true;
131 			if (rcu_access_pointer(sta->sta.rates))
132 				drv_sta_rate_tbl_update(local, sdata, &sta->sta);
133 		}
134 	} else if (old_state == IEEE80211_STA_ASSOC &&
135 		   new_state == IEEE80211_STA_AUTH) {
136 		drv_sta_remove(local, sdata, &sta->sta);
137 	}
138 	trace_drv_return_int(local, ret);
139 	return ret;
140 }
141 
142 __must_check
143 int drv_sta_set_txpwr(struct ieee80211_local *local,
144 		      struct ieee80211_sub_if_data *sdata,
145 		      struct sta_info *sta)
146 {
147 	int ret = -EOPNOTSUPP;
148 
149 	might_sleep();
150 
151 	sdata = get_bss_sdata(sdata);
152 	if (!check_sdata_in_driver(sdata))
153 		return -EIO;
154 
155 	trace_drv_sta_set_txpwr(local, sdata, &sta->sta);
156 	if (local->ops->sta_set_txpwr)
157 		ret = local->ops->sta_set_txpwr(&local->hw, &sdata->vif,
158 						&sta->sta);
159 	trace_drv_return_int(local, ret);
160 	return ret;
161 }
162 
163 void drv_sta_rc_update(struct ieee80211_local *local,
164 		       struct ieee80211_sub_if_data *sdata,
165 		       struct ieee80211_sta *sta, u32 changed)
166 {
167 	sdata = get_bss_sdata(sdata);
168 	if (!check_sdata_in_driver(sdata))
169 		return;
170 
171 	WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
172 		(sdata->vif.type != NL80211_IFTYPE_ADHOC &&
173 		 sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
174 
175 	trace_drv_sta_rc_update(local, sdata, sta, changed);
176 	if (local->ops->sta_rc_update)
177 		local->ops->sta_rc_update(&local->hw, &sdata->vif,
178 					  sta, changed);
179 
180 	trace_drv_return_void(local);
181 }
182 
183 int drv_conf_tx(struct ieee80211_local *local,
184 		struct ieee80211_link_data *link, u16 ac,
185 		const struct ieee80211_tx_queue_params *params)
186 {
187 	struct ieee80211_sub_if_data *sdata = link->sdata;
188 	int ret = -EOPNOTSUPP;
189 
190 	might_sleep();
191 
192 	if (!check_sdata_in_driver(sdata))
193 		return -EIO;
194 
195 	if (params->cw_min == 0 || params->cw_min > params->cw_max) {
196 		/*
197 		 * If we can't configure hardware anyway, don't warn. We may
198 		 * never have initialized the CW parameters.
199 		 */
200 		WARN_ONCE(local->ops->conf_tx,
201 			  "%s: invalid CW_min/CW_max: %d/%d\n",
202 			  sdata->name, params->cw_min, params->cw_max);
203 		return -EINVAL;
204 	}
205 
206 	trace_drv_conf_tx(local, sdata, link->link_id, ac, params);
207 	if (local->ops->conf_tx)
208 		ret = local->ops->conf_tx(&local->hw, &sdata->vif,
209 					  link->link_id, ac, params);
210 	trace_drv_return_int(local, ret);
211 	return ret;
212 }
213 
214 u64 drv_get_tsf(struct ieee80211_local *local,
215 		struct ieee80211_sub_if_data *sdata)
216 {
217 	u64 ret = -1ULL;
218 
219 	might_sleep();
220 
221 	if (!check_sdata_in_driver(sdata))
222 		return ret;
223 
224 	trace_drv_get_tsf(local, sdata);
225 	if (local->ops->get_tsf)
226 		ret = local->ops->get_tsf(&local->hw, &sdata->vif);
227 	trace_drv_return_u64(local, ret);
228 	return ret;
229 }
230 
231 void drv_set_tsf(struct ieee80211_local *local,
232 		 struct ieee80211_sub_if_data *sdata,
233 		 u64 tsf)
234 {
235 	might_sleep();
236 
237 	if (!check_sdata_in_driver(sdata))
238 		return;
239 
240 	trace_drv_set_tsf(local, sdata, tsf);
241 	if (local->ops->set_tsf)
242 		local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
243 	trace_drv_return_void(local);
244 }
245 
246 void drv_offset_tsf(struct ieee80211_local *local,
247 		    struct ieee80211_sub_if_data *sdata,
248 		    s64 offset)
249 {
250 	might_sleep();
251 
252 	if (!check_sdata_in_driver(sdata))
253 		return;
254 
255 	trace_drv_offset_tsf(local, sdata, offset);
256 	if (local->ops->offset_tsf)
257 		local->ops->offset_tsf(&local->hw, &sdata->vif, offset);
258 	trace_drv_return_void(local);
259 }
260 
261 void drv_reset_tsf(struct ieee80211_local *local,
262 		   struct ieee80211_sub_if_data *sdata)
263 {
264 	might_sleep();
265 
266 	if (!check_sdata_in_driver(sdata))
267 		return;
268 
269 	trace_drv_reset_tsf(local, sdata);
270 	if (local->ops->reset_tsf)
271 		local->ops->reset_tsf(&local->hw, &sdata->vif);
272 	trace_drv_return_void(local);
273 }
274 
275 int drv_switch_vif_chanctx(struct ieee80211_local *local,
276 			   struct ieee80211_vif_chanctx_switch *vifs,
277 			   int n_vifs, enum ieee80211_chanctx_switch_mode mode)
278 {
279 	int ret = 0;
280 	int i;
281 
282 	might_sleep();
283 
284 	if (!local->ops->switch_vif_chanctx)
285 		return -EOPNOTSUPP;
286 
287 	for (i = 0; i < n_vifs; i++) {
288 		struct ieee80211_chanctx *new_ctx =
289 			container_of(vifs[i].new_ctx,
290 				     struct ieee80211_chanctx,
291 				     conf);
292 		struct ieee80211_chanctx *old_ctx =
293 			container_of(vifs[i].old_ctx,
294 				     struct ieee80211_chanctx,
295 				     conf);
296 
297 		WARN_ON_ONCE(!old_ctx->driver_present);
298 		WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
299 			      new_ctx->driver_present) ||
300 			     (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
301 			      !new_ctx->driver_present));
302 	}
303 
304 	trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
305 	ret = local->ops->switch_vif_chanctx(&local->hw,
306 					     vifs, n_vifs, mode);
307 	trace_drv_return_int(local, ret);
308 
309 	if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
310 		for (i = 0; i < n_vifs; i++) {
311 			struct ieee80211_chanctx *new_ctx =
312 				container_of(vifs[i].new_ctx,
313 					     struct ieee80211_chanctx,
314 					     conf);
315 			struct ieee80211_chanctx *old_ctx =
316 				container_of(vifs[i].old_ctx,
317 					     struct ieee80211_chanctx,
318 					     conf);
319 
320 			new_ctx->driver_present = true;
321 			old_ctx->driver_present = false;
322 		}
323 	}
324 
325 	return ret;
326 }
327 
328 int drv_ampdu_action(struct ieee80211_local *local,
329 		     struct ieee80211_sub_if_data *sdata,
330 		     struct ieee80211_ampdu_params *params)
331 {
332 	int ret = -EOPNOTSUPP;
333 
334 	might_sleep();
335 
336 	sdata = get_bss_sdata(sdata);
337 	if (!check_sdata_in_driver(sdata))
338 		return -EIO;
339 
340 	trace_drv_ampdu_action(local, sdata, params);
341 
342 	if (local->ops->ampdu_action)
343 		ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params);
344 
345 	trace_drv_return_int(local, ret);
346 
347 	return ret;
348 }
349