1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/nrf52/radio/nrf802154/nrf802154_radio.c
2024-08-28 12:39:15 +02:00

819 lines
24 KiB
C

/*
* Copyright (C) 2020 HAW Hamburg
*
* 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_nrf52_802154
* @{
*
* @file
* @brief Implementation of the IEEE 802.15.4 for nRF52 radios
*
* @author José I. Alamos <jose.alamos@haw-hamburg.de>
* @}
*/
#include <assert.h>
#include <string.h>
#include <errno.h>
#include "cpu.h"
#include "luid.h"
#include "nrf_clock.h"
#include "net/ieee802154.h"
#include "periph/timer.h"
#include "nrf802154.h"
#include "net/ieee802154/radio.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define ED_RSSISCALE (4U) /**< RSSI scale for internal HW value */
#define ED_RSSIOFFS (-92) /**< RSSI offset for internal HW value */
/* Set timer period to 16 us (IEEE 802.15.4 symbol time) */
#define TIMER_FREQ (62500UL)
#define TX_POWER_MIN (-40) /* in dBm */
#define TX_POWER_MAX ((int)RADIO_TXPOWER_TXPOWER_Max) /* in dBm */
/**
* @brief Default nrf802154 radio shortcuts
*
* With this configuration the radio goes on the RXREADY event to RXSTART
* and on TXREADY to TXSTART, without requiring to trigger the task manually.
*/
#define DEFAULT_SHORTS (RADIO_SHORTS_RXREADY_START_Msk | \
RADIO_SHORTS_TXREADY_START_Msk)
/**
* @brief nrf52840 shortcuts for CCA on send
*
* With this configuration the radio automatically triggers a CCA request on
* RXREADY event. If the CCA succeeds, the radio will automatically send the
* frame. Otherwise it will simply go to DISABLE.
*/
#define CCA_SHORTS (RADIO_SHORTS_RXREADY_CCASTART_Msk | \
RADIO_SHORTS_CCAIDLE_STOP_Msk | \
RADIO_SHORTS_CCAIDLE_TXEN_Msk | \
RADIO_SHORTS_CCABUSY_DISABLE_Msk | \
RADIO_SHORTS_TXREADY_START_Msk)
#define MAC_TIMER_CHAN_ACK (0U) /**< MAC timer channel for transmitting an ACK frame */
#define MAC_TIMER_CHAN_IFS (1U) /**< MAC timer channel for handling IFS logic */
static uint8_t rxbuf[IEEE802154_FRAME_LEN_MAX + 3]; /* len PHR + PSDU + LQI */
static uint8_t txbuf[IEEE802154_FRAME_LEN_MAX + 3]; /* len PHR + PSDU + LQI */
static uint8_t ack[IEEE802154_ACK_FRAME_LEN];
typedef enum {
STATE_IDLE,
STATE_TX,
STATE_ACK,
STATE_RX,
STATE_CCA_CLEAR,
STATE_CCA_BUSY,
} nrf802154_state_t;
static volatile uint8_t _state;
static uint8_t nrf802154_short_addr[IEEE802154_SHORT_ADDRESS_LEN];
static uint8_t nrf802154_long_addr[IEEE802154_LONG_ADDRESS_LEN];
static uint16_t nrf802154_pan_id;
static struct {
bool ifs : 1; /**< if true, the device is currently inside the IFS period */
bool cca_send : 1; /**< whether the next transmission uses CCA or not */
bool ack_filter : 1; /**< whether the ACK filter is activated or not */
bool promisc : 1; /**< whether the device is in promiscuous mode or not */
bool pending : 1; /**< whether there pending bit should be set in the ACK frame or not */
} cfg = {
.cca_send = true,
.ack_filter = true,
};
static const ieee802154_radio_ops_t nrf802154_ops;
static ieee802154_dev_t *nrf802154_hal_dev;
static void _power_on(void)
{
if (NRF_RADIO->POWER == 0) {
clock_hfxo_request();
NRF_RADIO->POWER = 1;
}
}
static void _power_off(void)
{
if (NRF_RADIO->POWER == 1) {
NRF_RADIO->POWER = 0;
clock_hfxo_release();
}
}
static bool _l2filter(uint8_t *mhr)
{
uint8_t dst_addr[IEEE802154_LONG_ADDRESS_LEN];
le_uint16_t dst_pan;
uint8_t pan_bcast[] = IEEE802154_PANID_BCAST;
int addr_len = ieee802154_get_dst(mhr, dst_addr, &dst_pan);
if ((mhr[0] & IEEE802154_FCF_TYPE_MASK) == IEEE802154_FCF_TYPE_BEACON) {
if ((memcmp(&nrf802154_pan_id, pan_bcast, 2) == 0)) {
return true;
}
}
/* filter PAN ID */
/* Will only work on little endian platform (all?) */
if ((memcmp(pan_bcast, dst_pan.u8, 2) != 0) &&
(memcmp(&nrf802154_pan_id, dst_pan.u8, 2) != 0)) {
return false;
}
/* check destination address */
if (((addr_len == IEEE802154_SHORT_ADDRESS_LEN) &&
(memcmp(nrf802154_short_addr, dst_addr, addr_len) == 0 ||
memcmp(ieee802154_addr_bcast, dst_addr, addr_len) == 0)) ||
((addr_len == IEEE802154_LONG_ADDRESS_LEN) &&
(memcmp(nrf802154_long_addr, dst_addr, addr_len) == 0))) {
return true;
}
return false;
}
static int _write(ieee802154_dev_t *dev, const iolist_t *iolist)
{
(void)dev;
DEBUG("[nrf802154] Send a packet\n");
assert(iolist);
/* copy packet data into the transmit buffer */
unsigned int len = 0;
/* Load packet data into FIFO. Size checks are handled by higher
* layers */
for (; iolist; iolist = iolist->iol_next) {
/* Check if there is data to copy, prevents undefined behaviour with
* memcpy when iolist->iol_base == NULL */
if (iolist->iol_len) {
memcpy(&txbuf[len + 1], iolist->iol_base, iolist->iol_len);
len += iolist->iol_len;
}
}
DEBUG("[nrf802154] send: putting %i bytes into the frame buffer\n", len);
/* specify the length of the package. */
txbuf[0] = len + IEEE802154_FCS_LEN;
return 0;
}
static void _disable(void)
{
/* set device into DISABLED state */
if (NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) {
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
DEBUG("[nrf802154] Device state: DISABLED\n");
}
}
static void _disable_blocking(void)
{
_disable();
/* This will take in worst case 21 us */
while (NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled) {};
}
static int _request_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx)
{
(void) dev;
int res = -EBUSY;
int state = STATE_IDLE;
switch (op) {
case IEEE802154_HAL_OP_TRANSMIT:
if (cfg.ifs) {
goto end;
}
NRF_RADIO->SHORTS = cfg.cca_send ? CCA_SHORTS : DEFAULT_SHORTS;
NRF_RADIO->TASKS_TXEN = 1;
state = STATE_TX;
break;
case IEEE802154_HAL_OP_SET_RX:
if (_state != STATE_IDLE && _state != STATE_RX) {
goto end;
}
_disable_blocking();
state = STATE_RX;
NRF_RADIO->PACKETPTR = (uint32_t) rxbuf;
NRF_RADIO->SHORTS = DEFAULT_SHORTS;
NRF_RADIO->TASKS_RXEN = 1;
break;
case IEEE802154_HAL_OP_SET_IDLE: {
assert(ctx);
bool force = *((bool*) ctx);
if (!force && _state != STATE_IDLE && _state != STATE_RX) {
goto end;
}
_disable_blocking();
NRF_RADIO->SHORTS = DEFAULT_SHORTS;
NRF_RADIO->PACKETPTR = (uint32_t) txbuf;
state = STATE_IDLE;
break;
}
case IEEE802154_HAL_OP_CCA:
_disable_blocking();
NRF_RADIO->SHORTS = RADIO_SHORTS_RXREADY_CCASTART_Msk;
NRF_RADIO->TASKS_RXEN = 1;
state = STATE_IDLE;
break;
default:
assert(false);
state = 0;
break;
}
_state = state;
res = 0;
end:
return res;
}
static int _confirm_op(ieee802154_dev_t *dev, ieee802154_hal_op_t op, void *ctx)
{
(void) dev;
bool eagain;
ieee802154_tx_info_t *info = ctx;
int state = _state;
bool enable_shorts = false;
int radio_state = NRF_RADIO->STATE;
switch (op) {
case IEEE802154_HAL_OP_TRANSMIT:
info = ctx;
eagain = (state != STATE_IDLE
&& state != STATE_CCA_BUSY && NRF_RADIO->STATE != RADIO_STATE_STATE_Disabled);
state = STATE_IDLE;
enable_shorts = true;
if (info) {
info->status = (_state == STATE_CCA_BUSY) ? TX_STATUS_MEDIUM_BUSY : TX_STATUS_SUCCESS;
}
break;
case IEEE802154_HAL_OP_SET_RX:
eagain = (radio_state == RADIO_STATE_STATE_RxRu);
break;
case IEEE802154_HAL_OP_SET_IDLE:
eagain = (radio_state == RADIO_STATE_STATE_TxDisable ||
radio_state == RADIO_STATE_STATE_RxDisable);
break;
case IEEE802154_HAL_OP_CCA:
eagain = (state != STATE_CCA_BUSY && state != STATE_CCA_CLEAR);
assert(ctx);
*((bool*) ctx) = (state == STATE_CCA_CLEAR) ? true : false;
state = STATE_IDLE;
break;
default:
eagain = false;
assert(false);
break;
}
if (eagain) {
return -EAGAIN;
}
_state = state;
if (enable_shorts) {
NRF_RADIO->SHORTS = DEFAULT_SHORTS;
DEBUG("[nrf802154] TX Finished\n");
}
return 0;
}
/**
* @brief Convert from the internal representation to dBm, when the
* radio operates as a IEEE802.15.4 transceiver.
*/
static inline int8_t _hwval_to_ieee802154_dbm(uint8_t hwval)
{
return (ED_RSSISCALE * hwval) + ED_RSSIOFFS;
}
static int _read(ieee802154_dev_t *dev, void *buf, size_t max_size,
ieee802154_rx_info_t *info)
{
(void) dev;
size_t pktlen = (size_t)rxbuf[0] - IEEE802154_FCS_LEN;
int res = -ENOBUFS;
if (max_size < pktlen) {
DEBUG("[nrf802154] recv: buffer is to small\n");
return res;
}
else {
DEBUG("[nrf802154] recv: reading packet of length %i\n", pktlen);
if (info != NULL) {
ieee802154_rx_info_t *radio_info = info;
/* Hardware link quality indicator */
uint8_t hwlqi = rxbuf[pktlen + 1];
/* Convert to 802.15.4 LQI (page 319 of product spec v1.1) */
radio_info->lqi = (uint8_t)(hwlqi > UINT8_MAX/ED_RSSISCALE
? UINT8_MAX
: hwlqi * ED_RSSISCALE);
/* Converting the hardware-provided LQI value back to the
original RSSI value is not properly documented in the PS.
The linear mapping used here has been found empirically
through comparison with the RSSI value provided by NRF_RADIO->RSSISAMPLE
after enabling the ADDRESS_RSSISTART short. */
int8_t rssi_dbm = hwlqi + ED_RSSIOFFS - 1;
radio_info->rssi = ieee802154_dbm_to_rssi(rssi_dbm);
}
memcpy(buf, &rxbuf[1], pktlen);
}
return pktlen;
}
/**
* @brief Convert from dBm to the internal representation, when the
* radio operates as a IEEE802.15.4 transceiver.
*/
static inline uint8_t _dbm_to_ieee802154_hwval(int8_t dbm)
{
return ((dbm - ED_RSSIOFFS) / ED_RSSISCALE);
}
static int set_cca_threshold(ieee802154_dev_t *dev, int8_t threshold)
{
(void) dev;
if (threshold < ED_RSSIOFFS) {
return -EINVAL;
}
uint8_t hw_val = _dbm_to_ieee802154_hwval(threshold);
NRF_RADIO->CCACTRL &= ~RADIO_CCACTRL_CCAEDTHRES_Msk;
NRF_RADIO->CCACTRL |= hw_val << RADIO_CCACTRL_CCAEDTHRES_Pos;
return 0;
}
static void _set_txpower(int16_t txpower)
{
DEBUG("[nrf802154]: Setting TX power to %i\n", txpower);
if (txpower > (int)RADIO_TXPOWER_TXPOWER_Max) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Max;
}
else if (txpower > 1) {
NRF_RADIO->TXPOWER = (uint32_t)txpower;
}
else if (txpower > -1) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm;
}
else if (txpower > -5) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm;
}
else if (txpower > -9) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm;
}
else if (txpower > -13) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm;
}
else if (txpower > -17) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm;
}
else if (txpower > -21) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm;
}
else {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg40dBm;
}
}
static void _set_ifs_timer(bool lifs)
{
uint8_t timeout;
cfg.ifs = true;
if (lifs) {
timeout = IEEE802154_LIFS_SYMS;
}
else {
timeout = IEEE802154_SIFS_SYMS;
}
timer_set(NRF802154_TIMER, MAC_TIMER_CHAN_IFS, timeout);
timer_start(NRF802154_TIMER);
}
static void _timer_cb(void *arg, int chan)
{
(void)arg;
ieee802154_dev_t *dev = nrf802154_hal_dev;
if (chan == MAC_TIMER_CHAN_ACK) {
/* Copy sqn */
ack[1] = IEEE802154_FCF_TYPE_ACK;
if (cfg.pending) {
ack[1] |= IEEE802154_FCF_FRAME_PEND;
}
ack[3] = rxbuf[3];
NRF_RADIO->PACKETPTR = (uint32_t) &ack;
NRF_RADIO->TASKS_TXEN = 1;
dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE);
}
else if (chan == MAC_TIMER_CHAN_IFS) {
cfg.ifs = false;
}
timer_stop(NRF802154_TIMER);
}
/**
* @brief Set radio into DISABLED state
*/
int nrf802154_init(void)
{
DEBUG("[nrf802154]: Init\n");
/* reset buffer */
rxbuf[0] = 0;
txbuf[0] = 0;
ack[0] = IEEE802154_ACK_FRAME_LEN; /* PSDU length */
ack[1] = IEEE802154_FCF_TYPE_ACK; /* FCF */
ack[2] = 0; /* FCF */
int result = timer_init(NRF802154_TIMER, TIMER_FREQ, _timer_cb, NULL);
assert(result >= 0);
(void)result;
timer_stop(NRF802154_TIMER);
/* power off peripheral (but do not release the HFXO as we never requested
* it so far) */
NRF_RADIO->POWER = 0;
return 0;
}
void isr_radio(void)
{
ieee802154_dev_t *dev = nrf802154_hal_dev;
if (NRF_RADIO->EVENTS_FRAMESTART) {
NRF_RADIO->EVENTS_FRAMESTART = 0;
if (_state == STATE_TX) {
dev->cb(dev, IEEE802154_RADIO_INDICATION_TX_START);
}
else if (_state == STATE_RX) {
dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_START);
}
}
if (NRF_RADIO->EVENTS_CCAIDLE) {
NRF_RADIO->EVENTS_CCAIDLE = 0;
if (_state != STATE_TX) {
_state = STATE_CCA_CLEAR;
dev->cb(dev, IEEE802154_RADIO_CONFIRM_CCA);
}
}
if (NRF_RADIO->EVENTS_CCABUSY) {
NRF_RADIO->EVENTS_CCABUSY = 0;
if (_state == STATE_TX) {
_state = STATE_CCA_BUSY;
dev->cb(dev, IEEE802154_RADIO_CONFIRM_TX_DONE);
}
else {
_state = STATE_CCA_BUSY;
dev->cb(dev, IEEE802154_RADIO_CONFIRM_CCA);
}
}
if (NRF_RADIO->EVENTS_END) {
NRF_RADIO->EVENTS_END = 0;
switch (_state) {
case STATE_TX:
DEBUG("[nrf802154] TX state: %x\n", (uint8_t)NRF_RADIO->STATE);
_set_ifs_timer(txbuf[0] > IEEE802154_SIFS_MAX_FRAME_SIZE);
_state = STATE_IDLE;
dev->cb(dev, IEEE802154_RADIO_CONFIRM_TX_DONE);
break;
case STATE_RX:
if (NRF_RADIO->CRCSTATUS) {
bool l2filter_passed = _l2filter(rxbuf+1);
bool is_auto_ack_en = !IS_ACTIVE(CONFIG_IEEE802154_AUTO_ACK_DISABLE);
bool is_ack = rxbuf[1] & IEEE802154_FCF_TYPE_ACK;
bool ack_req = rxbuf[1] & IEEE802154_FCF_ACK_REQ;
/* If radio is in promiscuos mode, indicate packet and
* don't event think of sending an ACK frame :) */
if (cfg.promisc) {
DEBUG("[nrf802154] Promiscuous mode is enabled.\n");
_state = STATE_IDLE;
dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE);
}
/* If the L2 filter passes, device if the frame is indicated
* directly or if the driver should send an ACK frame before
* the indication */
else if (l2filter_passed) {
if (ack_req && is_auto_ack_en) {
timer_set(NRF802154_TIMER, MAC_TIMER_CHAN_ACK, IEEE802154_SIFS_SYMS);
timer_start(NRF802154_TIMER);
_disable();
_state = STATE_ACK;
}
else {
DEBUG("[nrf802154] RX frame doesn't require ACK frame.\n");
_state = STATE_IDLE;
dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE);
}
}
/* In case the packet is an ACK and the ACK filter is disabled,
* indicate the frame reception */
else if (is_ack && !cfg.ack_filter) {
DEBUG("[nrf802154] Received ACK.\n");
_state = STATE_IDLE;
dev->cb(dev, IEEE802154_RADIO_INDICATION_RX_DONE);
}
/* If all failed, simply drop the frame and continue listening
* to incoming frames */
else {
DEBUG("[nrf802154] Addr filter failed or ACK filter on.\n");
NRF_RADIO->TASKS_START = 1;
}
}
else {
DEBUG("[nrf802154] CRC fail.\n");
dev->cb(dev, IEEE802154_RADIO_INDICATION_CRC_ERROR);
}
break;
case STATE_ACK:
_state = STATE_IDLE;
/* We disable the radio to avoid unwanted emissions (see ERRATA
* ID 204, "Switching between TX and RX causes unwanted emissions")
*/
_disable();
DEBUG("[nrf52840] TX ACK done.");
_set_ifs_timer(false);
break;
default:
assert(false);
}
}
cortexm_isr_end();
}
static int _confirm_on(ieee802154_dev_t *dev)
{
(void) dev;
return 0;
}
static int _request_on(ieee802154_dev_t *dev)
{
(void) dev;
_state = STATE_IDLE;
DEBUG("[nrf802154]: Request to turn on\n");
_power_on();
/* make sure the radio is disabled/stopped */
_disable();
/* we configure it to run in IEEE802.15.4 mode */
NRF_RADIO->MODE = RADIO_MODE_MODE_Ieee802154_250Kbit;
/* and set some fitting configuration */
NRF_RADIO->PCNF0 = ((8 << RADIO_PCNF0_LFLEN_Pos) |
(RADIO_PCNF0_PLEN_32bitZero << RADIO_PCNF0_PLEN_Pos) |
(RADIO_PCNF0_CRCINC_Include << RADIO_PCNF0_CRCINC_Pos));
NRF_RADIO->PCNF1 = IEEE802154_FRAME_LEN_MAX;
/* set start frame delimiter */
NRF_RADIO->SFD = IEEE802154_SFD;
/* set MHR filters */
NRF_RADIO->MHRMATCHCONF = 0; /* Search Pattern Configuration */
NRF_RADIO->MHRMATCHMAS = 0xff0007ff; /* Pattern mask */
/* configure CRC conform to IEEE802154 */
NRF_RADIO->CRCCNF = ((RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos) |
(RADIO_CRCCNF_SKIPADDR_Ieee802154 << RADIO_CRCCNF_SKIPADDR_Pos));
NRF_RADIO->CRCPOLY = 0x011021;
NRF_RADIO->CRCINIT = 0;
/* Disable the hardware IFS handling */
NRF_RADIO->MODECNF0 |= RADIO_MODECNF0_RU_Msk;
NRF_RADIO->SHORTS = DEFAULT_SHORTS;
/* enable interrupts */
NVIC_EnableIRQ(RADIO_IRQn);
NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk |
RADIO_INTENSET_FRAMESTART_Msk |
RADIO_INTENSET_CCAIDLE_Msk |
RADIO_INTENSET_CCABUSY_Msk;
return 0;
}
static int _config_phy(ieee802154_dev_t *dev, const ieee802154_phy_conf_t *conf)
{
(void) dev;
int8_t pow = conf->pow;
if (pow < TX_POWER_MIN || pow > TX_POWER_MAX) {
return -EINVAL;
}
assert(NRF_RADIO->STATE == RADIO_STATE_STATE_Disabled);
/* The value of this register represents the frequency offset (in MHz) from
* 2400 MHz. Channel 11 (first 2.4 GHz band channel) starts at 2405 MHz
* and all channels have a bandwidth of 5 MHz. Thus, we subtract 10 to the
* channel number and multiply by 5 to calculate the offset.
*/
NRF_RADIO->FREQUENCY = (((uint8_t) conf->channel) - 10) * 5;
DEBUG("[nrf802154] setting channel to %i\n", conf->channel);
DEBUG("[nrf802154] setting TX power to %i\n", conf->pow);
_set_txpower(pow);
return 0;
}
static int _off(ieee802154_dev_t *dev)
{
(void) dev;
DEBUG("[nrf802154] Turning off the radio\n");
_power_off();
return 0;
}
int _len(ieee802154_dev_t *dev)
{
(void) dev;
DEBUG("[nrf802154] Length of frame is %i\n", (size_t)rxbuf[0] - IEEE802154_FCS_LEN);
return (size_t)rxbuf[0] - IEEE802154_FCS_LEN;
}
int _set_cca_mode(ieee802154_dev_t *dev, ieee802154_cca_mode_t mode)
{
(void) dev;
NRF_RADIO->CCACTRL &= RADIO_CCACTRL_CCAMODE_Msk;
uint8_t tmp = 0;
switch (mode) {
case IEEE802154_CCA_MODE_ED_THRESHOLD:
DEBUG("[nrf802154]: set CCA Mode to ED Threshold\n");
tmp = RADIO_CCACTRL_CCAMODE_EdMode;
break;
case IEEE802154_CCA_MODE_CARRIER_SENSING:
tmp = RADIO_CCACTRL_CCAMODE_CarrierOrEdMode;
DEBUG("[nrf802154]: set CCA Mode to Carrier Sensing\n");
break;
case IEEE802154_CCA_MODE_ED_THRESH_AND_CS:
DEBUG("[nrf802154]: set CCA Mode to ED Threshold AND Carrier Sensing\n");
tmp = RADIO_CCACTRL_CCAMODE_CarrierAndEdMode;
break;
case IEEE802154_CCA_MODE_ED_THRESH_OR_CS:
DEBUG("[nrf802154]: set CCA Mode to ED Threshold OR Carrier Sensing\n");
tmp = RADIO_CCACTRL_CCAMODE_CarrierOrEdMode;
break;
}
NRF_RADIO->CCACTRL |= tmp;
return 0;
}
static int _config_addr_filter(ieee802154_dev_t *dev, ieee802154_af_cmd_t cmd, const void *value)
{
(void) dev;
const uint16_t *pan_id = value;
switch(cmd) {
case IEEE802154_AF_SHORT_ADDR:
memcpy(nrf802154_short_addr, value, IEEE802154_SHORT_ADDRESS_LEN);
break;
case IEEE802154_AF_EXT_ADDR:
memcpy(nrf802154_long_addr, value, IEEE802154_LONG_ADDRESS_LEN);
break;
case IEEE802154_AF_PANID:
nrf802154_pan_id = *pan_id;
break;
case IEEE802154_AF_PAN_COORD:
return -ENOTSUP;
}
return 0;
}
static int _config_src_addr_match(ieee802154_dev_t *dev, ieee802154_src_match_t cmd, const void *value)
{
(void) dev;
switch(cmd) {
case IEEE802154_SRC_MATCH_EN:
cfg.pending = *((const bool*) value);
break;
default:
return -ENOTSUP;
}
return 0;
}
static int _set_frame_filter_mode(ieee802154_dev_t *dev, ieee802154_filter_mode_t mode)
{
(void) dev;
bool ackf = true;
bool _promisc = false;
switch (mode) {
case IEEE802154_FILTER_ACCEPT:
break;
case IEEE802154_FILTER_PROMISC:
_promisc = true;
break;
case IEEE802154_FILTER_ACK_ONLY:
ackf = false;
break;
default:
return -ENOTSUP;
}
cfg.ack_filter = ackf;
cfg.promisc = _promisc;
return 0;
}
static int _set_csma_params(ieee802154_dev_t *dev, const ieee802154_csma_be_t *bd,
int8_t retries)
{
(void) dev;
(void) bd;
if (retries > 0) {
return -ENOTSUP;
}
cfg.cca_send = (retries == 0);
return 0;
}
void nrf802154_setup(nrf802154_t *dev)
{
(void) dev;
nrf802154_init();
}
void nrf802154_hal_setup(ieee802154_dev_t *hal)
{
/* We don't set hal->priv because the context of this device is global */
/* We need to store a reference to the HAL descriptor though for the ISR */
hal->driver = &nrf802154_ops;
nrf802154_hal_dev = hal;
}
static const ieee802154_radio_ops_t nrf802154_ops = {
.caps = IEEE802154_CAP_24_GHZ
| IEEE802154_CAP_IRQ_CRC_ERROR
| IEEE802154_CAP_IRQ_RX_START
| IEEE802154_CAP_IRQ_TX_START
| IEEE802154_CAP_IRQ_TX_DONE
| IEEE802154_CAP_IRQ_CCA_DONE
| IEEE802154_CAP_PHY_OQPSK,
.write = _write,
.read = _read,
.request_on = _request_on,
.confirm_on = _confirm_on,
.len = _len,
.off = _off,
.request_op = _request_op,
.confirm_op = _confirm_op,
.set_cca_threshold = set_cca_threshold,
.set_cca_mode = _set_cca_mode,
.config_phy = _config_phy,
.set_csma_params = _set_csma_params,
.config_addr_filter = _config_addr_filter,
.config_src_addr_match = _config_src_addr_match,
.set_frame_filter_mode = _set_frame_filter_mode,
};