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

668 lines
25 KiB
C
Raw Normal View History

/*
* Copyright (C) 2019 Otto-von-Guericke-Universität Magdeburg
*
* 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_nrf24l01p_ng
* @{
*
* @file
* @brief Implementation of RIOT's netdev_driver API
* for the NRF24L01+ (NG) transceiver
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
* @}
*/
#include <errno.h>
#include <string.h>
#include <assert.h>
#define ENABLE_DEBUG 0
#include "debug.h"
#include "kernel_defines.h"
#include "iolist.h"
#include "irq.h"
#include "net/netdev.h"
#include "xtimer.h"
#include "gnrc_netif_nrf24l01p_ng.h"
#include "nrf24l01p_ng_constants.h"
#include "nrf24l01p_ng_registers.h"
#include "nrf24l01p_ng_communication.h"
#include "nrf24l01p_ng_states.h"
#include "nrf24l01p_ng_netdev.h"
#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS)
#include "nrf24l01p_ng_diagnostics.h"
#endif
#define NRF24L01P_NG_FLG_IRQ \
(NRF24L01P_NG_FLG_MAX_RT | NRF24L01P_NG_FLG_TX_DS | NRF24L01P_NG_FLG_RX_DR)
static int _init(netdev_t *netdev);
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
static int _send(netdev_t *netdev, const iolist_t *iolist);
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 max_len);
const netdev_driver_t nrf24l01p_ng_driver = {
.init = _init,
.recv = _recv,
.send = _send,
.isr = _isr,
.get = _get,
.set = _set
};
static inline
void _trigger_send(const nrf24l01p_ng_t *dev)
{
gpio_set(dev->params.pin_ce);
xtimer_usleep(NRF24L01P_NG_DELAY_US_CE_HIGH_PULSE);
gpio_clear(dev->params.pin_ce);
xtimer_usleep(NRF24L01P_NG_DELAY_US_TX_SETTLING);
}
static int _assert_awake(const nrf24l01p_ng_t *dev)
{
return nrf24l01p_ng_reg8_read(dev, NRF24L01P_NG_REG_CONFIG) &
NRF24L01P_NG_FLG_PWR_UP;
}
static netopt_state_t _state_to_netif(nrf24l01p_ng_state_t state)
{
if (state == NRF24L01P_NG_STATE_POWER_DOWN) {
return NETOPT_STATE_SLEEP;
}
if (state == NRF24L01P_NG_STATE_STANDBY_1) {
return NETOPT_STATE_STANDBY;
}
if (state == NRF24L01P_NG_STATE_STANDBY_2) {
return NETOPT_STATE_TX;
}
if (state == NRF24L01P_NG_STATE_TX_MODE) {
return NETOPT_STATE_TX;
}
if (state == NRF24L01P_NG_STATE_RX_MODE) {
return NETOPT_STATE_RX;
}
return NETOPT_STATE_OFF; /* error */
}
nrf24l01p_ng_state_t _state_from_netif(netopt_state_t state)
{
if (state == NETOPT_STATE_SLEEP) {
return NRF24L01P_NG_STATE_POWER_DOWN;
}
if (state == NETOPT_STATE_STANDBY) {
return NRF24L01P_NG_STATE_STANDBY_1;
}
if (state == NETOPT_STATE_TX) {
return NRF24L01P_NG_STATE_TX_MODE;
}
if (state == NETOPT_STATE_RX) {
return NRF24L01P_NG_STATE_RX_MODE;
}
return NRF24L01P_NG_STATE_UNDEFINED;
}
static void _nrf24l01p_ng_irq_handler(void *_dev)
{
nrf24l01p_ng_t *dev = _dev;
/* Once the IRQ pin has triggered,
do not congest the thread´s
message queue with IRQ events */
gpio_irq_disable(dev->params.pin_irq);
netdev_trigger_event_isr(&dev->netdev);
}
static void _isr_max_rt(nrf24l01p_ng_t *dev)
{
assert(dev->state == NRF24L01P_NG_STATE_STANDBY_1 ||
dev->state == NRF24L01P_NG_STATE_STANDBY_2 ||
dev->state == NRF24L01P_NG_STATE_RX_MODE ||
dev->state == NRF24L01P_NG_STATE_TX_MODE);
DEBUG_PUTS("[nrf24l01p_ng] IRS MAX_RT");
nrf24l01p_ng_acquire(dev);
nrf24l01p_ng_flush_tx(dev);
nrf24l01p_ng_release(dev);
dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_TX_NOACK);
}
static void _isr_rx_dr(nrf24l01p_ng_t *dev)
{
assert(dev->state == NRF24L01P_NG_STATE_STANDBY_1 ||
dev->state == NRF24L01P_NG_STATE_STANDBY_2 ||
dev->state == NRF24L01P_NG_STATE_RX_MODE ||
dev->state == NRF24L01P_NG_STATE_TX_MODE);
DEBUG_PUTS("[nrf24l01p_ng] IRS RX_DR");
/* read all RX data */
nrf24l01p_ng_acquire(dev);
while (!(nrf24l01p_ng_reg8_read(dev, NRF24L01P_NG_REG_FIFO_STATUS) &
NRF24L01P_NG_FLG_RX_EMPTY)) {
DEBUG_PUTS("[nrf24l01p_ng] ISR: read pending Rx frames");
dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_RX_COMPLETE);
}
nrf24l01p_ng_release(dev);
}
static void _isr_tx_ds(nrf24l01p_ng_t *dev)
{
assert(dev->state == NRF24L01P_NG_STATE_STANDBY_1 ||
dev->state == NRF24L01P_NG_STATE_STANDBY_2 ||
dev->state == NRF24L01P_NG_STATE_RX_MODE ||
dev->state == NRF24L01P_NG_STATE_TX_MODE);
DEBUG_PUTS("[nrf24l01p_ng] IRS TX_DS");
dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_TX_COMPLETE);
}
static int _init(netdev_t *netdev)
{
nrf24l01p_ng_t *dev = container_of(netdev, nrf24l01p_ng_t, netdev);
if (dev->params.config.cfg_data_rate >= NRF24L01P_NG_RF_DR_NUM_OF ||
dev->params.config.cfg_crc == NRF24L01P_NG_CRC_0BYTE ||
dev->params.config.cfg_channel >= NRF24L01P_NG_NUM_CHANNELS) {
return -ENOTSUP;
}
if (spi_init_cs(dev->params.spi, dev->params.pin_cs) != SPI_OK) {
DEBUG_PUTS("[nrf24l01p_ng] _init(): spi_init_cs() failed");
return -EIO;
}
if (gpio_init(dev->params.pin_ce, GPIO_OUT) < 0) {
DEBUG_PUTS("[nrf24l01p_ng] _init(): gpio_init() failed");
return -EIO;
}
gpio_clear(dev->params.pin_ce);
2021-02-01 14:25:22 +01:00
nrf24l01p_ng_acquire(dev);
if (dev->state != NRF24L01P_NG_STATE_POWER_DOWN) {
nrf24l01p_ng_transition_to_power_down(dev);
}
/* flush internal Tx and Rx FIFO */
nrf24l01p_ng_flush_tx(dev);
nrf24l01p_ng_flush_rx(dev);
uint8_t aw = NRF24L01P_NG_ADDR_WIDTH;
const uint8_t bc[] = NRF24L01P_NG_BROADCAST_ADDR;
memcpy(NRF24L01P_NG_ADDR_P0(dev), bc, aw);
/* assign to pipe 0 the broadcast address*/
nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P0,
NRF24L01P_NG_ADDR_P0(dev), aw);
/* "The LSByte must be unique for all six pipes" [datasheet p.38] */
nrf24l01p_ng_eui_get(&dev->netdev, NRF24L01P_NG_ADDR_P1(dev));
assert(NRF24L01P_NG_ADDR_P1(dev)[aw - 1] != NRF24L01P_NG_ADDR_P0(dev)[aw - 1]);
/* assign to pipe 1 the "main" listening address */
nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P1,
NRF24L01P_NG_ADDR_P1(dev), aw);
/* set the address width */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_SETUP_AW,
NRF24L01P_NG_FLG_AW(nrf24l01p_ng_valtoe_aw(aw)));
/* set Tx power and Tx data rate */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_RF_SETUP,
NRF24L01P_NG_FLG_RF_DR(dev->params.config.cfg_data_rate) |
NRF24L01P_NG_FLG_RF_PWR(dev->params.config.cfg_tx_power));
/* set retransmission delay and the maximum number of retransmisisons */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_SETUP_RETR,
NRF24L01P_NG_FLG_ARD(dev->params.config.cfg_retr_delay) |
NRF24L01P_NG_FLG_ARC(dev->params.config.cfg_max_retr));
/* set the radio channel */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_RF_CH,
NRF24L01P_NG_FLG_RF_CH(dev->params.config.cfg_channel));
/* enable pipe 0 and pipe 1 */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_EN_RXADDR,
NRF24L01P_NG_FLG_ERX_P0 |
NRF24L01P_NG_FLG_ERX_P1);
/* set CRC length */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_CONFIG,
NRF24L01P_NG_FLG_CRCO(dev->params.config.cfg_crc));
/* enable NRF24L01+ features:
automatic acknowledgements,
dynamic payload lengths,
piggyback acknowledgements */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_FEATURES,
NRF24L01P_NG_FLG_EN_DYN_ACK |
NRF24L01P_NG_FLG_EN_DPL |
NRF24L01P_NG_FLG_EN_ACK_PAY);
/* enable automatic acknowledgements for pipe 0 and pipe 1 */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_EN_AA,
NRF24L01P_NG_FLG_ENAA_P0 |
NRF24L01P_NG_FLG_ENAA_P1);
/* enable dynamic payload lengths for pipe 0 and pipe 1 */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_DYNPD,
NRF24L01P_NG_FLG_DPL_P0 |
NRF24L01P_NG_FLG_DPL_P1);
/* clear interrupts */
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_STATUS, NRF24L01P_NG_FLG_IRQ);
/* go to standby */
nrf24l01p_ng_transition_to_standby_1(dev);
#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) && ENABLE_DEBUG
nrf24l01p_ng_diagnostics_print_all_regs(dev);
nrf24l01p_ng_diagnostics_print_dev_info(dev);
#endif
/* check if the transceiver responds */
if (!_assert_awake(dev)) {
nrf24l01p_ng_release(dev);
return -ENODEV;
}
/* go to listening state */
nrf24l01p_ng_transition_to_rx_mode(dev);
nrf24l01p_ng_release(dev);
/* enable interrupt pins */
if (gpio_init_int(dev->params.pin_irq, GPIO_IN, GPIO_FALLING,
_nrf24l01p_ng_irq_handler, dev) < 0) {
DEBUG_PUTS("[nrf24l01p_ng] _init(): gpio_init_int() failed");
return -EIO;
}
/* signal link UP */
netdev->event_callback(netdev, NETDEV_EVENT_LINK_UP);
return 0;
}
/**
* @brief NRF24L01+ @ref netdev_driver_t::recv routine
*
* @pre @see nrf24l01p_ng_acquire must have been called before.
* @pre Interrupts should be disabled
*
* The SPI bus is not acquired in this function because it is called from
* @ref netdev_driver_t::isr, possibly for multiple times. If another
* device acquired the SPI bus within the ISR, the ISR would block
* until that device releases the bus.
*
* @param[in] netdev Abstract network device handle
* @param[out] buf Rx buffer
* @param[in] len Size of Rx buffer
* @param[out] LQI and RSSI information (unused)
*
* @return Size of received frame in @p buf
* @return Upper estimation of the frame width,
* if @p buf == NULL and len == 0
* @return Actual frame width,
* if @p buf == NULL and @p len != 0
* (frame is NOT dropped)
* @retval -ENOBUFS @p buf != NULL and @p len < actual frame width
* (frame is dropped)
* @retval -EINVAL @p buf == NULL
* (and none of the above cases are true)
* @retval -ENOTSUP Malformed header
* @retval 0 No data to read from Rx FIFO
*/
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
(void)info; /* nrf24l01+ supports neither lqi nor rssi */
/* return upper estaimation bound of frame size */
if (!buf && !len) {
DEBUG_PUTS("[nrf24l01p_ng] Return upper frame estimation");
return NRF24L01P_NG_ADDR_WIDTH + NRF24L01P_NG_MAX_PAYLOAD_WIDTH;
}
nrf24l01p_ng_t *dev = container_of(netdev, nrf24l01p_ng_t, netdev);
uint8_t pl_width;
uint8_t status = nrf24l01p_ng_read_rx_pl_width(dev, &pl_width);
uint8_t pno = NRF24L01P_NG_VAL_RX_P_NO(status);
if (!pl_width ||
pl_width > NRF24L01P_NG_MAX_PAYLOAD_WIDTH ||
pno >= NRF24L01P_NG_PX_NUM_OF) {
DEBUG_PUTS("[nrf24l01p_ng] RX error, flush RX FIFO");
/* In some rare cases the RX payload width (R_RX_PL_WID) exceeds
the maximum of 32 bytes. In that case it must be flushed.
See https://devzone.nordicsemi.com/f/nordic-q-a/26489/nrf24l01-the-length-of-received-data-exceed-32
and https://www.mikrocontroller.net/articles/NRF24L01_Tutorial */
nrf24l01p_ng_flush_rx(dev);
return 0;
}
uint8_t frame_len = NRF24L01P_NG_ADDR_WIDTH + pl_width;
/* do NOT drop frame and return exact frame size */
if (!buf) {
DEBUG_PUTS("[nrf24l01p_ng] Return exact frame length");
return frame_len;
}
/* drop frame, content in buf becomes invalid and return -ENOBUFS */
if (len < frame_len) {
DEBUG("[nrf24l01p_ng] Buffer too small: %u < %u, dropping frame\n",
len, frame_len);
uint8_t garbage[pl_width];
nrf24l01p_ng_read_rx_payload(dev, garbage, pl_width);
return -ENOBUFS;
}
/* get received frame */
uint8_t dst_addr[NRF24L01P_NG_ADDR_WIDTH];
if (pno == NRF24L01P_NG_P0) {
memcpy(dst_addr, NRF24L01P_NG_ADDR_P0(dev), sizeof(dst_addr));
}
else {
memcpy(dst_addr, NRF24L01P_NG_ADDR_P1(dev), sizeof(dst_addr));
if (pno > NRF24L01P_NG_P1) {
dst_addr[NRF24L01P_NG_ADDR_WIDTH - 1] =
NRF24L01P_NG_ADDR_PX_LSB(dev, pno);
}
}
DEBUG_PUTS("[nrf24l01p_ng] Handle received frame");
uint8_t *frame = (uint8_t *)buf;
memcpy(frame, dst_addr, sizeof(dst_addr));
frame += sizeof(dst_addr);
nrf24l01p_ng_read_rx_payload(dev, frame, pl_width);
#if IS_USED(MODULE_NRF24L01P_NG_DIAGNOSTICS) && ENABLE_DEBUG
nrf24l01p_ng_diagnostics_print_frame(dev, (uint8_t *)buf, frame_len);
#endif
DEBUG("[nrf24l01p_ng] Received frame length: %u\n", frame_len);
return (int)frame_len;
}
/**
* @brief NRF24L01+ @ref netdev_driver_t::send routine
*
* @param[in] netdev Abstract network device handle
* @param[in] iolist Linked list of data to be sent, where
* the base must be the destination address
*
* @return Size of sent payload
* @retval -ENOTSUP @p iolist had no base and no next link,
* or address was too big, or too short
* @retval -EAGAIN Pending interrupts have been handled first
* @retval -EBUSY The internal Tx FIFO is full
* @retval -E2BIG Resulting frame from iolist was too big to be sent
*/
static int _send(netdev_t *netdev, const iolist_t *iolist)
{
assert(netdev && iolist);
if (!(iolist->iol_base) || !(iolist->iol_next)) {
DEBUG_PUTS("[nrf24l01p_ng] No Tx address or no payload");
return -ENOTSUP;
}
nrf24l01p_ng_t *dev = container_of(netdev, nrf24l01p_ng_t, netdev);
uint8_t pl_width = 0;
const uint8_t bcast_addr[] = NRF24L01P_NG_BROADCAST_ADDR;
uint8_t payload[NRF24L01P_NG_MAX_PAYLOAD_WIDTH];
nrf24l01p_ng_acquire(dev);
uint8_t fifo_status;
uint8_t status =
nrf24l01p_ng_read_reg(dev, NRF24L01P_NG_REG_FIFO_STATUS,
&fifo_status, 1);
if (status & NRF24L01P_NG_FLG_IRQ) {
DEBUG_PUTS("[nrf24l01p_ng] Handle pending IRQ, before sending new data");
nrf24l01p_ng_release(dev);
_isr(&dev->netdev);
return -EAGAIN;
}
if (fifo_status & NRF24L01P_NG_FLG_TX_FULL_) {
/* If the TX FIFO is full, but no ACK has arrived yet,
so no TX_DS / MAX_RT interrupt has triggered so far that
could clean the TX FIFO. So we need to wait until an interrupt
occurs, before we can send a new frame. This is done
while this _send() function is called in a loop and the
interrupt status is polled.
If you flush the FIFO here, pending content will
be lost. */
DEBUG_PUTS("[nrf24l01p_ng] TX FIFO full");
nrf24l01p_ng_release(dev);
return -EBUSY; /* for gnrc_netif_pktq */
}
uint8_t *dst_addr = iolist->iol_base;
uint8_t dst_addr_len = iolist->iol_len;
if (dst_addr_len > NRF24L01P_NG_MAX_ADDR_WIDTH ||
dst_addr_len < NRF24L01P_NG_MIN_ADDR_WIDTH) {
nrf24l01p_ng_release(dev);
DEBUG("[nrf24l01p_ng] Destination address has an invalid length: %u\n",
dst_addr_len);
return -ENOTSUP;
}
for (const iolist_t *iol = iolist->iol_next; iol; iol = iol->iol_next) {
if (pl_width + iol->iol_len > sizeof(payload)) {
nrf24l01p_ng_release(dev);
DEBUG_PUTS("[nrf24l01p_ng] frame too big");
return -E2BIG;
}
memcpy(payload + pl_width, iol->iol_base, iol->iol_len);
pl_width += iol->iol_len;
}
nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_TX_ADDR,
dst_addr, dst_addr_len);
if (!memcmp(dst_addr, bcast_addr, dst_addr_len)) {
/* do not expect ACK for broadcast */
nrf24l01p_ng_write_tx_pl_no_ack(dev, payload, pl_width);
}
else {
nrf24l01p_ng_write_tx_payload(dev, payload, pl_width);
/* A PTX node must change pipe 0 Rx address to Tx address
* in order to receive ACKs.
* If node switches back to Rx mode, pipe 0 Rx address
* must be restored from params. */
nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_RX_ADDR_P0,
dst_addr, dst_addr_len);
nrf24l01p_ng_reg8_write(dev, NRF24L01P_NG_REG_SETUP_AW,
NRF24L01P_NG_FLG_AW(nrf24l01p_ng_valtoe_aw(dst_addr_len)));
}
if (dev->state != NRF24L01P_NG_STATE_TX_MODE &&
dev->state != NRF24L01P_NG_STATE_STANDBY_2) {
dev->idle_state = dev->state;
if (dev->state != NRF24L01P_NG_STATE_STANDBY_1) {
nrf24l01p_ng_transition_to_standby_1(dev);
}
nrf24l01p_ng_transition_to_tx_mode(dev);
}
nrf24l01p_ng_release(dev);
_trigger_send(dev);
DEBUG("[nrf24l01p_ng] Sending %u bytes\n", pl_width);
return (int)pl_width;
}
/**
* @brief NRF24L01+ @ref netdev_driver_t::isr
*
* @param[in] netdev Abstract network device
*/
static void _isr(netdev_t *netdev)
{
nrf24l01p_ng_t *dev = container_of(netdev, nrf24l01p_ng_t, netdev);
nrf24l01p_ng_acquire(dev);
gpio_irq_enable(dev->params.pin_irq);
uint8_t status = nrf24l01p_ng_get_status(dev);
/* clear interrupt flags */
nrf24l01p_ng_write_reg(dev, NRF24L01P_NG_REG_STATUS, &status, 1);
nrf24l01p_ng_release(dev);
if (status & NRF24L01P_NG_FLG_RX_DR) {
_isr_rx_dr(dev);
}
if (status & NRF24L01P_NG_FLG_MAX_RT) {
_isr_max_rt(dev);
}
if (status & NRF24L01P_NG_FLG_TX_DS) {
_isr_tx_ds(dev);
}
nrf24l01p_ng_acquire(dev);
if (dev->state == NRF24L01P_NG_STATE_TX_MODE ||
dev->state == NRF24L01P_NG_STATE_STANDBY_2) {
/* frame in FIFO is not an ACK */
if (!(nrf24l01p_ng_reg8_read(dev, NRF24L01P_NG_REG_FIFO_STATUS) &
NRF24L01P_NG_FLG_TX_EMPTY)) {
nrf24l01p_ng_release(dev);
_trigger_send(dev);
return;
}
}
/* no more data to transmit */
if (dev->state != NRF24L01P_NG_STATE_STANDBY_1) {
nrf24l01p_ng_transition_to_standby_1(dev);
}
/* go to idle state */
if (dev->idle_state != NRF24L01P_NG_STATE_STANDBY_1) {
if (dev->idle_state == NRF24L01P_NG_STATE_POWER_DOWN) {
nrf24l01p_ng_transition_to_power_down(dev);
}
else {
dev->idle_state = NRF24L01P_NG_STATE_RX_MODE;
nrf24l01p_ng_transition_to_rx_mode(dev);
}
}
nrf24l01p_ng_release(dev);
}
/**
* @brief @ref netdev_driver_t::get
*
* @param[in] netdev Abstract network device
* @param[in] opt netdev option type
* @param[out] val Option value
* @param[in] max_len Maximum option length
*
* @return Size of written option value
* @retval -ENOTSUP Unsupported netdev option @p opt
*/
static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
nrf24l01p_ng_t *dev = container_of(netdev, nrf24l01p_ng_t, netdev);
(void)max_len; /* only used in assert() */
switch (opt) {
case NETOPT_ADDR_LEN:
case NETOPT_SRC_LEN: {
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = NRF24L01P_NG_ADDR_WIDTH;
return sizeof(uint16_t);
} break;
case NETOPT_ADDRESS: {
assert(max_len >= NRF24L01P_NG_ADDR_WIDTH);
memcpy(val, NRF24L01P_NG_ADDR_P1(dev), NRF24L01P_NG_ADDR_WIDTH);
return NRF24L01P_NG_ADDR_WIDTH;
} break;
case NETOPT_AUTOACK: {
assert(max_len == sizeof(netopt_enable_t));
/* mandatory for Enhanced ShockBurst */
*((netopt_enable_t *)val) = NETOPT_ENABLE;
return sizeof(netopt_enable_t);
} break;
case NETOPT_CHANNEL: {
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = (uint16_t)nrf24l01p_ng_get_channel(dev);
return sizeof(uint16_t);
} break;
case NETOPT_CHECKSUM:
case NETOPT_INTEGRITY_CHECK: {
assert(max_len == sizeof(netopt_enable_t));
/* mandatory for Enhanced ShockBurst */
*((netopt_enable_t *)val) = NETOPT_ENABLE;
return sizeof(netopt_enable_t);
} break;
case NETOPT_DEVICE_TYPE: {
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = NETDEV_TYPE_NRF24L01P_NG;
return sizeof(uint16_t);
} break;
case NETOPT_PROTO: {
assert(max_len == sizeof(gnrc_nettype_t));
*((gnrc_nettype_t *)val) = NRF24L01P_NG_UPPER_LAYER_PROTOCOL;
return sizeof(gnrc_nettype_t);
} break;
case NETOPT_MAX_PDU_SIZE: {
assert(max_len == sizeof(uint16_t));
*((uint16_t *)val) = NRF24L01P_NG_MAX_PAYLOAD_WIDTH -
NRF24L01P_NG_ADDR_WIDTH
- 1;
return sizeof(uint16_t);
}
case NETOPT_RETRANS: {
assert(max_len == sizeof(uint8_t));
*((uint8_t *)val) = nrf24l01p_ng_get_max_retransm(dev);
return sizeof(uint8_t);
}
case NETOPT_RX_TIMEOUT: {
assert(max_len == sizeof(uint32_t));
*((uint32_t *)val) =
(uint32_t)nrf24l01p_ng_get_retransm_delay(dev, NULL);
return sizeof(uint32_t);
} break;
case NETOPT_STATE: {
assert(max_len == sizeof(netopt_state_t));
*((netopt_state_t *)val) = _state_to_netif(dev->state);
return sizeof(netopt_state_t);
} break;
case NETOPT_TX_POWER: {
assert(max_len == sizeof(int16_t));
*((int16_t *)val) = (int16_t)nrf24l01p_ng_get_tx_power(dev, NULL);
return sizeof(uint16_t);
} break;
default:
DEBUG("[nrf24l01p_ng] Unsupported netdev option %d\n", opt);
return -ENOTSUP;
}
}
/**
* @brief @ref netdev_driver_t::set
*
* @param[in] netdev Abstract network device handle
* @param[in] opt netdev option type
* @param[in] val Option value
* @param[in] len Size of option value
*
* @return Size of written option value
* @return negative number, on failure
* @retval -ENOTSUP Unsupported netdev option @p opt
*/
static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
{
nrf24l01p_ng_t *dev = container_of(netdev, nrf24l01p_ng_t, netdev);
switch (opt) {
case NETOPT_ADDRESS: {
/* common address length for all pipes */
assert(len == NRF24L01P_NG_ADDR_WIDTH);
int ret = nrf24l01p_ng_set_rx_address(dev, val, NRF24L01P_NG_P1);
return ret ? ret : (int)len;
} break;
case NETOPT_CHANNEL: {
assert(len == sizeof(uint16_t));
uint16_t ch = *((uint16_t *)val);
int ret = nrf24l01p_ng_set_channel(dev, (uint8_t)ch);
return ret ? ret : (int)sizeof(uint16_t);
} break;
case NETOPT_CHECKSUM:
case NETOPT_INTEGRITY_CHECK: {
assert(len == sizeof(netopt_enable_t));
nrf24l01p_ng_crc_t crc =
(*((netopt_enable_t *)val) == NETOPT_ENABLE)
? NRF24L01P_NG_CRC_2BYTE : NRF24L01P_NG_CRC_0BYTE;
int ret = nrf24l01p_ng_set_crc(dev, crc);
return ret ? ret : (int)sizeof(netopt_enable_t);
} break;
case NETOPT_RETRANS: {
assert(len == sizeof(uint8_t));
uint8_t n = *((uint8_t *)val);
int ret = nrf24l01p_ng_set_max_retransm(dev, n);
return ret ? ret : (int)sizeof(uint8_t);
} break;
case NETOPT_RX_TIMEOUT: {
assert(len == sizeof(uint32_t));
uint32_t us = *((uint32_t *)val);
nrf24l01p_ng_ard_t ard = nrf24l01p_ng_valtoe_ard(us);
int ret = nrf24l01p_ng_set_retransm_delay(dev, ard);
return ret ? ret : (int)sizeof(uint32_t);
} break;
case NETOPT_STATE: {
assert(len == sizeof(netopt_state_t));
nrf24l01p_ng_state_t s =
_state_from_netif(*((netopt_state_t *)val));
int ret = nrf24l01p_ng_set_state(dev, s);
return ret < 0 ? ret : (int)sizeof(netopt_state_t);
} break;
case NETOPT_TX_POWER: {
assert(len == sizeof(int16_t));
int16_t dbm = *((int16_t *)val);
nrf24l01p_ng_tx_power_t txp = nrf24l01p_ng_valtoe_tx_power(dbm);
int ret = nrf24l01p_ng_set_tx_power(dev, txp);
return ret ? ret : (int)sizeof(int16_t);
} break;
default:
DEBUG("[nrf24l01p_ng] Unsupported netdev option %d\n", opt);
return -ENOTSUP;
}
}