diff --git a/cpu/nrf52/Makefile.dep b/cpu/nrf52/Makefile.dep index 6064e77544c9..ba7809574423 100644 --- a/cpu/nrf52/Makefile.dep +++ b/cpu/nrf52/Makefile.dep @@ -12,6 +12,7 @@ ifneq (,$(filter nrf802154,$(USEMODULE))) FEATURES_REQUIRED += periph_timer FEATURES_REQUIRED += radio_nrf802154 USEMODULE += luid + USEMODULE += ieee802154 ifneq (,$(filter netdev,$(USEMODULE))) USEMODULE += netdev_ieee802154_submac endif diff --git a/drivers/at86rf2xx/Kconfig b/drivers/at86rf2xx/Kconfig index a0b3772cf9ae..a66cdbf50327 100644 --- a/drivers/at86rf2xx/Kconfig +++ b/drivers/at86rf2xx/Kconfig @@ -10,12 +10,12 @@ menuconfig MODULE_AT86RF2XX prompt "AT86RF2xx series radios" if !(MODULE_NETDEV_DEFAULT && HAVE_AT86RF2XX) default (MODULE_NETDEV_DEFAULT && HAVE_AT86RF2XX) depends on TEST_KCONFIG - select MODULE_IEEE802154 - select MODULE_NETDEV - select MODULE_NETDEV_IEEE802154 - select MODULE_NETDEV_LEGACY_API select MODULE_ZTIMER select MODULE_ZTIMER_USEC + select MODULE_EVENT + select MODULE_EVENT_THREAD + select MODULE_EVENT_THREAD_HIGHEST + select HAVE_IEEE802154_RADIO_HAL_INTERFACE if MODULE_AT86RF2XX @@ -84,7 +84,7 @@ config MODULE_AT86RF2XX_AES_SPI config AT86RF2XX_OQPSK bool "O-QPSK support" - select MODULE_NETDEV_IEEE802154_OQPSK + select MODULE_NETDEV_IEEE802154_OQPSK if MODULE_NETDEV default y endif # MODULE_AT86RF2XX diff --git a/drivers/at86rf2xx/Makefile.dep b/drivers/at86rf2xx/Makefile.dep index 0c81eb5cdbba..dbf0f2d2149a 100644 --- a/drivers/at86rf2xx/Makefile.dep +++ b/drivers/at86rf2xx/Makefile.dep @@ -1,11 +1,14 @@ DEFAULT_MODULE += auto_init_at86rf2xx -DEFAULT_MODULE += netdev_ieee802154_oqpsk USEMODULE += ztimer USEMODULE += ztimer_usec -USEMODULE += ieee802154 -USEMODULE += netdev_ieee802154 -USEMODULE += netdev_legacy_api +USEMODULE += event_thread +USEMODULE += event_thread_highest + +ifneq (,$(filter netdev,$(USEMODULE))) + USEMODULE += netdev_ieee802154_submac + DEFAULT_MODULE += netdev_ieee802154_oqpsk +endif # only needed for SPI based variants ifeq (,$(filter at86rfa1 at86rfr2,$(USEMODULE))) diff --git a/drivers/at86rf2xx/at86rf2xx.c b/drivers/at86rf2xx/at86rf2xx.c index 77e6daab93e9..dddf540521ff 100644 --- a/drivers/at86rf2xx/at86rf2xx.c +++ b/drivers/at86rf2xx/at86rf2xx.c @@ -37,7 +37,7 @@ #define ENABLE_DEBUG 0 #include "debug.h" -static void at86rf2xx_disable_clock_output(at86rf2xx_t *dev) +void at86rf2xx_disable_clock_output(at86rf2xx_t *dev) { #if AT86RF2XX_IS_PERIPH (void) dev; @@ -73,101 +73,6 @@ void at86rf2xx_disable_smart_idle(at86rf2xx_t *dev) #endif } -void at86rf2xx_reset(at86rf2xx_t *dev) -{ - uint8_t tmp; - - /* Reset state machine to ensure a known state */ - if (dev->state == AT86RF2XX_STATE_P_ON) { - at86rf2xx_set_state(dev, AT86RF2XX_STATE_FORCE_TRX_OFF); - } - - /* set default channel, page and TX power */ - at86rf2xx_configure_phy(dev, AT86RF2XX_DEFAULT_CHANNEL, AT86RF2XX_DEFAULT_PAGE, AT86RF2XX_DEFAULT_TXPOWER); - - /* set default options */ - - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - at86rf2xx_set_option(dev, AT86RF2XX_OPT_AUTOACK, true); - at86rf2xx_set_option(dev, AT86RF2XX_OPT_CSMA, true); - } - - /* enable safe mode (protect RX FIFO until reading data starts) */ - at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_2, - AT86RF2XX_TRX_CTRL_2_MASK__RX_SAFE_MODE); - -#if !AT86RF2XX_IS_PERIPH - /* don't populate masked interrupt flags to IRQ_STATUS register */ - tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_CTRL_1); - tmp &= ~(AT86RF2XX_TRX_CTRL_1_MASK__IRQ_MASK_MODE); - at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_1, tmp); -#endif - - /* configure smart idle listening feature */ - at86rf2xx_enable_smart_idle(dev); - - /* disable clock output to save power */ - at86rf2xx_disable_clock_output(dev); - - /* enable interrupts */ - at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, - AT86RF2XX_IRQ_STATUS_MASK__TRX_END); - - /* enable TX start interrupt for retry counter */ -#if AT86RF2XX_HAVE_TX_START_IRQ - at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK1, - AT86RF2XX_IRQ_STATUS_MASK1__TX_START); -#endif - - /* clear interrupt flags */ - at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); - - /* State to return after receiving or transmitting */ - dev->idle_state = AT86RF2XX_PHY_STATE_RX; - /* go into RX state */ - at86rf2xx_set_state(dev, AT86RF2XX_PHY_STATE_RX); - - /* Enable RX start IRQ */ - tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_MASK); - tmp |= AT86RF2XX_IRQ_STATUS_MASK__RX_START; - at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, tmp); - - DEBUG("at86rf2xx_reset(): reset complete.\n"); -} - -void at86rf2xx_tx_prepare(at86rf2xx_t *dev) -{ - uint8_t state; - - dev->pending_tx++; - state = at86rf2xx_set_state(dev, AT86RF2XX_PHY_STATE_TX); - if (state != AT86RF2XX_PHY_STATE_TX) { - dev->idle_state = state; - } - dev->tx_frame_len = IEEE802154_FCS_LEN; -} - -size_t at86rf2xx_tx_load(at86rf2xx_t *dev, const uint8_t *data, - size_t len, size_t offset) -{ - dev->tx_frame_len += (uint8_t)len; - at86rf2xx_sram_write(dev, offset + 1, data, len); - return offset + len; -} - -void at86rf2xx_tx_exec(at86rf2xx_t *dev) -{ -#if AT86RF2XX_HAVE_RETRIES - dev->tx_retries = -1; -#endif - - /* write frame length field in FIFO */ - at86rf2xx_sram_write(dev, 0, &(dev->tx_frame_len), 1); - /* trigger sending of pre-loaded frame */ - at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, - AT86RF2XX_TRX_STATE__TX_START); -} - bool at86rf2xx_cca(at86rf2xx_t *dev) { uint8_t reg; diff --git a/drivers/at86rf2xx/at86rf2xx_getset.c b/drivers/at86rf2xx/at86rf2xx_getset.c index 56f6332430eb..d2343d7e5361 100644 --- a/drivers/at86rf2xx/at86rf2xx_getset.c +++ b/drivers/at86rf2xx/at86rf2xx_getset.c @@ -353,9 +353,6 @@ void at86rf2xx_set_option(at86rf2xx_t *dev, uint16_t option, bool state) DEBUG("set option %i to %i\n", option, state); - /* set option field */ - dev->flags = (state) ? (dev->flags | option) - : (dev->flags & ~option); /* trigger option specific actions */ switch (option) { case AT86RF2XX_OPT_CSMA: @@ -434,8 +431,6 @@ static inline void _set_state(at86rf2xx_t *dev, uint8_t state, uint8_t cmd) else { while (at86rf2xx_get_status(dev) == AT86RF2XX_STATE_IN_PROGRESS) {} } - - dev->state = state; } uint8_t at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state) @@ -473,18 +468,18 @@ uint8_t at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state) /* Go to SLEEP mode from TRX_OFF */ #if AT86RF2XX_IS_PERIPH /* reset interrupts states in device */ - dev->irq_status = 0; /* Setting SLPTR bit brings radio transceiver to sleep in in TRX_OFF*/ *AT86RF2XX_REG__TRXPR |= (AT86RF2XX_TRXPR_SLPTR); #else gpio_set(dev->params.sleep_pin); #endif - dev->state = state; + dev->sleep = true; } else { if (old_state == AT86RF2XX_STATE_SLEEP) { DEBUG("at86rf2xx: waking up from sleep mode\n"); at86rf2xx_assert_awake(dev); + dev->sleep = false; } _set_state(dev, state, state); } diff --git a/drivers/at86rf2xx/at86rf2xx_internal.c b/drivers/at86rf2xx/at86rf2xx_internal.c index bc60316ba389..f91c87a8e82d 100644 --- a/drivers/at86rf2xx/at86rf2xx_internal.c +++ b/drivers/at86rf2xx/at86rf2xx_internal.c @@ -108,8 +108,8 @@ void at86rf2xx_fb_stop(const at86rf2xx_t *dev) uint8_t at86rf2xx_get_status(const at86rf2xx_t *dev) { /* if sleeping immediately return state */ - if (dev->state == AT86RF2XX_STATE_SLEEP) { - return dev->state; + if (dev->sleep) { + return AT86RF2XX_STATE_SLEEP; } return (at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) @@ -118,6 +118,7 @@ uint8_t at86rf2xx_get_status(const at86rf2xx_t *dev) void at86rf2xx_assert_awake(at86rf2xx_t *dev) { + uint8_t state; if (at86rf2xx_get_status(dev) == AT86RF2XX_STATE_SLEEP) { /* wake up and wait for transition to TRX_OFF */ #if AT86RF2XX_IS_PERIPH @@ -135,14 +136,15 @@ void at86rf2xx_assert_awake(at86rf2xx_t *dev) * Spin until we are actually awake */ do { - dev->state = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) + state = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; - } while (dev->state != AT86RF2XX_TRX_STATUS__TRX_OFF); + } while (state != AT86RF2XX_TRX_STATUS__TRX_OFF); } } void at86rf2xx_hardware_reset(at86rf2xx_t *dev) { + uint8_t state; /* trigger hardware reset */ #if AT86RF2XX_IS_PERIPH /* set reset Bit */ @@ -158,10 +160,10 @@ void at86rf2xx_hardware_reset(at86rf2xx_t *dev) * it remains P_ON. Otherwise, it should go to TRX_OFF */ do { - dev->state = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) + state = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; - } while ((dev->state != AT86RF2XX_STATE_TRX_OFF) - && (dev->state != AT86RF2XX_STATE_P_ON)); + } while ((state != AT86RF2XX_STATE_TRX_OFF) + && (state != AT86RF2XX_STATE_P_ON)); } #if AT86RF2XX_RANDOM_NUMBER_GENERATOR @@ -186,7 +188,7 @@ void at86rf2xx_get_random(at86rf2xx_t *dev, uint8_t *data, size_t len) #endif #if !AT86RF2XX_IS_PERIPH -void at86rf2xx_spi_init(at86rf2xx_t *dev, void (*irq_handler)(void *arg)) +void at86rf2xx_spi_init(at86rf2xx_t *dev, void (*irq_handler)(void *arg), void *ctx) { /* initialize GPIOs */ spi_init_cs(dev->params.spi, dev->params.cs_pin); @@ -194,7 +196,7 @@ void at86rf2xx_spi_init(at86rf2xx_t *dev, void (*irq_handler)(void *arg)) gpio_clear(dev->params.sleep_pin); gpio_init(dev->params.reset_pin, GPIO_OUT); gpio_set(dev->params.reset_pin); - gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_RISING, irq_handler, dev); + gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_RISING, irq_handler, ctx); /* Intentionally check if bus can be acquired, if assertions are on */ if (!IS_ACTIVE(NDEBUG)) { diff --git a/drivers/at86rf2xx/at86rf2xx_netdev.c b/drivers/at86rf2xx/at86rf2xx_netdev.c deleted file mode 100644 index 661243f6cb27..000000000000 --- a/drivers/at86rf2xx/at86rf2xx_netdev.c +++ /dev/null @@ -1,1154 +0,0 @@ -/* - * Copyright (C) 2018 Kaspar Schleiser - * 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 drivers_at86rf2xx - * @{ - * - * @file - * @brief Netdev adaption for the AT86RF2xx drivers - * - * @author Thomas Eichinger - * @author Hauke Petersen - * @author Kévin Roussel - * @author Martine Lenders - * @author Kaspar Schleiser - * @author Josua Arndt - * - * @} - */ - -#include -#include -#include - -#include "iolist.h" - -#include "net/eui64.h" -#include "net/ieee802154.h" -#include "net/netdev.h" -#include "net/netdev/ieee802154.h" - -#include "at86rf2xx.h" -#include "at86rf2xx_netdev.h" -#include "at86rf2xx_internal.h" -#include "at86rf2xx_registers.h" -#if IS_USED(IEEE802154_SECURITY) -#include "net/ieee802154_security.h" -#endif -#if IS_USED(MODULE_AT86RF2XX_AES_SPI) -#include "at86rf2xx_aes.h" -#endif - -#define ENABLE_DEBUG 0 -#include "debug.h" - -static int _send(netdev_t *netdev, const iolist_t *iolist); -static int _recv(netdev_t *netdev, void *buf, size_t len, void *info); -static int _init(netdev_t *netdev); -static void _isr(netdev_t *netdev); -static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len); -static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len); - -const netdev_driver_t at86rf2xx_driver = { - .send = _send, - .recv = _recv, - .init = _init, - .isr = _isr, - .get = _get, - .set = _set, -}; - -/* Default AT86RF2XX channel */ -static const uint16_t at86rf2xx_chan_default = AT86RF2XX_DEFAULT_CHANNEL; - -#if IS_USED(MODULE_AT86RF2XX_AES_SPI) && \ - IS_USED(MODULE_IEEE802154_SECURITY) -/** - * @brief Pass the 802.15.4 encryption key to the transceiver hardware - * - * @param[in] dev Abstract security device descriptor - * @param[in] key Encryption key to be used - * @param[in] key_size Size of the encryption key in bytes - */ -static void _at86rf2xx_set_key(ieee802154_sec_dev_t *dev, - const uint8_t *key, uint8_t key_size) -{ - (void)key_size; - at86rf2xx_aes_key_write_encrypt((at86rf2xx_t *)dev->ctx, key); -} - -/** - * @brief Compute CBC-MAC from IEEE 802.15.4 security context - * - * @param[in] dev Abstract security device descriptor - * @param[out] cipher Buffer to store cipher blocks - * @param[in] iv Initial vector - * @param[in] plain Input data blocks - * @param[in] nblocks Number of blocks - */ -static void _at86rf2xx_cbc(const ieee802154_sec_dev_t *dev, - uint8_t *cipher, - uint8_t *iv, - const uint8_t *plain, - uint8_t nblocks) -{ - at86rf2xx_aes_cbc_encrypt((at86rf2xx_t *)dev->ctx, - (aes_block_t *)cipher, - NULL, - iv, - (aes_block_t *)plain, - nblocks); -} - -/** - * @brief Perform ECB encryption - * - * @param[in] dev Abstract security device descriptor - * @param[out] cipher Output cipher blocks - * @param[in] plain Plain blocks - * @param[in] nblocks Number of blocks - */ -static void _at86rf2xx_ecb(const ieee802154_sec_dev_t *dev, - uint8_t *cipher, - const uint8_t *plain, - uint8_t nblocks) -{ - at86rf2xx_aes_ecb_encrypt((at86rf2xx_t *)dev->ctx, - (aes_block_t *)cipher, - NULL, - (aes_block_t *)plain, - nblocks); - -} -/** - * @brief Struct that contains IEEE 802.15.4 security operations - * which are implemented, using the transceiver´s hardware - * crypto capabilities - */ -static const ieee802154_radio_cipher_ops_t _at86rf2xx_cipher_ops = { - .set_key = _at86rf2xx_set_key, - .ecb = _at86rf2xx_ecb, - .cbc = _at86rf2xx_cbc -}; -#endif /* IS_USED(MODULE_AT86RF2XX_AES_SPI) && \ - IS_USED(MODULE_IEEE802154_SECURITY) */ - - -/* SOC has radio interrupts, store reference to netdev */ -static netdev_t *at86rfmega_dev; -static void _irq_handler(void *arg) -{ - netdev_trigger_event_isr(arg); -} - -static int _init(netdev_t *netdev) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - - if (AT86RF2XX_IS_PERIPH) { - at86rfmega_dev = netdev; - } - else { - at86rf2xx_spi_init(dev, _irq_handler); - } - - /* reset hardware into a defined state */ - at86rf2xx_hardware_reset(dev); - - /* test if the device is responding */ - if (at86rf2xx_reg_read(dev, AT86RF2XX_REG__PART_NUM) != AT86RF2XX_PARTNUM) { - DEBUG("[at86rf2xx] error: unable to read correct part number\n"); - return -ENOTSUP; - } - - /* reset device to default values and put it into RX state */ - netdev_ieee802154_reset(&dev->netdev); - at86rf2xx_set_addr_long(dev, (eui64_t *)dev->netdev.long_addr); - at86rf2xx_set_addr_short(dev, (network_uint16_t *)dev->netdev.short_addr); - - /* `netdev_ieee802154_reset` does not set the default channel. */ - netdev_ieee802154_set(&dev->netdev, NETOPT_CHANNEL, &at86rf2xx_chan_default, sizeof(at86rf2xx_chan_default)); - - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - static const netopt_enable_t ack_req = - IS_ACTIVE(CONFIG_IEEE802154_DEFAULT_ACK_REQ) ? NETOPT_ENABLE : NETOPT_DISABLE; - netdev_ieee802154_set(&dev->netdev, NETOPT_ACK_REQ, - &ack_req, sizeof(ack_req)); - } -#if IS_USED(MODULE_IEEE802154_SECURITY) && \ - IS_USED(MODULE_AT86RF2XX_AES_SPI) - dev->netdev.sec_ctx.dev.cipher_ops = &_at86rf2xx_cipher_ops; - dev->netdev.sec_ctx.dev.ctx = dev; -#endif - - at86rf2xx_reset(dev); - - /* Initialize CSMA seed with hardware address */ - at86rf2xx_set_csma_seed(dev, dev->netdev.long_addr); - - /* signal link UP */ - netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP); - - return 0; -} - -static int _send(netdev_t *netdev, const iolist_t *iolist) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - size_t len = 0; - - at86rf2xx_tx_prepare(dev); - - /* load packet data into FIFO */ - for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { - /* current packet data + FCS too long */ - if ((len + iol->iol_len + 2) > AT86RF2XX_MAX_PKT_LENGTH) { - DEBUG("[at86rf2xx] error: packet too large (%u byte) to be send\n", - (unsigned)len + 2); - return -EOVERFLOW; - } - if (iol->iol_len) { - len = at86rf2xx_tx_load(dev, iol->iol_base, iol->iol_len, len); - } - } - - /* send data out directly if pre-loading id disabled */ - if (!(dev->flags & AT86RF2XX_OPT_PRELOADING)) { - at86rf2xx_tx_exec(dev); - if (netdev->event_callback) { - netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED); - } - } - /* return the number of bytes that were actually loaded into the frame - * buffer/send out */ - return (int)len; -} - -static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - uint8_t phr; - size_t pkt_len; - - /* frame buffer protection will be unlocked as soon as at86rf2xx_fb_stop() is called, - * Set receiver to PLL_ON state to be able to free the SPI bus and avoid losing data. */ - at86rf2xx_set_state(dev, AT86RF2XX_STATE_PLL_ON); - - /* start frame buffer access */ - at86rf2xx_fb_start(dev); - - /* get the size of the received packet */ - phr = at86rf2xx_get_rx_len(dev); - - /* ignore MSB (refer p.80) and subtract length of FCS field */ - pkt_len = (phr & 0x7f) - 2; - - /* return length when buf == NULL */ - if (buf == NULL) { - /* release SPI bus */ - at86rf2xx_fb_stop(dev); - - /* drop packet, continue receiving */ - if (len > 0) { - /* set device back in operation state which was used before last transmission. - * This state is saved in at86rf2xx.c/at86rf2xx_tx_prepare() e.g RX_AACK_ON */ - at86rf2xx_set_state(dev, dev->idle_state); - } - - return pkt_len; - } - - /* not enough space in buf */ - if (pkt_len > len) { - at86rf2xx_fb_stop(dev); - /* set device back in operation state which was used before last transmission. - * This state is saved in at86rf2xx.c/at86rf2xx_tx_prepare() e.g RX_AACK_ON */ - at86rf2xx_set_state(dev, dev->idle_state); - return -ENOBUFS; - } - /* copy payload */ - at86rf2xx_fb_read(dev, (uint8_t *)buf, pkt_len); - - /* Ignore FCS but advance fb read - we must give a temporary buffer here, - * as we are not allowed to issue SPI transfers without any buffer */ - uint8_t tmp[2]; - at86rf2xx_fb_read(dev, tmp, 2); - (void)tmp; - - /* AT86RF212B RSSI_BASE_VAL + 1.03 * ED, base varies for diff. modulation and datarates - * AT86RF232 RSSI_BASE_VAL + ED, base -91dBm - * AT86RF233 RSSI_BASE_VAL + ED, base -94dBm - * AT86RF231 RSSI_BASE_VAL + ED, base -91dBm - * AT86RFA1 RSSI_BASE_VAL + ED, base -90dBm - * AT86RFR2 RSSI_BASE_VAL + ED, base -90dBm - * - * AT86RF231 MAN. p.92, 8.4.3 Data Interpretation - * AT86RF232 MAN. p.91, 8.4.3 Data Interpretation - * AT86RF233 MAN. p.102, 8.5.3 Data Interpretation - * - * for performance reasons we ignore the 1.03 scale factor on the 212B, - * which causes a slight error in the values, but the accuracy of the ED - * value is specified as +/- 5 dB, so it should not matter very much in real - * life. - */ - if (info != NULL) { - uint8_t ed = 0; - netdev_ieee802154_rx_info_t *radio_info = info; - at86rf2xx_fb_read(dev, &(radio_info->lqi), 1); - -#if AT86RF2XX_HAVE_ED_REGISTER - /* AT86RF231 does not provide ED at the end of the frame buffer, read - * from separate register instead */ - at86rf2xx_fb_stop(dev); - ed = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_ED_LEVEL); -#else - at86rf2xx_fb_read(dev, &ed, 1); - at86rf2xx_fb_stop(dev); -#endif - radio_info->rssi = RSSI_BASE_VAL + ed; - DEBUG("[at86rf2xx] LQI:%d high is good, RSSI:%d high is either good or" - "too much interference.\n", radio_info->lqi, radio_info->rssi); - } - else { - at86rf2xx_fb_stop(dev); - } - - /* set device back in operation state which was used before last transmission. - * This state is saved in at86rf2xx.c/at86rf2xx_tx_prepare() e.g RX_AACK_ON */ - at86rf2xx_set_state(dev, dev->idle_state); - - return pkt_len; -} - -static int _set_state(at86rf2xx_t *dev, netopt_state_t state) -{ - switch (state) { - case NETOPT_STATE_STANDBY: - at86rf2xx_set_state(dev, AT86RF2XX_STATE_TRX_OFF); - break; - case NETOPT_STATE_SLEEP: - at86rf2xx_set_state(dev, AT86RF2XX_STATE_SLEEP); - break; - case NETOPT_STATE_IDLE: - at86rf2xx_set_state(dev, AT86RF2XX_PHY_STATE_RX); - break; - case NETOPT_STATE_TX: - if (dev->flags & AT86RF2XX_OPT_PRELOADING) { - /* The netdev driver ISR switches the transceiver back to the - * previous idle state after a completed TX. If the user tries - * to initiate another transmission (retransmitting the same data) - * without first going to TX_ARET_ON, the command to start TX - * would be ignored, leading to a deadlock in this netdev driver - * thread. - * Additionally, avoids driver thread deadlock when PRELOADING - * is set and the user tries to initiate TX without first calling - * send() to write some frame data. - */ - if (dev->pending_tx == 0) { - /* retransmission of old data, at86rf2xx_tx_prepare normally - * increments this and the ISR for TX_END decrements it, to - * know when to switch back to the idle state. */ - ++dev->pending_tx; - } - at86rf2xx_set_state(dev, AT86RF2XX_PHY_STATE_TX); - at86rf2xx_tx_exec(dev); - if (dev->netdev.netdev.event_callback) { - dev->netdev.netdev.event_callback(&dev->netdev.netdev, NETDEV_EVENT_TX_STARTED); - } - } - break; - case NETOPT_STATE_RESET: - at86rf2xx_hardware_reset(dev); - netdev_ieee802154_reset(&dev->netdev); - /* set short and long address */ - at86rf2xx_set_addr_long(dev, (eui64_t *)dev->netdev.long_addr); - at86rf2xx_set_addr_short(dev, (network_uint16_t *)dev->netdev.short_addr); - - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - static const netopt_enable_t ack_req = - IS_ACTIVE(CONFIG_IEEE802154_DEFAULT_ACK_REQ) ? NETOPT_ENABLE : NETOPT_DISABLE; - netdev_ieee802154_set(&dev->netdev, NETOPT_ACK_REQ, - &ack_req, sizeof(ack_req)); - } - at86rf2xx_reset(dev); - break; - default: - return -ENOTSUP; - } - return sizeof(netopt_state_t); -} - -netopt_state_t _get_state(at86rf2xx_t *dev) -{ - switch (at86rf2xx_get_status(dev)) { - case AT86RF2XX_STATE_SLEEP: - return NETOPT_STATE_SLEEP; - case AT86RF2XX_STATE_TRX_OFF: - return NETOPT_STATE_STANDBY; - case AT86RF2XX_PHY_STATE_RX_BUSY: - return NETOPT_STATE_RX; - case AT86RF2XX_PHY_STATE_TX: - case AT86RF2XX_PHY_STATE_TX_BUSY: - return NETOPT_STATE_TX; - case AT86RF2XX_PHY_STATE_RX: - default: - return NETOPT_STATE_IDLE; - } -} - -static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - - if (netdev == NULL) { - return -ENODEV; - } - - /* getting these options doesn't require the transceiver to be responsive */ - switch (opt) { -#if AT86RF2XX_HAVE_SUBGHZ - case NETOPT_CHANNEL_PAGE: - assert(max_len >= sizeof(uint16_t)); - ((uint8_t *)val)[1] = 0; - ((uint8_t *)val)[0] = dev->page; - return sizeof(uint16_t); -#endif - - case NETOPT_STATE: - assert(max_len >= sizeof(netopt_state_t)); - *((netopt_state_t *)val) = _get_state(dev); - return sizeof(netopt_state_t); - - case NETOPT_PRELOADING: - if (dev->flags & AT86RF2XX_OPT_PRELOADING) { - *((netopt_enable_t *)val) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)val) = NETOPT_DISABLE; - } - return sizeof(netopt_enable_t); - - case NETOPT_PROMISCUOUSMODE: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - if (dev->flags & AT86RF2XX_OPT_PROMISCUOUS) { - *((netopt_enable_t *)val) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)val) = NETOPT_DISABLE; - } - return sizeof(netopt_enable_t); - } - break; - - case NETOPT_RX_START_IRQ: - case NETOPT_TX_START_IRQ: - case NETOPT_TX_END_IRQ: - *((netopt_enable_t *)val) = NETOPT_ENABLE; - return sizeof(netopt_enable_t); - - case NETOPT_CSMA: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - *((netopt_enable_t *)val) = - !!(dev->flags & AT86RF2XX_OPT_CSMA); - return sizeof(netopt_enable_t); - } - break; - -/* Only radios with the XAH_CTRL_2 register support frame retry reporting */ -#if AT86RF2XX_HAVE_RETRIES - case NETOPT_TX_RETRIES_NEEDED: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - assert(max_len >= sizeof(uint8_t)); - *((uint8_t *)val) = dev->tx_retries; - return sizeof(uint8_t); - } - break; -#endif - - default: - /* Can still be handled in second switch */ - break; - } - - int res; - - if (((res = netdev_ieee802154_get(container_of(netdev, netdev_ieee802154_t, netdev), - opt, val, max_len)) >= 0) - || (res != -ENOTSUP)) { - return res; - } - - uint8_t old_state = at86rf2xx_get_status(dev); - - /* temporarily wake up if sleeping */ - if (old_state == AT86RF2XX_STATE_SLEEP) { - at86rf2xx_assert_awake(dev); - } - - /* these options require the transceiver to be not sleeping*/ - switch (opt) { - case NETOPT_TX_POWER: - assert(max_len >= sizeof(int16_t)); - *((uint16_t *)val) = netdev_ieee802154->txpower; - res = sizeof(uint16_t); - break; - - case NETOPT_RETRANS: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - assert(max_len >= sizeof(uint8_t)); - *((uint8_t *)val) = at86rf2xx_get_max_retries(dev); - res = sizeof(uint8_t); - } - break; - - case NETOPT_CSMA_RETRIES: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - assert(max_len >= sizeof(uint8_t)); - *((uint8_t *)val) = at86rf2xx_get_csma_max_retries(dev); - res = sizeof(uint8_t); - } - break; - - case NETOPT_CCA_THRESHOLD: - assert(max_len >= sizeof(int8_t)); - *((int8_t *)val) = at86rf2xx_get_cca_threshold(dev); - res = sizeof(int8_t); - break; - - case NETOPT_IS_CHANNEL_CLR: - assert(max_len >= sizeof(netopt_enable_t)); - *((netopt_enable_t *)val) = at86rf2xx_cca(dev); - res = sizeof(netopt_enable_t); - break; - - case NETOPT_LAST_ED_LEVEL: - assert(max_len >= sizeof(int8_t)); - *((int8_t *)val) = at86rf2xx_get_ed_level(dev); - res = sizeof(int8_t); - break; - - case NETOPT_AUTOACK: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - assert(max_len >= sizeof(netopt_enable_t)); - uint8_t tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__CSMA_SEED_1); - *((netopt_enable_t *)val) = (tmp & AT86RF2XX_CSMA_SEED_1__AACK_DIS_ACK) ? false : true; - res = sizeof(netopt_enable_t); - } - break; - -#ifdef MODULE_NETDEV_IEEE802154_OQPSK - - case NETOPT_IEEE802154_PHY: - assert(max_len >= sizeof(int8_t)); - *(uint8_t *)val = at86rf2xx_get_phy_mode(dev); - return sizeof(uint8_t); - - case NETOPT_OQPSK_RATE: - assert(max_len >= sizeof(int8_t)); - *(uint8_t *)val = at86rf2xx_get_rate(dev); - return sizeof(uint8_t); - -#endif /* MODULE_NETDEV_IEEE802154_OQPSK */ -#if AT86RF2XX_RANDOM_NUMBER_GENERATOR - case NETOPT_RANDOM: - at86rf2xx_get_random(dev, (uint8_t*)val, max_len); - return max_len; -#endif - default: - res = -ENOTSUP; - break; - } - - /* go back to sleep if were sleeping */ - if (old_state == AT86RF2XX_STATE_SLEEP) { - at86rf2xx_set_state(dev, AT86RF2XX_STATE_SLEEP); - } - - return res; -} - -static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - if (dev == NULL) { - return -ENODEV; - } - uint8_t old_state = at86rf2xx_get_status(dev); - int res = -ENOTSUP; - - /* temporarily wake up if sleeping and opt != NETOPT_STATE. - * opt != NETOPT_STATE check prevents redundant wake-up. - * when opt == NETOPT_STATE, at86rf2xx_set_state() will wake up the - * radio if needed. */ - if ((old_state == AT86RF2XX_STATE_SLEEP) && (opt != NETOPT_STATE)) { - at86rf2xx_assert_awake(dev); - } - - switch (opt) { - case NETOPT_ADDRESS: - assert(len == sizeof(network_uint16_t)); - memcpy(dev->netdev.short_addr, val, len); -#ifdef MODULE_SIXLOWPAN - /* https://tools.ietf.org/html/rfc4944#section-12 requires the first bit to - * 0 for unicast addresses */ - dev->netdev.short_addr[0] &= 0x7F; -#endif - at86rf2xx_set_addr_short(dev, val); - /* don't set res to set netdev_ieee802154_t::short_addr */ - break; - case NETOPT_ADDRESS_LONG: - assert(len == sizeof(eui64_t)); - memcpy(dev->netdev.long_addr, val, len); - at86rf2xx_set_addr_long(dev, val); - /* don't set res to set netdev_ieee802154_t::long_addr */ - break; - case NETOPT_NID: - assert(len == sizeof(uint16_t)); - at86rf2xx_set_pan(dev, *((const uint16_t *)val)); - /* don't set res to set netdev_ieee802154_t::pan */ - break; - case NETOPT_CHANNEL: - assert(len == sizeof(uint16_t)); - uint8_t chan = (((const uint16_t *)val)[0]) & UINT8_MAX; -#if AT86RF2XX_MIN_CHANNEL - if (chan < AT86RF2XX_MIN_CHANNEL || chan > AT86RF2XX_MAX_CHANNEL) { -#else - if (chan > AT86RF2XX_MAX_CHANNEL) { -#endif /* AT86RF2XX_MIN_CHANNEL */ - res = -EINVAL; - break; - } - dev->netdev.chan = chan; -#if AT86RF2XX_HAVE_SUBGHZ - at86rf2xx_configure_phy(dev, chan, dev->page, dev->netdev.txpower); -#else - at86rf2xx_configure_phy(dev, chan, 0, dev->netdev.txpower); -#endif - /* don't set res to set netdev_ieee802154_t::chan */ - break; - - case NETOPT_CHANNEL_PAGE: - assert(len == sizeof(uint16_t)); - uint8_t page = (((const uint16_t *)val)[0]) & UINT8_MAX; -#if AT86RF2XX_HAVE_SUBGHZ - if ((page != 0) && (page != 2)) { - res = -EINVAL; - } - else { - dev->page = page; - at86rf2xx_configure_phy(dev, dev->netdev.chan, page, dev->netdev.txpower); - res = sizeof(uint16_t); - } -#else - /* rf23x only supports page 0, no need to configure anything in the driver. */ - if (page != 0) { - res = -EINVAL; - } - else { - res = sizeof(uint16_t); - } -#endif - break; - - case NETOPT_TX_POWER: - assert(len <= sizeof(int16_t)); - netdev_ieee802154->txpower = *((const int16_t *)val); -#if AT86RF2XX_HAVE_SUBGHZ - at86rf2xx_configure_phy(dev, dev->netdev.chan, dev->page, *((const int16_t *)val)); -#else - at86rf2xx_configure_phy(dev, dev->netdev.chan, 0, *((const int16_t *)val)); -#endif - res = sizeof(uint16_t); - break; - - case NETOPT_STATE: - assert(len <= sizeof(netopt_state_t)); - res = _set_state(dev, *((const netopt_state_t *)val)); - break; - - case NETOPT_AUTOACK: - - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - at86rf2xx_set_option(dev, AT86RF2XX_OPT_AUTOACK, - ((const bool *)val)[0]); - res = sizeof(netopt_enable_t); - } - break; - - case NETOPT_ACK_PENDING: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - at86rf2xx_set_option(dev, AT86RF2XX_OPT_ACK_PENDING, - ((const bool *)val)[0]); - res = sizeof(netopt_enable_t); - } - break; - - case NETOPT_RETRANS: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - assert(len <= sizeof(uint8_t)); - at86rf2xx_set_max_retries(dev, *((const uint8_t *)val)); - res = sizeof(uint8_t); - } - break; - - case NETOPT_PRELOADING: - at86rf2xx_set_option(dev, AT86RF2XX_OPT_PRELOADING, - ((const bool *)val)[0]); - res = sizeof(netopt_enable_t); - break; - - case NETOPT_PROMISCUOUSMODE: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - at86rf2xx_set_option(dev, AT86RF2XX_OPT_PROMISCUOUS, - ((const bool *)val)[0]); - res = sizeof(netopt_enable_t); - } - break; - - case NETOPT_CSMA: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - at86rf2xx_set_option(dev, AT86RF2XX_OPT_CSMA, - ((const bool *)val)[0]); - res = sizeof(netopt_enable_t); - } - break; - - case NETOPT_CSMA_RETRIES: - if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - assert(len <= sizeof(uint8_t)); - if (!(dev->flags & AT86RF2XX_OPT_CSMA) || - (*((uint8_t *)val) > 5)) { - /* If CSMA is disabled, don't allow setting retries */ - res = -EINVAL; - } - else { - at86rf2xx_set_csma_max_retries(dev, *((const uint8_t *)val)); - res = sizeof(uint8_t); - } - } - break; - - case NETOPT_CCA_THRESHOLD: - assert(len <= sizeof(int8_t)); - at86rf2xx_set_cca_threshold(dev, *((const int8_t *)val)); - res = sizeof(int8_t); - break; - -#ifdef MODULE_NETDEV_IEEE802154_OQPSK - - case NETOPT_OQPSK_RATE: - assert(len <= sizeof(int8_t)); - if (at86rf2xx_set_rate(dev, *((const uint8_t *)val)) < 0) { - res = -EINVAL; - } else { - res = sizeof(uint8_t); - } - break; - -#endif /* MODULE_NETDEV_IEEE802154_OQPSK */ - default: - break; - } - - /* go back to sleep if were sleeping and state hasn't been changed */ - if ((old_state == AT86RF2XX_STATE_SLEEP) - && (opt != NETOPT_STATE)) { - at86rf2xx_set_state(dev, AT86RF2XX_STATE_SLEEP); - } - - if (res == -ENOTSUP) { - res = netdev_ieee802154_set(container_of(netdev, netdev_ieee802154_t, netdev), - opt, val, len); - } - - return res; -} - -static void _isr_send_complete(at86rf2xx_t *dev, uint8_t trac_status) -{ - netdev_t *netdev = &dev->netdev.netdev; - if (IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); - return; - } -/* Only radios with the XAH_CTRL_2 register support frame retry reporting */ -#if AT86RF2XX_HAVE_RETRIES_REG - dev->tx_retries = (at86rf2xx_reg_read(dev, AT86RF2XX_REG__XAH_CTRL_2) - & AT86RF2XX_XAH_CTRL_2__ARET_FRAME_RETRIES_MASK) >> - AT86RF2XX_XAH_CTRL_2__ARET_FRAME_RETRIES_OFFSET; -#endif - - DEBUG("[at86rf2xx] EVT - TX_END\n"); - - if (netdev->event_callback) { - switch (trac_status) { -#ifdef MODULE_OPENTHREAD - case AT86RF2XX_TRX_STATE__TRAC_SUCCESS: - netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); - DEBUG("[at86rf2xx] TX SUCCESS\n"); - break; - case AT86RF2XX_TRX_STATE__TRAC_SUCCESS_DATA_PENDING: - netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE_DATA_PENDING); - DEBUG("[at86rf2xx] TX SUCCESS DATA PENDING\n"); - break; -#else - case AT86RF2XX_TRX_STATE__TRAC_SUCCESS: - case AT86RF2XX_TRX_STATE__TRAC_SUCCESS_DATA_PENDING: - netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); - DEBUG("[at86rf2xx] TX SUCCESS\n"); - break; -#endif - case AT86RF2XX_TRX_STATE__TRAC_NO_ACK: - netdev->event_callback(netdev, NETDEV_EVENT_TX_NOACK); - DEBUG("[at86rf2xx] TX NO_ACK\n"); - break; - case AT86RF2XX_TRX_STATE__TRAC_CHANNEL_ACCESS_FAILURE: - netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY); - DEBUG("[at86rf2xx] TX_CHANNEL_ACCESS_FAILURE\n"); - break; - default: - DEBUG("[at86rf2xx] Unhandled TRAC_STATUS: %d\n", - trac_status >> 5); - } - } -} - -static inline void _isr_recv_complete(netdev_t *netdev) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - if (!netdev->event_callback) { - return; - } - if (IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { - uint8_t phy_status = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_RSSI); - bool crc_ok = phy_status & AT86RF2XX_PHY_RSSI_MASK__RX_CRC_VALID; - - if (crc_ok) { - netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); - } - else { - netdev->event_callback(netdev, NETDEV_EVENT_CRC_ERROR); - } - } - else { - netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); - } -} - -static void _isr(netdev_t *netdev) -{ - netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - uint8_t irq_mask; - uint8_t state; - uint8_t trac_status; - - /* If transceiver is sleeping register access is impossible and frames are - * lost anyway, so return immediately. - */ - state = at86rf2xx_get_status(dev); - if (state == AT86RF2XX_STATE_SLEEP) { - return; - } - - /* read (consume) device status */ - irq_mask = at86rf2xx_get_irq_flags(dev); - - trac_status = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATE) - & AT86RF2XX_TRX_STATE_MASK__TRAC; - - if (irq_mask & AT86RF2XX_IRQ_STATUS_MASK__RX_START) { - netdev->event_callback(netdev, NETDEV_EVENT_RX_STARTED); - DEBUG("[at86rf2xx] EVT - RX_START\n"); - } - - if (irq_mask & AT86RF2XX_IRQ_STATUS_MASK__TRX_END) { - if ((state == AT86RF2XX_PHY_STATE_RX) - || (state == AT86RF2XX_PHY_STATE_RX_BUSY)) { - DEBUG("[at86rf2xx] EVT - RX_END\n"); - - _isr_recv_complete(netdev); - - } - else if (state == AT86RF2XX_PHY_STATE_TX) { - /* check for more pending TX calls and return to idle state if - * there are none */ - assert(dev->pending_tx != 0); - /* Radio is idle, any TX transaction is done */ - dev->pending_tx = 0; - at86rf2xx_set_state(dev, dev->idle_state); - DEBUG("[at86rf2xx] return to idle state 0x%x\n", dev->idle_state); - _isr_send_complete(dev, trac_status); - } - /* Only the case when an interrupt was received and the radio is busy - * with a next PDU transmission when _isr is called. - * dev->pending == 1 means a receive and immediately a send happened. - * The receive is discarded as the send already overwrote the internal - * buffer. - * dev->pending == 2 means two transmits occurred and this is the isr for - * the first. - */ - else if (state == AT86RF2XX_PHY_STATE_TX_BUSY) { - if (dev->pending_tx > 1) { - dev->pending_tx--; - _isr_send_complete(dev, trac_status); - } - } - } -} - -void at86rf2xx_setup(at86rf2xx_t *dev, const at86rf2xx_params_t *params, uint8_t index) -{ - netdev_t *netdev = &dev->netdev.netdev; - - netdev->driver = &at86rf2xx_driver; - /* State to return after receiving or transmitting */ - dev->idle_state = AT86RF2XX_STATE_TRX_OFF; - /* radio state is P_ON when first powered-on */ - dev->state = AT86RF2XX_STATE_P_ON; - dev->pending_tx = 0; - -#if AT86RF2XX_IS_PERIPH - (void) params; - /* set all interrupts off */ - at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, 0x00); -#else - /* initialize device descriptor */ - dev->params = *params; -#endif - - netdev_register(netdev, NETDEV_AT86RF2XX, index); - /* set device address */ - netdev_ieee802154_setup(&dev->netdev); -} - - -#if AT86RF2XX_IS_PERIPH - -/** - * @brief ISR for transceiver's TX_START interrupt - * - * In procedure TX_ARET the TRX24_TX_START interrupt is issued separately for every - * frame transmission and frame retransmission. - * Indicates the frame start of a transmitted acknowledge frame in procedure RX_AACK. - * - * Flow Diagram Manual p. 52 / 63 - */ -#if AT86RF2XX_HAVE_RETRIES -ISR(TRX24_TX_START_vect){ - /* __enter_isr(); is not necessary as there is nothing which causes a - * thread_yield and the interrupt can not be interrupted by an other ISR */ - - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - - dev->tx_retries++; -} -#endif - -/** - * @brief Transceiver PLL Lock - * - * Is triggered when PLL locked successfully. - * - * Manual p. 40 - */ -ISR(TRX24_PLL_LOCK_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - DEBUG("TRX24_PLL_LOCK\n"); - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__PLL_LOCK; - - avr8_exit_isr(); -} - -/** - * @brief Transceiver PLL Unlock - * - * Is triggered when PLL unlocked unexpectedly. - * - * Manual p. 89 - */ -ISR(TRX24_PLL_UNLOCK_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - DEBUG("TRX24_PLL_UNLOCK\n"); - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__PLL_UNLOCK; - - avr8_exit_isr(); -} - -/** - * @brief ISR for transceiver's receive start interrupt - * - * Is triggered when valid PHR is detected. - * Save IRQ status and inform upper layer of data reception. - * - * Flow Diagram Manual p. 52 / 63 - */ -ISR(TRX24_RX_START_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - uint8_t status = *AT86RF2XX_REG__TRX_STATE & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; - DEBUG("TRX24_RX_START 0x%x\n", status); - - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__RX_START; - /* Call upper layer to process valid PHR */ - netdev_trigger_event_isr(at86rfmega_dev); - - avr8_exit_isr(); -} - -/** - * @brief ISR for transceiver's receive end interrupt - * - * Is triggered when valid data is received. FCS check passed. - * Save IRQ status and inform upper layer of data reception. - * - * Flow Diagram Manual p. 52 / 63 - */ -ISR(TRX24_RX_END_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - uint8_t status = *AT86RF2XX_REG__TRX_STATE & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; - DEBUG("TRX24_RX_END 0x%x\n", status); - - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__RX_END; - /* Call upper layer to process received data */ - netdev_trigger_event_isr(at86rfmega_dev); - - avr8_exit_isr(); -} - -/** - * @brief Transceiver CCAED Measurement finished - * - * Is triggered when CCA or ED measurement completed. - * - * Manual p. 74 and p. 76 - */ -ISR(TRX24_CCA_ED_DONE_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - DEBUG("TRX24_CCA_ED_DONE\n"); - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__CCA_ED_DONE; - - avr8_exit_isr(); -} - -/** - * @brief Transceiver Frame Address Match, indicates incoming frame - * - * Is triggered when Frame with valid Address is received. - * Can be used to wake up MCU from sleep, etc. - * - * Flow Diagram Manual p. 52 / 63 - */ -ISR(TRX24_XAH_AMI_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - DEBUG("TRX24_XAH_AMI\n"); - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__AMI; - - avr8_exit_isr(); -} - -/** - * @brief ISR for transceiver's transmit end interrupt - * - * Is triggered when data or when acknowledge frames where send. - * - * Flow Diagram Manual p. 52 / 63 - */ -ISR(TRX24_TX_END_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - uint8_t status = *AT86RF2XX_REG__TRX_STATE & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; - DEBUG("TRX24_TX_END 0x%x\n", status); - - /* only inform upper layer when a transmission was done, - * not for sending acknowledge frames if data was received. */ - if (status != AT86RF2XX_PHY_STATE_RX) { - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__TX_END; - - /* Call upper layer to process if data was send successful */ - netdev_trigger_event_isr(at86rfmega_dev); - } - - avr8_exit_isr(); -} - -/** - * @brief ISR for transceiver's wakeup finished interrupt - * - * Is triggered when transceiver module reset is finished. - * Save IRQ status and inform upper layer the transceiver is operational. - * - * Manual p. 40 - */ -ISR(TRX24_AWAKE_vect, ISR_BLOCK) -{ - avr8_enter_isr(); - - DEBUG("TRX24_AWAKE\n"); - - netdev_ieee802154_t *netdev_ieee802154 = container_of(at86rfmega_dev, - netdev_ieee802154_t, netdev); - at86rf2xx_t *dev = container_of(netdev_ieee802154, at86rf2xx_t, netdev); - dev->irq_status |= AT86RF2XX_IRQ_STATUS_MASK__AWAKE; - /* Call upper layer to process transceiver wakeup finished */ - netdev_trigger_event_isr(at86rfmega_dev); - - avr8_exit_isr(); -} - -#endif /* AT86RF2XX_IS_PERIPH */ diff --git a/drivers/at86rf2xx/at86rf2xx_rf_ops.c b/drivers/at86rf2xx/at86rf2xx_rf_ops.c new file mode 100644 index 000000000000..03538c0777bc --- /dev/null +++ b/drivers/at86rf2xx/at86rf2xx_rf_ops.c @@ -0,0 +1,968 @@ +/* + * Copyright (C) 2023 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. + */ + +/** + * @ingroup drivers_at86rf2xx + * @{ + * + * @file + * @brief Implementation of 802.15.4 Radio HAL interface for AT86RF2xx + * transceivers. + * + * @author José I. Álamos + * @} + */ + +#include "at86rf2xx.h" +#include "at86rf2xx_internal.h" +#include "at86rf2xx_registers.h" +#include "macros/utils.h" +#include "net/ieee802154/radio.h" + +#if IS_USED(MODULE_AT86RF2XX_AES_SPI) +#include "at86rf2xx_aes.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static const ieee802154_radio_ops_t at86rf2xx_ops; +static ieee802154_dev_t *at86rf2xx_periph; + +#if IS_USED(MODULE_AT86RF2XX_AES_SPI) && \ + IS_USED(MODULE_IEEE802154_SECURITY) +/** + * @brief Pass the 802.15.4 encryption key to the transceiver hardware + * + * @param[in] dev Abstract security device descriptor + * @param[in] key Encryption key to be used + * @param[in] key_size Size of the encryption key in bytes + */ +static void _at86rf2xx_set_key(ieee802154_sec_dev_t *dev, + const uint8_t *key, uint8_t key_size) +{ + (void)key_size; + ieee802154_dev_t *hal = dev->ctx; + at86rf2xx_aes_key_write_encrypt(hal->priv, key); +} + +/** + * @brief Compute CBC-MAC from IEEE 802.15.4 security context + * + * @param[in] dev Abstract security device descriptor + * @param[out] cipher Buffer to store cipher blocks + * @param[in] iv Initial vector + * @param[in] plain Input data blocks + * @param[in] nblocks Number of blocks + */ +static void _at86rf2xx_cbc(const ieee802154_sec_dev_t *dev, + uint8_t *cipher, + uint8_t *iv, + const uint8_t *plain, + uint8_t nblocks) +{ + ieee802154_dev_t *hal = dev->ctx; + at86rf2xx_aes_cbc_encrypt(hal->priv, + (aes_block_t *)cipher, + NULL, + iv, + (aes_block_t *)plain, + nblocks); +} + +/** + * @brief Perform ECB encryption + * + * @param[in] dev Abstract security device descriptor + * @param[out] cipher Output cipher blocks + * @param[in] plain Plain blocks + * @param[in] nblocks Number of blocks + */ +static void _at86rf2xx_ecb(const ieee802154_sec_dev_t *dev, + uint8_t *cipher, + const uint8_t *plain, + uint8_t nblocks) +{ + ieee802154_dev_t *hal = dev->ctx; + at86rf2xx_aes_ecb_encrypt(hal->priv, + (aes_block_t *)cipher, + NULL, + (aes_block_t *)plain, + nblocks); + +} +/** + * @brief Struct that contains IEEE 802.15.4 security operations + * which are implemented, using the transceiver´s hardware + * crypto capabilities + */ +static const ieee802154_radio_cipher_ops_t _at86rf2xx_cipher_ops = { + .set_key = _at86rf2xx_set_key, + .ecb = _at86rf2xx_ecb, + .cbc = _at86rf2xx_cbc +}; +#endif /* IS_USED(MODULE_AT86RF2XX_AES_SPI) && \ + IS_USED(MODULE_IEEE802154_SECURITY) */ + +static int _write(ieee802154_dev_t *hal, const iolist_t *psdu) +{ + DEBUG("at86rf2xx_rf_ops: write\n"); + uint8_t len = 0; + + at86rf2xx_t *dev = hal->priv; + + mutex_lock(&dev->lock); + /* load packet data into FIFO */ + for (const iolist_t *iol = psdu; iol; iol = iol->iol_next) { + /* current packet data + FCS too long */ + if ((len + iol->iol_len + 2) > AT86RF2XX_MAX_PKT_LENGTH) { + DEBUG("[at86rf2xx] error: packet too large (%u byte) to be send\n", + (unsigned)len + 2); + mutex_unlock(&dev->lock); + return -EOVERFLOW; + } + + if (iol->iol_len) { + at86rf2xx_sram_write(dev, len + 1, iol->iol_base, iol->iol_len); + len += iol->iol_len; + } + } + + len += IEEE802154_FCS_LEN; + assert(len <= IEEE802154_FRAME_LEN_MAX); + /* write frame length field in FIFO */ + at86rf2xx_sram_write(dev, 0, &len, 1); + mutex_unlock(&dev->lock); + + return 0; +} + +static int _read(ieee802154_dev_t *hal, void *buf, size_t size, ieee802154_rx_info_t *info) +{ + at86rf2xx_t *dev = hal->priv; + uint8_t phr; + size_t pkt_len; + + mutex_lock(&dev->lock); + /* start frame buffer access */ + at86rf2xx_fb_start(dev); + + /* get the size of the received packet */ + phr = at86rf2xx_get_rx_len(dev); + + /* ignore MSB (refer p.80) and subtract length of FCS field */ + pkt_len = (phr & 0x7f) - 2; + + /* not enough space in buf */ + if (pkt_len > size) { + at86rf2xx_fb_stop(dev); + mutex_unlock(&dev->lock); + return -ENOBUFS; + } + + if (!buf) { + at86rf2xx_fb_stop(dev); + mutex_unlock(&dev->lock); + return 0; + } + + /* copy payload */ + at86rf2xx_fb_read(dev, (uint8_t *)buf, pkt_len); + + /* Ignore FCS but advance fb read - we must give a temporary buffer here, + * as we are not allowed to issue SPI transfers without any buffer */ + uint8_t tmp[2]; + at86rf2xx_fb_read(dev, tmp, 2); + (void)tmp; + + /* AT86RF212B RSSI_BASE_VAL + 1.03 * ED, base varies for diff. modulation and datarates + * AT86RF232 RSSI_BASE_VAL + ED, base -91dBm + * AT86RF233 RSSI_BASE_VAL + ED, base -94dBm + * AT86RF231 RSSI_BASE_VAL + ED, base -91dBm + * AT86RFA1 RSSI_BASE_VAL + ED, base -90dBm + * AT86RFR2 RSSI_BASE_VAL + ED, base -90dBm + * + * AT86RF231 MAN. p.92, 8.4.3 Data Interpretation + * AT86RF232 MAN. p.91, 8.4.3 Data Interpretation + * AT86RF233 MAN. p.102, 8.5.3 Data Interpretation + * + * for performance reasons we ignore the 1.03 scale factor on the 212B, + * which causes a slight error in the values, but the accuracy of the ED + * value is specified as +/- 5 dB, so it should not matter very much in real + * life. + */ + if (info != NULL) { + uint8_t ed = 0; + at86rf2xx_fb_read(dev, &(info->lqi), 1); + +#if AT86RF2XX_HAVE_ED_REGISTER + /* AT86RF231 does not provide ED at the end of the frame buffer, read + * from separate register instead */ + at86rf2xx_fb_stop(dev); + ed = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_ED_LEVEL); +#else + at86rf2xx_fb_read(dev, &ed, 1); + at86rf2xx_fb_stop(dev); +#endif + info->rssi = RSSI_BASE_VAL + ed; + DEBUG("[at86rf2xx] LQI:%d high is good, RSSI:%d high is either good or" + "too much interference.\n", info->lqi, info->rssi); + } + else { + at86rf2xx_fb_stop(dev); + } + + mutex_unlock(&dev->lock); + return pkt_len; +} + +static int _request_on(ieee802154_dev_t *hal) +{ + DEBUG("at86rf2xx_rf_ops: request_on\n"); + at86rf2xx_t *dev = hal->priv; + mutex_lock(&dev->lock); +#if !AT86RF2XX_IS_PERIPH + gpio_irq_enable(dev->params.int_pin); +#endif + /* enable interrupts */ + at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, + AT86RF2XX_IRQ_STATUS_MASK__TRX_END | AT86RF2XX_IRQ_STATUS_MASK__RX_START); + +#if AT86RF2XX_IS_PERIPH + /* Setting SLPTR bit in TRXPR to 0 returns the radio transceiver + * to the TRX_OFF state */ + *AT86RF2XX_REG__TRXPR &= ~(AT86RF2XX_TRXPR_SLPTR); +#else + gpio_clear(dev->params.sleep_pin); +#endif + + mutex_unlock(&dev->lock); + return 0; +} + +static int _confirm_on(ieee802154_dev_t *hal) +{ + DEBUG("at86rf2xx_rf_ops: confirm_on\n"); + at86rf2xx_t *dev = hal->priv; + mutex_lock(&dev->lock); + int status = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) + & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; + + if (status != AT86RF2XX_TRX_STATUS__TRX_OFF) { + mutex_unlock(&dev->lock); + return -EAGAIN; + } + at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); + mutex_unlock(&dev->lock); + return 0; +} + +static int _len(ieee802154_dev_t *hal) +{ + DEBUG("at86rf2xx_rf_ops: len\n"); + at86rf2xx_t *dev = hal->priv; + mutex_lock(&dev->lock); + uint8_t phr = at86rf2xx_peek_rx_len(dev); + mutex_unlock(&dev->lock); + return (phr & 0x7f) - 2; +} + +static int _off(ieee802154_dev_t *hal) +{ + DEBUG("at86rf2xx_rf_ops: off\n"); + at86rf2xx_t *dev = hal->priv; + mutex_lock(&dev->lock); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, AT86RF2XX_STATE_FORCE_TRX_OFF); + + /* Discard all IRQ flags, framebuffer is lost anyway */ + at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); + /* Go to SLEEP mode from TRX_OFF */ +#if AT86RF2XX_IS_PERIPH + /* reset interrupts states in device */ + /* Setting SLPTR bit brings radio transceiver to sleep in in TRX_OFF*/ + *AT86RF2XX_REG__TRXPR |= (AT86RF2XX_TRXPR_SLPTR); +#else + gpio_irq_disable(dev->params.int_pin); + gpio_set(dev->params.sleep_pin); +#endif + mutex_unlock(&dev->lock); + return 0; +} +static int _request_set_rx(at86rf2xx_t *dev) +{ + DEBUG("at86r2xx_rf_ops: request_set_rx\n"); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, AT86RF2XX_PHY_STATE_RX); + return 0; +} + +static int _request_set_tx(at86rf2xx_t *dev, bool force) +{ + DEBUG("at86r2xx_rf_ops: request_set_tx\n"); + /* Prevent issue described in https://github.com/RIOT-OS/RIOT/pull/11256 */ + if (at86rf2xx_get_status(dev) == AT86RF2XX_PHY_STATE_RX_BUSY && !force) { + return -EBUSY; + } + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, AT86RF2XX_TRX_STATE__FORCE_PLL_ON); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, AT86RF2XX_PHY_STATE_TX); + return 0; +} + +static int _request_cca(at86rf2xx_t *dev) +{ + DEBUG("at86rf2xx_rf_ops: request_cca\n"); + uint8_t reg; + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, AT86RF2XX_TRX_STATE__FORCE_PLL_ON); + at86rf2xx_set_state(dev, AT86RF2XX_STATE_RX_ON); + + reg = at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_MASK); + reg |= AT86RF2XX_IRQ_STATUS_MASK__CCA_ED_DONE; + at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, reg); + /* Perform CCA */ + reg = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_CC_CCA); + reg |= AT86RF2XX_PHY_CC_CCA_MASK__CCA_REQUEST; + at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_CC_CCA, reg); + return 0; +} + +static int _request_op(ieee802154_dev_t *hal, ieee802154_hal_op_t op, void *ctx) +{ + at86rf2xx_t *dev = hal->priv; + + int res = -ENOTSUP; + mutex_lock(&dev->lock); + switch (op) { + case IEEE802154_HAL_OP_TRANSMIT: +#if AT86RF2XX_HAVE_RETRIES && AT86RF2XX_IS_PERIPH + dev->tx_retries = -1; +#endif + /* trigger sending of pre-loaded frame */ + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, + AT86RF2XX_TRX_STATE__TX_START); + res = 0; + break; + case IEEE802154_HAL_OP_SET_RX: + res = _request_set_rx(dev); + break; + case IEEE802154_HAL_OP_SET_IDLE: + res = _request_set_tx(dev, *((bool*) ctx)); + break; + case IEEE802154_HAL_OP_CCA: + res = _request_cca(dev); + break; + default: + assert(false); + break; + } + mutex_unlock(&dev->lock); + return res; +} + +static int _confirm_transmit(at86rf2xx_t *dev, ieee802154_tx_info_t *info) +{ + DEBUG("at86rf2xx_rf_ops: confirm_transmit\n"); + uint8_t status = at86rf2xx_get_status(dev); + if (status == AT86RF2XX_STATE_BUSY_TX_ARET || status == AT86RF2XX_STATE_BUSY_TX) { + return -EAGAIN; + } + + if (info) { + uint8_t trac_status = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATE) + & AT86RF2XX_TRX_STATE_MASK__TRAC; +#if AT86RF2XX_HAVE_RETRIES && AT86RF2XX_HAVE_RETRIES_REG + info->retrans = (at86rf2xx_reg_read(dev, AT86RF2XX_REG__XAH_CTRL_2) + & AT86RF2XX_XAH_CTRL_2__ARET_FRAME_RETRIES_MASK) + >> AT86RF2XX_XAH_CTRL_2__ARET_FRAME_RETRIES_OFFSET; +#endif + switch (trac_status) { + case AT86RF2XX_TRX_STATE__TRAC_SUCCESS: + info->status = TX_STATUS_SUCCESS; + break; + case AT86RF2XX_TRX_STATE__TRAC_SUCCESS_DATA_PENDING: + info->status = TX_STATUS_FRAME_PENDING; + break; + case AT86RF2XX_TRX_STATE__TRAC_CHANNEL_ACCESS_FAILURE: + info->status = TX_STATUS_MEDIUM_BUSY; + break; + case AT86RF2XX_TRX_STATE__TRAC_NO_ACK: + info->status = TX_STATUS_NO_ACK; + break; + default: + DEBUG("[at86rf2xx] Unhandled TRAC_STATUS: %d\n", trac_status >> 5); + } + } + return 0; +} + +static int _confirm_set_trx_state(at86rf2xx_t *dev) +{ + DEBUG("at86rf2xx_rf_ops: confirm_set_trx_state\n"); + + if (at86rf2xx_get_status(dev) == AT86RF2XX_STATE_IN_PROGRESS) { + return -EAGAIN; + } + + return 0; +} + +static int _confirm_cca(at86rf2xx_t *dev) +{ + DEBUG("at86rf2xx_rf_ops: confirm_cca\n"); + uint8_t reg; + reg = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS); + if ((reg & AT86RF2XX_TRX_STATUS_MASK__CCA_DONE) == 0) { + return -EAGAIN; + } + + /* Restore to default IRQs */ + at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, + AT86RF2XX_IRQ_STATUS_MASK__TRX_END | AT86RF2XX_IRQ_STATUS_MASK__RX_START); + at86rf2xx_set_state(dev, AT86RF2XX_PHY_STATE_RX); + return !!(reg & AT86RF2XX_TRX_STATUS_MASK__CCA_STATUS); +} + +static int _confirm_op(ieee802154_dev_t *hal, ieee802154_hal_op_t op, void *ctx) +{ + at86rf2xx_t *dev = hal->priv; + int res = -ENOTSUP; + mutex_lock(&dev->lock); + switch (op) { + case IEEE802154_HAL_OP_TRANSMIT: + res = _confirm_transmit(dev, ctx); + break; + case IEEE802154_HAL_OP_SET_RX: + case IEEE802154_HAL_OP_SET_IDLE: + res = _confirm_set_trx_state(dev); + break; + case IEEE802154_HAL_OP_CCA: + res = _confirm_cca(dev); + break; + default: + assert(false); + break; + } + mutex_unlock(&dev->lock); + return res; +} + +static int _set_cca_threshold(ieee802154_dev_t *hal, int8_t threshold) +{ + DEBUG("at86rf2xx_rf_ops: set_cca_threshold to %i\n", threshold); + at86rf2xx_t *dev = hal->priv; + mutex_lock(&dev->lock); + at86rf2xx_set_cca_threshold(dev, threshold); + mutex_unlock(&dev->lock); + return 0; +} + +static int _set_cca_mode(ieee802154_dev_t *hal, ieee802154_cca_mode_t mode) +{ + (void) hal; + if (mode != IEEE802154_CCA_MODE_ED_THRESHOLD) { + DEBUG("at86rf2xx_rf_ops: CCA mode not supported\n"); + return -ENOTSUP; + } + DEBUG("at86rf2xx_rf_ops: set_cca_mode to ED Threshold\n"); + return 0; +} + +static int _config_phy(ieee802154_dev_t *hal, const ieee802154_phy_conf_t *conf) +{ + at86rf2xx_t *dev = hal->priv; + if (conf->pow < 0 || conf->pow > AT86RF2XX_TXPOWER_MAX) { + return -EINVAL; + } + mutex_lock(&dev->lock); + at86rf2xx_configure_phy(dev, conf->channel, conf->page, conf->pow); + mutex_unlock(&dev->lock); + return 0; +} + +static int _set_csma_params(ieee802154_dev_t *hal, const ieee802154_csma_be_t *bd, int8_t retries) +{ + DEBUG("at86rf2xx_rf_ops: set_csma_params."); + at86rf2xx_t *dev = hal->priv; + retries = MIN(retries, 5); /* valid values: 0-5 */ + retries = (retries < 0) ? 7 : retries; /* max < 0 => disable CSMA (set to 7) */ + DEBUG("Retries: %i ", retries); + mutex_lock(&dev->lock); + uint8_t tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__XAH_CTRL_0); + tmp &= ~(AT86RF2XX_XAH_CTRL_0__MAX_CSMA_RETRIES); + tmp |= (retries << 1); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__XAH_CTRL_0, tmp); + + if (bd) { + uint8_t max = bd->max; + uint8_t min = bd->min; + DEBUG("max: %i ", max); + DEBUG("min: %i ", min); + max = MIN(8, max); + min = MIN(min, max); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__CSMA_BE, (max << 4) | (min)); + } + DEBUG("\n"); + + mutex_unlock(&dev->lock); + return 0; +} + +static int _config_addr_filter(ieee802154_dev_t *hal, ieee802154_af_cmd_t cmd, const void *value) +{ + at86rf2xx_t *dev = hal->priv; + const uint16_t *pan_id = value; + const network_uint16_t *short_addr = value; + const eui64_t *ext_addr = value; + DEBUG("at86rf2xx_rf_ops: set_hw_addr_filter.\n"); + mutex_lock(&dev->lock); + switch (cmd) { + case IEEE802154_AF_SHORT_ADDR: + at86rf2xx_reg_write(dev, AT86RF2XX_REG__SHORT_ADDR_0, + short_addr->u8[1]); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__SHORT_ADDR_1, + short_addr->u8[0]); + DEBUG("SHORT_ADDR: %04x\n", byteorder_ntohs(*short_addr)); + break; + case IEEE802154_AF_EXT_ADDR: + DEBUG(" EXT_ADDR: "); + for (int i = 0; i < 8; i++) { + at86rf2xx_reg_write(dev, (AT86RF2XX_REG__IEEE_ADDR_0 + i), + ext_addr->uint8[IEEE802154_LONG_ADDRESS_LEN - 1 - i]); + DEBUG("%02x", ext_addr->uint8[i]); + } + DEBUG("\n"); + break; + case IEEE802154_AF_PANID: { + le_uint16_t le_pan = byteorder_btols(byteorder_htons(*pan_id)); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__PAN_ID_0, le_pan.u8[0]); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__PAN_ID_1, le_pan.u8[1]); + DEBUG("PANID: %04x\n", *pan_id); + } + break; + case IEEE802154_AF_PAN_COORD: + mutex_unlock(&dev->lock); + return -ENOTSUP; + } + + mutex_unlock(&dev->lock); + return 0; +} + +static int _config_src_addr_match(ieee802154_dev_t *hal, ieee802154_src_match_t cmd, const void *value) +{ + at86rf2xx_t *dev = hal->priv; + int res; + mutex_lock(&dev->lock); + switch (cmd) { + case IEEE802154_SRC_MATCH_EN: { + const bool en = *((const bool*) value); + at86rf2xx_set_option(dev, AT86RF2XX_OPT_ACK_PENDING, en); + res = 0; + break; + } + default: + res = -ENOTSUP; + } + mutex_unlock(&dev->lock); + return res; +} + +static int _set_frame_filter_mode(ieee802154_dev_t *hal, ieee802154_filter_mode_t mode) +{ + at86rf2xx_t *dev = hal->priv; + bool promisc = false; + switch(mode) { + case IEEE802154_FILTER_ACCEPT: + promisc = false; + break; + case IEEE802154_FILTER_PROMISC: + promisc = true; + break; + case IEEE802154_FILTER_ACK_ONLY: + case IEEE802154_FILTER_SNIFFER: + return -ENOTSUP; + } + + DEBUG("[at86rf2xx] set promiscuous to %i\n", promisc); + mutex_lock(&dev->lock); + at86rf2xx_set_option(dev, AT86RF2XX_OPT_PROMISCUOUS, promisc); + mutex_unlock(&dev->lock); + return 0; +} + +int at86rf2xx_init(at86rf2xx_t *dev, const at86rf2xx_params_t *params, ieee802154_dev_t *hal, void (*cb)(void*), void *ctx) +{ + uint8_t tmp; + (void) tmp; + + (void) at86rf2xx_periph; + mutex_init(&dev->lock); + mutex_lock(&dev->lock); +#if AT86RF2XX_IS_PERIPH + (void) params; + /* set all interrupts off */ + at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, 0x00); + at86rf2xx_periph = hal; +#else + /* initialize device descriptor */ + dev->params = *params; +#endif + + if (!AT86RF2XX_IS_PERIPH) { + at86rf2xx_spi_init(dev, cb, ctx); + } + + at86rf2xx_hardware_reset(dev); + + /* test if the device is responding */ + if (at86rf2xx_reg_read(dev, AT86RF2XX_REG__PART_NUM) != AT86RF2XX_PARTNUM) { + DEBUG("[at86rf2xx] error: unable to read correct part number\n"); + mutex_unlock(&dev->lock); + return -ENOTSUP; + } + + hal->driver = &at86rf2xx_ops; + hal->priv = dev; + + if (!IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { + at86rf2xx_set_option(dev, AT86RF2XX_OPT_AUTOACK, true); + at86rf2xx_set_option(dev, AT86RF2XX_OPT_CSMA, true); + } + + at86rf2xx_set_state(dev, AT86RF2XX_STATE_FORCE_TRX_OFF); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_2, + AT86RF2XX_TRX_CTRL_2_MASK__RX_SAFE_MODE); + +#if !AT86RF2XX_IS_PERIPH + /* don't populate masked interrupt flags to IRQ_STATUS register */ + tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_CTRL_1); + tmp &= ~(AT86RF2XX_TRX_CTRL_1_MASK__IRQ_MASK_MODE); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_1, tmp); +#endif + + /* configure smart idle listening feature */ + at86rf2xx_enable_smart_idle(dev); + + /* disable clock output to save power */ + at86rf2xx_disable_clock_output(dev); + + /* enable interrupts */ + at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, + AT86RF2XX_IRQ_STATUS_MASK__TRX_END + | AT86RF2XX_IRQ_STATUS_MASK__RX_START); + + /* enable TX start interrupt for retry counter */ +#if AT86RF2XX_HAVE_TX_START_IRQ + at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK1, + AT86RF2XX_IRQ_STATUS_MASK1__TX_START); +#endif + + /* clear interrupt flags */ + at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); + + /* Go to SLEEP mode from TRX_OFF */ +#if AT86RF2XX_IS_PERIPH + /* Setting SLPTR bit brings radio transceiver to sleep in in TRX_OFF*/ + *AT86RF2XX_REG__TRXPR |= (AT86RF2XX_TRXPR_SLPTR); +#else + gpio_set(dev->params.sleep_pin); +#endif + + mutex_unlock(&dev->lock); + DEBUG("[at86rf2xx] reset complete.\n"); + + return 0; +} + +static void _dispatch_event(ieee802154_dev_t *hal, ieee802154_trx_ev_t ev) +{ + at86rf2xx_t *dev = hal->priv; + mutex_unlock(&dev->lock); + hal->cb(hal, ev); + mutex_lock(&dev->lock); +} + +static inline void _isr_recv_complete(ieee802154_dev_t *hal) +{ + at86rf2xx_t *dev = hal->priv; + + if (IS_ACTIVE(AT86RF2XX_BASIC_MODE)) { + uint8_t phy_status = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_RSSI); + bool crc_ok = phy_status & AT86RF2XX_PHY_RSSI_MASK__RX_CRC_VALID; + + if (crc_ok) { + _dispatch_event(hal, IEEE802154_RADIO_INDICATION_RX_DONE); + } + else { + _dispatch_event(hal, IEEE802154_RADIO_INDICATION_CRC_ERROR); + } + } + else { + _dispatch_event(hal, IEEE802154_RADIO_INDICATION_RX_DONE); + } +} + +void at86rf2xx_irq_handler(ieee802154_dev_t *hal) +{ + at86rf2xx_t *dev = hal->priv; + uint8_t irq_mask; + uint8_t state; + + /* If transceiver is sleeping register access is impossible and frames are + * lost anyway, so return immediately. + */ + mutex_lock(&dev->lock); + state = at86rf2xx_get_status(dev); + if (state == AT86RF2XX_STATE_SLEEP) { + mutex_unlock(&dev->lock); + return; + } + + /* read (consume) device status */ + irq_mask = at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); + + if (irq_mask & AT86RF2XX_IRQ_STATUS_MASK__RX_START) { + _dispatch_event(hal, IEEE802154_RADIO_INDICATION_RX_START); + DEBUG("[at86rf2xx] EVT - RX_START\n"); + } + + if (irq_mask & AT86RF2XX_IRQ_STATUS_MASK__TRX_END) { + if ((state == AT86RF2XX_PHY_STATE_RX) + || (state == AT86RF2XX_PHY_STATE_RX_BUSY)) { + DEBUG("[at86rf2xx] EVT - RX_END\n"); + + _isr_recv_complete(hal); + + } + else if (state == AT86RF2XX_PHY_STATE_TX) { + _dispatch_event(hal, IEEE802154_RADIO_CONFIRM_TX_DONE); + } + } + + if (irq_mask & AT86RF2XX_IRQ_STATUS_MASK__CCA_ED_DONE) { + _dispatch_event(hal, IEEE802154_RADIO_CONFIRM_CCA); + } + + mutex_unlock(&dev->lock); +} + +static void _event_handler(event_t *event) +{ + at86rf2xx_bhp_ev_t *bhp = container_of(event, at86rf2xx_bhp_ev_t, ev); + ieee802154_dev_t *hal = bhp->hal; + at86rf2xx_irq_handler(hal); +} + +void _irq_handler(void *ctx) +{ + at86rf2xx_bhp_ev_t *bhp = ctx; + event_post(bhp->evq, &bhp->ev); +} + +int at86rf2xx_init_event(at86rf2xx_bhp_ev_t *bhp, const at86rf2xx_params_t *params, + ieee802154_dev_t *hal, event_queue_t *evq) +{ + bhp->hal = hal; + bhp->evq = evq; + bhp->ev.handler = _event_handler; + + return at86rf2xx_init(&bhp->dev, params, hal, _irq_handler, bhp); +} + +static const ieee802154_radio_ops_t at86rf2xx_ops = { + .caps = IEEE802154_CAP_24_GHZ + | IEEE802154_CAP_IRQ_CRC_ERROR + | IEEE802154_CAP_IRQ_RX_START + | IEEE802154_CAP_IRQ_TX_DONE +#if !IS_ACTIVE(AT86RF2XX_BASIC_MODE) + | IEEE802154_CAP_FRAME_RETRANS +#if AT86RF2XX_HAVE_RETRIES + | IEEE802154_CAP_FRAME_RETRANS_INFO +#endif +#endif + | IEEE802154_CAP_PHY_OQPSK + | IEEE802154_CAP_REG_RETENTION, + .write = _write, + .read = _read, + .request_on = _request_on, + .confirm_on = _confirm_on, + .len = _len, + .off = _off, + .request_op = _request_op, + .confirm_op = _confirm_op, + .set_cca_threshold = _set_cca_threshold, + .set_cca_mode = _set_cca_mode, + .config_phy = _config_phy, + .set_csma_params = _set_csma_params, + .config_addr_filter = _config_addr_filter, + .config_src_addr_match = _config_src_addr_match, + .set_frame_filter_mode = _set_frame_filter_mode, +#if IS_USED(MODULE_AT86RF2XX_AES_SPI) && IS_USED(MODULE_IEEE802154_SECURITY) + .cipher_ops = &_at86rf2xx_cipher_ops, +#endif +}; + +#if AT86RF2XX_IS_PERIPH + +/** + * @brief ISR for transceiver's TX_START interrupt + * + * In procedure TX_ARET the TRX24_TX_START interrupt is issued separately for every + * frame transmission and frame retransmission. + * Indicates the frame start of a transmitted acknowledge frame in procedure RX_AACK. + * + * Flow Diagram Manual p. 52 / 63 + */ +#if AT86RF2XX_HAVE_RETRIES +ISR(TRX24_TX_START_vect){ + /* __enter_isr(); is not necessary as there is nothing which causes a + * thread_yield and the interrupt can not be interrupted by an other ISR */ + at86rf2xx_t *dev = at86rf2xx_periph->priv; + + dev->tx_retries++; +} +#endif + +/** + * @brief Transceiver PLL Lock + * + * Is triggered when PLL locked successfully. + * + * Manual p. 40 + */ +ISR(TRX24_PLL_LOCK_vect, ISR_BLOCK) +{ + /* __enter_isr(); is not necessary as there is nothing which causes a + * thread_yield and the interrupt can not be interrupted by an other ISR */ + DEBUG("TRX24_PLL_LOCK\n"); +} + +/** + * @brief Transceiver PLL Unlock + * + * Is triggered when PLL unlocked unexpectedly. + * + * Manual p. 89 + */ +ISR(TRX24_PLL_UNLOCK_vect, ISR_BLOCK) +{ + /* __enter_isr(); is not necessary as there is nothing which causes a + * thread_yield and the interrupt can not be interrupted by an other ISR */ + DEBUG("TRX24_PLL_UNLOCK\n"); +} + +/** + * @brief ISR for transceiver's receive start interrupt + * + * Is triggered when valid PHR is detected. + * Save IRQ status and inform upper layer of data reception. + * + * Flow Diagram Manual p. 52 / 63 + */ +ISR(TRX24_RX_START_vect, ISR_BLOCK) +{ + avr8_enter_isr(); + + uint8_t status = *AT86RF2XX_REG__TRX_STATE & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; + DEBUG("TRX24_RX_START 0x%x\n", status); + + at86rf2xx_periph->cb(at86rf2xx_periph, IEEE802154_RADIO_INDICATION_RX_START); + + avr8_exit_isr(); +} + +/** + * @brief ISR for transceiver's receive end interrupt + * + * Is triggered when valid data is received. FCS check passed. + * Save IRQ status and inform upper layer of data reception. + * + * Flow Diagram Manual p. 52 / 63 + */ +ISR(TRX24_RX_END_vect, ISR_BLOCK) +{ + avr8_enter_isr(); + + uint8_t status = *AT86RF2XX_REG__TRX_STATE & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; + DEBUG("TRX24_RX_END 0x%x\n", status); + + at86rf2xx_periph->cb(at86rf2xx_periph, IEEE802154_RADIO_INDICATION_RX_DONE); + + avr8_exit_isr(); +} + +/** + * @brief Transceiver CCAED Measurement finished + * + * Is triggered when CCA or ED measurement completed. + * + * Manual p. 74 and p. 76 + */ +ISR(TRX24_CCA_ED_DONE_vect, ISR_BLOCK) +{ + avr8_enter_isr(); + + DEBUG("TRX24_CCA_ED_DONE\n"); + + at86rf2xx_periph->cb(at86rf2xx_periph, IEEE802154_RADIO_CONFIRM_CCA); + + avr8_exit_isr(); +} + +/** + * @brief Transceiver Frame Address Match, indicates incoming frame + * + * Is triggered when Frame with valid Address is received. + * Can be used to wake up MCU from sleep, etc. + * + * Flow Diagram Manual p. 52 / 63 + */ +ISR(TRX24_XAH_AMI_vect, ISR_BLOCK) +{ + /* __enter_isr(); is not necessary as there is nothing which causes a + * thread_yield and the interrupt can not be interrupted by an other ISR */ + DEBUG("TRX24_XAH_AMI\n"); +} + +/** + * @brief ISR for transceiver's transmit end interrupt + * + * Is triggered when data or when acknowledge frames where send. + * + * Flow Diagram Manual p. 52 / 63 + */ +ISR(TRX24_TX_END_vect, ISR_BLOCK) +{ + avr8_enter_isr(); + + uint8_t status = *AT86RF2XX_REG__TRX_STATE & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; + DEBUG("TRX24_TX_END 0x%x\n", status); + + /* only inform upper layer when a transmission was done, + * not for sending acknowledge frames if data was received. */ + if (status != AT86RF2XX_PHY_STATE_RX) { + at86rf2xx_periph->cb(at86rf2xx_periph, IEEE802154_RADIO_CONFIRM_TX_DONE); + } + + avr8_exit_isr(); +} + +/** + * @brief ISR for transceiver's wakeup finished interrupt + * + * Is triggered when transceiver module reset is finished. + * Save IRQ status and inform upper layer the transceiver is operational. + * + * Manual p. 40 + */ +ISR(TRX24_AWAKE_vect, ISR_BLOCK) +{ + /* __enter_isr(); is not necessary as there is nothing which causes a + * thread_yield and the interrupt can not be interrupted by an other ISR */ + DEBUG("TRX24_AWAKE\n"); +} + +#endif /* AT86RF2XX_IS_PERIPH */ diff --git a/drivers/at86rf2xx/include/at86rf2xx_internal.h b/drivers/at86rf2xx/include/at86rf2xx_internal.h index ce9da53fb8a7..be11e1b5ed3b 100644 --- a/drivers/at86rf2xx/include/at86rf2xx_internal.h +++ b/drivers/at86rf2xx/include/at86rf2xx_internal.h @@ -250,12 +250,15 @@ void at86rf2xx_get_random(at86rf2xx_t *dev, uint8_t *data, size_t len); * * @param[in,out] dev device to initialize * @param[in] irq_handler IRQ handler + * @param[in,out] ctx Context of the IRQ handler */ -void at86rf2xx_spi_init(at86rf2xx_t *dev, void (*irq_handler)(void *arg)); +void at86rf2xx_spi_init(at86rf2xx_t *dev, void (*irq_handler)(void *arg), void *ctx); /** * @brief Get the PSDU length of the received frame. * + * This function increases the FIFO counter. + * * @param[in] dev pointer to the device descriptor * * @return the PSDU length @@ -273,24 +276,24 @@ static inline uint8_t at86rf2xx_get_rx_len(at86rf2xx_t *dev) } /** - * @brief Get the IRQ flags. + * @brief Peek the PSDU length of the received frame. * - * This function clears the IRQ flags - * @param[in,out] dev pointer to the device descriptor + * @param[in] dev pointer to the device descriptor * - * @return IRQ flags + * @return the PSDU length */ -static inline uint8_t at86rf2xx_get_irq_flags(at86rf2xx_t *dev) +static inline uint8_t at86rf2xx_peek_rx_len(at86rf2xx_t *dev) { (void) dev; #if AT86RF2XX_IS_PERIPH - uint8_t irq_mask = dev->irq_status; - dev->irq_status = 0; - return irq_mask; + return TST_RX_LENGTH; #else - return at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); + uint8_t phr; + at86rf2xx_fb_start(dev); + phr = at86rf2xx_get_rx_len(dev); + at86rf2xx_fb_stop(dev); + return phr; #endif - } #ifdef __cplusplus diff --git a/drivers/include/at86rf2xx.h b/drivers/include/at86rf2xx.h index 9799e0dbe8a3..7e0496a5a19a 100644 --- a/drivers/include/at86rf2xx.h +++ b/drivers/include/at86rf2xx.h @@ -37,6 +37,8 @@ #include "net/netdev.h" #include "net/netdev/ieee802154.h" #include "net/gnrc/nettype.h" +#include "net/ieee802154/radio.h" +#include "event.h" /* we need no peripherals for memory mapped radios */ #if !defined(MODULE_AT86RFA1) && !defined(MODULE_AT86RFR2) @@ -281,53 +283,27 @@ typedef struct at86rf2xx_params { * @extends netdev_ieee802154_t */ typedef struct { - netdev_ieee802154_t netdev; /**< netdev parent struct */ -#if AT86RF2XX_IS_PERIPH - /* ATmega256rfr2 signals transceiver events with different interrupts - * they have to be stored to mimic the same flow as external transceiver - * Use irq_status to map saved interrupts of SOC transceiver, - * as they clear after IRQ callback. - * - * irq_status = IRQ_STATUS - */ - uint8_t irq_status; /**< save irq status */ -#else +#if !AT86RF2XX_IS_PERIPH /* device specific fields */ at86rf2xx_params_t params; /**< parameters for initialization */ #endif - uint16_t flags; /**< Device specific flags */ - uint8_t state; /**< current state of the radio */ - uint8_t tx_frame_len; /**< length of the current TX frame */ -#ifdef MODULE_AT86RF212B - /* Only AT86RF212B supports multiple pages (PHY modes) */ - uint8_t page; /**< currently used channel page */ -#endif - uint8_t idle_state; /**< state to return to after sending */ - uint8_t pending_tx; /**< keep track of pending TX calls - this is required to know when to - return to @ref at86rf2xx_t::idle_state */ -#if AT86RF2XX_HAVE_RETRIES +#if AT86RF2XX_HAVE_RETRIES && AT86RF2XX_IS_PERIPH /* Only radios with the XAH_CTRL_2 register support frame retry reporting */ int8_t tx_retries; /**< Number of NOACK retransmissions */ #endif + bool sleep; /**< whether the device is sleeping or not */ + mutex_t lock; /**< device lock */ } at86rf2xx_t; /** - * @brief Setup an AT86RF2xx based device state - * - * @param[out] dev device descriptor - * @param[in] params parameters for device initialization - * @param[in] index index of @p params in a global parameter struct array. - * If initialized manually, pass a unique identifier instead. - */ -void at86rf2xx_setup(at86rf2xx_t *dev, const at86rf2xx_params_t *params, uint8_t index); - -/** - * @brief Trigger a hardware reset and configure radio with default values - * - * @param[in,out] dev device to reset + * @brief Event Bottom Half Processor descriptor for AT86RF2XX transceivers. */ -void at86rf2xx_reset(at86rf2xx_t *dev); +typedef struct { + at86rf2xx_t dev; /**< Device descriptor */ + ieee802154_dev_t *hal; /**< Pointer to the Radio HAL descriptor */ + event_queue_t *evq; /**< Pointer to the event queue */ + event_t ev; /**< ISR offload event */ +} at86rf2xx_bhp_ev_t; /** * @brief Set the short address of the given device @@ -386,20 +362,6 @@ int at86rf2xx_set_rate(at86rf2xx_t *dev, uint8_t rate); */ void at86rf2xx_set_pan(at86rf2xx_t *dev, uint16_t pan); -/** - * @brief Set the transmission power of the given device [in dBm] - * - * If the device does not support the exact dBm value given, it will set a value - * as close as possible to the given value. If the given value is larger or - * lower then the maximal or minimal possible value, the min or max value is - * set, respectively. - * - * @param[in] dev device to write to - * @param[in] txpower transmission power in dBm - * @param[in] channel the current channel - */ -void at86rf2xx_set_txpower(const at86rf2xx_t *dev, int16_t txpower, uint8_t channel); - /** * @brief Get the configured receiver sensitivity of the given device [in dBm] * @@ -531,36 +493,6 @@ void at86rf2xx_set_option(at86rf2xx_t *dev, uint16_t option, bool state); */ uint8_t at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state); -/** - * @brief Prepare for sending of data - * - * This function puts the given device into the TX state, so no receiving of - * data is possible after it was called. - * - * @param[in,out] dev device to prepare for sending - */ -void at86rf2xx_tx_prepare(at86rf2xx_t *dev); - -/** - * @brief Load chunks of data into the transmit buffer of the given device - * - * @param[in,out] dev device to write data to - * @param[in] data buffer containing the data to load - * @param[in] len number of bytes in @p buffer - * @param[in] offset offset used when writing data to internal buffer - * - * @return offset + number of bytes written - */ -size_t at86rf2xx_tx_load(at86rf2xx_t *dev, const uint8_t *data, - size_t len, size_t offset); - -/** - * @brief Trigger sending of data previously loaded into transmit buffer - * - * @param[in] dev device to trigger - */ -void at86rf2xx_tx_exec(at86rf2xx_t *dev); - /** * @brief Perform one manual channel clear assessment (CCA) * @@ -591,6 +523,55 @@ void at86rf2xx_enable_smart_idle(at86rf2xx_t *dev); */ void at86rf2xx_disable_smart_idle(at86rf2xx_t *dev); +/** + * @brief Disable AT86RF2XX clock output. + * + * @note This function only works for periph versions of AT86RF2XX. + * + * @param[in,out] dev pointer to device descriptor. + */ +void at86rf2xx_disable_clock_output(at86rf2xx_t *dev); + +/** + * @brief Offload pending IRQ. + * + * Should be called in thread context from a thread with higher priority than + * the thread that controls the device. + * + * @param[in] hal device to offload IRQs. + * + */ +void at86rf2xx_irq_handler(ieee802154_dev_t *hal); + +/** + * @brief Init an AT86RF2XX device + * + * @param[in,out] dev pointer to device descriptor + * @param[in] params parameters for device initialization. Can be NULL for periph variant. + * @param[in,out] hal pointer to the Radio HAL descriptor + * @param[in] cb ISR callback. Can be NULL for periph variant. + * @param[in] ctx Context of the ISR callback. Can be NULL for periph variant. + * + * @return error code + * @retval 0 on success + * @retval negative errno on failure + */ +int at86rf2xx_init(at86rf2xx_t *dev, const at86rf2xx_params_t *params, ieee802154_dev_t *hal, void (*cb)(void*), void *ctx); + +/** + * @brief Init an AT86RF2XX device with an event based Bottom Half Processor. + * + * @param[in,out] bhp pointer to the event BHP descriptor + * @param[in] params parameters for device initialization + * @param[in,out] hal pointer to the Radio HAL descriptor + * @param[in] evq pointer to the event queue + * + * @return error code + * @retval 0 on success + * @retval negative errno on failure + */ +int at86rf2xx_init_event(at86rf2xx_bhp_ev_t *bhp, const at86rf2xx_params_t *params, ieee802154_dev_t *hal, event_queue_t *evq); + #ifdef __cplusplus } #endif diff --git a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c index 1e6bf71d7838..5d5b59efa648 100644 --- a/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c +++ b/drivers/netdev_ieee802154_submac/netdev_ieee802154_submac.c @@ -354,6 +354,14 @@ static int _init(netdev_t *netdev) /* This function already sets the PAN ID to the default one */ netdev_ieee802154_reset(netdev_ieee802154); +#if IS_USED(MODULE_IEEE802154_SECURITY) + const ieee802154_radio_cipher_ops_t *cipher_ops; + if ((cipher_ops = ieee802154_radio_get_cipher_ops(&submac->dev))) { + netdev_ieee802154->sec_ctx.dev.cipher_ops = cipher_ops; + netdev_ieee802154->sec_ctx.dev.ctx = &submac->dev; + } +#endif + uint16_t chan = submac->channel_num; int16_t tx_power = submac->tx_pow; static const netopt_enable_t ack_req = diff --git a/pkg/lwip/init_devs/auto_init_at86rf2xx.c b/pkg/lwip/init_devs/auto_init_at86rf2xx.c index b017120a665a..40ea404f6452 100644 --- a/pkg/lwip/init_devs/auto_init_at86rf2xx.c +++ b/pkg/lwip/init_devs/auto_init_at86rf2xx.c @@ -21,6 +21,8 @@ #include "at86rf2xx_params.h" #include "lwip_init_devs.h" +#include "net/netdev/ieee802154_submac.h" +#include "event/thread.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -28,13 +30,16 @@ #define NETIF_AT86RF2XX_NUMOF ARRAY_SIZE(at86rf2xx_params) static lwip_netif_t netif[NETIF_AT86RF2XX_NUMOF]; -static at86rf2xx_t at86rf2xx_devs[NETIF_AT86RF2XX_NUMOF]; +static at86rf2xx_bhp_ev_t at86rf2xx_bhp[NETIF_AT86RF2XX_NUMOF]; +static netdev_ieee802154_submac_t at86rf2xx_netdev[NETIF_AT86RF2XX_NUMOF]; static void auto_init_at86rf2xx(void) { for (unsigned i = 0; i < NETIF_AT86RF2XX_NUMOF; i++) { - at86rf2xx_setup(&at86rf2xx_devs[i], &at86rf2xx_params[i], i); - if (lwip_add_6lowpan(&netif[i], &at86rf2xx_devs[i].netdev.netdev) == NULL) { + at86rf2xx_init_event(&at86rf2xx_bhp[i], &at86rf2xx_params[i], &at86rf2xx_netdev[i].submac.dev, EVENT_PRIO_HIGHEST); + netdev_register(&at86rf2xx_netdev[i].dev.netdev, NETDEV_AT86RF2XX, i); + netdev_ieee802154_submac_init(&at86rf2xx_netdev[i]); + if (lwip_add_6lowpan(&netif[i], &at86rf2xx_netdev[i].dev.netdev) == NULL) { DEBUG("Could not add at86rf2xx device\n"); return; } diff --git a/pkg/openthread/contrib/openthread.c b/pkg/openthread/contrib/openthread.c index 706f6394f4dd..e83557ddb4be 100644 --- a/pkg/openthread/contrib/openthread.c +++ b/pkg/openthread/contrib/openthread.c @@ -21,6 +21,7 @@ #include "ot.h" #include "random.h" #include "thread.h" +#include "event/thread.h" #ifdef MODULE_AT86RF2XX #include "at86rf2xx.h" @@ -59,7 +60,8 @@ static netdev_ieee802154_submac_t cc2538_rf_netdev; #endif #ifdef MODULE_AT86RF2XX -static at86rf2xx_t at86rf2xx_dev; +static at86rf2xx_bhp_ev_t at86rf2xx_bhp; +static netdev_ieee802154_submac_t at86rf2xx_netdev; #endif #ifdef MODULE_KW41ZRF @@ -78,8 +80,10 @@ void openthread_bootstrap(void) { /* setup netdev modules */ #ifdef MODULE_AT86RF2XX - at86rf2xx_setup(&at86rf2xx_dev, &at86rf2xx_params[0], 0); - netdev_t *netdev = &at86rf2xx_dev.netdev.netdev; + at86rf2xx_init_event(&at86rf2xx_bhp, &at86rf2xx_params[0], &at86rf2xx_netdev.submac.dev, EVENT_PRIO_HIGHEST); + netdev_register(&at86rf2xx_netdev.dev.netdev, NETDEV_AT86RF2XX, 0); + netdev_ieee802154_submac_init(&at86rf2xx_netdev); + netdev_t *netdev = &at86rf2xx_netdev.dev.netdev; #endif #ifdef MODULE_KW41ZRF kw41zrf_setup(&kw41z_dev, 0); diff --git a/pkg/openwsn/Makefile.dep b/pkg/openwsn/Makefile.dep index e5f7986fdc29..637fd48ce79b 100644 --- a/pkg/openwsn/Makefile.dep +++ b/pkg/openwsn/Makefile.dep @@ -51,14 +51,8 @@ endif ifneq (,$(filter openwsn_radio,$(USEMODULE))) # enable default network devices on the platform USEMODULE += netdev_default - USEMODULE += netdev USEMODULE += luid - ifneq (,$(filter cc2538_rf nrf802154,$(USEMODULE))) - USEMODULE += openwsn_radio_hal - endif - ifneq (,$(filter at86rf2xx,$(USEMODULE))) - USEMODULE += openwsn_radio_netdev - endif + USEMODULE += openwsn_radio_hal endif ifneq (,$(filter openwsn_radio_hal,$(USEMODULE))) diff --git a/pkg/openwsn/contrib/Makefile b/pkg/openwsn/contrib/Makefile index cdbd3c2b8de5..95cfe736d15d 100644 --- a/pkg/openwsn/contrib/Makefile +++ b/pkg/openwsn/contrib/Makefile @@ -14,8 +14,4 @@ ifneq (,$(filter openwsn_radio_hal,$(USEMODULE))) SRC += radio_hal.c endif -ifneq (,$(filter openwsn_radio_netdev,$(USEMODULE))) - SRC += radio_netdev.c -endif - include $(RIOTBASE)/Makefile.base diff --git a/pkg/openwsn/contrib/eui64.c b/pkg/openwsn/contrib/eui64.c index 3762dfb83a70..170c3199ec55 100644 --- a/pkg/openwsn/contrib/eui64.c +++ b/pkg/openwsn/contrib/eui64.c @@ -23,38 +23,20 @@ #include "openwsn_radio.h" #include "luid.h" -#include "net/netdev.h" #include "net/netopt.h" #include "net/ieee802154.h" extern openwsn_radio_t openwsn_radio; -#ifdef MODULE_OPENWSN_RADIO_HAL -/* HACK: temporary hack while eui_provider still depends on netdev */ static eui64_t _eui64; static bool _eui64_is_set = false; -#endif void eui64_get(uint8_t *addressToWrite) { -#ifdef MODULE_OPENWSN_RADIO_HAL if (!_eui64_is_set) { luid_get_eui64(&_eui64); _eui64_is_set = true; } memcpy(addressToWrite, _eui64.uint8, sizeof(_eui64.uint8)); -#elif MODULE_OPENWSN_RADIO_NETDEV - eui64_t eui64; - - if (openwsn_radio.dev->driver->get(openwsn_radio.dev, NETOPT_ADDRESS_LONG, - &eui64, - sizeof(eui64_t)) == sizeof(eui64_t)) { - memcpy(addressToWrite, eui64.uint8, sizeof(eui64.uint8)); - } - else { - luid_get_eui64((eui64_t *) addressToWrite); - } -#else memset(addressToWrite, 0, sizeof(eui64_t)); -#endif } diff --git a/pkg/openwsn/contrib/openwsn.c b/pkg/openwsn/contrib/openwsn.c index d195eaaea316..6cf19bc16cc1 100644 --- a/pkg/openwsn/contrib/openwsn.c +++ b/pkg/openwsn/contrib/openwsn.c @@ -37,6 +37,8 @@ #include "nrf802154.h" #endif +#include "event/thread.h" + #define LOG_LEVEL LOG_NONE #include "log.h" @@ -44,18 +46,17 @@ #define OPENWSN_SCHED_PRIO (THREAD_PRIORITY_MAIN - 4) #define OPENWSN_SCHED_STACKSIZE (2048) -#ifdef MODULE_OPENWSN_RADIO_NETDEV #ifdef MODULE_AT86RF2XX -static at86rf2xx_t at86rf2xx_dev; +static at86rf2xx_bhp_ev_t at86rf2xx_bhp; +static ieee802154_dev_t at86rf2xx_dev; #endif -#else + #ifdef MODULE_CC2538_RF static ieee802154_dev_t cc2538_rf_dev; #endif #ifdef MODULE_NRF802154 static ieee802154_dev_t nrf802154_hal_dev; #endif -#endif static char _stack[OPENWSN_SCHED_STACKSIZE]; @@ -73,12 +74,10 @@ void* _radio_init_dev(void) void* dev = NULL; /* avoid cppcheck style (redundantAssignment)*/ (void) dev; -#ifdef MODULE_OPENWSN_RADIO_NETDEV #ifdef MODULE_AT86RF2XX - dev = &at86rf2xx_dev.netdev.netdev; - at86rf2xx_setup(&at86rf2xx_dev, &at86rf2xx_params[0], 0); + dev = &at86rf2xx_dev; + at86rf2xx_init_event(&at86rf2xx_bhp, &at86rf2xx_params[0], &at86rf2xx_dev, EVENT_PRIO_HIGHEST); #endif -#else #ifdef MODULE_CC2538_RF dev = &cc2538_rf_dev; cc2538_rf_hal_setup(dev); @@ -89,7 +88,6 @@ void* _radio_init_dev(void) nrf802154_hal_setup(dev); nrf802154_init(); #endif -#endif return dev; } diff --git a/pkg/openwsn/contrib/radio_hal.c b/pkg/openwsn/contrib/radio_hal.c index d91f6ccd7ece..70626bb398bb 100644 --- a/pkg/openwsn/contrib/radio_hal.c +++ b/pkg/openwsn/contrib/radio_hal.c @@ -225,7 +225,7 @@ void radio_rfOff(void) void radio_loadPacket(uint8_t *packet, uint16_t len) { /* OpenWSN `len` accounts for the FCS field which is set by default by - netdev, so remove from the actual packet `len` */ + the Radio HAL, so remove from the actual packet `len` */ iolist_t pkt = { .iol_base = (void *)packet, .iol_len = (size_t)(len - IEEE802154_FCS_LEN), diff --git a/pkg/openwsn/contrib/radio_netdev.c b/pkg/openwsn/contrib/radio_netdev.c deleted file mode 100644 index 2aebdd0b44cf..000000000000 --- a/pkg/openwsn/contrib/radio_netdev.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (C) 2018 Hamburg University of Applied Sciences - * 2020 Inria - * - * 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 pkg_openwsn - * @{ - * - * @file - * @brief RIOT adaption of the "radio" bsp module - * - * @author Peter Kietzmann - * @author Oliver Hahm - * @author Francisco Molina - * @} - */ - -#include -#include - -#include "leds.h" -#include "debugpins.h" -#include "sctimer.h" -#include "idmanager.h" - -#include "net/netopt.h" -#include "net/ieee802154.h" -#include "net/netdev/ieee802154.h" - -#include "openwsn.h" -#include "openwsn_radio.h" - -#ifdef MODULE_AT86RF2XX -#include "at86rf2xx.h" -#include "at86rf2xx_params.h" -#endif - -#ifdef MODULE_CC2538_RF -#include "cc2538_rf.h" -#endif - -#define LOG_LEVEL LOG_NONE -#include "log.h" - -openwsn_radio_t openwsn_radio; - -static void _event_cb(netdev_t *dev, netdev_event_t event); - -/* stores the NETDEV_EVENT_ISR capture time to tag the following NETDEV_EVENT */ -static PORT_TIMER_WIDTH _txrx_event_capture_time = 0; - -static void _set_addr(void) -{ - netdev_t* dev = openwsn_radio.dev; - /* Initiate Id manager here and not in `openstack_init` function to allow - overriding the short id address before additional stack components are - initiated */ - idmanager_init(); - - /* override 16b address to avoid short address collision */ - uint8_t addr[IEEE802154_SHORT_ADDRESS_LEN]; - dev->driver->get(dev, NETOPT_ADDRESS, addr, IEEE802154_SHORT_ADDRESS_LEN); - open_addr_t id; - id.type = ADDR_16B; - memcpy(&id.addr_16b, addr, IEEE802154_SHORT_ADDRESS_LEN); - idmanager_setMyID(&id); - /* override PANID */ - id.type = ADDR_PANID; - uint16_t panid = OPENWSN_PANID; - memcpy(&id.addr_16b, &panid, IEEE802154_SHORT_ADDRESS_LEN); - idmanager_setMyID(&id); -} - -int openwsn_radio_init(void *radio_dev) -{ - assert(radio_dev); - netdev_t *netdev = radio_dev; - - LOG_DEBUG("[openwsn/radio]: initialize riot-adaptation\n"); - openwsn_radio.dev = netdev; - - if (netdev->driver->init(netdev)) { - LOG_ERROR("[openwsn/radio]: unable to initialize device\n"); - return -1; - } - netdev->event_callback = _event_cb; - - LOG_DEBUG("[openwsn/radio]: put radio in standby\n"); - netopt_state_t state = NETOPT_STATE_STANDBY; - netdev->driver->set(netdev, NETOPT_STATE, &(state), sizeof(state)); - - LOG_DEBUG("[openwsn/radio]: set needed netdev options\n"); - netopt_enable_t enable; - /* Enable needed IRQs */ - enable = NETOPT_ENABLE; - netdev->driver->set(netdev, NETOPT_TX_START_IRQ, &(enable), sizeof(enable)); - enable = NETOPT_ENABLE; - netdev->driver->set(netdev, NETOPT_RX_START_IRQ, &(enable), sizeof(enable)); - enable = NETOPT_ENABLE; - netdev->driver->set(netdev, NETOPT_TX_END_IRQ, &(enable), sizeof(enable)); - enable = NETOPT_DISABLE; - /* Enable basic mode, no AUTOACK. no CSMA , no frame filtering */ - netdev->driver->set(netdev, NETOPT_AUTOACK, &(enable), sizeof(enable)); - enable = NETOPT_DISABLE; - netdev->driver->set(netdev, NETOPT_CSMA, &(enable), sizeof(enable)); - enable = NETOPT_ENABLE; - netdev->driver->set(netdev, NETOPT_RAWMODE, &(enable), sizeof(enable)); - uint8_t retrans = 0; - /* MAC layer will handle retransmissions */ - netdev->driver->set(netdev, NETOPT_RETRANS, &(retrans), sizeof(uint8_t)); - /* Enable TX with preloading */ - enable = NETOPT_ENABLE; - netdev->driver->set(netdev, NETOPT_PRELOADING, &(enable), sizeof(enable)); - /* Set default PANID */ - uint16_t panid = OPENWSN_PANID; - netdev->driver->set(netdev, NETOPT_NID, &(panid), sizeof(uint16_t)); - - _set_addr(); - - return 0; -} - -void radio_setStartFrameCb(radio_capture_cbt cb) -{ - openwsn_radio.startFrame_cb = cb; -} - -void radio_setEndFrameCb(radio_capture_cbt cb) -{ - openwsn_radio.endFrame_cb = cb; -} - -void radio_reset(void) -{ - netopt_state_t state = NETOPT_STATE_RESET; - - openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), - sizeof(netopt_state_t)); - - state = NETOPT_STATE_STANDBY; - openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), - sizeof(netopt_state_t)); -} - -void radio_setFrequency(uint8_t frequency, radio_freq_t tx_or_rx) -{ - (void)tx_or_rx; - - uint16_t chan = frequency; - openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_CHANNEL, &(chan), - sizeof(chan)); -} - -void radio_rfOn(void) -{ - netopt_state_t state = NETOPT_STATE_IDLE; - - openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), - sizeof(netopt_state_t)); -} - -void radio_rfOff(void) -{ - netopt_state_t state = NETOPT_STATE_STANDBY; - - openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), - sizeof(netopt_state_t)); - - debugpins_radio_clr(); - leds_radio_off(); -} - -void radio_loadPacket(uint8_t *packet, uint16_t len) -{ - /* NETOPT_PRELOADING is enabled in radio_init so this will only load the - packet */ - /* OpenWSN `len` accounts for the FCS field which is set by default by - netdev, so remove from the actual packet `len` */ - iolist_t pkt = { - .iol_base = (void *)packet, - .iol_len = (size_t)(len - IEEE802154_FCS_LEN), - }; - - if (openwsn_radio.dev->driver->send(openwsn_radio.dev, &pkt) < 0) { - LOG_DEBUG("[openwsn/radio]: couldn't load pkt\n"); - } - LOG_DEBUG("[openwsn/radio]: loaded radio packet\n"); -} - -void radio_txEnable(void) -{ - debugpins_radio_set(); - leds_radio_on(); -} - -void radio_txNow(void) -{ - netopt_state_t state = NETOPT_STATE_TX; - - openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &state, - sizeof(netopt_state_t)); -} - -void radio_rxEnable(void) -{ - debugpins_radio_set(); - leds_radio_on(); - netopt_state_t state = NETOPT_STATE_IDLE; - openwsn_radio.dev->driver->set(openwsn_radio.dev, NETOPT_STATE, &(state), - sizeof(state)); -} - -void radio_rxNow(void) -{ - /* nothing to do */ -} - -void radio_getReceivedFrame(uint8_t *bufRead, - uint8_t *lenRead, - uint8_t maxBufLen, - int8_t *rssi, - uint8_t *lqi, - bool *crc) -{ - /* OpenWSN packets are 130 bytes to hold all required data for an SPI - transaction since in some implementations it's used directly in the SPI - shift register: - - 1B spi address, 1B length, 125B data, 2B CRC, 1B LQI - In RIOT we don't do this so maxBufLen is irrelevant, packet size will - always be enough to hold IEEE802154_FRAME_LEN_MAX, but in practice only - 125B of data are copied into bufRead. - */ - (void)maxBufLen; - netdev_ieee802154_rx_info_t rx_info; - - int bytes_expected = openwsn_radio.dev->driver->recv(openwsn_radio.dev, - NULL, 0, - NULL); - - if (bytes_expected < (int)(IEEE802154_ACK_FRAME_LEN - IEEE802154_FCS_LEN)) { - /* drop invalid packet */ - openwsn_radio.dev->driver->recv(openwsn_radio.dev, NULL, bytes_expected, - NULL); - radio_rxEnable(); - return; - } - - int nread = openwsn_radio.dev->driver->recv(openwsn_radio.dev, bufRead, - bytes_expected, &rx_info); - - /* FCS is skipped by netdev in the returned length, but OpenWSN includes - IEEE802154_FCS_LEN in its length value */ - *lenRead = nread + IEEE802154_FCS_LEN; - - /* get rssi, lqi & crc */ - *rssi = rx_info.rssi; - *lqi = rx_info.lqi; - /* only valid crc frames are currently accepted */ - *crc = 1; - - radio_rxEnable(); -} - -static void _event_cb(netdev_t *dev, netdev_event_t event) -{ - (void)dev; - - if (event == NETDEV_EVENT_ISR) { - /* capture the time */ - debugpins_isr_set(); - _txrx_event_capture_time = sctimer_readCounter(); - openwsn_radio.dev->driver->isr(openwsn_radio.dev); - debugpins_isr_clr(); - } - else { - LOG_DEBUG("[openwsn/radio]: event triggered -> %i\n", event); - switch (event) { - case NETDEV_EVENT_RX_STARTED: - openwsn_radio.startFrame_cb(_txrx_event_capture_time); - LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_RX_STARTED\n"); - break; - case NETDEV_EVENT_TX_STARTED: - openwsn_radio.startFrame_cb(_txrx_event_capture_time); - LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_TX_STARTED\n"); - break; - case NETDEV_EVENT_RX_COMPLETE: - openwsn_radio.endFrame_cb(_txrx_event_capture_time); - LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_RX_COMPLETE\n"); - break; - case NETDEV_EVENT_TX_COMPLETE: - openwsn_radio.endFrame_cb(_txrx_event_capture_time); - LOG_DEBUG("[openwsn/radio]: NETDEV_EVENT_TX_COMPLETE\n"); - break; - default: - break; - } - } -} diff --git a/pkg/openwsn/doc.txt b/pkg/openwsn/doc.txt index 1e55689de1cc..cf6f26c18021 100644 --- a/pkg/openwsn/doc.txt +++ b/pkg/openwsn/doc.txt @@ -103,9 +103,7 @@ ### radio - The radio adaptation maps to RIOT's @ref netdev API or on - @ref drivers_ieee802154_hal API. The later is preferred but not yet - supported by all boards. + The radio adaptation maps to RIOT's on @ref drivers_ieee802154_hal API. Hardware MAC layer features such as CSMA/CA, ACK handling and retransmissions are handled by OpenWSN, so the radio driver must support disabling AUTOACK @@ -131,18 +129,6 @@ - `IEEE802154_CAP_IRQ_CRC_ERROR` - #### netdev - - The radio adaptation preloads the buffer so `NETOPT_PRELOADING` must be - supported. - - The radio drivers should support the following netdev events: - - - `NETDEV_EVENT_RX_STARTED` - - `NETDEV_EVENT_TX_STARTED` - - `NETDEV_EVENT_RX_COMPLETE` - - `NETDEV_EVENT_TX_COMPLETE` - ### uart In RIOT, the first configured uart device is mapped to STDIO in most cases. diff --git a/pkg/openwsn/include/openwsn_radio.h b/pkg/openwsn/include/openwsn_radio.h index 3f8019367594..1ec32107b797 100644 --- a/pkg/openwsn/include/openwsn_radio.h +++ b/pkg/openwsn/include/openwsn_radio.h @@ -15,24 +15,12 @@ * @brief RIOT adaption of the "radio" bsp module definitions * * The radio adaptation runs in an own thread with the highest priority - * (`THREAD_PRIORITY_MAIN - 4`) and maps to RIOT's `netdev` API. + * (`THREAD_PRIORITY_MAIN - 4`) and maps to RIOT's `802.15.4 Radio HAL` API. * * Hardware MAC layer features such as CSMA/CA, ACK handling and retransmissions * are handled by OpenWSN, so the radio driver must support disabling AUTOACK * and CSMA handling by the hardware. Frame filtering must as well be disabled. * - * The radio adaptation preloads the buffer so `NETOPT_PRELOADING` must be - * supported. - * - * OpenWSN needs to be notified when a frame reception/transmission stats and - * when it ends. Therefore radio drivers need to support the following netdev - * events: - * - * - `NETDEV_EVENT_RX_STARTED` - * - `NETDEV_EVENT_TX_STARTED` - * - `NETDEV_EVENT_RX_COMPLETE` - * - `NETDEV_EVENT_TX_COMPLETE` - * * OpenWSN expects to recover crc information on every received frame even if it * will simply drop frames with invalid crc. The stack can function correctly if * radio drivers automatically drop frames with an invalid crc (i.e. the stack @@ -52,7 +40,6 @@ extern "C" { #endif -#include "net/netdev.h" #include "net/ieee802154/radio.h" #include "radio.h" @@ -70,11 +57,7 @@ int openwsn_radio_init(void *radio_dev); * @brief OpenWSN radio variables structure */ typedef struct { -#if IS_ACTIVE(MODULE_OPENWSN_RADIO_NETDEV) - netdev_t *dev; /**< netdev device */ -#else ieee802154_dev_t *dev; /**< radio hal */ -#endif radio_capture_cbt startFrame_cb; /**< start of frame capture callback */ radio_capture_cbt endFrame_cb; /**< end of frame capture callback */ } openwsn_radio_t; diff --git a/sys/include/net/ieee802154/radio.h b/sys/include/net/ieee802154/radio.h index c8f71afa909a..b9e3e5f18735 100644 --- a/sys/include/net/ieee802154/radio.h +++ b/sys/include/net/ieee802154/radio.h @@ -40,6 +40,11 @@ extern "C" { */ typedef struct ieee802154_radio_ops ieee802154_radio_ops_t; +/** + * @brief Forward declaration of radio cipher ops structure. + */ +struct ieee802154_radio_cipher_ops; + /** * @brief IEEE802.15.4 Radio capabilities * @@ -775,6 +780,13 @@ struct ieee802154_radio_ops { */ int (*config_src_addr_match)(ieee802154_dev_t *dev, ieee802154_src_match_t cmd, const void *value); + + /** + * @brief Radio cipher ops. + * + * May be NULL if the radio does not support crypto acceleration. + */ + const struct ieee802154_radio_cipher_ops *cipher_ops; }; /** @@ -1272,6 +1284,19 @@ static inline int ieee802154_radio_cca(ieee802154_dev_t *dev) return res; } +/** + * @brief Retrieve radio cipher ops + * + * @param[in] dev IEEE802.15.4 device descriptor + * + * @return Radio cipher ops + * @return NULL if device has no cipher ops + */ +static inline const struct ieee802154_radio_cipher_ops *ieee802154_radio_get_cipher_ops(const ieee802154_dev_t *dev) +{ + return dev->driver->cipher_ops; +} + /** * @brief Check if the device supports ACK timeout * diff --git a/sys/net/gnrc/netif/init_devs/auto_init_at86rf2xx.c b/sys/net/gnrc/netif/init_devs/auto_init_at86rf2xx.c index f29b1a5ca0a5..444327a4fa7e 100644 --- a/sys/net/gnrc/netif/init_devs/auto_init_at86rf2xx.c +++ b/sys/net/gnrc/netif/init_devs/auto_init_at86rf2xx.c @@ -32,6 +32,9 @@ #include "at86rf2xx.h" #include "at86rf2xx_params.h" +#include "net/netdev/ieee802154_submac.h" +#include "event/thread.h" + /** * @brief Define stack parameters for the MAC layer thread * @{ @@ -43,7 +46,8 @@ #define AT86RF2XX_NUM ARRAY_SIZE(at86rf2xx_params) -static at86rf2xx_t at86rf2xx_devs[AT86RF2XX_NUM]; +static at86rf2xx_bhp_ev_t at86rf2xx_bhp[AT86RF2XX_NUM]; +static netdev_ieee802154_submac_t at86rf2xx_netdev[AT86RF2XX_NUM]; static gnrc_netif_t _netif[AT86RF2XX_NUM]; static char _at86rf2xx_stacks[AT86RF2XX_NUM][AT86RF2XX_MAC_STACKSIZE]; @@ -52,22 +56,24 @@ void auto_init_at86rf2xx(void) for (unsigned i = 0; i < AT86RF2XX_NUM; i++) { LOG_DEBUG("[auto_init_netif] initializing at86rf2xx #%u\n", i); - at86rf2xx_setup(&at86rf2xx_devs[i], &at86rf2xx_params[i], i); + at86rf2xx_init_event(&at86rf2xx_bhp[i], &at86rf2xx_params[i], &at86rf2xx_netdev[i].submac.dev, EVENT_PRIO_HIGHEST); + netdev_register(&at86rf2xx_netdev[i].dev.netdev, NETDEV_AT86RF2XX, i); + netdev_ieee802154_submac_init(&at86rf2xx_netdev[i]); #if defined(MODULE_GNRC_GOMACH) gnrc_netif_gomach_create(&_netif[i], _at86rf2xx_stacks[i], AT86RF2XX_MAC_STACKSIZE, AT86RF2XX_MAC_PRIO, "at86rf2xx-gomach", - &at86rf2xx_devs[i].netdev.netdev); + &at86rf2xx_netdev[i].dev.netdev); #elif defined(MODULE_GNRC_LWMAC) gnrc_netif_lwmac_create(&_netif[i], _at86rf2xx_stacks[i], AT86RF2XX_MAC_STACKSIZE, AT86RF2XX_MAC_PRIO, "at86rf2xx-lwmac", - &at86rf2xx_devs[i].netdev.netdev); + &at86rf2xx_netdev[i].dev.netdev); #else gnrc_netif_ieee802154_create(&_netif[i], _at86rf2xx_stacks[i], AT86RF2XX_MAC_STACKSIZE, AT86RF2XX_MAC_PRIO, "at86rf2xx", - &at86rf2xx_devs[i].netdev.netdev); + &at86rf2xx_netdev[i].dev.netdev); #endif } } diff --git a/tests/driver_at86rf2xx/Makefile.ci b/tests/driver_at86rf2xx/Makefile.ci index c9ac296b6cac..70b60934a46d 100644 --- a/tests/driver_at86rf2xx/Makefile.ci +++ b/tests/driver_at86rf2xx/Makefile.ci @@ -7,5 +7,6 @@ BOARD_INSUFFICIENT_MEMORY := \ atmega328p-xplained-mini \ nucleo-l011k4 \ samd10-xmini \ + stk3200 \ stm32f030f4-demo \ # diff --git a/tests/driver_at86rf2xx/main.c b/tests/driver_at86rf2xx/main.c index a24251eab3ed..15ddc69fbc77 100644 --- a/tests/driver_at86rf2xx/main.c +++ b/tests/driver_at86rf2xx/main.c @@ -23,41 +23,27 @@ #include "at86rf2xx.h" #include "at86rf2xx_params.h" #include "at86rf2xx_internal.h" +#include "event/thread.h" #include "init_dev.h" #include "net/ieee802154.h" -#include "net/netdev/ieee802154.h" +#include "net/ieee802154/radio.h" +#include "net/netdev/ieee802154_submac.h" #include "shell.h" #include "test_utils/netdev_ieee802154_minimal.h" #include "test_utils/expect.h" -static at86rf2xx_t at86rf2xx[AT86RF2XX_NUM]; +static at86rf2xx_bhp_ev_t at86rf2xx_bhp[AT86RF2XX_NUM]; +static netdev_ieee802154_submac_t at86rf2xx_netdev[AT86RF2XX_NUM]; #if AT86RF2XX_RANDOM_NUMBER_GENERATOR -void random_net_api(uint8_t idx, uint32_t *value) -{ - netdev_ieee802154_t *dev = &at86rf2xx[idx].netdev; - dev->netdev.driver->get(&dev->netdev, NETOPT_RANDOM, value, sizeof(uint32_t)); - int retval = dev->netdev.driver->get(&dev->netdev, NETOPT_RANDOM, - value, sizeof(uint32_t)); - if (retval < 0) { - printf("get(NETOPT_RANDOM) failed: %s\n", strerror(-retval)); - } - else { - expect(retval == sizeof(*value)); - } -} - int random_by_at86rf2xx(int argc, char **argv) { (void)argc; (void)argv; for (unsigned int i = 0; i < AT86RF2XX_NUM; i++) { uint32_t test = 0; - at86rf2xx_get_random(&at86rf2xx[i], (uint8_t *)&test, sizeof(test)); + at86rf2xx_get_random(&at86rf2xx_bhp[i].dev, (uint8_t *)&test, sizeof(test)); printf("Random number for device %u via native API: %" PRIx32 "\n", i, test); - test = 0; - random_net_api(i, &test); - printf("Random number for device %u via netopt: %" PRIx32 "\n", i, test); } return 0; } @@ -75,13 +61,15 @@ int netdev_ieee802154_minimal_init_devs(netdev_event_cb_t cb) { for (unsigned i = 0; i < AT86RF2XX_NUM; i++) { printf("%d out of %d\n", i + 1, AT86RF2XX_NUM); /* setup the specific driver */ - at86rf2xx_setup(&at86rf2xx[i], &at86rf2xx_params[i], i); + at86rf2xx_init_event(&at86rf2xx_bhp[i], &at86rf2xx_params[i], &at86rf2xx_netdev[i].submac.dev, EVENT_PRIO_HIGHEST); + netdev_register(&at86rf2xx_netdev[i].dev.netdev, NETDEV_AT86RF2XX, i); + netdev_ieee802154_submac_init(&at86rf2xx_netdev[i]); /* set the application-provided callback */ - at86rf2xx[i].netdev.netdev.event_callback = cb; + at86rf2xx_netdev[i].dev.netdev.event_callback = cb; /* initialize the device driver */ - int res = at86rf2xx[i].netdev.netdev.driver->init(&at86rf2xx[i].netdev.netdev); + int res = at86rf2xx_netdev[i].dev.netdev.driver->init(&at86rf2xx_netdev[i].dev.netdev); if (res != 0) { return -1; } diff --git a/tests/driver_at86rf2xx_aes/main.c b/tests/driver_at86rf2xx_aes/main.c index c562d0588837..52a5e5d266bb 100644 --- a/tests/driver_at86rf2xx_aes/main.c +++ b/tests/driver_at86rf2xx_aes/main.c @@ -102,11 +102,10 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } #endif -static void _event_cb(netdev_t *dev, netdev_event_t event) +static void _irq_cb(void *arg) { /* Ignore interrupts */ - (void)dev; - (void)event; + (void)arg; } static void _ecb(at86rf2xx_t *dev, @@ -138,11 +137,13 @@ static void _cbc(at86rf2xx_t *dev, int main(void) { - at86rf2xx_t dev; + /* initialize descriptors to zero */ + ieee802154_dev_t hal = {0}; + at86rf2xx_t dev = {0}; + bool success = true; - at86rf2xx_setup(&dev, &at86rf2xx_params[0], 0); - dev.netdev.netdev.event_callback = _event_cb; - if (dev.netdev.netdev.driver->init(&dev.netdev.netdev) != 0) { + int res = at86rf2xx_init(&dev, &at86rf2xx_params[0], &hal, _irq_cb, NULL); + if (res < 0) { return EXIT_FAILURE; } diff --git a/tests/ieee802154_hal/Makefile b/tests/ieee802154_hal/Makefile index 7cb7cb8c2450..b19f961e7df8 100644 --- a/tests/ieee802154_hal/Makefile +++ b/tests/ieee802154_hal/Makefile @@ -7,8 +7,13 @@ include ../Makefile.tests_common BOARD_WHITELIST += adafruit-clue \ adafruit-itsybitsy-nrf52 \ arduino-nano-33-ble \ + atmega256rfr2-xpro \ + avr-rss2 \ cc2538dk \ + derfmega128 \ + derfmega256 \ feather-nrf52840 \ + microduino-corerf \ native \ nrf52840dk \ nrf52840dongle \ @@ -22,7 +27,9 @@ BOARD_WHITELIST += adafruit-clue \ remote-pa \ remote-reva \ remote-revb \ + samr21-xpro \ pba-d-01-kw2x \ + zigduino \ # ifeq ($(BOARD), native) diff --git a/tests/ieee802154_hal/common.h b/tests/ieee802154_hal/common.h index e2ce68d3ed3a..5802319615fb 100644 --- a/tests/ieee802154_hal/common.h +++ b/tests/ieee802154_hal/common.h @@ -29,7 +29,8 @@ IS_USED(MODULE_NRF802154) + \ SOCKET_ZEP_MAX + \ IS_USED(MODULE_MRF24J40) + \ - IS_USED(MODULE_KW2XRF) + IS_USED(MODULE_KW2XRF) + \ + IS_USED(MODULE_AT86RF2XX) #ifdef __cplusplus extern "C" { @@ -46,6 +47,7 @@ typedef enum { IEEE802154_DEV_TYPE_SOCKET_ZEP, IEEE802154_DEV_TYPE_KW2XRF, IEEE802154_DEV_TYPE_MRF24J40, + IEEE802154_DEV_TYPE_AT86RF2XX, } ieee802154_dev_type_t; typedef ieee802154_dev_t* (*ieee802154_dev_cb_t)(ieee802154_dev_type_t type, diff --git a/tests/ieee802154_hal/init_devs.c b/tests/ieee802154_hal/init_devs.c index acb44862bddb..92e648885961 100644 --- a/tests/ieee802154_hal/init_devs.c +++ b/tests/ieee802154_hal/init_devs.c @@ -41,6 +41,14 @@ #include "event/thread.h" extern void auto_init_event_thread(void); +#ifdef MODULE_AT86RF2XX +#include "at86rf2xx.h" +#include "at86rf2xx_params.h" +#include "event/thread.h" +#define AT86RF2XX_NUM ARRAY_SIZE(at86rf2xx_params) +static at86rf2xx_bhp_ev_t at86rf2xx_bhp[AT86RF2XX_NUM]; +#endif + #ifdef MODULE_KW2XRF #include "kw2xrf.h" #include "kw2xrf_params.h" @@ -65,7 +73,9 @@ void ieee802154_hal_test_init_devs(ieee802154_dev_cb_t cb, void *opaque) /* Call the init function of the device (this should be handled by * `auto_init`) */ + ieee802154_dev_t *radio = NULL; + (void) radio; (void) cb; (void) opaque; @@ -89,6 +99,16 @@ void ieee802154_hal_test_init_devs(ieee802154_dev_cb_t cb, void *opaque) } #endif +#ifdef MODULE_AT86RF2XX + if ((radio = cb(IEEE802154_DEV_TYPE_AT86RF2XX, opaque)) ){ + for (unsigned i = 0; i < AT86RF2XX_NUM; i++) { + const at86rf2xx_params_t *p = &at86rf2xx_params[i]; + at86rf2xx_init_event(&at86rf2xx_bhp[i], p, radio, EVENT_PRIO_HIGHEST); + break; + } + } +#endif + #ifdef MODULE_KW2XRF if ((radio = cb(IEEE802154_DEV_TYPE_KW2XRF, opaque)) ){ for (unsigned i = 0; i < KW2XRF_NUM; i++) { diff --git a/tests/ieee802154_hal/main.c b/tests/ieee802154_hal/main.c index ab382e3db982..417a50602c14 100644 --- a/tests/ieee802154_hal/main.c +++ b/tests/ieee802154_hal/main.c @@ -268,6 +268,9 @@ static ieee802154_dev_t *_reg_callback(ieee802154_dev_type_t type, void *opaque) case IEEE802154_DEV_TYPE_MRF24J40: printf("mrf24j40"); break; + case IEEE802154_DEV_TYPE_AT86RF2XX: + printf("at86rf2xx"); + break; } puts("."); @@ -299,6 +302,7 @@ static int _init(void) /* Since the device was already initialized, turn on the radio. * The transceiver state will be "TRX_OFF" */ res = ieee802154_radio_request_on(&_radio); + expect(res >= 0); while(ieee802154_radio_confirm_on(&_radio) == -EAGAIN) {} diff --git a/tests/ieee802154_submac/Makefile b/tests/ieee802154_submac/Makefile index 8f4b22467a9d..0d532cb0359f 100644 --- a/tests/ieee802154_submac/Makefile +++ b/tests/ieee802154_submac/Makefile @@ -7,8 +7,13 @@ include ../Makefile.tests_common BOARD_WHITELIST += adafruit-clue \ adafruit-itsybitsy-nrf52 \ arduino-nano-33-ble \ + atmega256rfr2-xpro \ + avr-rss2 \ cc2538dk \ + derfmega128 \ + derfmega256 \ feather-nrf52840 \ + microduino-corerf \ native \ nrf52840dk \ nrf52840dongle \ @@ -22,7 +27,9 @@ BOARD_WHITELIST += adafruit-clue \ remote-pa \ remote-reva \ remote-revb \ + samr21-xpro \ pba-d-01-kw2x \ + zigduino \ # USEMODULE += od diff --git a/tests/ieee802154_submac/main.c b/tests/ieee802154_submac/main.c index 212e38716845..19efdf92c50b 100644 --- a/tests/ieee802154_submac/main.c +++ b/tests/ieee802154_submac/main.c @@ -195,6 +195,9 @@ static ieee802154_dev_t *_reg_callback(ieee802154_dev_type_t type, void *opaque) case IEEE802154_DEV_TYPE_MRF24J40: printf("mrf24j40"); break; + case IEEE802154_DEV_TYPE_AT86RF2XX: + printf("at86rf2xx"); + break; } puts(".");