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 | |
13 | static inline int |
14 | drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb) |
15 | { |
16 | return local->ops->xmit_async(&local->hw, skb); |
17 | } |
18 | |
19 | static inline int |
20 | drv_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 | |
27 | static 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 | |
48 | static inline int |
49 | drv_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 | |
70 | static inline int |
71 | drv_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 | |
92 | static inline int |
93 | drv_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 | |
114 | static inline int |
115 | drv_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 | |
132 | static 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, pan_id: addr_filt->pan_id); |
143 | if (ret < 0) |
144 | return ret; |
145 | |
146 | ret = drv_set_short_addr(local, short_addr: addr_filt->short_addr); |
147 | if (ret < 0) |
148 | return ret; |
149 | |
150 | ret = drv_set_extended_addr(local, extended_addr: 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, on: 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, on: 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 | |
215 | static 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(t: &local->tasklet); |
225 | tasklet_enable(t: &local->tasklet); |
226 | |
227 | barrier(); |
228 | |
229 | local->started = false; |
230 | } |
231 | |
232 | static inline int |
233 | drv_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 | |
245 | static 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, power: mbm); |
257 | ret = local->ops->set_txpower(&local->hw, mbm); |
258 | trace_802154_drv_return_int(local, ret); |
259 | return ret; |
260 | } |
261 | |
262 | static 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 | |
280 | static 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 | |
297 | static inline int |
298 | drv_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 | |
315 | static inline int |
316 | drv_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 | |
336 | static inline int |
337 | drv_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 | |