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
2022-09-16 22:57:28 +02:00

668 lines
25 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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

/*
* Copyright (C) 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);
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;
}
}