1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/at86rf215/at86rf215_netdev.c

1288 lines
39 KiB
C

/*
* Copyright (C) 2019 ML!PA Consulting GmbH
*
* 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_at86rf215
* @{
*
* @file
* @brief Netdev adaption for the AT86RF215 driver
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @author Georg von Zengen <vonzengen@ibr.cs.tu-bs.de>
* @}
*/
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "byteorder.h"
#include "iolist.h"
#include "net/eui64.h"
#include "net/ieee802154.h"
#include "net/netdev.h"
#include "net/netdev/ieee802154.h"
#include "net/gnrc/netif/internal.h"
#include "sys/bus.h"
#include "at86rf215.h"
#include "at86rf215_netdev.h"
#include "at86rf215_internal.h"
#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);
static int _confirm_send(netdev_t *netdev, void *info);
const netdev_driver_t at86rf215_driver = {
.send = _send,
.confirm_send = _confirm_send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};
static bool _is_busy(at86rf215_t *dev)
{
if (dev->flags & AT86RF215_OPT_TX_PENDING) {
return true;
}
if (dev->state == AT86RF215_STATE_TX ||
dev->state == AT86RF215_STATE_TX_WAIT_ACK ||
dev->state == AT86RF215_STATE_RX_SEND_ACK) {
return true;
}
return false;
}
__attribute__((unused))
static uint8_t _get_best_match(const uint8_t *array, uint8_t len, uint8_t val)
{
uint8_t res = 0;
uint8_t best = 0xFF;
for (uint8_t i = 0; i < len; ++i) {
if (abs((int)array[i] - val) < best) {
best = abs((int)array[i] - val);
res = i;
}
}
return res;
}
/* executed in the GPIO ISR context */
static void _irq_handler(void *arg)
{
netdev_t *netdev = arg;
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
}
/* if only one interface is active, but the other one to sleep */
static inline void _put_sibling_to_sleep(at86rf215_t *dev) {
if (is_subGHz(dev)) {
at86rf215_reg_write(dev, RG_RF24_CMD, CMD_RF_SLEEP);
} else {
at86rf215_reg_write(dev, RG_RF09_CMD, CMD_RF_SLEEP);
}
}
static int _init(netdev_t *netdev)
{
int res;
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
/* don't call HW init for both radios */
if (is_subGHz(dev) || dev->sibling == NULL) {
/* initialize GPIOs */
spi_init_cs(dev->params.spi, dev->params.cs_pin);
gpio_init(dev->params.reset_pin, GPIO_OUT);
gpio_set(dev->params.reset_pin);
/* reset the entire chip */
if ((res = at86rf215_hardware_reset(dev))) {
return res;
}
/* turn off unused interface */
if (dev->sibling == NULL) {
_put_sibling_to_sleep(dev);
}
gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_RISING, _irq_handler, dev);
}
res = at86rf215_reg_read(dev, RG_RF_PN);
if ((res != AT86RF215_PN) && (res != AT86RF215M_PN)) {
DEBUG("[at86rf215] error: unable to read correct part number: %x\n", res);
return -ENOTSUP;;
}
/* reset device to default values and put it into RX state */
at86rf215_reset_and_cfg(dev);
/* 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);
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
ssize_t len = at86rf215_tx_prepare(dev);
if (len) {
return len;
}
/* 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 + IEEE802154_FCS_LEN) > AT86RF215_MAX_PKT_LENGTH) {
DEBUG("[at86rf215] error: packet too large (%" PRIuSIZE
" byte) to be send\n", len + IEEE802154_FCS_LEN);
at86rf215_tx_abort(dev);
return -EOVERFLOW;
}
if (iol->iol_len) {
len = at86rf215_tx_load(dev, iol->iol_base, iol->iol_len, len);
}
}
/* send data out directly if pre-loading id disabled */
if (!(dev->flags & AT86RF215_OPT_PRELOADING)) {
at86rf215_tx_exec(dev);
}
/* netdev_new just returns 0 on success */
return 0;
}
static int _confirm_send(netdev_t *netdev, void *info)
{
(void)info;
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
if (dev->flags & AT86RF215_OPT_TX_PENDING) {
return -EAGAIN;
}
return (int16_t)dev->tx_frame_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);
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
int16_t pkt_len;
/* get the size of the received packet */
at86rf215_reg_read_bytes(dev, dev->BBC->RG_RXFLL, &pkt_len, sizeof(pkt_len));
/* subtract length of FCS field */
pkt_len = (pkt_len & 0x7ff) - IEEE802154_FCS_LEN;
/* just return length when buf == NULL */
if (buf == NULL) {
return pkt_len;
}
/* not enough space in buf */
if (pkt_len > (int) len) {
return -ENOBUFS;
}
/* copy payload */
at86rf215_reg_read_bytes(dev, dev->BBC->RG_FBRXS, buf, pkt_len);
if (info != NULL) {
netdev_ieee802154_rx_info_t *radio_info = info;
radio_info->rssi = (int8_t) at86rf215_reg_read(dev, dev->RF->RG_EDV);
if (IS_USED(MODULE_NETDEV_IEEE802154_RX_TIMESTAMP)) {
uint32_t rx_timestamp;
at86rf215_reg_read_bytes(dev, dev->BBC->RG_CNT0, &rx_timestamp,
sizeof(rx_timestamp));
/* convert counter value to ns */
uint64_t res = rx_timestamp * 1000ULL / 32;
netdev_ieee802154_rx_info_set_timestamp(radio_info, res);
}
}
return pkt_len;
}
static int _set_state(at86rf215_t *dev, netopt_state_t state)
{
if (_is_busy(dev)) {
return -EBUSY;
}
switch (state) {
case NETOPT_STATE_STANDBY:
at86rf215_set_idle_from_rx(dev, CMD_RF_TRXOFF);
break;
case NETOPT_STATE_SLEEP:
at86rf215_set_idle_from_rx(dev, CMD_RF_SLEEP);
break;
case NETOPT_STATE_RX:
case NETOPT_STATE_IDLE:
at86rf215_set_rx_from_idle(dev, NULL);
break;
case NETOPT_STATE_TX:
if (dev->flags & AT86RF215_OPT_PRELOADING) {
return at86rf215_tx_exec(dev);
}
break;
case NETOPT_STATE_RESET:
at86rf215_reset(dev);
break;
default:
return -ENOTSUP;
}
return sizeof(netopt_state_t);
}
static netopt_state_t _get_state(at86rf215_t *dev)
{
switch (dev->state) {
case AT86RF215_STATE_SLEEP:
return NETOPT_STATE_SLEEP;
case AT86RF215_STATE_RX_SEND_ACK:
return NETOPT_STATE_RX;
case AT86RF215_STATE_TX:
case AT86RF215_STATE_TX_WAIT_ACK:
return NETOPT_STATE_TX;
case AT86RF215_STATE_OFF:
return NETOPT_STATE_OFF;
case AT86RF215_STATE_IDLE:
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);
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
if (netdev == NULL) {
return -ENODEV;
}
/* getting these options doesn't require the transceiver to be responsive */
switch (opt) {
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 & AT86RF215_OPT_PRELOADING) {
*((netopt_enable_t *)val) = NETOPT_ENABLE;
}
else {
*((netopt_enable_t *)val) = NETOPT_DISABLE;
}
return sizeof(netopt_enable_t);
case NETOPT_PROMISCUOUSMODE:
if (dev->flags & AT86RF215_OPT_PROMISCUOUS) {
*((netopt_enable_t *)val) = NETOPT_ENABLE;
}
else {
*((netopt_enable_t *)val) = NETOPT_DISABLE;
}
return sizeof(netopt_enable_t);
case NETOPT_RX_START_IRQ:
case NETOPT_TX_START_IRQ:
*((netopt_enable_t *)val) = NETOPT_ENABLE;
return sizeof(netopt_enable_t);
case NETOPT_CSMA:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_CSMA);
return sizeof(netopt_enable_t);
case NETOPT_CSMA_RETRIES:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->csma_retries_max;
return sizeof(uint8_t);
case NETOPT_CSMA_MAXBE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->csma_maxbe;
return sizeof(uint8_t);
case NETOPT_CSMA_MINBE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->csma_minbe;
return sizeof(uint8_t);
case NETOPT_RETRANS:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->retries_max;
return sizeof(uint8_t);
case NETOPT_TX_RETRIES_NEEDED:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->retries_max - dev->retries;
return sizeof(uint8_t);
case NETOPT_AUTOACK:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_AUTOACK);
return sizeof(netopt_enable_t);
case NETOPT_CHANNEL_PAGE:
assert(max_len >= sizeof(uint16_t));
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_OQPSK) {
return -ENOTSUP;
}
((uint8_t *)val)[1] = 0;
((uint8_t *)val)[0] = is_subGHz(dev) ? 2 : 0;
return sizeof(uint16_t);
case NETOPT_AUTOCCA:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_CCATX);
return sizeof(netopt_enable_t);
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;
}
/* properties are not available if the device is sleeping */
if (dev->state == AT86RF215_STATE_SLEEP) {
return -ENOTSUP;
}
/* these options require the transceiver to be not sleeping*/
switch (opt) {
case NETOPT_TX_POWER:
assert(max_len >= sizeof(int16_t));
*((uint16_t *)val) = at86rf215_get_txpower(dev);
res = sizeof(uint16_t);
break;
case NETOPT_CCA_THRESHOLD:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_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) = at86rf215_cca(dev);
res = sizeof(netopt_enable_t);
break;
case NETOPT_LAST_ED_LEVEL:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_get_ed_level(dev);
res = sizeof(int8_t);
break;
case NETOPT_RANDOM:
at86rf215_get_random(dev, val, max_len);
res = max_len;
break;
case NETOPT_IEEE802154_PHY:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_get_phy_mode(dev);
res = max_len;
break;
#ifdef MODULE_NETDEV_IEEE802154_MR_FSK
case NETOPT_MR_FSK_MODULATION_INDEX:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_FSK_get_mod_idx(dev);
res = max_len;
break;
case NETOPT_MR_FSK_MODULATION_ORDER:
assert(max_len >= sizeof(int8_t));
/* 0 -> 2-FSK, 1 -> 4-FSK */
*((int8_t *)val) = 2 + 2 * at86rf215_FSK_get_mod_order(dev);
res = max_len;
break;
case NETOPT_MR_FSK_SRATE:
assert(max_len >= sizeof(uint16_t));
/* netopt expects symbol rate in kHz, internally it's stored in 10kHz steps */
*((uint16_t *)val) = _at86rf215_fsk_srate_10kHz[at86rf215_FSK_get_srate(dev)]
* 10;
res = max_len;
break;
case NETOPT_MR_FSK_FEC:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = at86rf215_FSK_get_fec(dev);
res = max_len;
break;
case NETOPT_CHANNEL_SPACING:
assert(max_len >= sizeof(uint16_t));
*((uint16_t *)val) = at86rf215_get_channel_spacing(dev);
res = max_len;
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_FSK */
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
case NETOPT_MR_OFDM_OPTION:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OFDM_get_option(dev);
res = max_len;
break;
case NETOPT_MR_OFDM_MCS:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OFDM_get_scheme(dev);
res = max_len;
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK
case NETOPT_MR_OQPSK_CHIPS:
assert(max_len >= sizeof(int16_t));
switch (at86rf215_OQPSK_get_chips(dev)) {
case 0: *((int16_t *)val) = 100; break;
case 1: *((int16_t *)val) = 200; break;
case 2: *((int16_t *)val) = 1000; break;
case 3: *((int16_t *)val) = 2000; break;
}
res = max_len;
break;
case NETOPT_MR_OQPSK_RATE:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OQPSK_get_mode(dev);
res = max_len;
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
case NETOPT_OQPSK_RATE:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OQPSK_get_mode_legacy(dev);
res = max_len;
break;
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
default:
res = -ENOTSUP;
break;
}
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);
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
int res = -ENOTSUP;
if (dev == NULL) {
return -ENODEV;
}
/* no need to wake up the device when it's sleeping - all registers
are reset on wakeup. */
switch (opt) {
case NETOPT_ADDRESS:
assert(len <= sizeof(uint16_t));
at86rf215_set_addr_short(dev, 0, *((const uint16_t *)val));
/* don't set res to set netdev_ieee802154_t::short_addr */
break;
case NETOPT_ADDRESS_LONG:
assert(len <= sizeof(uint64_t));
at86rf215_set_addr_long(dev, *((const uint64_t *)val));
/* don't set res to set netdev_ieee802154_t::long_addr */
break;
case NETOPT_NID:
assert(len <= sizeof(uint16_t));
at86rf215_set_pan(dev, 0, *((const uint16_t *)val));
/* don't set res to set netdev_ieee802154_t::pan */
break;
case NETOPT_CHANNEL:
assert(len == sizeof(uint16_t));
uint16_t chan = *((const uint16_t *)val);
if (at86rf215_chan_valid(dev, chan) != chan) {
res = -EINVAL;
break;
}
at86rf215_set_chan(dev, chan);
/* don't set res to set netdev_ieee802154_t::chan */
break;
case NETOPT_TX_POWER:
assert(len <= sizeof(int16_t));
at86rf215_set_txpower(dev, *((const int16_t *)val));
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:
at86rf215_set_option(dev, AT86RF215_OPT_AUTOACK,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_AUTOCCA:
at86rf215_set_option(dev, AT86RF215_OPT_CCATX,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
#ifdef MODULE_AT86RF215_BATMON
case NETOPT_BATMON:
assert(len <= sizeof(uint16_t));
{
uint16_t mV = *(const uint16_t *)val;
if (mV) {
res = at86rf215_enable_batmon(dev, mV);
res = (res == 0) ? (int)sizeof(uint16_t) : res;
} else {
at86rf215_disable_batmon(dev);
res = sizeof(uint16_t);
}
}
break;
#endif
case NETOPT_RETRANS:
assert(len <= sizeof(uint8_t));
dev->retries_max = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_PRELOADING:
at86rf215_set_option(dev, AT86RF215_OPT_PRELOADING,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_PROMISCUOUSMODE:
at86rf215_set_option(dev, AT86RF215_OPT_PROMISCUOUS,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_CSMA:
at86rf215_set_option(dev, AT86RF215_OPT_CSMA,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_CSMA_RETRIES:
assert(len <= sizeof(uint8_t));
dev->csma_retries_max = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_CSMA_MAXBE:
assert(len <= sizeof(uint8_t));
dev->csma_maxbe = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_CSMA_MINBE:
assert(len <= sizeof(uint8_t));
dev->csma_minbe = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_CCA_THRESHOLD:
assert(len <= sizeof(int8_t));
at86rf215_set_cca_threshold(dev, *((const int8_t *)val));
res = sizeof(int8_t);
break;
case NETOPT_IEEE802154_PHY:
assert(len <= sizeof(uint8_t));
switch (*(uint8_t *)val) {
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
case IEEE802154_PHY_OQPSK:
at86rf215_configure_legacy_OQPSK(dev, at86rf215_OQPSK_get_mode_legacy(dev));
res = sizeof(uint8_t);
break;
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK
case IEEE802154_PHY_MR_OQPSK:
at86rf215_configure_OQPSK(dev,
at86rf215_OQPSK_get_chips(dev),
at86rf215_OQPSK_get_mode(dev));
res = sizeof(uint8_t);
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
case IEEE802154_PHY_MR_OFDM:
at86rf215_configure_OFDM(dev,
at86rf215_OFDM_get_option(dev),
at86rf215_OFDM_get_scheme(dev));
res = sizeof(uint8_t);
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
#ifdef MODULE_NETDEV_IEEE802154_MR_FSK
case IEEE802154_PHY_MR_FSK:
at86rf215_configure_FSK(dev,
at86rf215_FSK_get_srate(dev),
at86rf215_FSK_get_mod_idx(dev),
at86rf215_FSK_get_mod_order(dev),
at86rf215_FSK_get_fec(dev));
res = sizeof(uint8_t);
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_FSK */
default:
return -ENOTSUP;
}
break;
#ifdef MODULE_NETDEV_IEEE802154_MR_FSK
case NETOPT_MR_FSK_MODULATION_INDEX:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_FSK) {
return -ENOTSUP;
}
if (at86rf215_FSK_set_mod_idx(dev, *(uint8_t *)val) == 0) {
res = at86rf215_FSK_get_mod_idx(dev);
} else {
res = -ERANGE;
}
break;
case NETOPT_MR_FSK_MODULATION_ORDER:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_FSK) {
return -ENOTSUP;
}
if (*(uint8_t *)val != 2 && *(uint8_t *)val != 4) {
res = -ERANGE;
} else {
/* 4-FSK -> 1, 2-FSK -> 0 */
at86rf215_FSK_set_mod_order(dev, *(uint8_t *)val >> 2);
res = sizeof(uint8_t);
}
break;
case NETOPT_MR_FSK_SRATE:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_FSK) {
return -ENOTSUP;
}
/* find the closest symbol rate value (in 10 kHz) that matches
the requested input (in kHz) */
res = _get_best_match(_at86rf215_fsk_srate_10kHz,
FSK_SRATE_400K + 1, *(uint16_t *)val / 10);
if (at86rf215_FSK_set_srate(dev, res) == 0) {
res = 10 * _at86rf215_fsk_srate_10kHz[res];
} else {
res = -ERANGE;
}
break;
case NETOPT_MR_FSK_FEC:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_FSK) {
return -ENOTSUP;
}
if (at86rf215_FSK_set_fec(dev, *(uint8_t *)val) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
case NETOPT_CHANNEL_SPACING:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_FSK) {
return -ENOTSUP;
}
/* find the closest channel spacing value (in 25 kHz) that matches
the requested input (in kHz) */
res = _get_best_match(_at86rf215_fsk_channel_spacing_25kHz,
FSK_CHANNEL_SPACING_400K + 1, *(uint16_t *)val / 25);
if (at86rf215_FSK_set_channel_spacing(dev, res) == 0) {
res = 25 * _at86rf215_fsk_channel_spacing_25kHz[res];
} else {
res = -ERANGE;
}
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_FSK */
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
case NETOPT_MR_OFDM_OPTION:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OFDM) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OFDM_set_option(dev, *((const uint8_t *)val)) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
case NETOPT_MR_OFDM_MCS:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OFDM) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OFDM_set_scheme(dev, *((const uint8_t *)val)) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK
case NETOPT_MR_OQPSK_CHIPS:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OQPSK) {
return -ENOTSUP;
}
uint8_t chips;
assert(len <= sizeof(uint16_t));
if (*((const uint16_t *)val) == 100) {
chips = 0;
} else if (*((const uint16_t *)val) == 200) {
chips = 1;
} else if (*((const uint16_t *)val) == 1000) {
chips = 2;
} else if (*((const uint16_t *)val) == 2000) {
chips = 3;
} else {
res = -EINVAL;
break;
}
if (at86rf215_OQPSK_set_chips(dev, chips) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
case NETOPT_MR_OQPSK_RATE:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OQPSK) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OQPSK_set_mode(dev, *(uint8_t *)val) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
case NETOPT_OQPSK_RATE:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_OQPSK) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OQPSK_set_mode_legacy(dev, *(uint8_t *)val) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
default:
break;
}
if (res == -ENOTSUP) {
res = netdev_ieee802154_set(container_of(netdev, netdev_ieee802154_t, netdev),
opt, val, len);
}
return res;
}
static void _enable_tx2rx(at86rf215_t *dev)
{
uint8_t amcs = at86rf215_reg_read(dev, dev->BBC->RG_AMCS);
/* disable AACK, enable TX2RX */
amcs |= AMCS_TX2RX_MASK;
amcs &= ~AMCS_AACK_MASK;
at86rf215_reg_write(dev, dev->BBC->RG_AMCS, amcs);
}
static void _tx_end(at86rf215_t *dev)
{
netdev_t *netdev = &dev->netdev.netdev;
/* listen to non-ACK packets again */
if (dev->flags & AT86RF215_OPT_ACK_REQUESTED) {
dev->flags &= ~AT86RF215_OPT_ACK_REQUESTED;
at86rf215_filter_ack(dev, false);
}
at86rf215_tx_done(dev);
if (netdev->event_callback) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
}
dev->timeout = 0;
dev->state = AT86RF215_STATE_IDLE;
}
static void __tx_end_timeout(at86rf215_t *dev)
{
/* signal error to confirm_send */
dev->tx_frame_len = (int16_t)-EHOSTUNREACH;
_tx_end(dev);
}
static void _ack_timeout_cb(void* arg) {
at86rf215_t *dev = arg;
netdev_t *netdev = &dev->netdev.netdev;
dev->timeout = AT86RF215_TIMEOUT_ACK;
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
}
static void _backoff_timeout_cb(void* arg) {
at86rf215_t *dev = arg;
netdev_t *netdev = &dev->netdev.netdev;
dev->timeout = AT86RF215_TIMEOUT_CSMA;
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
}
static void _set_idle(at86rf215_t *dev)
{
dev->state = AT86RF215_STATE_IDLE;
uint8_t next_state;
if (dev->flags & AT86RF215_OPT_TX_PENDING) {
next_state = CMD_RF_TXPREP;
} else {
next_state = CMD_RF_RX;
}
at86rf215_rf_cmd(dev, next_state);
}
/* wake up the radio thread after ACK timeout */
static void _start_ack_timer(at86rf215_t *dev)
{
dev->timer.arg = dev;
dev->timer.callback = _ack_timeout_cb;
xtimer_set(&dev->timer, dev->ack_timeout_usec);
}
/* wake up the radio thread after CSMA backoff period */
static void _start_backoff_timer(at86rf215_t *dev)
{
uint8_t be; /* backoff exponent */
uint32_t base;
/* energy detect interrupt happened -> hardware is still in RX mode */
at86rf215_get_random(dev, &base, sizeof(base));
be = ((dev->csma_retries_max - dev->csma_retries) - 1) + dev->csma_minbe;
if (be > dev->csma_maxbe) {
be = dev->csma_maxbe;
}
uint32_t csma_backoff_usec = ((1LU << be) - 1) * dev->csma_backoff_period;
/* limit the 32bit random value to the current backoff */
csma_backoff_usec = base % csma_backoff_usec;
DEBUG("Set CSMA backoff to %"PRIu32" (be %u min %u max %u base: %"PRIu32")\n",
csma_backoff_usec, be, dev->csma_minbe, dev->csma_maxbe, base);
dev->timer.arg = dev;
dev->timer.callback = _backoff_timeout_cb;
xtimer_set(&dev->timer, csma_backoff_usec);
}
static inline bool _ack_frame_received(at86rf215_t *dev)
{
/* check if the sequence numbers (3rd byte) match */
return at86rf215_reg_read(dev, dev->BBC->RG_FBRXS + 2)
== at86rf215_reg_read(dev, dev->BBC->RG_FBTXS + 2);
}
static void _handle_ack_timeout(at86rf215_t *dev)
{
if (dev->retries) {
--dev->retries;
if (dev->flags & AT86RF215_OPT_CSMA) {
dev->csma_retries = dev->csma_retries_max;
if (!(dev->flags & AT86RF215_OPT_CCATX)){
dev->flags |= AT86RF215_OPT_CCA_PENDING;
}
}
dev->flags |= AT86RF215_OPT_TX_PENDING;
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
} else {
/* no retransmissions left */
__tx_end_timeout(dev);
}
}
/* clear the other IRQ if the sibling is not ready yet */
static inline void _clear_sibling_irq(at86rf215_t *dev) {
if (is_subGHz(dev)) {
at86rf215_reg_read(dev, RG_RF24_IRQS);
at86rf215_reg_read(dev, RG_BBC1_IRQS);
} else {
at86rf215_reg_read(dev, RG_RF09_IRQS);
at86rf215_reg_read(dev, RG_BBC0_IRQS);
}
}
static void _handle_edc(at86rf215_t *dev)
{
netdev_t *netdev = &dev->netdev.netdev;
/* In CCATX mode this function is only triggered if busy */
if (!(dev->flags & AT86RF215_OPT_CCATX)) {
/* channel clear -> TX */
if ((int8_t)at86rf215_reg_read(dev, dev->RF->RG_EDV) <= at86rf215_get_cca_threshold(dev)) {
dev->flags &= ~AT86RF215_OPT_CCA_PENDING;
at86rf215_enable_baseband(dev);
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
return;
}
}
DEBUG("CSMA busy\n");
if (dev->csma_retries) {
--dev->csma_retries;
_start_backoff_timer(dev);
} else {
/* channel busy and no retries left */
dev->flags &= ~(AT86RF215_OPT_CCA_PENDING | AT86RF215_OPT_TX_PENDING);
dev->state = AT86RF215_STATE_IDLE;
at86rf215_enable_baseband(dev);
at86rf215_enable_rpc(dev);
at86rf215_tx_done(dev);
/* signal error to confirm_send */
dev->tx_frame_len = (int16_t)-EBUSY;
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
DEBUG("CSMA give up");
/* radio is still in RX mode, tx_done sets IDLE state */
}
}
/* executed in the radio thread */
static void _isr(netdev_t *netdev)
{
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
uint8_t bb_irq_mask, rf_irq_mask;
uint8_t bb_irqs_enabled = BB_IRQ_RXFE | BB_IRQ_TXFE;
/* not using IRQMM because we want to know about AGCH */
bb_irqs_enabled |= BB_IRQ_RXAM;
rf_irq_mask = at86rf215_reg_read(dev, dev->RF->RG_IRQS);
bb_irq_mask = at86rf215_reg_read(dev, dev->BBC->RG_IRQS);
uint8_t timeout = dev->timeout;
if (timeout) {
dev->timeout = 0;
}
/* mark AGC Hold bit */
if (bb_irq_mask & BB_IRQ_AGCH) {
dev->flags |= AT86RF215_OPT_AGCH;
}
/* clear AGC Hold bit */
if (bb_irq_mask & BB_IRQ_AGCR) {
dev->flags &= ~AT86RF215_OPT_AGCH;
}
/* we got here because of CMSA timeout */
if (timeout & AT86RF215_TIMEOUT_CSMA) {
timeout = 0;
if (!(dev->flags & AT86RF215_OPT_CCATX)) {
at86rf215_reg_write(dev, dev->RF->RG_EDC, 1);
} else {
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
}
}
/* If the interrupt pin is still high, there was an IRQ on the other radio */
if (gpio_read(dev->params.int_pin)) {
if (dev->sibling && dev->sibling->state != AT86RF215_STATE_OFF) {
netdev->event_callback(&dev->sibling->netdev.netdev, NETDEV_EVENT_ISR);
} else {
_clear_sibling_irq(dev);
}
}
/* Handle Low Battery IRQ */
#if MODULE_AT86RF215_BATMON
if ((rf_irq_mask & RF_IRQ_BATLOW)) {
msg_bus_t *bus = sys_bus_get(SYS_BUS_POWER);
msg_bus_post(bus, SYS_BUS_POWER_EVENT_LOW_VOLTAGE, NULL);
}
#endif
/* exit early if the interrupt was not for this interface */
if (!((bb_irq_mask & bb_irqs_enabled) ||
(rf_irq_mask & (RF_IRQ_EDC | RF_IRQ_TRXRDY)) || timeout)) {
return;
}
/* check if the received packet has the ACK request bit set */
bool rx_ack_req;
if (bb_irq_mask & BB_IRQ_RXFE) {
rx_ack_req = at86rf215_reg_read(dev, dev->BBC->RG_FBRXS) & IEEE802154_FCF_ACK_REQ;
} else {
rx_ack_req = 0;
}
#ifdef MODULE_NETDEV_IEEE802154_MR_FSK
/* listen for short preamble in RX */
if (bb_irq_mask & BB_IRQ_TXFE && dev->fsk_pl) {
at86rf215_FSK_prepare_rx(dev);
}
#endif /* MODULE_NETDEV_IEEE802154_MR_FSK */
if (dev->flags & AT86RF215_OPT_CCA_PENDING) {
/* Start ED or handle result */
if (rf_irq_mask & RF_IRQ_EDC) {
_handle_edc(dev);
} else if (rf_irq_mask & RF_IRQ_TRXRDY) {
/* disable baseband for energy detection */
at86rf215_disable_baseband(dev);
at86rf215_disable_rpc(dev);
/* switch to state RX for energy detection */
at86rf215_rf_cmd(dev, CMD_RF_RX);
/* start energy measurement */
at86rf215_reg_write(dev, dev->RF->RG_EDC, 1);
}
} else if (dev->flags & AT86RF215_OPT_TX_PENDING) {
/* start transmitting the frame */
if (rf_irq_mask & RF_IRQ_TRXRDY) {
#ifdef MODULE_NETDEV_IEEE802154_MR_FSK
/* send long preamble in TX */
if (dev->fsk_pl) {
at86rf215_FSK_prepare_tx(dev);
}
#endif /* MODULE_NETDEV_IEEE802154_MR_FSK */
/* automatically switch to RX when TX is done */
_enable_tx2rx(dev);
/* only listen for ACK frames */
if (dev->flags & AT86RF215_OPT_ACK_REQUESTED) {
at86rf215_filter_ack(dev, true);
}
/* switch to state TX */
dev->state = AT86RF215_STATE_TX;
dev->flags &= ~AT86RF215_OPT_TX_PENDING;
at86rf215_rf_cmd(dev, CMD_RF_TX);
/* This also tells the upper layer about retransmissions - should it be like that? */
if (netdev->event_callback) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED);
}
}
}
/* CCATX signals medium busy */
if ((dev->flags & AT86RF215_OPT_CCATX) && (rf_irq_mask & RF_IRQ_EDC) && (bb_irq_mask & BB_IRQ_TXFE)) {
bb_irq_mask &= ~BB_IRQ_TXFE;
rf_irq_mask &= ~RF_IRQ_EDC;
_handle_edc(dev);
}
int iter = 0;
while (timeout || (bb_irq_mask & (BB_IRQ_RXFE | BB_IRQ_TXFE))) {
/* This should never happen */
if (++iter > 3) {
puts("AT86RF215: stuck in ISR");
printf("\tnum_channels: %d\n", dev->num_chans);
printf("\tHW: %s\n", at86rf215_hw_state2a(at86rf215_get_rf_state(dev)));
printf("\tSW: %s\n", at86rf215_sw_state2a(dev->state));
printf("\trf_irq_mask: %x\n", rf_irq_mask);
printf("\tbb_irq_mask: %x\n", bb_irq_mask);
printf("\ttimeout: %x\n", timeout);
break;
}
switch (dev->state) {
case AT86RF215_STATE_IDLE:
if (!(bb_irq_mask & (BB_IRQ_RXFE | BB_IRQ_RXAM))) {
DEBUG("IDLE: only RXFE/RXAM expected (%x)\n", bb_irq_mask);
break;
}
if ((bb_irq_mask & BB_IRQ_RXAM) && netdev->event_callback) {
/* will be executed in the same thread */
netdev->event_callback(netdev, NETDEV_EVENT_RX_STARTED);
}
bb_irq_mask &= ~BB_IRQ_RXAM;
if (!(bb_irq_mask & BB_IRQ_RXFE)) {
break;
}
bb_irq_mask &= ~BB_IRQ_RXFE;
if (netdev->event_callback) {
/* will be executed in the same thread */
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
if (rx_ack_req) {
dev->state = AT86RF215_STATE_RX_SEND_ACK;
break;
}
_set_idle(dev);
break;
case AT86RF215_STATE_RX_SEND_ACK:
if (!(bb_irq_mask & BB_IRQ_TXFE)) {
DEBUG("RX_SEND_ACK: only TXFE expected (%x)\n", bb_irq_mask);
break;
}
bb_irq_mask &= ~BB_IRQ_TXFE;
_set_idle(dev);
break;
case AT86RF215_STATE_TX:
if (!(bb_irq_mask & BB_IRQ_TXFE)) {
DEBUG("TX: only TXFE expected (%x)\n", bb_irq_mask);
break;
}
bb_irq_mask &= ~BB_IRQ_TXFE;
if (dev->flags & AT86RF215_OPT_ACK_REQUESTED) {
dev->state = AT86RF215_STATE_TX_WAIT_ACK;
_start_ack_timer(dev);
} else {
_tx_end(dev);
}
break;
case AT86RF215_STATE_TX_WAIT_ACK:
if (!((bb_irq_mask & BB_IRQ_RXFE) || timeout)) {
DEBUG("TX_WAIT_ACK: only RXFE or timeout expected (%x)\n", bb_irq_mask);
break;
}
/* handle timeout case */
if (!(bb_irq_mask & BB_IRQ_RXFE)) {
goto timeout;
}
bb_irq_mask &= ~BB_IRQ_RXFE;
if (_ack_frame_received(dev)) {
timeout = 0;
xtimer_remove(&dev->timer);
_tx_end(dev);
at86rf215_rf_cmd(dev, CMD_RF_RX);
break;
}
/* we got a spurious ACK */
if (!timeout) {
at86rf215_rf_cmd(dev, CMD_RF_RX);
break;
}
timeout:
/* For a yet unknown reason, the device spends an excessive amount of time
* transmitting the preamble in non-legacy modes.
* This means the calculated ACK timeouts are often too short.
* To mitigate this, postpone the ACK timeout if the device is still RXign
* the ACK frame when the timeout expires.
*/
if (dev->flags & AT86RF215_OPT_AGCH) {
DEBUG("[at86rf215] Ack timeout postponed\n");
_start_ack_timer(dev);
} else {
DEBUG("[at86rf215] Ack timeout\n");
_handle_ack_timeout(dev);
}
timeout = 0;
break;
}
}
}