2019-12-13 17:47:03 +01:00
|
|
|
/*
|
|
|
|
* 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>
|
|
|
|
|
2022-04-22 14:24:42 +02:00
|
|
|
#include "byteorder.h"
|
2019-12-13 17:47:03 +01:00
|
|
|
#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"
|
|
|
|
|
2020-09-07 23:37:06 +02:00
|
|
|
#include "sys/bus.h"
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
#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);
|
2024-05-15 17:23:52 +02:00
|
|
|
static int _confirm_send(netdev_t *netdev, void *info);
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
const netdev_driver_t at86rf215_driver = {
|
|
|
|
.send = _send,
|
2024-05-15 17:23:52 +02:00
|
|
|
.confirm_send = _confirm_send,
|
2019-12-13 17:47:03 +01:00
|
|
|
.recv = _recv,
|
|
|
|
.init = _init,
|
|
|
|
.isr = _isr,
|
|
|
|
.get = _get,
|
|
|
|
.set = _set,
|
|
|
|
};
|
|
|
|
|
drivers/at86rf215: return error when switching state while busy
Previously the function attempted to block here and manually service
the ISR.
This lead to unexpected results, in particular messages queuing up in
the threads message queue.
The result was that the radio would not end up in the correct state.
E.g. sending SLEEP to both interfaces while a transmission was ongoing
would lead to the interfaces waking up again.
With this patch the operation will just return -ERRNO so the caller can
try again.
To reproduce, try the attached patch for the `gnrc_networking` example:
On master you will find that the radio still consumes ~2.4mA after 'shutdown'.
(It is in fact in the state TRXOFF as it woke up again)
With this change the radio should consume less than 1µA (DEEP SLEEP).
diff --git a/examples/gnrc_networking/main.c b/examples/gnrc_networking/main.c
index 6301f4291d..93b96eb939 100644
--- a/examples/gnrc_networking/main.c
+++ b/examples/gnrc_networking/main.c
@@ -23,12 +23,47 @@
#include "shell.h"
#include "msg.h"
+#include "periph/pm.h"
+
+#include "net/netopt.h"
+#include "net/gnrc/netif.h"
+
#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
extern int udp_cmd(int argc, char **argv);
+extern void send(char *addr_str, char *port_str, char *data, unsigned int num,
+ unsigned int delay);
+
+static int send_and_shutdown(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+
+ /* the address must not exist */
+ char addr[] = "fe80::2068:3123:59f5:d238%7";
+ char port[] = "1234";
+ char data[] = "Hello World!";
+
+ send(addr, port, data, 1, 0);
+
+ /* disable radio */
+ gnrc_netif_t* netif = NULL;
+ netopt_state_t state = NETOPT_STATE_SLEEP;
+ while ((netif = gnrc_netif_iter(netif))) {
+ /* retry while busy */
+ while (gnrc_netapi_set(netif->pid, NETOPT_STATE, 0, &state,
+ sizeof(netopt_state_t)) == -EBUSY);
+ }
+
+ pm_set(0);
+
+ return 0;
+}
+
static const shell_command_t shell_commands[] = {
+ { "shutdown", "turn off the radio & shut down", send_and_shutdown },
{ "udp", "send data over UDP and listen on UDP ports", udp_cmd },
{ NULL, NULL, NULL }
};
diff --git a/examples/gnrc_networking/udp.c b/examples/gnrc_networking/udp.c
index e8a559846e..cb80855b76 100644
--- a/examples/gnrc_networking/udp.c
+++ b/examples/gnrc_networking/udp.c
@@ -36,7 +36,7 @@ static gnrc_netreg_entry_t server = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX
KERNEL_PID_UNDEF);
-static void send(char *addr_str, char *port_str, char *data, unsigned int num,
+void send(char *addr_str, char *port_str, char *data, unsigned int num,
unsigned int delay)
{
gnrc_netif_t *netif = NULL;
2020-04-01 22:57:18 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-02-16 18:01:07 +01:00
|
|
|
__attribute__((unused))
|
2020-06-13 18:03:32 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
/* executed in the GPIO ISR context */
|
|
|
|
static void _irq_handler(void *arg)
|
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_t *netdev = arg;
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
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;
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
|
|
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2022-08-18 12:02:47 +02:00
|
|
|
/* signal link UP */
|
|
|
|
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _send(netdev_t *netdev, const iolist_t *iolist)
|
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
|
|
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
|
2019-12-13 17:47:03 +01:00
|
|
|
|
2024-11-22 19:44:12 +01:00
|
|
|
ssize_t len = at86rf215_tx_prepare(dev);
|
|
|
|
if (len) {
|
|
|
|
return len;
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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) {
|
2023-12-18 17:41:57 +01:00
|
|
|
DEBUG("[at86rf215] error: packet too large (%" PRIuSIZE
|
|
|
|
" byte) to be send\n", len + IEEE802154_FCS_LEN);
|
2019-12-13 17:47:03 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-11-13 18:55:50 +01:00
|
|
|
/* netdev_new just returns 0 on success */
|
|
|
|
return 0;
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
|
2024-05-15 17:23:52 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
|
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
|
|
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
|
2019-12-13 17:47:03 +01:00
|
|
|
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);
|
2021-03-20 13:58:14 +01:00
|
|
|
|
2022-04-22 14:24:42 +02:00
|
|
|
if (IS_USED(MODULE_NETDEV_IEEE802154_RX_TIMESTAMP)) {
|
2021-03-20 13:58:14 +01:00
|
|
|
uint32_t rx_timestamp;
|
|
|
|
at86rf215_reg_read_bytes(dev, dev->BBC->RG_CNT0, &rx_timestamp,
|
|
|
|
sizeof(rx_timestamp));
|
|
|
|
|
|
|
|
/* convert counter value to ns */
|
2022-04-22 14:24:42 +02:00
|
|
|
uint64_t res = rx_timestamp * 1000ULL / 32;
|
|
|
|
netdev_ieee802154_rx_info_set_timestamp(radio_info, res);
|
2021-03-20 13:58:14 +01:00
|
|
|
}
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return pkt_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _set_state(at86rf215_t *dev, netopt_state_t state)
|
|
|
|
{
|
drivers/at86rf215: return error when switching state while busy
Previously the function attempted to block here and manually service
the ISR.
This lead to unexpected results, in particular messages queuing up in
the threads message queue.
The result was that the radio would not end up in the correct state.
E.g. sending SLEEP to both interfaces while a transmission was ongoing
would lead to the interfaces waking up again.
With this patch the operation will just return -ERRNO so the caller can
try again.
To reproduce, try the attached patch for the `gnrc_networking` example:
On master you will find that the radio still consumes ~2.4mA after 'shutdown'.
(It is in fact in the state TRXOFF as it woke up again)
With this change the radio should consume less than 1µA (DEEP SLEEP).
diff --git a/examples/gnrc_networking/main.c b/examples/gnrc_networking/main.c
index 6301f4291d..93b96eb939 100644
--- a/examples/gnrc_networking/main.c
+++ b/examples/gnrc_networking/main.c
@@ -23,12 +23,47 @@
#include "shell.h"
#include "msg.h"
+#include "periph/pm.h"
+
+#include "net/netopt.h"
+#include "net/gnrc/netif.h"
+
#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
extern int udp_cmd(int argc, char **argv);
+extern void send(char *addr_str, char *port_str, char *data, unsigned int num,
+ unsigned int delay);
+
+static int send_and_shutdown(int argc, char **argv)
+{
+ (void) argc;
+ (void) argv;
+
+ /* the address must not exist */
+ char addr[] = "fe80::2068:3123:59f5:d238%7";
+ char port[] = "1234";
+ char data[] = "Hello World!";
+
+ send(addr, port, data, 1, 0);
+
+ /* disable radio */
+ gnrc_netif_t* netif = NULL;
+ netopt_state_t state = NETOPT_STATE_SLEEP;
+ while ((netif = gnrc_netif_iter(netif))) {
+ /* retry while busy */
+ while (gnrc_netapi_set(netif->pid, NETOPT_STATE, 0, &state,
+ sizeof(netopt_state_t)) == -EBUSY);
+ }
+
+ pm_set(0);
+
+ return 0;
+}
+
static const shell_command_t shell_commands[] = {
+ { "shutdown", "turn off the radio & shut down", send_and_shutdown },
{ "udp", "send data over UDP and listen on UDP ports", udp_cmd },
{ NULL, NULL, NULL }
};
diff --git a/examples/gnrc_networking/udp.c b/examples/gnrc_networking/udp.c
index e8a559846e..cb80855b76 100644
--- a/examples/gnrc_networking/udp.c
+++ b/examples/gnrc_networking/udp.c
@@ -36,7 +36,7 @@ static gnrc_netreg_entry_t server = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX
KERNEL_PID_UNDEF);
-static void send(char *addr_str, char *port_str, char *data, unsigned int num,
+void send(char *addr_str, char *port_str, char *data, unsigned int num,
unsigned int delay)
{
gnrc_netif_t *netif = NULL;
2020-04-01 22:57:18 +02:00
|
|
|
if (_is_busy(dev)) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
|
|
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
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:
|
2021-02-26 11:23:45 +01:00
|
|
|
*((netopt_enable_t *)val) = NETOPT_ENABLE;
|
2019-12-13 17:47:03 +01:00
|
|
|
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));
|
2020-03-23 22:44:03 +01:00
|
|
|
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_OQPSK) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
((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;
|
|
|
|
|
2021-06-21 17:01:25 +02:00
|
|
|
if (((res = netdev_ieee802154_get(container_of(netdev, netdev_ieee802154_t, netdev),
|
|
|
|
opt, val, max_len)) >= 0)
|
|
|
|
|| (res != -ENOTSUP)) {
|
2019-12-13 17:47:03 +01:00
|
|
|
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;
|
|
|
|
|
2020-03-23 22:39:35 +01:00
|
|
|
case NETOPT_IEEE802154_PHY:
|
|
|
|
assert(max_len >= sizeof(int8_t));
|
|
|
|
*((int8_t *)val) = at86rf215_get_phy_mode(dev);
|
|
|
|
res = max_len;
|
|
|
|
break;
|
2020-06-13 18:03:32 +02:00
|
|
|
#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;
|
2020-03-23 22:39:35 +01:00
|
|
|
|
2020-06-13 18:03:32 +02:00
|
|
|
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 */
|
2020-06-03 17:21:22 +02:00
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
|
2020-05-04 03:25:22 +02:00
|
|
|
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;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
|
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK
|
2020-03-23 22:44:03 +01:00
|
|
|
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;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
|
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
|
2020-03-23 22:44:03 +01:00
|
|
|
case NETOPT_OQPSK_RATE:
|
|
|
|
assert(max_len >= sizeof(int8_t));
|
|
|
|
*((int8_t *)val) = at86rf215_OQPSK_get_mode_legacy(dev);
|
|
|
|
res = max_len;
|
|
|
|
break;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
|
2019-12-13 17:47:03 +01:00
|
|
|
default:
|
|
|
|
res = -ENOTSUP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
|
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
|
|
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
|
2019-12-13 17:47:03 +01:00
|
|
|
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;
|
|
|
|
|
2020-09-07 23:37:06 +02:00
|
|
|
#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
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
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;
|
|
|
|
|
2020-03-23 22:39:35 +01:00
|
|
|
case NETOPT_IEEE802154_PHY:
|
|
|
|
assert(len <= sizeof(uint8_t));
|
|
|
|
switch (*(uint8_t *)val) {
|
2020-06-03 17:21:22 +02:00
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
|
2020-03-23 22:39:35 +01:00
|
|
|
case IEEE802154_PHY_OQPSK:
|
|
|
|
at86rf215_configure_legacy_OQPSK(dev, at86rf215_OQPSK_get_mode_legacy(dev));
|
|
|
|
res = sizeof(uint8_t);
|
|
|
|
break;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
|
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK
|
2020-03-23 22:44:03 +01:00
|
|
|
case IEEE802154_PHY_MR_OQPSK:
|
|
|
|
at86rf215_configure_OQPSK(dev,
|
|
|
|
at86rf215_OQPSK_get_chips(dev),
|
|
|
|
at86rf215_OQPSK_get_mode(dev));
|
|
|
|
res = sizeof(uint8_t);
|
|
|
|
break;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
|
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
|
2020-05-04 03:25:22 +02:00
|
|
|
case IEEE802154_PHY_MR_OFDM:
|
|
|
|
at86rf215_configure_OFDM(dev,
|
|
|
|
at86rf215_OFDM_get_option(dev),
|
|
|
|
at86rf215_OFDM_get_scheme(dev));
|
|
|
|
res = sizeof(uint8_t);
|
|
|
|
break;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
|
2020-06-13 18:03:32 +02:00
|
|
|
#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 */
|
2020-03-23 22:39:35 +01:00
|
|
|
default:
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
2020-03-23 22:44:03 +01:00
|
|
|
break;
|
|
|
|
|
2020-06-13 18:03:32 +02:00
|
|
|
#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 */
|
2020-06-03 17:21:22 +02:00
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
|
2020-05-04 03:25:22 +02:00
|
|
|
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;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
|
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK
|
2020-03-23 22:44:03 +01:00
|
|
|
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;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
|
|
|
|
#ifdef MODULE_NETDEV_IEEE802154_OQPSK
|
2020-03-23 22:44:03 +01:00
|
|
|
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;
|
2020-06-03 17:21:22 +02:00
|
|
|
#endif /* MODULE_NETDEV_IEEE802154_OQPSK */
|
2019-12-13 17:47:03 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res == -ENOTSUP) {
|
2021-06-21 17:01:25 +02:00
|
|
|
res = netdev_ieee802154_set(container_of(netdev, netdev_ieee802154_t, netdev),
|
|
|
|
opt, val, len);
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-11-15 14:08:44 +01:00
|
|
|
static void _tx_end(at86rf215_t *dev)
|
2019-12-13 17:47:03 +01:00
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_t *netdev = &dev->netdev.netdev;
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2021-02-26 11:23:45 +01:00
|
|
|
if (netdev->event_callback) {
|
2024-05-15 17:23:52 +02:00
|
|
|
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dev->timeout = 0;
|
|
|
|
dev->state = AT86RF215_STATE_IDLE;
|
|
|
|
}
|
|
|
|
|
2024-11-15 14:08:44 +01:00
|
|
|
static void __tx_end_timeout(at86rf215_t *dev)
|
|
|
|
{
|
|
|
|
/* signal error to confirm_send */
|
|
|
|
dev->tx_frame_len = (int16_t)-EHOSTUNREACH;
|
|
|
|
|
|
|
|
_tx_end(dev);
|
|
|
|
}
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
static void _ack_timeout_cb(void* arg) {
|
|
|
|
at86rf215_t *dev = arg;
|
2021-08-16 15:28:06 +02:00
|
|
|
netdev_t *netdev = &dev->netdev.netdev;
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
dev->timeout = AT86RF215_TIMEOUT_ACK;
|
2021-08-16 15:28:06 +02:00
|
|
|
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void _backoff_timeout_cb(void* arg) {
|
|
|
|
at86rf215_t *dev = arg;
|
2021-08-16 15:28:06 +02:00
|
|
|
netdev_t *netdev = &dev->netdev.netdev;
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
dev->timeout = AT86RF215_TIMEOUT_CSMA;
|
2021-08-16 15:28:06 +02:00
|
|
|
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
2024-11-15 14:08:44 +01:00
|
|
|
__tx_end_timeout(dev);
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_t *netdev = &dev->netdev.netdev;
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
/* 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);
|
2020-09-07 01:03:39 +02:00
|
|
|
at86rf215_enable_rpc(dev);
|
2019-12-13 17:47:03 +01:00
|
|
|
at86rf215_tx_done(dev);
|
|
|
|
|
2024-05-15 17:23:52 +02:00
|
|
|
/* signal error to confirm_send */
|
|
|
|
dev->tx_frame_len = (int16_t)-EBUSY;
|
|
|
|
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev_ieee802154_t *netdev_ieee802154 = container_of(netdev, netdev_ieee802154_t, netdev);
|
|
|
|
at86rf215_t *dev = container_of(netdev_ieee802154, at86rf215_t, netdev);
|
2019-12-13 17:47:03 +01:00
|
|
|
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 */
|
2021-02-26 11:23:45 +01:00
|
|
|
bb_irqs_enabled |= BB_IRQ_RXAM;
|
2019-12-13 17:47:03 +01:00
|
|
|
|
|
|
|
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) {
|
2021-06-21 17:01:25 +02:00
|
|
|
netdev->event_callback(&dev->sibling->netdev.netdev, NETDEV_EVENT_ISR);
|
2019-12-13 17:47:03 +01:00
|
|
|
} else {
|
|
|
|
_clear_sibling_irq(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-07 23:37:06 +02:00
|
|
|
/* 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
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2020-06-13 18:03:32 +02:00
|
|
|
#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 */
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
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);
|
2020-09-07 01:03:39 +02:00
|
|
|
at86rf215_disable_rpc(dev);
|
2019-12-13 17:47:03 +01:00
|
|
|
/* 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) {
|
|
|
|
|
2020-06-13 18:03:32 +02:00
|
|
|
#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 */
|
|
|
|
|
2019-12-13 17:47:03 +01:00
|
|
|
/* 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? */
|
2021-02-26 11:23:45 +01:00
|
|
|
if (netdev->event_callback) {
|
2019-12-13 17:47:03 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-02-26 11:23:45 +01:00
|
|
|
if ((bb_irq_mask & BB_IRQ_RXAM) && netdev->event_callback) {
|
2019-12-13 17:47:03 +01:00
|
|
|
/* 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;
|
|
|
|
|
2021-02-26 11:23:45 +01:00
|
|
|
if (netdev->event_callback) {
|
2019-12-13 17:47:03 +01:00
|
|
|
/* 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 {
|
2024-11-15 14:08:44 +01:00
|
|
|
_tx_end(dev);
|
2019-12-13 17:47:03 +01:00
|
|
|
}
|
|
|
|
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);
|
2024-11-15 14:08:44 +01:00
|
|
|
_tx_end(dev);
|
2019-12-13 17:47:03 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|