xref: /openbmc/linux/net/mac80211/driver-ops.c (revision 3f58ff6b)
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 #include "debugfs_sta.h"
11 
12 int drv_start(struct ieee80211_local *local)
13 {
14 	int ret;
15 
16 	might_sleep();
17 
18 	if (WARN_ON(local->started))
19 		return -EALREADY;
20 
21 	trace_drv_start(local);
22 	local->started = true;
23 	/* allow rx frames */
24 	smp_mb();
25 	ret = local->ops->start(&local->hw);
26 	trace_drv_return_int(local, ret);
27 
28 	if (ret)
29 		local->started = false;
30 
31 	return ret;
32 }
33 
34 void drv_stop(struct ieee80211_local *local)
35 {
36 	might_sleep();
37 
38 	if (WARN_ON(!local->started))
39 		return;
40 
41 	trace_drv_stop(local);
42 	local->ops->stop(&local->hw);
43 	trace_drv_return_void(local);
44 
45 	/* sync away all work on the tasklet before clearing started */
46 	tasklet_disable(&local->tasklet);
47 	tasklet_enable(&local->tasklet);
48 
49 	barrier();
50 
51 	local->started = false;
52 }
53 
54 int drv_add_interface(struct ieee80211_local *local,
55 		      struct ieee80211_sub_if_data *sdata)
56 {
57 	int ret;
58 
59 	might_sleep();
60 
61 	if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
62 		    (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
63 		     !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
64 		     !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
65 		return -EINVAL;
66 
67 	trace_drv_add_interface(local, sdata);
68 	ret = local->ops->add_interface(&local->hw, &sdata->vif);
69 	trace_drv_return_int(local, ret);
70 
71 	if (ret == 0)
72 		sdata->flags |= IEEE80211_SDATA_IN_DRIVER;
73 
74 	return ret;
75 }
76 
77 int drv_change_interface(struct ieee80211_local *local,
78 			 struct ieee80211_sub_if_data *sdata,
79 			 enum nl80211_iftype type, bool p2p)
80 {
81 	int ret;
82 
83 	might_sleep();
84 
85 	if (!check_sdata_in_driver(sdata))
86 		return -EIO;
87 
88 	trace_drv_change_interface(local, sdata, type, p2p);
89 	ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p);
90 	trace_drv_return_int(local, ret);
91 	return ret;
92 }
93 
94 void drv_remove_interface(struct ieee80211_local *local,
95 			  struct ieee80211_sub_if_data *sdata)
96 {
97 	might_sleep();
98 
99 	if (!check_sdata_in_driver(sdata))
100 		return;
101 
102 	trace_drv_remove_interface(local, sdata);
103 	local->ops->remove_interface(&local->hw, &sdata->vif);
104 	sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER;
105 	trace_drv_return_void(local);
106 }
107 
108 __must_check
109 int drv_sta_state(struct ieee80211_local *local,
110 		  struct ieee80211_sub_if_data *sdata,
111 		  struct sta_info *sta,
112 		  enum ieee80211_sta_state old_state,
113 		  enum ieee80211_sta_state new_state)
114 {
115 	int ret = 0;
116 
117 	might_sleep();
118 
119 	sdata = get_bss_sdata(sdata);
120 	if (!check_sdata_in_driver(sdata))
121 		return -EIO;
122 
123 	trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
124 	if (local->ops->sta_state) {
125 		ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
126 					    old_state, new_state);
127 	} else if (old_state == IEEE80211_STA_AUTH &&
128 		   new_state == IEEE80211_STA_ASSOC) {
129 		ret = drv_sta_add(local, sdata, &sta->sta);
130 		if (ret == 0) {
131 			sta->uploaded = true;
132 			if (rcu_access_pointer(sta->sta.rates))
133 				drv_sta_rate_tbl_update(local, sdata, &sta->sta);
134 		}
135 	} else if (old_state == IEEE80211_STA_ASSOC &&
136 		   new_state == IEEE80211_STA_AUTH) {
137 		drv_sta_remove(local, sdata, &sta->sta);
138 	}
139 	trace_drv_return_int(local, ret);
140 	return ret;
141 }
142 
143 __must_check
144 int drv_sta_set_txpwr(struct ieee80211_local *local,
145 		      struct ieee80211_sub_if_data *sdata,
146 		      struct sta_info *sta)
147 {
148 	int ret = -EOPNOTSUPP;
149 
150 	might_sleep();
151 
152 	sdata = get_bss_sdata(sdata);
153 	if (!check_sdata_in_driver(sdata))
154 		return -EIO;
155 
156 	trace_drv_sta_set_txpwr(local, sdata, &sta->sta);
157 	if (local->ops->sta_set_txpwr)
158 		ret = local->ops->sta_set_txpwr(&local->hw, &sdata->vif,
159 						&sta->sta);
160 	trace_drv_return_int(local, ret);
161 	return ret;
162 }
163 
164 void drv_sta_rc_update(struct ieee80211_local *local,
165 		       struct ieee80211_sub_if_data *sdata,
166 		       struct ieee80211_sta *sta, u32 changed)
167 {
168 	sdata = get_bss_sdata(sdata);
169 	if (!check_sdata_in_driver(sdata))
170 		return;
171 
172 	WARN_ON(changed & IEEE80211_RC_SUPP_RATES_CHANGED &&
173 		(sdata->vif.type != NL80211_IFTYPE_ADHOC &&
174 		 sdata->vif.type != NL80211_IFTYPE_MESH_POINT));
175 
176 	trace_drv_sta_rc_update(local, sdata, sta, changed);
177 	if (local->ops->sta_rc_update)
178 		local->ops->sta_rc_update(&local->hw, &sdata->vif,
179 					  sta, changed);
180 
181 	trace_drv_return_void(local);
182 }
183 
184 int drv_conf_tx(struct ieee80211_local *local,
185 		struct ieee80211_link_data *link, u16 ac,
186 		const struct ieee80211_tx_queue_params *params)
187 {
188 	struct ieee80211_sub_if_data *sdata = link->sdata;
189 	int ret = -EOPNOTSUPP;
190 
191 	might_sleep();
192 
193 	if (!check_sdata_in_driver(sdata))
194 		return -EIO;
195 
196 	if (sdata->vif.active_links &&
197 	    !(sdata->vif.active_links & BIT(link->link_id)))
198 		return 0;
199 
200 	if (params->cw_min == 0 || params->cw_min > params->cw_max) {
201 		/*
202 		 * If we can't configure hardware anyway, don't warn. We may
203 		 * never have initialized the CW parameters.
204 		 */
205 		WARN_ONCE(local->ops->conf_tx,
206 			  "%s: invalid CW_min/CW_max: %d/%d\n",
207 			  sdata->name, params->cw_min, params->cw_max);
208 		return -EINVAL;
209 	}
210 
211 	trace_drv_conf_tx(local, sdata, link->link_id, ac, params);
212 	if (local->ops->conf_tx)
213 		ret = local->ops->conf_tx(&local->hw, &sdata->vif,
214 					  link->link_id, ac, params);
215 	trace_drv_return_int(local, ret);
216 	return ret;
217 }
218 
219 u64 drv_get_tsf(struct ieee80211_local *local,
220 		struct ieee80211_sub_if_data *sdata)
221 {
222 	u64 ret = -1ULL;
223 
224 	might_sleep();
225 
226 	if (!check_sdata_in_driver(sdata))
227 		return ret;
228 
229 	trace_drv_get_tsf(local, sdata);
230 	if (local->ops->get_tsf)
231 		ret = local->ops->get_tsf(&local->hw, &sdata->vif);
232 	trace_drv_return_u64(local, ret);
233 	return ret;
234 }
235 
236 void drv_set_tsf(struct ieee80211_local *local,
237 		 struct ieee80211_sub_if_data *sdata,
238 		 u64 tsf)
239 {
240 	might_sleep();
241 
242 	if (!check_sdata_in_driver(sdata))
243 		return;
244 
245 	trace_drv_set_tsf(local, sdata, tsf);
246 	if (local->ops->set_tsf)
247 		local->ops->set_tsf(&local->hw, &sdata->vif, tsf);
248 	trace_drv_return_void(local);
249 }
250 
251 void drv_offset_tsf(struct ieee80211_local *local,
252 		    struct ieee80211_sub_if_data *sdata,
253 		    s64 offset)
254 {
255 	might_sleep();
256 
257 	if (!check_sdata_in_driver(sdata))
258 		return;
259 
260 	trace_drv_offset_tsf(local, sdata, offset);
261 	if (local->ops->offset_tsf)
262 		local->ops->offset_tsf(&local->hw, &sdata->vif, offset);
263 	trace_drv_return_void(local);
264 }
265 
266 void drv_reset_tsf(struct ieee80211_local *local,
267 		   struct ieee80211_sub_if_data *sdata)
268 {
269 	might_sleep();
270 
271 	if (!check_sdata_in_driver(sdata))
272 		return;
273 
274 	trace_drv_reset_tsf(local, sdata);
275 	if (local->ops->reset_tsf)
276 		local->ops->reset_tsf(&local->hw, &sdata->vif);
277 	trace_drv_return_void(local);
278 }
279 
280 int drv_assign_vif_chanctx(struct ieee80211_local *local,
281 			   struct ieee80211_sub_if_data *sdata,
282 			   struct ieee80211_bss_conf *link_conf,
283 			   struct ieee80211_chanctx *ctx)
284 {
285 	int ret = 0;
286 
287 	drv_verify_link_exists(sdata, link_conf);
288 	if (!check_sdata_in_driver(sdata))
289 		return -EIO;
290 
291 	if (sdata->vif.active_links &&
292 	    !(sdata->vif.active_links & BIT(link_conf->link_id)))
293 		return 0;
294 
295 	trace_drv_assign_vif_chanctx(local, sdata, link_conf, ctx);
296 	if (local->ops->assign_vif_chanctx) {
297 		WARN_ON_ONCE(!ctx->driver_present);
298 		ret = local->ops->assign_vif_chanctx(&local->hw,
299 						     &sdata->vif,
300 						     link_conf,
301 						     &ctx->conf);
302 	}
303 	trace_drv_return_int(local, ret);
304 
305 	return ret;
306 }
307 
308 void drv_unassign_vif_chanctx(struct ieee80211_local *local,
309 			      struct ieee80211_sub_if_data *sdata,
310 			      struct ieee80211_bss_conf *link_conf,
311 			      struct ieee80211_chanctx *ctx)
312 {
313 	might_sleep();
314 
315 	drv_verify_link_exists(sdata, link_conf);
316 	if (!check_sdata_in_driver(sdata))
317 		return;
318 
319 	if (sdata->vif.active_links &&
320 	    !(sdata->vif.active_links & BIT(link_conf->link_id)))
321 		return;
322 
323 	trace_drv_unassign_vif_chanctx(local, sdata, link_conf, ctx);
324 	if (local->ops->unassign_vif_chanctx) {
325 		WARN_ON_ONCE(!ctx->driver_present);
326 		local->ops->unassign_vif_chanctx(&local->hw,
327 						 &sdata->vif,
328 						 link_conf,
329 						 &ctx->conf);
330 	}
331 	trace_drv_return_void(local);
332 }
333 
334 int drv_switch_vif_chanctx(struct ieee80211_local *local,
335 			   struct ieee80211_vif_chanctx_switch *vifs,
336 			   int n_vifs, enum ieee80211_chanctx_switch_mode mode)
337 {
338 	int ret = 0;
339 	int i;
340 
341 	might_sleep();
342 
343 	if (!local->ops->switch_vif_chanctx)
344 		return -EOPNOTSUPP;
345 
346 	for (i = 0; i < n_vifs; i++) {
347 		struct ieee80211_chanctx *new_ctx =
348 			container_of(vifs[i].new_ctx,
349 				     struct ieee80211_chanctx,
350 				     conf);
351 		struct ieee80211_chanctx *old_ctx =
352 			container_of(vifs[i].old_ctx,
353 				     struct ieee80211_chanctx,
354 				     conf);
355 
356 		WARN_ON_ONCE(!old_ctx->driver_present);
357 		WARN_ON_ONCE((mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
358 			      new_ctx->driver_present) ||
359 			     (mode == CHANCTX_SWMODE_REASSIGN_VIF &&
360 			      !new_ctx->driver_present));
361 	}
362 
363 	trace_drv_switch_vif_chanctx(local, vifs, n_vifs, mode);
364 	ret = local->ops->switch_vif_chanctx(&local->hw,
365 					     vifs, n_vifs, mode);
366 	trace_drv_return_int(local, ret);
367 
368 	if (!ret && mode == CHANCTX_SWMODE_SWAP_CONTEXTS) {
369 		for (i = 0; i < n_vifs; i++) {
370 			struct ieee80211_chanctx *new_ctx =
371 				container_of(vifs[i].new_ctx,
372 					     struct ieee80211_chanctx,
373 					     conf);
374 			struct ieee80211_chanctx *old_ctx =
375 				container_of(vifs[i].old_ctx,
376 					     struct ieee80211_chanctx,
377 					     conf);
378 
379 			new_ctx->driver_present = true;
380 			old_ctx->driver_present = false;
381 		}
382 	}
383 
384 	return ret;
385 }
386 
387 int drv_ampdu_action(struct ieee80211_local *local,
388 		     struct ieee80211_sub_if_data *sdata,
389 		     struct ieee80211_ampdu_params *params)
390 {
391 	int ret = -EOPNOTSUPP;
392 
393 	might_sleep();
394 
395 	sdata = get_bss_sdata(sdata);
396 	if (!check_sdata_in_driver(sdata))
397 		return -EIO;
398 
399 	trace_drv_ampdu_action(local, sdata, params);
400 
401 	if (local->ops->ampdu_action)
402 		ret = local->ops->ampdu_action(&local->hw, &sdata->vif, params);
403 
404 	trace_drv_return_int(local, ret);
405 
406 	return ret;
407 }
408 
409 void drv_link_info_changed(struct ieee80211_local *local,
410 			   struct ieee80211_sub_if_data *sdata,
411 			   struct ieee80211_bss_conf *info,
412 			   int link_id, u64 changed)
413 {
414 	might_sleep();
415 
416 	if (WARN_ON_ONCE(changed & (BSS_CHANGED_BEACON |
417 				    BSS_CHANGED_BEACON_ENABLED) &&
418 			 sdata->vif.type != NL80211_IFTYPE_AP &&
419 			 sdata->vif.type != NL80211_IFTYPE_ADHOC &&
420 			 sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
421 			 sdata->vif.type != NL80211_IFTYPE_OCB))
422 		return;
423 
424 	if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
425 			 sdata->vif.type == NL80211_IFTYPE_NAN ||
426 			 (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
427 			  !sdata->vif.bss_conf.mu_mimo_owner &&
428 			  !(changed & BSS_CHANGED_TXPOWER))))
429 		return;
430 
431 	if (!check_sdata_in_driver(sdata))
432 		return;
433 
434 	if (sdata->vif.active_links &&
435 	    !(sdata->vif.active_links & BIT(link_id)))
436 		return;
437 
438 	trace_drv_link_info_changed(local, sdata, info, changed);
439 	if (local->ops->link_info_changed)
440 		local->ops->link_info_changed(&local->hw, &sdata->vif,
441 					      info, changed);
442 	else if (local->ops->bss_info_changed)
443 		local->ops->bss_info_changed(&local->hw, &sdata->vif,
444 					     info, changed);
445 	trace_drv_return_void(local);
446 }
447 
448 int drv_set_key(struct ieee80211_local *local,
449 		enum set_key_cmd cmd,
450 		struct ieee80211_sub_if_data *sdata,
451 		struct ieee80211_sta *sta,
452 		struct ieee80211_key_conf *key)
453 {
454 	int ret;
455 
456 	might_sleep();
457 
458 	sdata = get_bss_sdata(sdata);
459 	if (!check_sdata_in_driver(sdata))
460 		return -EIO;
461 
462 	if (WARN_ON(key->link_id >= 0 && sdata->vif.active_links &&
463 		    !(sdata->vif.active_links & BIT(key->link_id))))
464 		return -ENOLINK;
465 
466 	trace_drv_set_key(local, cmd, sdata, sta, key);
467 	ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key);
468 	trace_drv_return_int(local, ret);
469 	return ret;
470 }
471 
472 int drv_change_vif_links(struct ieee80211_local *local,
473 			 struct ieee80211_sub_if_data *sdata,
474 			 u16 old_links, u16 new_links,
475 			 struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
476 {
477 	int ret = -EOPNOTSUPP;
478 
479 	might_sleep();
480 
481 	if (!check_sdata_in_driver(sdata))
482 		return -EIO;
483 
484 	if (old_links == new_links)
485 		return 0;
486 
487 	trace_drv_change_vif_links(local, sdata, old_links, new_links);
488 	if (local->ops->change_vif_links)
489 		ret = local->ops->change_vif_links(&local->hw, &sdata->vif,
490 						   old_links, new_links, old);
491 	trace_drv_return_int(local, ret);
492 
493 	return ret;
494 }
495 
496 int drv_change_sta_links(struct ieee80211_local *local,
497 			 struct ieee80211_sub_if_data *sdata,
498 			 struct ieee80211_sta *sta,
499 			 u16 old_links, u16 new_links)
500 {
501 	struct sta_info *info = container_of(sta, struct sta_info, sta);
502 	struct link_sta_info *link_sta;
503 	unsigned long links_to_add;
504 	unsigned long links_to_rem;
505 	unsigned int link_id;
506 	int ret = -EOPNOTSUPP;
507 
508 	might_sleep();
509 
510 	if (!check_sdata_in_driver(sdata))
511 		return -EIO;
512 
513 	old_links &= sdata->vif.active_links;
514 	new_links &= sdata->vif.active_links;
515 
516 	if (old_links == new_links)
517 		return 0;
518 
519 	links_to_add = ~old_links & new_links;
520 	links_to_rem = old_links & ~new_links;
521 
522 	for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) {
523 		link_sta = rcu_dereference_protected(info->link[link_id],
524 						     lockdep_is_held(&local->sta_mtx));
525 
526 		ieee80211_link_sta_debugfs_drv_remove(link_sta);
527 	}
528 
529 	trace_drv_change_sta_links(local, sdata, sta, old_links, new_links);
530 	if (local->ops->change_sta_links)
531 		ret = local->ops->change_sta_links(&local->hw, &sdata->vif, sta,
532 						   old_links, new_links);
533 	trace_drv_return_int(local, ret);
534 
535 	if (ret)
536 		return ret;
537 
538 	for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) {
539 		link_sta = rcu_dereference_protected(info->link[link_id],
540 						     lockdep_is_held(&local->sta_mtx));
541 		ieee80211_link_sta_debugfs_drv_add(link_sta);
542 	}
543 
544 	return 0;
545 }
546