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