1 /*
2  * Copyright (c) 2017 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief IEEE 802.15.4 internal MAC and PHY Utils
10  *
11  * All references to the standard in this file cite IEEE 802.15.4-2020.
12  */
13 
14 #ifndef __IEEE802154_UTILS_H__
15 #define __IEEE802154_UTILS_H__
16 
17 #include <zephyr/net/ieee802154_radio.h>
18 #include <zephyr/sys/util_macro.h>
19 
20 /**
21  * PHY utilities
22  */
23 
ieee802154_radio_get_hw_capabilities(struct net_if * iface)24 static inline enum ieee802154_hw_caps ieee802154_radio_get_hw_capabilities(struct net_if *iface)
25 {
26 	const struct ieee802154_radio_api *radio =
27 		net_if_get_device(iface)->api;
28 
29 	if (!radio) {
30 		return 0;
31 	}
32 
33 	return radio->get_capabilities(net_if_get_device(iface));
34 }
35 
ieee802154_radio_cca(struct net_if * iface)36 static inline int ieee802154_radio_cca(struct net_if *iface)
37 {
38 	const struct ieee802154_radio_api *radio =
39 		net_if_get_device(iface)->api;
40 
41 	if (!radio) {
42 		return -ENOENT;
43 	}
44 
45 	return radio->cca(net_if_get_device(iface));
46 }
47 
ieee802154_radio_set_channel(struct net_if * iface,uint16_t channel)48 static inline int ieee802154_radio_set_channel(struct net_if *iface, uint16_t channel)
49 {
50 	const struct ieee802154_radio_api *radio =
51 		net_if_get_device(iface)->api;
52 
53 	if (!radio) {
54 		return -ENOENT;
55 	}
56 
57 	return radio->set_channel(net_if_get_device(iface), channel);
58 }
59 
ieee802154_radio_set_tx_power(struct net_if * iface,int16_t dbm)60 static inline int ieee802154_radio_set_tx_power(struct net_if *iface, int16_t dbm)
61 {
62 	const struct ieee802154_radio_api *radio =
63 		net_if_get_device(iface)->api;
64 
65 	if (!radio) {
66 		return -ENOENT;
67 	}
68 
69 	return radio->set_txpower(net_if_get_device(iface), dbm);
70 }
71 
ieee802154_radio_tx(struct net_if * iface,enum ieee802154_tx_mode mode,struct net_pkt * pkt,struct net_buf * buf)72 static inline int ieee802154_radio_tx(struct net_if *iface, enum ieee802154_tx_mode mode,
73 				      struct net_pkt *pkt, struct net_buf *buf)
74 {
75 	const struct ieee802154_radio_api *radio =
76 		net_if_get_device(iface)->api;
77 
78 	if (!radio) {
79 		return -ENOENT;
80 	}
81 
82 	return radio->tx(net_if_get_device(iface), mode, pkt, buf);
83 }
84 
ieee802154_radio_start(struct net_if * iface)85 static inline int ieee802154_radio_start(struct net_if *iface)
86 {
87 	const struct ieee802154_radio_api *radio =
88 		net_if_get_device(iface)->api;
89 
90 	if (!radio) {
91 		return -ENOENT;
92 	}
93 
94 	return radio->start(net_if_get_device(iface));
95 }
96 
ieee802154_radio_stop(struct net_if * iface)97 static inline int ieee802154_radio_stop(struct net_if *iface)
98 {
99 	const struct ieee802154_radio_api *radio =
100 		net_if_get_device(iface)->api;
101 
102 	if (!radio) {
103 		return -ENOENT;
104 	}
105 
106 	return radio->stop(net_if_get_device(iface));
107 }
108 
ieee802154_radio_attr_get(struct net_if * iface,enum ieee802154_attr attr,struct ieee802154_attr_value * value)109 static inline int ieee802154_radio_attr_get(struct net_if *iface,
110 					    enum ieee802154_attr attr,
111 					    struct ieee802154_attr_value *value)
112 {
113 	const struct ieee802154_radio_api *radio =
114 		net_if_get_device(iface)->api;
115 
116 	if (!radio || !radio->attr_get) {
117 		return -ENOENT;
118 	}
119 
120 	return radio->attr_get(net_if_get_device(iface), attr, value);
121 }
122 
123 /**
124  * Sets the radio drivers extended address filter.
125  *
126  * @param iface pointer to the IEEE 802.15.4 interface
127  * @param ieee_addr Pointer to an extended address in little endian byte order
128  */
ieee802154_radio_filter_ieee_addr(struct net_if * iface,uint8_t * ieee_addr)129 static inline void ieee802154_radio_filter_ieee_addr(struct net_if *iface, uint8_t *ieee_addr)
130 {
131 	const struct ieee802154_radio_api *radio =
132 		net_if_get_device(iface)->api;
133 
134 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
135 		      IEEE802154_HW_FILTER)) {
136 		struct ieee802154_filter filter;
137 
138 		filter.ieee_addr = ieee_addr;
139 
140 		if (radio->filter(net_if_get_device(iface), true,
141 				  IEEE802154_FILTER_TYPE_IEEE_ADDR,
142 				  &filter) != 0) {
143 			NET_WARN("Could not apply IEEE address filter");
144 		}
145 	}
146 }
147 
ieee802154_radio_filter_short_addr(struct net_if * iface,uint16_t short_addr)148 static inline void ieee802154_radio_filter_short_addr(struct net_if *iface, uint16_t short_addr)
149 {
150 	const struct ieee802154_radio_api *radio =
151 		net_if_get_device(iface)->api;
152 
153 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
154 		      IEEE802154_HW_FILTER)) {
155 		struct ieee802154_filter filter;
156 
157 		filter.short_addr = short_addr;
158 
159 		if (radio->filter(net_if_get_device(iface), true,
160 				  IEEE802154_FILTER_TYPE_SHORT_ADDR,
161 				  &filter) != 0) {
162 			NET_WARN("Could not apply short address filter");
163 		}
164 	}
165 }
166 
ieee802154_radio_filter_pan_id(struct net_if * iface,uint16_t pan_id)167 static inline void ieee802154_radio_filter_pan_id(struct net_if *iface, uint16_t pan_id)
168 {
169 	const struct ieee802154_radio_api *radio =
170 		net_if_get_device(iface)->api;
171 
172 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
173 		      IEEE802154_HW_FILTER)) {
174 		struct ieee802154_filter filter;
175 
176 		filter.pan_id = pan_id;
177 
178 		if (radio->filter(net_if_get_device(iface), true,
179 				  IEEE802154_FILTER_TYPE_PAN_ID,
180 				  &filter) != 0) {
181 			NET_WARN("Could not apply PAN ID filter");
182 		}
183 	}
184 }
185 
ieee802154_radio_filter_src_ieee_addr(struct net_if * iface,uint8_t * ieee_addr)186 static inline void ieee802154_radio_filter_src_ieee_addr(struct net_if *iface, uint8_t *ieee_addr)
187 {
188 	const struct ieee802154_radio_api *radio =
189 		net_if_get_device(iface)->api;
190 
191 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
192 		      IEEE802154_HW_FILTER)) {
193 		struct ieee802154_filter filter;
194 
195 		filter.ieee_addr = ieee_addr;
196 
197 		if (radio->filter(net_if_get_device(iface), true,
198 				  IEEE802154_FILTER_TYPE_SRC_IEEE_ADDR,
199 				  &filter) != 0) {
200 			NET_WARN("Could not apply SRC IEEE address filter");
201 		}
202 	}
203 }
204 
ieee802154_radio_filter_src_short_addr(struct net_if * iface,uint16_t short_addr)205 static inline void ieee802154_radio_filter_src_short_addr(struct net_if *iface, uint16_t short_addr)
206 {
207 	const struct ieee802154_radio_api *radio =
208 		net_if_get_device(iface)->api;
209 
210 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
211 		      IEEE802154_HW_FILTER)) {
212 		struct ieee802154_filter filter;
213 
214 		filter.short_addr = short_addr;
215 
216 		if (radio->filter(net_if_get_device(iface), true,
217 				  IEEE802154_FILTER_TYPE_SRC_SHORT_ADDR,
218 				  &filter) != 0) {
219 			NET_WARN("Could not apply SRC short address filter");
220 		}
221 	}
222 }
223 
ieee802154_radio_remove_src_ieee_addr(struct net_if * iface,uint8_t * ieee_addr)224 static inline void ieee802154_radio_remove_src_ieee_addr(struct net_if *iface, uint8_t *ieee_addr)
225 {
226 	const struct ieee802154_radio_api *radio =
227 		net_if_get_device(iface)->api;
228 
229 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
230 		      IEEE802154_HW_FILTER)) {
231 		struct ieee802154_filter filter;
232 
233 		filter.ieee_addr = ieee_addr;
234 
235 		if (radio->filter(net_if_get_device(iface), false,
236 				  IEEE802154_FILTER_TYPE_SRC_IEEE_ADDR,
237 				  &filter) != 0) {
238 			NET_WARN("Could not remove SRC IEEE address filter");
239 		}
240 	}
241 }
242 
ieee802154_radio_remove_src_short_addr(struct net_if * iface,uint16_t short_addr)243 static inline void ieee802154_radio_remove_src_short_addr(struct net_if *iface, uint16_t short_addr)
244 {
245 	const struct ieee802154_radio_api *radio =
246 		net_if_get_device(iface)->api;
247 
248 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
249 		      IEEE802154_HW_FILTER)) {
250 		struct ieee802154_filter filter;
251 
252 		filter.short_addr = short_addr;
253 
254 		if (radio->filter(net_if_get_device(iface), false,
255 				  IEEE802154_FILTER_TYPE_SRC_SHORT_ADDR,
256 				  &filter) != 0) {
257 			NET_WARN("Could not remove SRC short address filter");
258 		}
259 	}
260 }
261 
ieee802154_radio_remove_pan_id(struct net_if * iface,uint16_t pan_id)262 static inline void ieee802154_radio_remove_pan_id(struct net_if *iface, uint16_t pan_id)
263 {
264 	const struct ieee802154_radio_api *radio =
265 		net_if_get_device(iface)->api;
266 
267 	if (radio && (radio->get_capabilities(net_if_get_device(iface)) &
268 		      IEEE802154_HW_FILTER)) {
269 		struct ieee802154_filter filter;
270 
271 		filter.pan_id = pan_id;
272 
273 		if (radio->filter(net_if_get_device(iface), false,
274 				  IEEE802154_FILTER_TYPE_PAN_ID,
275 				  &filter) != 0) {
276 			NET_WARN("Could not remove PAN ID filter");
277 		}
278 	}
279 }
280 
281 
282 /**
283  * MAC utilities
284  *
285  * @note While MAC utilities may refer to PHY utilities, the inverse is not
286  * true.
287  */
288 
289 /**
290  * @brief Retrieves the currently selected channel page from the driver (see
291  * phyCurrentPage, section 11.3, table 11-2). This is PHY-related information
292  * not configured by L2 but directly provided by the driver.
293  *
294  * @param iface pointer to the IEEE 802.15.4 interface
295  *
296  * @returns The currently active channel page.
297  * @retval 0 if an error occurred
298  */
299 static inline enum ieee802154_phy_channel_page
ieee802154_radio_current_channel_page(struct net_if * iface)300 ieee802154_radio_current_channel_page(struct net_if *iface)
301 {
302 	struct ieee802154_attr_value value;
303 
304 	/* Currently we assume that drivers are statically configured to only
305 	 * support a single channel page. Once drivers need to switch channels at
306 	 * runtime this can be changed here w/o affecting clients.
307 	 */
308 	if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_PAGES, &value)) {
309 		return 0;
310 	}
311 
312 	return value.phy_supported_channel_pages;
313 }
314 
315 /**
316  * @brief Calculates a multiple of the PHY's symbol period in nanoseconds.
317  *
318  * @details The PHY's symbol period depends on the interface's current PHY
319  * configuration which usually can be derived from the currently chosen channel
320  * page and channel (phyCurrentPage and phyCurrentChannel, section 11.3, table
321  * 11-2).
322  *
323  * To calculate the symbol period of HRP UWB PHYs, the nominal pulse repetition
324  * frequency (PRF) is required. HRP UWB drivers will be expected to expose the
325  * supported norminal PRF rates as a driver attribute. Existing drivers do not
326  * allow for runtime switching of the PRF, so currently the PRF is considered to
327  * be read-only and known.
328  *
329  * TODO: Add an UwbPrf argument once drivers need to support PRF switching at
330  * runtime.
331  *
332  * @note We do not expose an API for a single symbol period to avoid having to
333  * deal with floats for PHYs that don't require it while maintaining precision
334  * in calculations where PHYs operate at symbol periods involving fractions of
335  * nanoseconds.
336  *
337  * @param iface pointer to the IEEE 802.15.4 interface
338  * @param channel The channel for which the symbol period is to be calculated.
339  * @param multiplier The factor by which the symbol period is to be multiplied.
340  *
341  * @returns A multiple of the symbol period for the given interface with
342  * nanosecond precision.
343  * @retval 0 if an error occurred.
344  */
ieee802154_radio_get_multiple_of_symbol_period(struct net_if * iface,uint16_t channel,uint16_t multiplier)345 static inline net_time_t ieee802154_radio_get_multiple_of_symbol_period(struct net_if *iface,
346 									uint16_t channel,
347 									uint16_t multiplier)
348 {
349 	/* To keep things simple we only calculate symbol periods for channel
350 	 * pages that are implemented by existing in-tree drivers. Add additional
351 	 * channel pages as required.
352 	 */
353 	switch (ieee802154_radio_current_channel_page(iface)) {
354 	case IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915:
355 		return (channel >= 11
356 				? IEEE802154_PHY_OQPSK_780_TO_2450MHZ_SYMBOL_PERIOD_NS
357 				: (channel > 0 ? IEEE802154_PHY_BPSK_915MHZ_SYMBOL_PERIOD_NS
358 					       : IEEE802154_PHY_BPSK_868MHZ_SYMBOL_PERIOD_NS)) *
359 		       multiplier;
360 
361 	case IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915:
362 		return (channel > 0 ? IEEE802154_PHY_OQPSK_780_TO_2450MHZ_SYMBOL_PERIOD_NS
363 				    : IEEE802154_PHY_OQPSK_868MHZ_SYMBOL_PERIOD_NS) *
364 		       multiplier;
365 
366 	case IEEE802154_ATTR_PHY_CHANNEL_PAGE_FOUR_HRP_UWB: {
367 		struct ieee802154_attr_value value;
368 
369 		/* Currently we assume that drivers are statically configured to
370 		 * only support a single PRF. Once drivers support switching PRF
371 		 * at runtime an UWB PRF argument needs to be added to this
372 		 * function which then must be validated against the set of
373 		 * supported PRFs.
374 		 */
375 		if (ieee802154_radio_attr_get(iface, IEEE802154_ATTR_PHY_HRP_UWB_SUPPORTED_PRFS,
376 					      &value)) {
377 			return 0;
378 		}
379 
380 		switch (value.phy_hrp_uwb_supported_nominal_prfs) {
381 		case IEEE802154_PHY_HRP_UWB_NOMINAL_4_M:
382 			return IEEE802154_PHY_HRP_UWB_PRF4_TPSYM_SYMBOL_PERIOD_NS * multiplier;
383 
384 		case IEEE802154_PHY_HRP_UWB_NOMINAL_16_M:
385 			return IEEE802154_PHY_HRP_UWB_PRF16_TPSYM_SYMBOL_PERIOD_NS * multiplier;
386 
387 		case IEEE802154_PHY_HRP_UWB_NOMINAL_64_M:
388 			return IEEE802154_PHY_HRP_UWB_PRF64_TPSYM_SYMBOL_PERIOD_NS * multiplier;
389 
390 		case IEEE802154_PHY_HRP_UWB_NOMINAL_64_M_BPRF:
391 		case IEEE802154_PHY_HRP_UWB_NOMINAL_128_M_HPRF:
392 		case IEEE802154_PHY_HRP_UWB_NOMINAL_256_M_HPRF:
393 			return IEEE802154_PHY_HRP_UWB_ERDEV_TPSYM_SYMBOL_PERIOD_NS * multiplier;
394 
395 		default:
396 			CODE_UNREACHABLE;
397 		}
398 	}
399 
400 	case IEEE802154_ATTR_PHY_CHANNEL_PAGE_FIVE_OQPSK_780:
401 		return IEEE802154_PHY_OQPSK_780_TO_2450MHZ_SYMBOL_PERIOD_NS * multiplier;
402 
403 	case IEEE802154_ATTR_PHY_CHANNEL_PAGE_NINE_SUN_PREDEFINED:
404 		/* Current SUN FSK drivers only implement legacy IEEE 802.15.4g
405 		 * 863 MHz (Europe) and 915 MHz (US ISM) bands, see IEEE
406 		 * 802.15.4g, section 5.1, table 0. Once more bands are required
407 		 * we need to request the currently active frequency band from
408 		 * the driver.
409 		 */
410 		return IEEE802154_PHY_SUN_FSK_863MHZ_915MHZ_SYMBOL_PERIOD_NS * multiplier;
411 
412 	default:
413 		CODE_UNREACHABLE;
414 	}
415 }
416 
417 /**
418  * @brief Calculates the PHY's turnaround time for the current channel page (see
419  * section 11.3, table 11-1, aTurnaroundTime) in PHY symbols.
420  *
421  * @details The PHY's turnaround time is used to calculate - among other
422  * parameters - the TX-to-RX turnaround time (see section 10.2.2) and the
423  * RX-to-TX turnaround time (see section 10.2.3).
424  *
425  * @param iface pointer to the IEEE 802.15.4 interface
426  *
427  * @returns The turnaround time for the given interface in symbols.
428  * @retval 0 if an error occurred.
429  */
ieee802154_radio_get_a_turnaround_time(struct net_if * iface)430 static inline uint32_t ieee802154_radio_get_a_turnaround_time(struct net_if *iface)
431 {
432 	enum ieee802154_phy_channel_page channel_page =
433 		ieee802154_radio_current_channel_page(iface);
434 
435 	if (!channel_page) {
436 		return 0;
437 	}
438 
439 	/* Section 11.3, table 11-1, aTurnaroundTime: "For the SUN [...] PHYs,
440 	 * the value is 1 ms expressed in symbol periods, rounded up to the next
441 	 * integer number of symbol periods using the ceiling() function. [...]
442 	 * The value is 12 [symbol periods] for all other PHYs.
443 	 */
444 
445 	if (channel_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_NINE_SUN_PREDEFINED) {
446 		/* Current SUN FSK drivers only implement legacy IEEE 802.15.4g
447 		 * 863 MHz (Europe) and 915 MHz (US ISM) bands, see IEEE
448 		 * 802.15.4g, section 5.1, table 0. Once more bands are required
449 		 * we need to request the currently active frequency band from
450 		 * the driver.
451 		 */
452 		return IEEE802154_PHY_A_TURNAROUND_TIME_1MS(
453 			IEEE802154_PHY_SUN_FSK_863MHZ_915MHZ_SYMBOL_PERIOD_NS);
454 	}
455 
456 	return IEEE802154_PHY_A_TURNAROUND_TIME_DEFAULT;
457 }
458 
459 /**
460  * @brief Verify if the given channel lies within the allowed range of available
461  * channels of the driver's currently selected channel page.
462  *
463  * @param iface pointer to the IEEE 802.15.4 interface
464  * @param channel The channel to verify or IEEE802154_NO_CHANNEL
465  *
466  * @returns true if the channel is available, false otherwise
467  */
468 bool ieee802154_radio_verify_channel(struct net_if *iface, uint16_t channel);
469 
470 /**
471  * @brief Counts all available channels of the driver's currently selected
472  * channel page.
473  *
474  * @param iface pointer to the IEEE 802.15.4 interface
475  *
476  * @returns The number of available channels.
477  */
478 uint16_t ieee802154_radio_number_of_channels(struct net_if *iface);
479 
480 /**
481  * @brief Calculates the MAC's superframe duration (see section 8.4.2,
482  * table 8-93, aBaseSuperframeDuration) in microseconds.
483  *
484  * @details The number of symbols forming a superframe when the superframe order
485  * is equal to zero.
486  *
487  * @param iface pointer to the IEEE 802.15.4 interface
488  *
489  * @returns The base superframe duration for the given interface in microseconds.
490  */
ieee802154_get_a_base_superframe_duration(struct net_if * iface)491 static inline uint32_t ieee802154_get_a_base_superframe_duration(struct net_if *iface)
492 {
493 	struct ieee802154_context *ctx = net_if_l2_data(iface);
494 
495 	return ieee802154_radio_get_multiple_of_symbol_period(
496 		       iface, ctx->channel, IEEE802154_MAC_A_BASE_SUPERFRAME_DURATION) /
497 	       NSEC_PER_USEC;
498 }
499 
500 /**
501  * @brief Retrieves macResponseWaitTime, see section 8.4.3.1, table 8-94,
502  * converted to microseconds.
503  *
504  * @details The maximum time, in multiples of aBaseSuperframeDuration converted
505  * to microseconds, a device shall wait for a response command to be available
506  * following a request command.
507  *
508  * macResponseWaitTime is a network-topology-dependent parameter and may be set
509  * to match the specific requirements of the network that a device is operating
510  * on.
511  *
512  * @note Currently this parameter is read-only and uses the specified default of 32.
513  *
514  * @param iface pointer to the IEEE 802.15.4 interface
515  *
516  * @returns The response wait time for the given interface in microseconds.
517  */
ieee802154_get_response_wait_time_us(struct net_if * iface)518 static inline uint32_t ieee802154_get_response_wait_time_us(struct net_if *iface)
519 {
520 	/* TODO: Make this parameter configurable. */
521 	return IEEE802154_MAC_RESPONSE_WAIT_TIME_DEFAULT *
522 	       ieee802154_get_a_base_superframe_duration(iface);
523 }
524 
525 #endif /* __IEEE802154_UTILS_H__ */
526