1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/at86rf2xx/at86rf2xx_netdev.c
Frederik Haxel ec7fe8d598 drivers: Use size_t print format specifier
Co-authored-by: Marian Buschsieweke <marian.buschsieweke@posteo.net>
2023-12-21 12:02:28 +01:00

1149 lines
40 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
* 2015 Freie Universität Berlin
* 2023 Gerson Fernando Budke
*
* 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 <thomas.eichinger@fu-berlin.de>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @author Kévin Roussel <Kevin.Roussel@inria.fr>
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @author Josua Arndt <jarndt@ias.rwth-aachen.de>
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "architecture.h"
#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 (%" PRIuSIZE
" byte) to be send\n", 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);
#if AT86RF2XX_IS_PERIPH && IS_USED(MODULE_NETDEV_IEEE802154_RX_TIMESTAMP)
/* AT86RF2XX_IS_PERIPH means the MCU is ATmegaRFR2 that has symbol counter */
{
uint32_t rx_sc;
rx_sc = at86rf2xx_get_sc(dev);
/* convert counter value to ns */
uint64_t res = SC_TO_NS * (uint64_t)rx_sc;
netdev_ieee802154_rx_info_set_timestamp(radio_info, res);
DEBUG("[at86rf2xx] CS: %" PRIu32 " timestamp: %" PRIu32 ".%09" PRIu32 " ",
rx_sc, (uint32_t)(radio_info->timestamp / NS_PER_SEC),
(uint32_t)(radio_info->timestamp % NS_PER_SEC));
}
#endif
}
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
*/
static inline void txr24_pll_lock_handler(void)
{
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_ISR(TRX24_PLL_LOCK_vect, txr24_pll_lock_handler);
/**
* @brief Transceiver PLL Unlock
*
* Is triggered when PLL unlocked unexpectedly.
*
* Manual p. 89
*/
static inline void txr24_pll_unlock_handler(void)
{
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_ISR(TRX24_PLL_UNLOCK_vect, txr24_pll_unlock_handler);
/**
* @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
*/
static inline void txr24_rx_start_handler(void)
{
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_ISR(TRX24_RX_START_vect, txr24_rx_start_handler);
/**
* @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
*/
static inline void txr24_rx_end_handler(void)
{
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_ISR(TRX24_RX_END_vect, txr24_rx_end_handler);
/**
* @brief Transceiver CCAED Measurement finished
*
* Is triggered when CCA or ED measurement completed.
*
* Manual p. 74 and p. 76
*/
static inline void txr24_cca_ed_done_handler(void)
{
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_ISR(TRX24_CCA_ED_DONE_vect, txr24_cca_ed_done_handler);
/**
* @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
*/
static inline void txr24_xah_ami_handler(void)
{
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_ISR(TRX24_XAH_AMI_vect, txr24_xah_ami_handler);
/**
* @brief ISR for transceiver's transmit end interrupt
*
* Is triggered when data or when acknowledge frames where send.
*
* Flow Diagram Manual p. 52 / 63
*/
static inline void txr24_tx_end_handler(void)
{
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_ISR(TRX24_TX_END_vect, txr24_tx_end_handler);
/**
* @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
*/
static inline void txr24_awake_handler(void)
{
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_ISR(TRX24_AWAKE_vect, txr24_awake_handler);
#endif /* AT86RF2XX_IS_PERIPH */