From e100a67099ba4d9df6a6045c20468f5a969f7ecb Mon Sep 17 00:00:00 2001 From: Jose Alamos Date: Fri, 4 Sep 2020 11:18:02 +0200 Subject: [PATCH] ieee802154_submac: add initial implementation --- Makefile.dep | 5 + makefiles/pseudomodules.inc.mk | 1 + sys/include/net/ieee802154/submac.h | 391 ++++++++++++++++++++++++ sys/net/link_layer/ieee802154/Makefile | 10 + sys/net/link_layer/ieee802154/submac.c | 398 +++++++++++++++++++++++++ 5 files changed, 805 insertions(+) create mode 100644 sys/include/net/ieee802154/submac.h create mode 100644 sys/net/link_layer/ieee802154/submac.c diff --git a/Makefile.dep b/Makefile.dep index 5dece5690e..b48a10e402 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -437,6 +437,11 @@ ifneq (,$(filter gnrc_pktdump,$(USEMODULE))) USEMODULE += od endif +ifneq (,$(filter ieee802154_submac,$(USEMODULE))) + USEMODULE += luid + USEMODULE += xtimer +endif + ifneq (,$(filter od,$(USEMODULE))) USEMODULE += fmt endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index d3e9d84756..42f09ab9e9 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -60,6 +60,7 @@ PSEUDOMODULES += gnrc_txtsnd PSEUDOMODULES += heap_cmd PSEUDOMODULES += i2c_scan PSEUDOMODULES += ieee802154_radio_hal +PSEUDOMODULES += ieee802154_submac PSEUDOMODULES += ina3221_alerts PSEUDOMODULES += l2filter_blacklist PSEUDOMODULES += l2filter_whitelist diff --git a/sys/include/net/ieee802154/submac.h b/sys/include/net/ieee802154/submac.h new file mode 100644 index 0000000000..514de51281 --- /dev/null +++ b/sys/include/net/ieee802154/submac.h @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @defgroup net_ieee802154_submac IEEE802.15.4 SubMAC layer + * @ingroup net_ieee802154 + * @experimental This API is experimental and in an early state - expect + * changes! + + * @brief This module defines a common layer for handling the lower + * part of the IEEE 802.15.4 MAC layer. + * + * This layer is responsible for: + * - Handling CSMA-CA and retransmissions. + * - Maintaining part of the MAC Information Base, e.g IEEE 802.15.4 addresses, + * channel settings, CSMA-CA params, etc. + * + * @{ + * + * @author José I. Alamos + */ +#ifndef NET_IEEE802154_SUBMAC_H +#define NET_IEEE802154_SUBMAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "net/ieee802154.h" +#include "net/ieee802154/radio.h" + +#define IEEE802154_SUBMAC_MAX_RETRANSMISSIONS (4U) /**< maximum number of frame retransmissions */ + +/** + * @brief IEEE 802.15.4 SubMAC forward declaration + */ +typedef struct ieee802154_submac ieee802154_submac_t; + +/** + * @brief SubMAC states + */ +typedef enum { + /** + * @brief SubMAC and network devices are off. + * + * The corresponding network device is put in a state with the + * lowest energy consumption. + */ + IEEE802154_STATE_OFF, + + /** + * @brief SubMAC is ready to be used. + */ + IEEE802154_STATE_IDLE, + + /** + * @brief SubMAC is ready to be used and listening to incoming frames. + */ + IEEE802154_STATE_LISTEN, +} ieee802154_submac_state_t; + +/** + * @brief IEEE 802.15.4 SubMAC callbacks. + */ +typedef struct { + /** + * @brief RX done event + * + * This function is called from the SubMAC to indicate a IEEE 802.15.4 + * frame is ready to be fetched from the device. + * + * If @ref ieee802154_submac_t::state is @ref IEEE802154_STATE_LISTEN, the + * SubMAC is ready to receive frames. + * + * @note ACK frames are automatically handled and discarded by the SubMAC. + * @param[in] submac pointer to the SubMAC descriptor + */ + void (*rx_done)(ieee802154_submac_t *submac); + /** + * @brief TX done event + * + * This function is called from the SubMAC to indicate that the TX + * procedure finished. + * + * If @ref ieee802154_submac_t::state is @ref IEEE802154_STATE_LISTEN, the + * SubMAC is ready to receive frames. + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[out] info TX information associated to the transmission (status, + * number of retransmissions, pending bit, etc). + */ + void (*tx_done)(ieee802154_submac_t *submac, int status, + ieee802154_tx_info_t *info); +} ieee802154_submac_cb_t; + +/** + * @brief IEEE 802.15.4 SubMAC descriptor + */ +struct ieee802154_submac { + eui64_t ext_addr; /**< IEEE 802.15.4 extended address */ + network_uint16_t short_addr; /**< IEEE 802.15.4 short address */ + ieee802154_dev_t *dev; /**< pointer to the 802.15.4 HAL descriptor */ + const ieee802154_submac_cb_t *cb; /**< pointer to the SubMAC callbacks */ + ieee802154_csma_be_t be; /**< CSMA-CA backoff exponent params */ + bool wait_for_ack; /**< SubMAC is waiting for an ACK frame */ + bool tx; /**< SubMAC is currently transmitting a frame */ + uint16_t panid; /**< IEEE 802.15.4 PAN ID */ + uint16_t channel_num; /**< IEEE 802.15.4 channel number */ + uint8_t channel_page; /**< IEEE 802.15.4 channel page */ + uint8_t retrans; /**< current number of retransmissions */ + uint8_t csma_retries_nb; /**< current number of CSMA-CA retries */ + uint8_t backoff_mask; /**< internal value used for random backoff calculation */ + uint8_t csma_retries; /**< maximum number of CSMA-CA retries */ + int8_t tx_pow; /**< Transmission power (in dBm) */ + ieee802154_submac_state_t state; /**< State of the SubMAC */ +}; + +/** + * @brief Get the internal state of the SubMAC + * + * @param[in] submac pointer to the SubMAC descriptor + * + * @return the SubMAC state + */ +static inline ieee802154_submac_state_t ieee802154_get_state(ieee802154_submac_t *submac) +{ + return submac->state; +} + +/** + * @brief Set the internal state of the SubMAC + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[in] state the desired state + * + * @return 0 on success + * @return negative errno on error. + */ +int ieee802154_set_state(ieee802154_submac_t *submac, ieee802154_submac_state_t state); + +/** + * @brief Transmit an IEEE 802.15.4 PSDU + * + * This function performs an IEEE 802.15.4 transmission, including CSMA-CA and + * retransmissions (if ACK Request bit is set). When the transmission finishes + * an @ref ieee802154_submac_cb_t::tx_done event is issued. + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[in] iolist pointer to the PSDU frame (without FCS) + * + * @return 0 on success + * @return negative errno on error + */ +int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist); + +/** + * @brief Set the IEEE 802.15.4 short address + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[in] short_addr IEEE 802.15.4 short address + * + * @return 0 on success + * @return negative errno on error + */ +static inline int ieee802154_set_short_addr(ieee802154_submac_t *submac, + const network_uint16_t *short_addr) +{ + int res = ieee802154_radio_set_hw_addr_filter(submac->dev, short_addr, NULL, + NULL); + + if (res >= 0) { + memcpy(&submac->short_addr, short_addr, IEEE802154_SHORT_ADDRESS_LEN); + } + + return res; +} + +/** + * @brief Set the IEEE 802.15.4 extended address + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[in] ext_addr IEEE 802.15.4 extended address + * + * @return 0 on success + * @return negative errno on error + */ +static inline int ieee802154_set_ext_addr(ieee802154_submac_t *submac, + const eui64_t *ext_addr) +{ + int res = ieee802154_radio_set_hw_addr_filter(submac->dev, NULL, ext_addr, + NULL); + + if (res >= 0) { + memcpy(&submac->ext_addr, ext_addr, IEEE802154_LONG_ADDRESS_LEN); + } + return res; +} + +/** + * @brief Set the IEEE 802.15.4 PAN ID + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[in] panid IEEE 802.15.4 PAN ID + * + * @return 0 on success + * @return negative errno on error + */ +static inline int ieee802154_set_panid(ieee802154_submac_t *submac, + const uint16_t *panid) +{ + int res = ieee802154_radio_set_hw_addr_filter(submac->dev, NULL, NULL, + panid); + + if (res >= 0) { + submac->panid = *panid; + } + + return res; +} + +/** + * @brief Set IEEE 802.15.4 PHY configuration (channel, TX power) + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[in] channel_num channel number + * @param[in] channel_page channel page + * @param[in] tx_pow transmission power (in dBm) + * + * @return 0 on success + * @return -ENOTSUP if the PHY settings are not supported + * @return negative errno on error + */ +int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num, + uint8_t channel_page, int8_t tx_pow); + +/** + * @brief Set IEEE 802.15.4 channel number + * + * This is a shortcut to @ref ieee802154_set_phy_conf + * + * @param[in] submac pointer the SubMAC descriptor + * @param[in] channel_num channel number + * + * @return 0 on success + * @return -ENOTSUP if the channel number is not supported + * @return negative errno on error + */ +static inline int ieee802154_set_channel_number(ieee802154_submac_t *submac, + uint16_t channel_num) +{ + return ieee802154_set_phy_conf(submac, channel_num, submac->channel_page, + submac->tx_pow); +} + +/** + * @brief Set IEEE 802.15.4 channel page + * + * This is a shortcut to @ref ieee802154_set_phy_conf + * + * @param[in] submac pointer the SubMAC descriptor + * @param[in] channel_page channel page + * + * @return 0 on success + * @return -ENOTSUP if the channel page is not supported + * @return negative errno on error + */ +static inline int ieee802154_set_channel_page(ieee802154_submac_t *submac, + uint16_t channel_page) +{ + return ieee802154_set_phy_conf(submac, submac->channel_num, channel_page, + submac->tx_pow); +} + +/** + * @brief Set IEEE 802.15.4 transmission power + * + * This is a shortcut to @ref ieee802154_set_phy_conf + * + * @param[in] submac pointer the SubMAC descriptor + * @param[in] tx_pow transmission power (in dBm) + * + * @return 0 on success + * @return -ENOTSUP if the transmission power is not supported + * @return negative errno on error + */ +static inline int ieee802154_set_tx_power(ieee802154_submac_t *submac, + int8_t tx_pow) +{ + return ieee802154_set_phy_conf(submac, submac->channel_num, + submac->channel_page, tx_pow); +} + +/** + * @brief Get the received frame length + * + * @param[in] submac pointer to the SubMAC + * + * @return length of the PSDU (excluding FCS length) + */ +static inline int ieee802154_get_frame_length(ieee802154_submac_t *submac) +{ + return ieee802154_radio_len(submac->dev); +} + +/** + * @brief Read the received frame + * + * This functions reads the received PSDU from the device (excluding FCS) + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[out] buf buffer to write into. If NULL, the packet is discarded + * @param[in] len length of the buffer + * @param[out] info RX information of the packet. If NULL, the information is not fetched. + * + * @return the number of bytes written to @p buf + * @return negative errno on error + */ +static inline int ieee802154_read_frame(ieee802154_submac_t *submac, void *buf, + size_t len, ieee802154_rx_info_t *info) +{ + return ieee802154_radio_indication_rx(submac->dev, buf, len, info); +} + +/** + * @brief Init the IEEE 802.15.4 SubMAC + * + * @param[in] submac pointer to the SubMAC descriptor + * + * @return 0 on success + * @return negative errno on error + */ +int ieee802154_submac_init(ieee802154_submac_t *submac); + +/** + * @brief Set the ACK timeout timer + * + * @note This function should be implemented by the user of the SubMAC. + * + * @param[in] submac pointer to the SubMAC descriptor + * @param[in] us microseconds until the ACK timeout timer is fired + */ +extern void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac, + uint16_t us); + +/** + * @brief Cancel the ACK timeout timer + * + * @note This function should be implemented by the user of the SubMAC. + * + * @param[in] submac pointer to the SubMAC descriptor + */ +extern void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac); + +/** + * @brief Indicate the SubMAC that the ACK timeout fired. + * + * This function must be called when the ACK timeout timer fires. + * + * @note this function should not be called inside ISR context. + * + * @param[in] submac pointer to the SubMAC descriptor + */ +void ieee802154_submac_ack_timeout_fired(ieee802154_submac_t *submac); + +/** + * @brief Indicate the SubMAC that the device received a frame. + * + * @param[in] submac pointer to the SubMAC descriptor + */ +void ieee802154_submac_rx_done_cb(ieee802154_submac_t *submac); + +/** + * @brief Indicate the SubMAC that the device finished the transmission procedure. + * + * @param[in] submac pointer to the SubMAC descriptor + */ +void ieee802154_submac_tx_done_cb(ieee802154_submac_t *submac); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_IEEE802154_SUBMAC_H */ +/** @} */ diff --git a/sys/net/link_layer/ieee802154/Makefile b/sys/net/link_layer/ieee802154/Makefile index 48422e909a..75f19ccb7f 100644 --- a/sys/net/link_layer/ieee802154/Makefile +++ b/sys/net/link_layer/ieee802154/Makefile @@ -1 +1,11 @@ +MODULE = ieee802154 + +SRC = \ + ieee802154.c \ + # + +ifneq (,$(filter ieee802154_submac,$(USEMODULE))) + SRC += submac.c +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/net/link_layer/ieee802154/submac.c b/sys/net/link_layer/ieee802154/submac.c new file mode 100644 index 0000000000..518b737ea0 --- /dev/null +++ b/sys/net/link_layer/ieee802154/submac.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2020 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author José I. Alamos + */ + +#include +#include +#include "net/ieee802154/submac.h" +#include "net/ieee802154.h" +#include "xtimer.h" +#include "random.h" +#include "luid.h" +#include "kernel_defines.h" +#include "errno.h" +#include + +#define CSMA_SENDER_BACKOFF_PERIOD_UNIT_MS (320U) +#define ACK_TIMEOUT_US (864U) + +static void _handle_tx_no_ack(ieee802154_submac_t *submac); + +static void _tx_end(ieee802154_submac_t *submac, int status, + ieee802154_tx_info_t *info) +{ + ieee802154_dev_t *dev = submac->dev; + + ieee802154_radio_request_set_trx_state(dev, submac->state == IEEE802154_STATE_LISTEN ? IEEE802154_TRX_STATE_RX_ON : IEEE802154_TRX_STATE_TRX_OFF); + + submac->tx = false; + while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {} + submac->cb->tx_done(submac, status, info); +} + +static inline bool _does_handle_ack(ieee802154_dev_t *dev) +{ + return ieee802154_radio_has_frame_retrans(dev) || + ieee802154_radio_has_irq_ack_timeout(dev); +} + +static int _perform_csma_ca(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + + if (submac->csma_retries_nb <= submac->csma_retries) { + ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_TX_ON); + /* delay for an adequate random backoff period */ + uint32_t bp = (random_uint32() & submac->backoff_mask) * + CSMA_SENDER_BACKOFF_PERIOD_UNIT_MS; + + xtimer_usleep(bp); + + /* try to send after a CCA */ + while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {} + + while (ieee802154_radio_request_transmit(dev) == -EBUSY) {} + + /* Prepare for next iteration */ + if (submac->backoff_mask + 1 < submac->be.max) { + submac->backoff_mask = (submac->backoff_mask << 1) | 1; + } + else { + submac->backoff_mask = (1 << submac->be.max) - 1; + } + + submac->csma_retries_nb++; + } + else { + ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_AACK_ENABLED); + _tx_end(submac, TX_STATUS_MEDIUM_BUSY, NULL); + } + + return 0; +} + +/** + * @brief Perform CSMA-CA transmission (possibly with retransmission) + * + * If radio supports @ref IEEE802154_CAP_FRAME_RETRANS, the device will automatically retransmit. + * If radio supports @ref IEEE802154_CAP_AUTO_CSMA, this function will use the + * internal CSMA-CA acceleration to perform the transmission. + * + * @param submac pointer to the SubMAC + * + * @return 0 on success + * @return negative errno on error + */ +int ieee802154_csma_ca_transmit(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + + /* If radio has Auto CSMA-CA or Frame Retransmissions, simply send and wait for the transmit confirmation. */ + if (ieee802154_radio_has_auto_csma(dev) || + ieee802154_radio_has_frame_retrans(dev)) { + + /* Make sure we are in TX_ON */ + ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_TX_ON); + while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {} + + int res; + while ((res = ieee802154_radio_request_transmit(dev)) == -EBUSY) {} + return res; + } + else { + submac->csma_retries_nb = 0; + submac->backoff_mask = (1 << submac->be.min) - 1; + _perform_csma_ca(submac); + } + + return 0; +} + +static bool _has_retrans_left(ieee802154_submac_t *submac) +{ + return submac->retrans < IEEE802154_SUBMAC_MAX_RETRANSMISSIONS; +} + +static void _perform_retrans(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + + if (_has_retrans_left(submac)) { + submac->retrans++; + ieee802154_csma_ca_transmit(submac); + } + else { + ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_AACK_ENABLED); + _tx_end(submac, TX_STATUS_NO_ACK, NULL); + } +} + +void ieee802154_submac_ack_timeout_fired(ieee802154_submac_t *submac) +{ + /* This is required to avoid race conditions */ + if (submac->wait_for_ack) { + _handle_tx_no_ack(submac); + } +} + +/* All callbacks run in the same context */ +void ieee802154_submac_rx_done_cb(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + + if (!_does_handle_ack(dev) && submac->wait_for_ack) { + uint8_t ack[3]; + + if (ieee802154_radio_indication_rx(dev, ack, 3, NULL) && + ack[0] & IEEE802154_FCF_TYPE_ACK) { + ieee802154_submac_ack_timer_cancel(submac); + ieee802154_tx_info_t tx_info; + tx_info.retrans = submac->retrans; + bool fp = (ack[0] & IEEE802154_FCF_FRAME_PEND); + submac->wait_for_ack = false; + ieee802154_radio_set_rx_mode(submac->dev, + IEEE802154_RX_AACK_ENABLED); + _tx_end(submac, fp ? TX_STATUS_FRAME_PENDING : TX_STATUS_SUCCESS, + &tx_info); + } + } + else { + submac->cb->rx_done(submac); + } +} + +static void _handle_tx_success(ieee802154_submac_t *submac, + ieee802154_tx_info_t *info) +{ + ieee802154_dev_t *dev = submac->dev; + + ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON); + while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {} + + if (ieee802154_radio_has_frame_retrans(dev) || + ieee802154_radio_has_irq_ack_timeout(dev) || !submac->wait_for_ack) { + _tx_end(submac, info->status, info); + } + else { + ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_WAIT_FOR_ACK); + + /* Handle ACK reception */ + ieee802154_submac_ack_timer_set(submac, ACK_TIMEOUT_US); + } +} + +static void _handle_tx_medium_busy(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + + if (ieee802154_radio_has_frame_retrans(dev) || + ieee802154_radio_has_auto_csma(dev)) { + ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON); + _tx_end(submac, TX_STATUS_MEDIUM_BUSY, NULL); + } + else { + /* CCA failed. Continue with the CSMA-CA algorithm */ + _perform_csma_ca(submac); + } +} + +static void _handle_tx_no_ack(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + + if (ieee802154_radio_has_frame_retrans(dev)) { + ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON); + submac->wait_for_ack = false; + _tx_end(submac, TX_STATUS_NO_ACK, NULL); + } + else { + /* Perform retransmissions */ + _perform_retrans(submac); + } +} + +void ieee802154_submac_tx_done_cb(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + ieee802154_tx_info_t info; + + ieee802154_radio_confirm_transmit(dev, &info); + + switch (info.status) { + case TX_STATUS_MEDIUM_BUSY: + _handle_tx_medium_busy(submac); + break; + case TX_STATUS_NO_ACK: + _handle_tx_no_ack(submac); + break; + case TX_STATUS_SUCCESS: + case TX_STATUS_FRAME_PENDING: + _handle_tx_success(submac, &info); + break; + default: + assert(false); + break; + } +} + +int ieee802154_send(ieee802154_submac_t *submac, const iolist_t *iolist) +{ + ieee802154_dev_t *dev = submac->dev; + + uint8_t *buf = iolist->iol_base; + bool cnf = buf[0] & IEEE802154_FCF_ACK_REQ; + + if (submac->state == IEEE802154_STATE_OFF) { + return -ENETDOWN; + } + + if (submac->tx || + ieee802154_radio_request_set_trx_state(dev, + IEEE802154_TRX_STATE_TX_ON) < 0) { + return -EBUSY; + } + + submac->tx = true; + + ieee802154_radio_write(dev, iolist); + while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN) {} + + submac->wait_for_ack = cnf; + submac->retrans = 0; + + ieee802154_csma_ca_transmit(submac); + return 0; +} + +int ieee802154_submac_init(ieee802154_submac_t *submac) +{ + ieee802154_dev_t *dev = submac->dev; + + submac->tx = false; + submac->state = IEEE802154_STATE_LISTEN; + + ieee802154_radio_request_on(dev); + + /* generate EUI-64 and short address */ + luid_get_eui64(&submac->ext_addr); + luid_get_short(&submac->short_addr); + submac->panid = CONFIG_IEEE802154_DEFAULT_PANID; + + submac->be.min = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MIN_BE; + submac->csma_retries = CONFIG_IEEE802154_DEFAULT_CSMA_CA_RETRIES; + submac->be.max = CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE; + + submac->tx_pow = CONFIG_IEEE802154_DEFAULT_TXPOWER; + + if (ieee802154_radio_has_24_ghz(dev)) { + submac->channel_num = CONFIG_IEEE802154_DEFAULT_CHANNEL; + + /* 2.4 GHz only use page 0 */ + submac->channel_page = 0; + } + else { + submac->channel_num = CONFIG_IEEE802154_DEFAULT_SUBGHZ_CHANNEL; + submac->channel_page = CONFIG_IEEE802154_DEFAULT_SUBGHZ_PAGE; + } + + /* If the radio is still not in TRX_OFF state, spin */ + while (ieee802154_radio_confirm_on(dev) == -EAGAIN) {} + + /* Enable Auto ACK */ + ieee802154_radio_set_rx_mode(dev, IEEE802154_RX_AACK_ENABLED); + + /* Configure address filter */ + ieee802154_radio_set_hw_addr_filter(dev, &submac->short_addr, + &submac->ext_addr, &submac->panid); + + /* Configure PHY settings (channel, TX power) */ + ieee802154_phy_conf_t conf = + { .channel = CONFIG_IEEE802154_DEFAULT_CHANNEL, + .page = CONFIG_IEEE802154_DEFAULT_CHANNEL, + .pow = CONFIG_IEEE802154_DEFAULT_TXPOWER }; + + ieee802154_radio_config_phy(dev, &conf); + assert(ieee802154_radio_set_cca_threshold(dev, + CONFIG_IEEE802154_CCA_THRESH_DEFAULT) >= 0); + + ieee802154_radio_request_set_trx_state(dev, IEEE802154_TRX_STATE_RX_ON); + + return 0; +} + +int ieee802154_set_phy_conf(ieee802154_submac_t *submac, uint16_t channel_num, + uint8_t channel_page, int8_t tx_pow) +{ + ieee802154_dev_t *dev = submac->dev; + const ieee802154_phy_conf_t conf = + { .channel = channel_num, .page = channel_page, .pow = tx_pow }; + + if (submac->state == IEEE802154_STATE_OFF) { + return -ENETDOWN; + } + + int res = ieee802154_radio_config_phy(dev, &conf); + + if (res >= 0) { + submac->channel_num = channel_num; + submac->channel_page = channel_page; + submac->tx_pow = tx_pow; + } + + return res; +} + +int ieee802154_set_state(ieee802154_submac_t *submac, ieee802154_submac_state_t state) +{ + int res; + + ieee802154_dev_t *dev = submac->dev; + + if (submac->tx) { + return -EBUSY; + } + + if (state == submac->state) { + return -EALREADY; + } + + /* Wake up the radio if it was off */ + if (submac->state == IEEE802154_STATE_OFF) { + if ((res = ieee802154_radio_request_on(dev)) < 0) { + return res; + } + while (ieee802154_radio_confirm_on(dev) == -EAGAIN); + } + + if (state == IEEE802154_STATE_OFF) { + res = ieee802154_radio_off(dev); + } + else { + ieee802154_submac_state_t new_state = + state == IEEE802154_STATE_IDLE + ? IEEE802154_TRX_STATE_TRX_OFF + : IEEE802154_TRX_STATE_RX_ON; + + if ((res = ieee802154_radio_request_set_trx_state(dev, new_state)) < 0) { + return res; + } + + while (ieee802154_radio_confirm_set_trx_state(dev) == -EAGAIN); + } + + submac->state = state; + return res; +} + +/** @} */