From e100a67099ba4d9df6a6045c20468f5a969f7ecb Mon Sep 17 00:00:00 2001 From: Jose Alamos Date: Fri, 4 Sep 2020 11:18:02 +0200 Subject: [PATCH 1/6] 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; +} + +/** @} */ From 4bb77b0b2f9b9e52e7e3f4adb4bbc6039129eefb Mon Sep 17 00:00:00 2001 From: Jose Alamos Date: Fri, 4 Sep 2020 11:22:12 +0200 Subject: [PATCH 2/6] netdev/ieee802154_submac: add compatibility layer --- Makefile.dep | 8 + .../include/net/netdev/ieee802154_submac.h | 70 ++++ drivers/netdev_ieee802154_submac/Makefile | 1 + .../netdev_ieee802154_submac.c | 326 ++++++++++++++++++ makefiles/pseudomodules.inc.mk | 1 + 5 files changed, 406 insertions(+) create mode 100644 drivers/include/net/netdev/ieee802154_submac.h create mode 100644 drivers/netdev_ieee802154_submac/Makefile create mode 100644 drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c diff --git a/Makefile.dep b/Makefile.dep index b48a10e402..fc4c8c22d8 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -108,6 +108,11 @@ ifneq (,$(filter netdev_ieee802154,$(USEMODULE))) USEMODULE += random endif +ifneq (,$(filter netdev_ieee802154_submac,$(USEMODULE))) + USEMODULE += ieee802154_radio_hal + USEMODULE += ieee802154_submac +endif + ifneq (,$(filter gnrc_dhcpv6_%, $(USEMODULE))) USEMODULE += gnrc_dhcpv6 endif @@ -191,6 +196,9 @@ ifneq (,$(filter gnrc_netif,$(USEMODULE))) USEMODULE += netif USEMODULE += l2util USEMODULE += fmt + ifneq (,$(filter netdev_ieee802154_submac,$(USEMODULE))) + USEMODULE += gnrc_netif_pktq + endif ifneq (,$(filter netdev_ieee802154,$(USEMODULE))) USEMODULE += gnrc_netif_ieee802154 endif diff --git a/drivers/include/net/netdev/ieee802154_submac.h b/drivers/include/net/netdev/ieee802154_submac.h new file mode 100644 index 0000000000..c78862962d --- /dev/null +++ b/drivers/include/net/netdev/ieee802154_submac.h @@ -0,0 +1,70 @@ +/* + * 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 drivers_netdev_ieee802154_submac IEEE802.15.4 SubMAC netdev layer + * @ingroup drivers_netdev_api + * @experimental This API is experimental and in an early state - expect + * changes! + + * @brief This module defines implements the netdev API on top of the + * IEEE 802.15.4 radio HAL + * + * @{ + * + * @author José I. Alamos + */ +#ifndef NET_NETDEV_IEEE802154_SUBMAC_H +#define NET_NETDEV_IEEE802154_SUBMAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/netdev.h" +#include "net/netdev/ieee802154.h" +#include "net/ieee802154/submac.h" +#include "net/ieee802154/radio.h" +#include "xtimer.h" + +#include "od.h" +#include "event/thread.h" +#include "event/callback.h" +#include "xtimer.h" + +#define NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT (1 << 0) /**< Flag for ACK Timeout event */ +#define NETDEV_SUBMAC_FLAGS_TX_DONE (1 << 1) /**< Flag for TX Done event */ +#define NETDEV_SUBMAC_FLAGS_RX_DONE (1 << 2) /**< Flag for RX Done event */ + +/** + * @brief IEEE 802.15.4 SubMAC netdev descriptor + */ +typedef struct { + netdev_ieee802154_t dev; /**< IEEE 802.15.4 netdev descriptor */ + ieee802154_submac_t submac; /**< IEEE 802.15.4 SubMAC descriptor */ + xtimer_t ack_timer; /**< xtimer descriptor for the ACK timeout timer */ + int isr_flags; /**< netdev submac @ref NETDEV_EVENT_ISR flags */ +} netdev_ieee802154_submac_t; + +/** + * @brief Init the IEEE 802.15.4 SubMAC netdev adoption. + * + * @param[in] netdev_submac pointer to the netdev submac descriptor. + * @param[in] dev pointer to the device associated to @p netdev_submac. + * + * @return 0 on success. + * @return negative errno on failure. + */ +int netdev_ieee802154_submac_init(netdev_ieee802154_submac_t *netdev_submac, + ieee802154_dev_t *dev); +#ifdef __cplusplus +} +#endif + +#endif /* NET_NETDEV_IEEE802154_SUBMAC_H */ +/** @} */ diff --git a/drivers/netdev_ieee802154_submac/Makefile b/drivers/netdev_ieee802154_submac/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/netdev_ieee802154_submac/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c new file mode 100644 index 0000000000..5c231403e0 --- /dev/null +++ b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c @@ -0,0 +1,326 @@ +/* + * 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 "net/netdev/ieee802154_submac.h" +#include "event/thread.h" + +static const ieee802154_submac_cb_t _cb; + +static const netdev_driver_t netdev_submac_driver; + +static void _ack_timeout(void *arg) +{ + (void)arg; + netdev_ieee802154_submac_t *netdev_submac = arg; + netdev_t *netdev = arg; + + netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT; + + netdev->event_callback(netdev, NETDEV_EVENT_ISR); +} + +static netopt_state_t _get_submac_state(ieee802154_submac_t *submac) +{ + ieee802154_submac_state_t state = ieee802154_get_state(submac); + + netopt_state_t netopt_state; + switch (state) { + case IEEE802154_STATE_OFF: + netopt_state = NETOPT_STATE_SLEEP; + break; + case IEEE802154_STATE_IDLE: + netopt_state = NETOPT_STATE_STANDBY; + break; + case IEEE802154_STATE_LISTEN: + default: + netopt_state = NETOPT_STATE_IDLE; + break; + } + + return netopt_state; +} + +static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len) +{ + netdev_ieee802154_submac_t *netdev_submac = (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + + switch (opt) { + case NETOPT_STATE: + *((netopt_state_t*) value) = _get_submac_state(submac); + return 0; + default: + break; + } + + return netdev_ieee802154_get((netdev_ieee802154_t *)netdev, opt, + value, max_len); +} + +static int _set_submac_state(ieee802154_submac_t *submac, netopt_state_t state) +{ + switch (state) { + case NETOPT_STATE_STANDBY: + return ieee802154_set_state(submac, IEEE802154_STATE_IDLE); + case NETOPT_STATE_SLEEP: + return ieee802154_set_state(submac, IEEE802154_STATE_OFF); + case NETOPT_STATE_IDLE: + return ieee802154_set_state(submac, IEEE802154_STATE_LISTEN); + default: + return -ENOTSUP; + } +} + +static int _set(netdev_t *netdev, netopt_t opt, const void *value, + size_t value_len) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + + int res; + int16_t tx_power; + + switch (opt) { + case NETOPT_ADDRESS: + ieee802154_set_short_addr(submac, value); + break; + case NETOPT_ADDRESS_LONG: + ieee802154_set_ext_addr(submac, value); + break; + case NETOPT_NID: + ieee802154_set_panid(submac, value); + break; + case NETOPT_CHANNEL: + ieee802154_set_channel_number(submac, *((uint16_t *)value)); + break; + case NETOPT_TX_POWER: + tx_power = *((int16_t *)value); + res = ieee802154_set_tx_power(submac, tx_power); + if (res >= 0) { + netdev_submac->dev.txpower = tx_power; + } + return res; + case NETOPT_STATE: + return _set_submac_state(submac, *((netopt_state_t*) value)); + default: + break; + } + + return netdev_ieee802154_set((netdev_ieee802154_t *)netdev, opt, + value, value_len); +} + +void ieee802154_submac_ack_timer_set(ieee802154_submac_t *submac, uint16_t us) +{ + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + + xtimer_set(&netdev_submac->ack_timer, us); +} + +void ieee802154_submac_ack_timer_cancel(ieee802154_submac_t *submac) +{ + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + + xtimer_remove(&netdev_submac->ack_timer); +} + +static int _send(netdev_t *netdev, const iolist_t *pkt) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + + return ieee802154_send(&netdev_submac->submac, pkt); +} + +static void _isr(netdev_t *netdev) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + + do { + irq_disable(); + int flags = netdev_submac->isr_flags; + netdev_submac->isr_flags = 0; + irq_enable(); + + if (flags & NETDEV_SUBMAC_FLAGS_ACK_TIMEOUT) { + ieee802154_submac_ack_timeout_fired(&netdev_submac->submac); + } + + if (flags & NETDEV_SUBMAC_FLAGS_TX_DONE) { + ieee802154_submac_tx_done_cb(&netdev_submac->submac); + } + + if (flags & NETDEV_SUBMAC_FLAGS_RX_DONE) { + ieee802154_submac_rx_done_cb(submac); + } + } while (netdev_submac->isr_flags != 0); +} + +static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + ieee802154_submac_t *submac = &netdev_submac->submac; + ieee802154_rx_info_t rx_info; + + if (buf == NULL && len == 0) { + return ieee802154_get_frame_length(submac); + } + + int res = ieee802154_read_frame(submac, buf, len, &rx_info); + + if (info) { + netdev_ieee802154_rx_info_t *netdev_rx_info = info; + netdev_rx_info->rssi = rx_info.rssi; + netdev_rx_info->lqi = rx_info.lqi; + } + + return res; +} + +static void submac_tx_done(ieee802154_submac_t *submac, int status, + ieee802154_tx_info_t *info) +{ + (void)status; + (void)info; + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + netdev_t *netdev = (netdev_t *)netdev_submac; + + switch (status) { + case TX_STATUS_SUCCESS: + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); + break; + case TX_STATUS_FRAME_PENDING: + netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE_DATA_PENDING); + break; + case TX_STATUS_MEDIUM_BUSY: + netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); + break; + case TX_STATUS_NO_ACK: + netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK); + break; + default: + break; + } +} + +static void submac_rx_done(ieee802154_submac_t *submac) +{ + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + netdev_t *netdev = (netdev_t *)netdev_submac; + + netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); +} + +static const ieee802154_submac_cb_t _cb = { + .rx_done = submac_rx_done, + .tx_done = submac_tx_done, +}; + +/* Event Notification callback */ +static void _hal_radio_cb(ieee802154_dev_t *dev, ieee802154_trx_ev_t status) +{ + ieee802154_submac_t *submac = dev->ctx; + netdev_ieee802154_submac_t *netdev_submac = container_of(submac, + netdev_ieee802154_submac_t, + submac); + netdev_t *netdev = (netdev_t *)netdev_submac; + + switch (status) { + case IEEE802154_RADIO_CONFIRM_TX_DONE: + netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_TX_DONE; + break; + case IEEE802154_RADIO_INDICATION_RX_DONE: + netdev_submac->isr_flags |= NETDEV_SUBMAC_FLAGS_RX_DONE; + default: + break; + } + netdev->event_callback(netdev, NETDEV_EVENT_ISR); +} + +static int _init(netdev_t *netdev) +{ + netdev_ieee802154_submac_t *netdev_submac = + (netdev_ieee802154_submac_t *)netdev; + /* Call the init function of the device (this will be handled by + * `auto_init`) */ + + ieee802154_submac_t *submac = &netdev_submac->submac; + + ieee802154_submac_init(submac); + + netdev_ieee802154_t *netdev_ieee802154 = (netdev_ieee802154_t *)netdev; + + /* This function already sets the PAN ID to the default one */ + netdev_ieee802154_reset(netdev_ieee802154); + + uint16_t chan = CONFIG_IEEE802154_DEFAULT_CHANNEL; + int16_t tx_power = CONFIG_IEEE802154_DEFAULT_TXPOWER; + + /* Initialise netdev_ieee802154_t struct */ + netdev_ieee802154_set(netdev_ieee802154, NETOPT_CHANNEL, + &chan, sizeof(chan)); + netdev_ieee802154_set(netdev_ieee802154, NETOPT_ADDRESS, + &submac->short_addr, sizeof(submac->short_addr)); + netdev_ieee802154_set(netdev_ieee802154, NETOPT_ADDRESS_LONG, + &submac->ext_addr, sizeof(submac->ext_addr)); + + netdev_submac->dev.txpower = tx_power; + + return 0; +} + +int netdev_ieee802154_submac_init(netdev_ieee802154_submac_t *netdev_submac, + ieee802154_dev_t *dev) +{ + netdev_t *netdev = (netdev_t *)netdev_submac; + + netdev->driver = &netdev_submac_driver; + ieee802154_submac_t *submac = &netdev_submac->submac; + + submac->dev = dev; + submac->cb = &_cb; + submac->dev->ctx = submac; + + /* Set the Event Notification */ + submac->dev->cb = _hal_radio_cb; + + netdev_submac->ack_timer.callback = _ack_timeout; + netdev_submac->ack_timer.arg = netdev_submac; + + return 0; +} + +static const netdev_driver_t netdev_submac_driver = { + .get = _get, + .set = _set, + .send = _send, + .recv = _recv, + .isr = _isr, + .init = _init, +}; + +/** @} */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 42f09ab9e9..71942c9f60 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -128,6 +128,7 @@ PSEUDOMODULES += ztimer% # ztimer's main module is called "ztimer_core" NO_PSEUDOMODULES += ztimer_core +NO_PSEUDOMODULES += netdev_ieee802154_submac # print ascii representation in function od_hex_dump() PSEUDOMODULES += od_string From d0a0190d58b87e9b6e7f0e8413652a43acfbfd08 Mon Sep 17 00:00:00 2001 From: Jose Alamos Date: Fri, 4 Sep 2020 11:19:01 +0200 Subject: [PATCH 3/6] tests/ieee802154_submac: add test application --- tests/ieee802154_submac/Makefile | 28 +++ tests/ieee802154_submac/common.h | 1 + tests/ieee802154_submac/init_devs.c | 1 + tests/ieee802154_submac/main.c | 361 ++++++++++++++++++++++++++++ 4 files changed, 391 insertions(+) create mode 100644 tests/ieee802154_submac/Makefile create mode 120000 tests/ieee802154_submac/common.h create mode 120000 tests/ieee802154_submac/init_devs.c create mode 100644 tests/ieee802154_submac/main.c diff --git a/tests/ieee802154_submac/Makefile b/tests/ieee802154_submac/Makefile new file mode 100644 index 0000000000..b0e6ee2faa --- /dev/null +++ b/tests/ieee802154_submac/Makefile @@ -0,0 +1,28 @@ +include ../Makefile.tests_common + +BOARD_WHITELIST := \ + adafruit-clue \ + adafruit-itsybitsy-nrf52 \ + arduino-nano-33-ble \ + cc2538dk \ + feather-nrf52840 \ + nrf52840dk \ + nrf52840dongle \ + nrf52840-mdk \ + omote \ + openmote-cc2538 \ + reel \ + remote-pa \ + remote-reva \ + remote-revb \ + # +USEMODULE += od +USEMODULE += shell +USEMODULE += ps +USEMODULE += event_thread_highest +USEMODULE += netdev_ieee802154_submac +USEMODULE += netdev_default + +CFLAGS += -DEVENT_THREAD_HIGHEST_STACKSIZE=1024 + +include $(RIOTBASE)/Makefile.include diff --git a/tests/ieee802154_submac/common.h b/tests/ieee802154_submac/common.h new file mode 120000 index 0000000000..e95bd2d8c6 --- /dev/null +++ b/tests/ieee802154_submac/common.h @@ -0,0 +1 @@ +../ieee802154_hal/common.h \ No newline at end of file diff --git a/tests/ieee802154_submac/init_devs.c b/tests/ieee802154_submac/init_devs.c new file mode 120000 index 0000000000..ddf5f5ff33 --- /dev/null +++ b/tests/ieee802154_submac/init_devs.c @@ -0,0 +1 @@ +../ieee802154_hal/init_devs.c \ No newline at end of file diff --git a/tests/ieee802154_submac/main.c b/tests/ieee802154_submac/main.c new file mode 100644 index 0000000000..af116636db --- /dev/null +++ b/tests/ieee802154_submac/main.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for AT86RF2xx network device driver + * + * @author Hauke Petersen + * + * @} + */ + +#include +#include +#include +#include +#include "sys/uio.h" +#include "luid.h" + +#include "net/netdev.h" +#include "shell.h" +#include "shell_commands.h" +#include "net/ieee802154/submac.h" +#include "net/ieee802154.h" +#include "net/netdev/ieee802154_submac.h" + +#include "common.h" + +#define MAX_LINE (80) + +/* Only the first radio is supported so far */ +#define RADIO_DEFAULT_ID (0U) + +netdev_ieee802154_submac_t netdev_submac; + +void _ack_timeout(void *arg); + +uint8_t buffer[IEEE802154_FRAME_LEN_MAX]; +uint8_t seq; + +static int print_addr(int argc, char **argv) +{ + (void)argc; + (void)argv; + uint8_t *_p = (uint8_t *)&netdev_submac.submac.ext_addr; + + for (int i = 0; i < 8; i++) { + printf("%02x", *_p++); + } + printf("\n"); + return 0; +} + +extern const netdev_driver_t netdev_submac_driver; + +static void _netdev_isr_handler(event_t *event) +{ + (void)event; + netdev_t *netdev = (netdev_t *)&netdev_submac; + + netdev->driver->isr(netdev); +} + +void _print_addr(uint8_t *addr, size_t addr_len) +{ + for (size_t i = 0; i < addr_len; i++) { + if (i != 0) { + printf(":"); + } + printf("%02x", (unsigned)addr[i]); + } +} +static event_t _netdev_ev = { .handler = _netdev_isr_handler }; + +void recv(netdev_t *dev) +{ + uint8_t src[IEEE802154_LONG_ADDRESS_LEN], dst[IEEE802154_LONG_ADDRESS_LEN]; + size_t mhr_len, data_len, src_len, dst_len; + netdev_ieee802154_rx_info_t rx_info; + le_uint16_t src_pan, dst_pan; + + putchar('\n'); + data_len = dev->driver->recv(dev, buffer, sizeof(buffer), &rx_info); + mhr_len = ieee802154_get_frame_hdr_len(buffer); + if (mhr_len == 0) { + puts("Unexpected MHR for incoming packet"); + return; + } + dst_len = ieee802154_get_dst(buffer, dst, &dst_pan); + src_len = ieee802154_get_src(buffer, src, &src_pan); + switch (buffer[0] & IEEE802154_FCF_TYPE_MASK) { + case IEEE802154_FCF_TYPE_BEACON: + puts("BEACON"); + break; + case IEEE802154_FCF_TYPE_DATA: + puts("DATA"); + break; + case IEEE802154_FCF_TYPE_ACK: + puts("ACK"); + break; + case IEEE802154_FCF_TYPE_MACCMD: + puts("MACCMD"); + break; + default: + puts("UNKNOWN"); + break; + } + printf("Dest. PAN: 0x%04x, Dest. addr.: ", + byteorder_ntohs(byteorder_ltobs(dst_pan))); + _print_addr(dst, dst_len); + printf("\nSrc. PAN: 0x%04x, Src. addr.: ", + byteorder_ntohs(byteorder_ltobs(src_pan))); + _print_addr(src, src_len); + printf("\nSecurity: "); + if (buffer[0] & IEEE802154_FCF_SECURITY_EN) { + printf("1, "); + } + else { + printf("0, "); + } + printf("Frame pend.: "); + if (buffer[0] & IEEE802154_FCF_FRAME_PEND) { + printf("1, "); + } + else { + printf("0, "); + } + printf("ACK req.: "); + if (buffer[0] & IEEE802154_FCF_ACK_REQ) { + printf("1, "); + } + else { + printf("0, "); + } + printf("PAN comp.: "); + if (buffer[0] & IEEE802154_FCF_PAN_COMP) { + puts("1"); + } + else { + puts("0"); + } + printf("Version: "); + printf("%u, ", (unsigned)((buffer[1] & IEEE802154_FCF_VERS_MASK) >> 4)); + printf("Seq.: %u\n", (unsigned)ieee802154_get_seq(buffer)); + od_hex_dump(buffer + mhr_len, data_len - mhr_len, 0); + printf("txt: "); + for (size_t i = mhr_len; i < data_len; i++) { + if ((buffer[i] > 0x1F) && (buffer[i] < 0x80)) { + putchar((char)buffer[i]); + } + else { + putchar('?'); + } + if (((((i - mhr_len) + 1) % (MAX_LINE - sizeof("txt: "))) == 1) && + (i - mhr_len) != 0) { + printf("\n "); + } + } + printf("\n"); + printf("RSSI: %i, LQI: %u\n\n", rx_info.rssi, rx_info.lqi); +} +static void _event_cb(netdev_t *dev, netdev_event_t event) +{ + (void)dev; + if (event == NETDEV_EVENT_ISR) { + event_post(EVENT_PRIO_HIGHEST, &_netdev_ev); + } + else { + switch (event) { + case NETDEV_EVENT_RX_COMPLETE: + { + recv(dev); + return; + } + case NETDEV_EVENT_TX_COMPLETE: + puts("Tx complete"); + break; + case NETDEV_EVENT_TX_COMPLETE_DATA_PENDING: + puts("Tx complete with pending data"); + break; + case NETDEV_EVENT_TX_MEDIUM_BUSY: + puts("Medium Busy"); + break; + case NETDEV_EVENT_TX_NOACK: + puts("No ACK"); + break; + default: + assert(false); + } + } +} +static int _init(void) +{ + ieee802154_hal_test_init_devs(); + + netdev_t *dev = (netdev_t *)&netdev_submac; + + dev->event_callback = _event_cb; + netdev_ieee802154_submac_init(&netdev_submac, + ieee802154_hal_test_get_dev(RADIO_DEFAULT_ID)); + dev->driver->init(dev); + return 0; +} + +uint8_t payload[] = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam ornare lacinia mi elementum interdum ligula."; + +static iolist_t iol_hdr; + +static int send(uint8_t *dst, size_t dst_len, + size_t len) +{ + uint8_t flags; + uint8_t mhr[IEEE802154_MAX_HDR_LEN]; + int mhr_len; + + le_uint16_t src_pan, dst_pan; + iolist_t iol_data = { + .iol_base = payload, + .iol_len = len, + .iol_next = NULL, + }; + + flags = IEEE802154_FCF_TYPE_DATA | 0x20; + src_pan = byteorder_btols(byteorder_htons(0x23)); + dst_pan = byteorder_btols(byteorder_htons(0x23)); + uint8_t src_len = 8; + void *src = &netdev_submac.submac.ext_addr; + + /* fill MAC header, seq should be set by device */ + if ((mhr_len = ieee802154_set_frame_hdr(mhr, src, src_len, + dst, dst_len, + src_pan, dst_pan, + flags, seq++)) < 0) { + puts("txtsnd: Error preperaring frame"); + return 1; + } + + iol_hdr.iol_next = &iol_data; + iol_hdr.iol_base = mhr; + iol_hdr.iol_len = mhr_len; + + netdev_t *dev = (netdev_t *)&netdev_submac; + + dev->driver->send(dev, &iol_hdr); + return 0; +} + +static inline int _dehex(char c, int default_) +{ + if ('0' <= c && c <= '9') { + return c - '0'; + } + else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } + else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + else { + return default_; + } +} + +static size_t _parse_addr(uint8_t *out, size_t out_len, const char *in) +{ + const char *end_str = in; + uint8_t *out_end = out; + size_t count = 0; + int assert_cell = 1; + + if (!in || !*in) { + return 0; + } + while (end_str[1]) { + ++end_str; + } + + while (end_str >= in) { + int a = 0, b = _dehex(*end_str--, -1); + if (b < 0) { + if (assert_cell) { + return 0; + } + else { + assert_cell = 1; + continue; + } + } + assert_cell = 0; + + if (end_str >= in) { + a = _dehex(*end_str--, 0); + } + + if (++count > out_len) { + return 0; + } + *out_end++ = (a << 4) | b; + } + if (assert_cell) { + return 0; + } + /* out is reversed */ + + while (out < --out_end) { + uint8_t tmp = *out_end; + *out_end = *out; + *out++ = tmp; + } + + return count; +} + +int txtsnd(int argc, char **argv) +{ + uint8_t addr[8]; + size_t len; + size_t res; + + if (argc != 3) { + puts("Usage: txtsnd "); + return 1; + } + + res = _parse_addr(addr, sizeof(addr), argv[1]); + if (res == 0) { + puts("Usage: txtsnd "); + return 1; + } + len = atoi(argv[2]); + return send(addr, res, len); +} + +static const shell_command_t shell_commands[] = { + { "print_addr", "Print IEEE802.15.4 addresses", print_addr }, + { "txtsnd", "Send IEEE 802.15.4 packet", txtsnd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + _init(); + + /* start the shell */ + puts("Initialization successful - starting the shell now"); + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} From 1de5b34adfa7d09309ad782d08ac20e73f1415eb Mon Sep 17 00:00:00 2001 From: Jose Alamos Date: Fri, 4 Sep 2020 11:25:56 +0200 Subject: [PATCH 4/6] auto_init/cc2538_rf: add support for SubMAC --- .../gnrc/netif/init_devs/auto_init_cc2538_rf.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sys/net/gnrc/netif/init_devs/auto_init_cc2538_rf.c b/sys/net/gnrc/netif/init_devs/auto_init_cc2538_rf.c index 302cda7e24..bd49f1961e 100644 --- a/sys/net/gnrc/netif/init_devs/auto_init_cc2538_rf.c +++ b/sys/net/gnrc/netif/init_devs/auto_init_cc2538_rf.c @@ -19,6 +19,8 @@ #include "log.h" #include "net/gnrc/netif/ieee802154.h" +#include "net/ieee802154/radio.h" +#include "net/netdev/ieee802154_submac.h" #include "cc2538_rf.h" @@ -31,7 +33,12 @@ #define CC2538_MAC_PRIO (GNRC_NETIF_PRIO) #endif +#if IS_USED(MODULE_IEEE802154_RADIO_HAL) +extern ieee802154_dev_t cc2538_rf_dev; +static netdev_ieee802154_submac_t cc2538_rf_submac; +#else static cc2538_rf_t cc2538_rf_dev; +#endif static char _cc2538_rf_stack[CC2538_MAC_STACKSIZE]; static gnrc_netif_t _netif; @@ -39,10 +46,18 @@ void auto_init_cc2538_rf(void) { LOG_DEBUG("[auto_init_netif] initializing cc2538 radio\n"); + netdev_t *netdev; +#if IS_USED(MODULE_IEEE802154_RADIO_HAL) + netdev_ieee802154_submac_init(&cc2538_rf_submac, &cc2538_rf_dev); + netdev = (netdev_t*) &cc2538_rf_submac; + cc2538_init(); +#else + netdev = &cc2538_rf_dev.netdev.netdev; cc2538_setup(&cc2538_rf_dev); +#endif gnrc_netif_ieee802154_create(&_netif, _cc2538_rf_stack, CC2538_MAC_STACKSIZE, CC2538_MAC_PRIO, "cc2538_rf", - (netdev_t *)&cc2538_rf_dev); + netdev); } /** @} */ From 7cf6943eabd1b3516d1bbcd5ed5a29e934edc99a Mon Sep 17 00:00:00 2001 From: Jose Alamos Date: Wed, 30 Sep 2020 14:38:25 +0200 Subject: [PATCH 5/6] net/ieee802154: add config for default CCA threshold --- sys/include/net/ieee802154.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sys/include/net/ieee802154.h b/sys/include/net/ieee802154.h index 3d4fe628fc..a99d94c52a 100644 --- a/sys/include/net/ieee802154.h +++ b/sys/include/net/ieee802154.h @@ -239,6 +239,13 @@ extern const uint8_t ieee802154_addr_bcast[IEEE802154_ADDR_BCAST_LEN]; #define CONFIG_IEEE802154_DEFAULT_CSMA_CA_MAX_BE (5U) #endif +/** + * @brief IEEE802.15.4 default value for CCA threshold (in dBm) + */ +#ifndef CONFIG_IEEE802154_CCA_THRESH_DEFAULT +#define CONFIG_IEEE802154_CCA_THRESH_DEFAULT (-70) +#endif + /** * @brief Initializes an IEEE 802.15.4 MAC frame header in @p buf. * From 9ebc3f8bd86c458195e861f8b59b24fe3e9ebd4c Mon Sep 17 00:00:00 2001 From: Jose Alamos Date: Fri, 4 Sep 2020 11:30:31 +0200 Subject: [PATCH 6/6] auto_init/nrf52840: add support for SubMAC --- .../netif/init_devs/auto_init_nrf802154.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sys/net/gnrc/netif/init_devs/auto_init_nrf802154.c b/sys/net/gnrc/netif/init_devs/auto_init_nrf802154.c index ddc389ebc8..2b2bd271ee 100644 --- a/sys/net/gnrc/netif/init_devs/auto_init_nrf802154.c +++ b/sys/net/gnrc/netif/init_devs/auto_init_nrf802154.c @@ -22,6 +22,9 @@ #include "nrf802154.h" #include "net/gnrc/netif/ieee802154.h" +#include "net/ieee802154/radio.h" +#include "net/netdev/ieee802154_submac.h" + /** * @brief Define stack parameters for the MAC layer thread * @{ @@ -37,13 +40,27 @@ static char _stack[NRF802154_MAC_STACKSIZE]; static gnrc_netif_t _netif; +#if IS_USED(MODULE_IEEE802154_RADIO_HAL) +extern ieee802154_dev_t nrf802154_hal_dev; +static netdev_ieee802154_submac_t nrf802154_submac; +#endif + void auto_init_nrf802154(void) { LOG_DEBUG("[auto_init_netif] initializing nrf802154\n"); + netdev_t *netdev; +#if IS_USED(MODULE_IEEE802154_RADIO_HAL) + netdev_ieee802154_submac_init(&nrf802154_submac, &nrf802154_hal_dev); + netdev = (netdev_t*) &nrf802154_submac; + nrf802154_init(); +#else + netdev = (netdev_t*) &nrf802154_dev; +#endif + gnrc_netif_ieee802154_create(&_netif, _stack, NRF802154_MAC_STACKSIZE, NRF802154_MAC_PRIO, "nrf802154", - (netdev_t *)&nrf802154_dev); + netdev); } /** @} */