1 /*
2  * Copyright (c) 2016 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * All references to the spec refer to IEEE 802.15.4-2020.
7  */
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(net_ieee802154_csma, CONFIG_NET_L2_IEEE802154_LOG_LEVEL);
11 
12 #include <zephyr/net/ieee802154.h>
13 #include <zephyr/net/ieee802154_radio.h>
14 #include <zephyr/net/net_if.h>
15 #include <zephyr/random/random.h>
16 #include <zephyr/toolchain.h>
17 
18 #include <errno.h>
19 #include <stdlib.h>
20 
21 #include "ieee802154_priv.h"
22 #include "ieee802154_utils.h"
23 
24 BUILD_ASSERT(CONFIG_NET_L2_IEEE802154_RADIO_CSMA_CA_MIN_BE <=
25 		     CONFIG_NET_L2_IEEE802154_RADIO_CSMA_CA_MAX_BE,
26 	     "The CSMA/CA min backoff exponent must be less or equal max backoff exponent.");
27 
28 /* See section 6.2.5.1. */
unslotted_csma_ca_channel_access(struct net_if * iface)29 static inline int unslotted_csma_ca_channel_access(struct net_if *iface)
30 {
31 	struct ieee802154_context *ctx = net_if_l2_data(iface);
32 	uint8_t be = CONFIG_NET_L2_IEEE802154_RADIO_CSMA_CA_MIN_BE;
33 	uint32_t turnaround_time, unit_backoff_period_us;
34 
35 	turnaround_time = ieee802154_radio_get_a_turnaround_time(iface);
36 	unit_backoff_period_us = ieee802154_radio_get_multiple_of_symbol_period(
37 					 iface, ctx->channel,
38 					 IEEE802154_MAC_A_UNIT_BACKOFF_PERIOD(turnaround_time)) /
39 				 NSEC_PER_USEC;
40 
41 	for (uint8_t nb = 0U; nb <= CONFIG_NET_L2_IEEE802154_RADIO_CSMA_CA_MAX_BO; nb++) {
42 		int ret;
43 
44 		if (be) {
45 			uint8_t bo_n = sys_rand32_get() & ((1 << be) - 1);
46 
47 			/* TODO: k_busy_wait() is too inaccurate on many platforms, the
48 			 * radio API should expose a precise radio clock instead (which may
49 			 * fall back to k_busy_wait() if the radio does not have a clock).
50 			 */
51 			k_busy_wait(bo_n * unit_backoff_period_us);
52 		}
53 
54 		ret = ieee802154_radio_cca(iface);
55 		if (ret == 0) {
56 			/* Channel is idle -> CSMA Success */
57 			return 0;
58 		} else if (ret != -EBUSY) {
59 			/* CCA exited with failure code -> CSMA Abort */
60 			return -EIO;
61 		}
62 
63 		/* Channel is busy -> CSMA Backoff */
64 		be = MIN(be + 1, CONFIG_NET_L2_IEEE802154_RADIO_CSMA_CA_MAX_BE);
65 	}
66 
67 	/* Channel is still busy after max backoffs -> CSMA Failure */
68 	return -EBUSY;
69 }
70 
71 /* Declare the public channel access algorithm function used by L2. */
72 FUNC_ALIAS(unslotted_csma_ca_channel_access, ieee802154_wait_for_clear_channel, int);
73