1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef __MAC802154_DRIVER_OPS
3#define __MAC802154_DRIVER_OPS
4
5#include <linux/types.h>
6#include <linux/rtnetlink.h>
7
8#include <net/mac802154.h>
9
10#include "ieee802154_i.h"
11#include "trace.h"
12
13static inline int
14drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
15{
16	return local->ops->xmit_async(&local->hw, skb);
17}
18
19static inline int
20drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
21{
22	might_sleep();
23
24	return local->ops->xmit_sync(&local->hw, skb);
25}
26
27static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
28{
29	struct ieee802154_hw_addr_filt filt;
30	int ret;
31
32	might_sleep();
33
34	if (!local->ops->set_hw_addr_filt) {
35		WARN_ON(1);
36		return -EOPNOTSUPP;
37	}
38
39	filt.pan_id = pan_id;
40
41	trace_802154_drv_set_pan_id(local, pan_id);
42	ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
43					    IEEE802154_AFILT_PANID_CHANGED);
44	trace_802154_drv_return_int(local, ret);
45	return ret;
46}
47
48static inline int
49drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
50{
51	struct ieee802154_hw_addr_filt filt;
52	int ret;
53
54	might_sleep();
55
56	if (!local->ops->set_hw_addr_filt) {
57		WARN_ON(1);
58		return -EOPNOTSUPP;
59	}
60
61	filt.ieee_addr = extended_addr;
62
63	trace_802154_drv_set_extended_addr(local, extended_addr);
64	ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
65					    IEEE802154_AFILT_IEEEADDR_CHANGED);
66	trace_802154_drv_return_int(local, ret);
67	return ret;
68}
69
70static inline int
71drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
72{
73	struct ieee802154_hw_addr_filt filt;
74	int ret;
75
76	might_sleep();
77
78	if (!local->ops->set_hw_addr_filt) {
79		WARN_ON(1);
80		return -EOPNOTSUPP;
81	}
82
83	filt.short_addr = short_addr;
84
85	trace_802154_drv_set_short_addr(local, short_addr);
86	ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
87					    IEEE802154_AFILT_SADDR_CHANGED);
88	trace_802154_drv_return_int(local, ret);
89	return ret;
90}
91
92static inline int
93drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
94{
95	struct ieee802154_hw_addr_filt filt;
96	int ret;
97
98	might_sleep();
99
100	if (!local->ops->set_hw_addr_filt) {
101		WARN_ON(1);
102		return -EOPNOTSUPP;
103	}
104
105	filt.pan_coord = is_coord;
106
107	trace_802154_drv_set_pan_coord(local, is_coord);
108	ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
109					    IEEE802154_AFILT_PANC_CHANGED);
110	trace_802154_drv_return_int(local, ret);
111	return ret;
112}
113
114static inline int
115drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
116{
117	int ret;
118
119	might_sleep();
120
121	if (!local->ops->set_promiscuous_mode) {
122		WARN_ON(1);
123		return -EOPNOTSUPP;
124	}
125
126	trace_802154_drv_set_promiscuous_mode(local, on);
127	ret = local->ops->set_promiscuous_mode(&local->hw, on);
128	trace_802154_drv_return_int(local, ret);
129	return ret;
130}
131
132static inline int drv_start(struct ieee802154_local *local,
133			    enum ieee802154_filtering_level level,
134			    const struct ieee802154_hw_addr_filt *addr_filt)
135{
136	int ret;
137
138	might_sleep();
139
140	/* setup receive mode parameters e.g. address mode */
141	if (local->hw.flags & IEEE802154_HW_AFILT) {
142		ret = drv_set_pan_id(local, addr_filt->pan_id);
143		if (ret < 0)
144			return ret;
145
146		ret = drv_set_short_addr(local, addr_filt->short_addr);
147		if (ret < 0)
148			return ret;
149
150		ret = drv_set_extended_addr(local, addr_filt->ieee_addr);
151		if (ret < 0)
152			return ret;
153	}
154
155	switch (level) {
156	case IEEE802154_FILTERING_NONE:
157		fallthrough;
158	case IEEE802154_FILTERING_1_FCS:
159		fallthrough;
160	case IEEE802154_FILTERING_2_PROMISCUOUS:
161		/* TODO: Requires a different receive mode setup e.g.
162		 * at86rf233 hardware.
163		 */
164		fallthrough;
165	case IEEE802154_FILTERING_3_SCAN:
166		if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
167			ret = drv_set_promiscuous_mode(local, true);
168			if (ret < 0)
169				return ret;
170		} else {
171			return -EOPNOTSUPP;
172		}
173
174		/* In practice other filtering levels can be requested, but as
175		 * for now most hardware/drivers only support
176		 * IEEE802154_FILTERING_NONE, we fallback to this actual
177		 * filtering level in hardware and make our own additional
178		 * filtering in mac802154 receive path.
179		 *
180		 * TODO: Move this logic to the device drivers as hardware may
181		 * support more higher level filters. Hardware may also require
182		 * a different order how register are set, which could currently
183		 * be buggy, so all received parameters need to be moved to the
184		 * start() callback and let the driver go into the mode before
185		 * it will turn on receive handling.
186		 */
187		local->phy->filtering = IEEE802154_FILTERING_NONE;
188		break;
189	case IEEE802154_FILTERING_4_FRAME_FIELDS:
190		/* Do not error out if IEEE802154_HW_PROMISCUOUS because we
191		 * expect the hardware to operate at the level
192		 * IEEE802154_FILTERING_4_FRAME_FIELDS anyway.
193		 */
194		if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
195			ret = drv_set_promiscuous_mode(local, false);
196			if (ret < 0)
197				return ret;
198		}
199
200		local->phy->filtering = IEEE802154_FILTERING_4_FRAME_FIELDS;
201		break;
202	default:
203		WARN_ON(1);
204		return -EINVAL;
205	}
206
207	trace_802154_drv_start(local);
208	local->started = true;
209	smp_mb();
210	ret = local->ops->start(&local->hw);
211	trace_802154_drv_return_int(local, ret);
212	return ret;
213}
214
215static inline void drv_stop(struct ieee802154_local *local)
216{
217	might_sleep();
218
219	trace_802154_drv_stop(local);
220	local->ops->stop(&local->hw);
221	trace_802154_drv_return_void(local);
222
223	/* sync away all work on the tasklet before clearing started */
224	tasklet_disable(&local->tasklet);
225	tasklet_enable(&local->tasklet);
226
227	barrier();
228
229	local->started = false;
230}
231
232static inline int
233drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel)
234{
235	int ret;
236
237	might_sleep();
238
239	trace_802154_drv_set_channel(local, page, channel);
240	ret = local->ops->set_channel(&local->hw, page, channel);
241	trace_802154_drv_return_int(local, ret);
242	return ret;
243}
244
245static inline int drv_set_tx_power(struct ieee802154_local *local, s32 mbm)
246{
247	int ret;
248
249	might_sleep();
250
251	if (!local->ops->set_txpower) {
252		WARN_ON(1);
253		return -EOPNOTSUPP;
254	}
255
256	trace_802154_drv_set_tx_power(local, mbm);
257	ret = local->ops->set_txpower(&local->hw, mbm);
258	trace_802154_drv_return_int(local, ret);
259	return ret;
260}
261
262static inline int drv_set_cca_mode(struct ieee802154_local *local,
263				   const struct wpan_phy_cca *cca)
264{
265	int ret;
266
267	might_sleep();
268
269	if (!local->ops->set_cca_mode) {
270		WARN_ON(1);
271		return -EOPNOTSUPP;
272	}
273
274	trace_802154_drv_set_cca_mode(local, cca);
275	ret = local->ops->set_cca_mode(&local->hw, cca);
276	trace_802154_drv_return_int(local, ret);
277	return ret;
278}
279
280static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
281{
282	int ret;
283
284	might_sleep();
285
286	if (!local->ops->set_lbt) {
287		WARN_ON(1);
288		return -EOPNOTSUPP;
289	}
290
291	trace_802154_drv_set_lbt_mode(local, mode);
292	ret = local->ops->set_lbt(&local->hw, mode);
293	trace_802154_drv_return_int(local, ret);
294	return ret;
295}
296
297static inline int
298drv_set_cca_ed_level(struct ieee802154_local *local, s32 mbm)
299{
300	int ret;
301
302	might_sleep();
303
304	if (!local->ops->set_cca_ed_level) {
305		WARN_ON(1);
306		return -EOPNOTSUPP;
307	}
308
309	trace_802154_drv_set_cca_ed_level(local, mbm);
310	ret = local->ops->set_cca_ed_level(&local->hw, mbm);
311	trace_802154_drv_return_int(local, ret);
312	return ret;
313}
314
315static inline int
316drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
317		    u8 max_csma_backoffs)
318{
319	int ret;
320
321	might_sleep();
322
323	if (!local->ops->set_csma_params) {
324		WARN_ON(1);
325		return -EOPNOTSUPP;
326	}
327
328	trace_802154_drv_set_csma_params(local, min_be, max_be,
329					 max_csma_backoffs);
330	ret = local->ops->set_csma_params(&local->hw, min_be, max_be,
331					   max_csma_backoffs);
332	trace_802154_drv_return_int(local, ret);
333	return ret;
334}
335
336static inline int
337drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
338{
339	int ret;
340
341	might_sleep();
342
343	if (!local->ops->set_frame_retries) {
344		WARN_ON(1);
345		return -EOPNOTSUPP;
346	}
347
348	trace_802154_drv_set_max_frame_retries(local, max_frame_retries);
349	ret = local->ops->set_frame_retries(&local->hw, max_frame_retries);
350	trace_802154_drv_return_int(local, ret);
351	return ret;
352}
353
354#endif /* __MAC802154_DRIVER_OPS */
355