blob: a668f92ab43d4b6d06e3c0b076afe4f3cbd00a07 [file] [log] [blame]
/* ieee802154_rf2xx.c - ATMEL RF2XX IEEE 802.15.4 Driver */
#define DT_DRV_COMPAT atmel_rf2xx
/*
* Copyright (c) 2019-2020 Gerson Fernando Budke
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_MODULE_NAME ieee802154_rf2xx
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <errno.h>
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/debug/stack.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_pkt.h>
#include <zephyr/sys/byteorder.h>
#include <string.h>
#include <zephyr/random/rand32.h>
#include <zephyr/linker/sections.h>
#include <zephyr/sys/atomic.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/net/ieee802154_radio.h>
#include "ieee802154_rf2xx.h"
#include "ieee802154_rf2xx_regs.h"
#include "ieee802154_rf2xx_iface.h"
#if defined(CONFIG_NET_L2_OPENTHREAD)
#include <zephyr/net/openthread.h>
#define RF2XX_OT_PSDU_LENGTH 1280
#define RF2XX_ACK_FRAME_LEN 3
#define RF2XX_ACK_FRAME_TYPE (2 << 0)
#define RF2XX_ACK_FRAME_PENDING_BIT (1 << 4)
#define RF2XX_FRAME_CTRL_ACK_REQUEST_BIT (1 << 5)
static uint8_t rf2xx_ack_psdu[RF2XX_ACK_FRAME_LEN] = { 0 };
static struct net_buf rf2xx_ack_frame = {
.data = rf2xx_ack_psdu,
.size = RF2XX_ACK_FRAME_LEN,
.len = RF2XX_ACK_FRAME_LEN,
.__buf = rf2xx_ack_psdu,
.frags = NULL,
};
static struct net_pkt rf2xx_ack_pkt = {
.buffer = &rf2xx_ack_frame,
.ieee802154_lqi = 80,
.ieee802154_rssi = -40,
};
#endif /* CONFIG_NET_L2_OPENTHREAD */
/**
* RF output power for RF2xx
*
* The table below is exact for RF233. For RF231/2 the TX power might
* be a bit off, but good enough.
*
* RF233: http://ww1.microchip.com/downloads/en/devicedoc/atmel-8351-mcu_wireless-at86rf233_datasheet.pdf
* 9.2.5 Register Description Register 0x05 (PHY_TX_PWR)
* 0x0 = 4dBm .. 0xF = -17dBm
*
* RF232: http://ww1.microchip.com/downloads/en/DeviceDoc/doc8321.pdf
* 9.2.5 Register Description Register 0x05 (PHY_TX_PWR)
* 0x0 = 3dBm .. 0xF = -17dBm
*
* RF231: http://ww1.microchip.com/downloads/en/DeviceDoc/doc8111.pdf
* 9.2.5 Register Description Register 0x05 (PHY_TX_PWR)
* 0x0 = 3dBm .. 0xF = -17dBm
*/
#define RF2XX_OUTPUT_POWER_MAX 4
#define RF2XX_OUTPUT_POWER_MIN (-17)
/* Lookup table for PHY_TX_PWR register for RF233 */
static const uint8_t phy_tx_pwr_lt[] = {
0xf, /* -17 dBm: -17 */
0xe, 0xe, 0xe, 0xe, 0xe, /* -12 dBm: -16, -15, -14, -13, -12 */
0xd, 0xd, 0xd, 0xd, /* -8 dBm: -11, -10, -9, -8 */
0xc, 0xc, /* -6 dBm: -7, -6 */
0xb, 0xb, /* -4 dBm: -5, -4 */
0xa, /* -3 dBm: -3 */
0x9, /* -2 dBm: -2 */
0x8, /* -1 dBm: -1 */
0x7, /* 0.0 dBm: 0 */
0x6, /* 1 dBm: 1 */
0x5, /* 2 dBm: 2 */
/* 0x4, */ /* 2.5 dBm */
0x3, /* 3 dBm: 3 */
/* 0x2, */ /* 3.4 dBm */
/* 0x1, */ /* 3.7 dBm */
0x0 /* 4 dBm: 4 */
};
/* Radio Transceiver ISR */
static inline void trx_isr_handler(const struct device *port,
struct gpio_callback *cb,
uint32_t pins)
{
struct rf2xx_context *ctx = CONTAINER_OF(cb,
struct rf2xx_context,
irq_cb);
ARG_UNUSED(port);
ARG_UNUSED(pins);
k_sem_give(&ctx->trx_isr_lock);
}
static void rf2xx_trx_set_state(const struct device *dev,
enum rf2xx_trx_state_cmd_t state)
{
do {
rf2xx_iface_reg_write(dev, RF2XX_TRX_STATE_REG,
RF2XX_TRX_PHY_STATE_CMD_FORCE_TRX_OFF);
} while (RF2XX_TRX_PHY_STATUS_TRX_OFF !=
(rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
RF2XX_TRX_PHY_STATUS_MASK));
do {
rf2xx_iface_reg_write(dev, RF2XX_TRX_STATE_REG, state);
} while (state !=
(rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
RF2XX_TRX_PHY_STATUS_MASK));
}
static void rf2xx_trx_set_tx_state(const struct device *dev)
{
uint8_t status;
/**
* Ensures that RX automatically ACK will be sent when requested.
* Datasheet: Chapter 7.2.3 RX_AACK_ON – Receive with Automatic ACK
* Datasheet: Figure 7-13. Timing Example of an RX_AACK Transaction
* for Slotted Operation.
*
* This will create a spin lock that wait transceiver be free from
* current receive frame process
*/
do {
status = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
RF2XX_TRX_PHY_STATUS_MASK);
} while (status == RF2XX_TRX_PHY_STATUS_BUSY_RX_AACK ||
status == RF2XX_TRX_PHY_STATUS_STATE_TRANSITION);
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TX_ARET_ON);
}
static void rf2xx_trx_set_rx_state(const struct device *dev)
{
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
/**
* Set extended RX mode
* Datasheet: chapter 7.2 Extended Operating Mode
*/
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_RX_AACK_ON);
}
static void rf2xx_trx_rx(const struct device *dev)
{
struct rf2xx_context *ctx = dev->data;
struct net_pkt *pkt = NULL;
uint8_t rx_buf[RX2XX_MAX_FRAME_SIZE];
uint8_t pkt_len;
uint8_t frame_len;
uint8_t trac;
/*
* The rf2xx frame buffer can have length > 128 bytes. The
* net_pkt_alloc_with_buffer allocates max value of 128 bytes.
*
* This obligate the driver to have rx_buf statically allocated with
* RX2XX_MAX_FRAME_SIZE.
*/
if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
pkt_len = ctx->rx_phr;
} else {
rf2xx_iface_frame_read(dev, rx_buf, RX2XX_FRAME_HEADER_SIZE);
pkt_len = rx_buf[RX2XX_FRAME_PHR_INDEX];
}
if (pkt_len < RX2XX_FRAME_MIN_PHR_SIZE) {
LOG_ERR("invalid RX frame length");
return;
}
frame_len = RX2XX_FRAME_HEADER_SIZE + pkt_len +
RX2XX_FRAME_FOOTER_SIZE;
rf2xx_iface_frame_read(dev, rx_buf, frame_len);
trac = rx_buf[pkt_len + RX2XX_FRAME_TRAC_INDEX];
trac = (trac >> RF2XX_RX_TRAC_STATUS) & RF2XX_RX_TRAC_BIT_MASK;
if (trac == RF2XX_TRX_PHY_STATE_TRAC_INVALID) {
LOG_ERR("invalid RX frame");
return;
}
ctx->pkt_lqi = rx_buf[pkt_len + RX2XX_FRAME_LQI_INDEX];
ctx->pkt_ed = rx_buf[pkt_len + RX2XX_FRAME_ED_INDEX];
if (!IS_ENABLED(CONFIG_IEEE802154_RAW_MODE) &&
!IS_ENABLED(CONFIG_NET_L2_OPENTHREAD)) {
pkt_len -= RX2XX_FRAME_FCS_LENGTH;
}
pkt = net_pkt_alloc_with_buffer(ctx->iface, pkt_len,
AF_UNSPEC, 0, K_NO_WAIT);
if (!pkt) {
LOG_ERR("No buf available");
return;
}
memcpy(pkt->buffer->data, rx_buf + RX2XX_FRAME_HEADER_SIZE, pkt_len);
net_buf_add(pkt->buffer, pkt_len);
net_pkt_set_ieee802154_lqi(pkt, ctx->pkt_lqi);
net_pkt_set_ieee802154_rssi(pkt, ctx->pkt_ed + ctx->trx_rssi_base);
LOG_DBG("Caught a packet (%02X) (LQI: %02X, RSSI: %d, ED: %02X)",
pkt_len, ctx->pkt_lqi, ctx->trx_rssi_base + ctx->pkt_ed,
ctx->pkt_ed);
if (net_recv_data(ctx->iface, pkt) < 0) {
LOG_DBG("Packet dropped by NET stack");
net_pkt_unref(pkt);
return;
}
if (LOG_LEVEL >= LOG_LEVEL_DBG) {
log_stack_usage(&ctx->trx_thread);
}
}
static void rf2xx_process_rx_frame(const struct device *dev)
{
struct rf2xx_context *ctx = dev->data;
/*
* NOTE: In promiscuous mode invalid frames will be processed.
*/
if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
rf2xx_trx_rx(dev);
} else {
/* Ensures that automatically ACK will be sent
* when requested
*/
while (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) ==
RF2XX_TRX_PHY_STATUS_BUSY_RX_AACK) {
;
}
/* Set PLL_ON to avoid transceiver receive
* new data until finish reading process
*/
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_PLL_ON);
rf2xx_trx_rx(dev);
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_RX_AACK_ON);
}
}
static void rf2xx_process_tx_frame(const struct device *dev)
{
struct rf2xx_context *ctx = dev->data;
ctx->trx_trac = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATE_REG) >>
RF2XX_TRAC_STATUS) & 7;
k_sem_give(&ctx->trx_tx_sync);
rf2xx_trx_set_rx_state(dev);
}
static void rf2xx_process_trx_end(const struct device *dev)
{
uint8_t trx_status = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
RF2XX_TRX_PHY_STATUS_MASK);
if (trx_status == RF2XX_TRX_PHY_STATUS_TX_ARET_ON) {
rf2xx_process_tx_frame(dev);
} else {
rf2xx_process_rx_frame(dev);
}
}
static void rf2xx_thread_main(void *arg)
{
struct rf2xx_context *ctx = arg;
uint8_t isr_status;
while (true) {
k_sem_take(&ctx->trx_isr_lock, K_FOREVER);
isr_status = rf2xx_iface_reg_read(ctx->dev,
RF2XX_IRQ_STATUS_REG);
/*
* IRQ_7 (BAT_LOW) Indicates a supply voltage below the
* programmed threshold. 9.5.4
* IRQ_6 (TRX_UR) Indicates a Frame Buffer access
* violation. 9.3.3
* IRQ_5 (AMI) Indicates address matching. 8.2
* IRQ_4 (CCA_ED_DONE) Multi-functional interrupt:
* 1. AWAKE_END: 7.1.2.5
* • Indicates finished transition to TRX_OFF state
* from P_ON, SLEEP, DEEP_SLEEP, or RESET state.
* 2. CCA_ED_DONE: 8.5.4
* • Indicates the end of a CCA or ED
* measurement. 8.6.4
* IRQ_3 (TRX_END)
* RX: Indicates the completion of a frame
* reception. 7.1.3
* TX: Indicates the completion of a frame
* transmission. 7.1.3
* IRQ_2 (RX_START) Indicates the start of a PSDU
* reception; the AT86RF233 state changed to BUSY_RX;
* the PHR can be read from Frame Buffer. 7.1.3
* IRQ_1 (PLL_UNLOCK) Indicates PLL unlock. If the radio
* transceiver is in BUSY_TX / BUSY_TX_ARET state, the
* PA is turned off immediately. 9.7.5
* IRQ_0 (PLL_LOCK) Indicates PLL lock.
*/
if (isr_status & (1 << RF2XX_RX_START)) {
if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
rf2xx_iface_sram_read(ctx->dev, 0,
&ctx->rx_phr, 1);
}
} else if (isr_status & (1 << RF2XX_TRX_END)) {
rf2xx_process_trx_end(ctx->dev);
}
}
}
static inline uint8_t *get_mac(const struct device *dev)
{
const struct rf2xx_config *conf = dev->config;
struct rf2xx_context *ctx = dev->data;
uint32_t *ptr = (uint32_t *)(ctx->mac_addr);
if (!conf->has_mac) {
UNALIGNED_PUT(sys_rand32_get(), ptr);
ptr = (uint32_t *)(ctx->mac_addr + 4);
UNALIGNED_PUT(sys_rand32_get(), ptr);
}
/*
* Clear bit 0 to ensure it isn't a multicast address and set
* bit 1 to indicate address is locally administered and may
* not be globally unique.
*/
ctx->mac_addr[0] = (ctx->mac_addr[0] & ~0x01) | 0x02;
return ctx->mac_addr;
}
static enum ieee802154_hw_caps rf2xx_get_capabilities(const struct device *dev)
{
ARG_UNUSED(dev);
return IEEE802154_HW_FCS |
IEEE802154_HW_PROMISC |
IEEE802154_HW_FILTER |
IEEE802154_HW_CSMA |
IEEE802154_HW_TX_RX_ACK |
IEEE802154_HW_2_4_GHZ;
}
static int rf2xx_cca(const struct device *dev)
{
ARG_UNUSED(dev);
return 0;
}
static int rf2xx_set_channel(const struct device *dev, uint16_t channel)
{
uint8_t reg;
if (channel < 11 || channel > 26) {
LOG_ERR("Unsupported channel %u", channel);
return -EINVAL;
}
reg = rf2xx_iface_reg_read(dev, RF2XX_PHY_CC_CCA_REG) & ~0x1f;
rf2xx_iface_reg_write(dev, RF2XX_PHY_CC_CCA_REG, reg | channel);
return 0;
}
static int rf2xx_set_txpower(const struct device *dev, int16_t dbm)
{
if (dbm < RF2XX_OUTPUT_POWER_MIN) {
LOG_INF("TX-power %d dBm below min of %d dBm, using %d dBm",
dbm,
RF2XX_OUTPUT_POWER_MIN,
RF2XX_OUTPUT_POWER_MAX);
dbm = RF2XX_OUTPUT_POWER_MIN;
} else if (dbm > RF2XX_OUTPUT_POWER_MAX) {
LOG_INF("TX-power %d dBm above max of %d dBm, using %d dBm",
dbm,
RF2XX_OUTPUT_POWER_MIN,
RF2XX_OUTPUT_POWER_MAX);
dbm = RF2XX_OUTPUT_POWER_MAX;
}
rf2xx_iface_reg_write(dev, RF2XX_PHY_TX_PWR_REG,
phy_tx_pwr_lt[dbm - RF2XX_OUTPUT_POWER_MIN]);
return 0;
}
static int rf2xx_set_ieee_addr(const struct device *dev, bool set,
const uint8_t *ieee_addr)
{
const uint8_t *ptr_to_reg = ieee_addr;
LOG_DBG("IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
ieee_addr[7], ieee_addr[6], ieee_addr[5], ieee_addr[4],
ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]);
if (set) {
for (uint8_t i = 0; i < 8; i++, ptr_to_reg++) {
rf2xx_iface_reg_write(dev, (RF2XX_IEEE_ADDR_0_REG + i),
*ptr_to_reg);
}
} else {
for (uint8_t i = 0; i < 8; i++) {
rf2xx_iface_reg_write(dev, (RF2XX_IEEE_ADDR_0_REG + i),
0);
}
}
return 0;
}
static int rf2xx_set_short_addr(const struct device *dev, bool set,
uint16_t short_addr)
{
uint8_t short_addr_le[2] = { 0xFF, 0xFF };
if (set) {
sys_put_le16(short_addr, short_addr_le);
}
rf2xx_iface_reg_write(dev, RF2XX_SHORT_ADDR_0_REG, short_addr_le[0]);
rf2xx_iface_reg_write(dev, RF2XX_SHORT_ADDR_1_REG, short_addr_le[1]);
rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_0_REG,
short_addr_le[0] + short_addr_le[1]);
LOG_DBG("Short Address: 0x%02X%02X", short_addr_le[1],
short_addr_le[0]);
return 0;
}
static int rf2xx_set_pan_id(const struct device *dev, bool set,
uint16_t pan_id)
{
uint8_t pan_id_le[2] = { 0xFF, 0xFF };
if (set) {
sys_put_le16(pan_id, pan_id_le);
}
rf2xx_iface_reg_write(dev, RF2XX_PAN_ID_0_REG, pan_id_le[0]);
rf2xx_iface_reg_write(dev, RF2XX_PAN_ID_1_REG, pan_id_le[1]);
LOG_DBG("Pan Id: 0x%02X%02X", pan_id_le[1], pan_id_le[0]);
return 0;
}
static int rf2xx_filter(const struct device *dev,
bool set, enum ieee802154_filter_type type,
const struct ieee802154_filter *filter)
{
LOG_DBG("Applying filter %u", type);
if (type == IEEE802154_FILTER_TYPE_IEEE_ADDR) {
return rf2xx_set_ieee_addr(dev, set, filter->ieee_addr);
} else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) {
return rf2xx_set_short_addr(dev, set, filter->short_addr);
} else if (type == IEEE802154_FILTER_TYPE_PAN_ID) {
return rf2xx_set_pan_id(dev, set, filter->pan_id);
}
return -ENOTSUP;
}
#if defined(CONFIG_NET_L2_OPENTHREAD)
static void rf2xx_handle_ack(struct rf2xx_context *ctx, struct net_buf *frag)
{
if ((frag->data[0] & RF2XX_FRAME_CTRL_ACK_REQUEST_BIT) == 0) {
return;
}
rf2xx_ack_psdu[0] = RF2XX_ACK_FRAME_TYPE;
rf2xx_ack_psdu[2] = frag->data[2];
if (ctx->trx_trac == RF2XX_TRX_PHY_STATE_TRAC_SUCCESS_DATA_PENDING) {
rf2xx_ack_psdu[0] |= RF2XX_ACK_FRAME_PENDING_BIT;
}
net_pkt_cursor_init(&rf2xx_ack_pkt);
if (ieee802154_radio_handle_ack(ctx->iface, &rf2xx_ack_pkt) != NET_OK) {
LOG_INF("ACK packet not handled.");
}
}
#else
#define rf2xx_handle_ack(...)
#endif
static int rf2xx_tx(const struct device *dev,
enum ieee802154_tx_mode mode,
struct net_pkt *pkt,
struct net_buf *frag)
{
ARG_UNUSED(pkt);
struct rf2xx_context *ctx = dev->data;
int response = 0;
if (ctx->tx_mode != mode) {
switch (mode) {
case IEEE802154_TX_MODE_DIRECT:
/* skip retries & csma/ca algorithm */
rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_0_REG, 0x0E);
break;
case IEEE802154_TX_MODE_CSMA_CA:
/* backoff maxBE = 5, minBE = 3 */
rf2xx_iface_reg_write(dev, RF2XX_CSMA_BE_REG, 0x53);
/* max frame retries = 3, csma/ca retries = 4 */
rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_0_REG, 0x38);
break;
case IEEE802154_TX_MODE_CCA:
/* backoff period = 0 */
rf2xx_iface_reg_write(dev, RF2XX_CSMA_BE_REG, 0x00);
/* no frame retries & no csma/ca retries */
rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_0_REG, 0x00);
break;
case IEEE802154_TX_MODE_TXTIME:
case IEEE802154_TX_MODE_TXTIME_CCA:
default:
NET_ERR("TX mode %d not supported", mode);
return -ENOTSUP;
}
ctx->tx_mode = mode;
}
rf2xx_trx_set_tx_state(dev);
rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
k_sem_reset(&ctx->trx_tx_sync);
rf2xx_iface_frame_write(dev, frag->data, frag->len);
rf2xx_iface_phy_tx_start(dev);
k_sem_take(&ctx->trx_tx_sync, K_FOREVER);
switch (ctx->trx_trac) {
/* Channel is still busy after attempting MAX_CSMA_RETRIES of
* CSMA-CA
*/
case RF2XX_TRX_PHY_STATE_TRAC_CHANNEL_ACCESS_FAILED:
response = -EBUSY;
break;
/* No acknowledgment frames were received during all retry
* attempts
*/
case RF2XX_TRX_PHY_STATE_TRAC_NO_ACK:
response = -EAGAIN;
break;
/* Transaction not yet finished */
case RF2XX_TRX_PHY_STATE_TRAC_INVALID:
response = -EINTR;
break;
/* RF2XX_TRX_PHY_STATE_TRAC_SUCCESS:
* The transaction was responded to by a valid ACK, or, if no
* ACK is requested, after a successful frame transmission.
*
* RF2XX_TRX_PHY_STATE_TRAC_SUCCESS_DATA_PENDING:
* Equivalent to SUCCESS and indicating that the “Frame
* Pending” bit (see Section 8.1.2.2) of the received
* acknowledgment frame was set.
*/
default:
rf2xx_handle_ack(ctx, frag);
break;
}
return response;
}
static int rf2xx_start(const struct device *dev)
{
const struct rf2xx_config *conf = dev->config;
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
gpio_pin_interrupt_configure_dt(&conf->irq_gpio,
GPIO_INT_EDGE_TO_ACTIVE);
rf2xx_trx_set_rx_state(dev);
return 0;
}
static int rf2xx_stop(const struct device *dev)
{
const struct rf2xx_config *conf = dev->config;
gpio_pin_interrupt_configure_dt(&conf->irq_gpio, GPIO_INT_DISABLE);
rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
return 0;
}
static int rf2xx_pan_coord_set(const struct device *dev, bool pan_coordinator)
{
uint8_t reg;
if (pan_coordinator) {
reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
reg |= (1 << RF2XX_AACK_I_AM_COORD);
rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
} else {
reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
reg &= ~(1 << RF2XX_AACK_I_AM_COORD);
rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
}
return 0;
}
static int rf2xx_promiscuous_set(const struct device *dev, bool promiscuous)
{
uint8_t reg;
if (promiscuous) {
reg = rf2xx_iface_reg_read(dev, RF2XX_XAH_CTRL_1_REG);
reg |= (1 << RF2XX_AACK_PROM_MODE);
rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_1_REG, reg);
reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
reg |= (1 << RF2XX_AACK_DIS_ACK);
rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
} else {
reg = rf2xx_iface_reg_read(dev, RF2XX_XAH_CTRL_1_REG);
reg &= ~(1 << RF2XX_AACK_PROM_MODE);
rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_1_REG, reg);
reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
reg &= ~(1 << RF2XX_AACK_DIS_ACK);
rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
}
return 0;
}
int rf2xx_configure(const struct device *dev,
enum ieee802154_config_type type,
const struct ieee802154_config *config)
{
int ret = -EINVAL;
switch (type) {
case IEEE802154_CONFIG_AUTO_ACK_FPB:
case IEEE802154_CONFIG_ACK_FPB:
break;
case IEEE802154_CONFIG_PAN_COORDINATOR:
ret = rf2xx_pan_coord_set(dev, config->pan_coordinator);
break;
case IEEE802154_CONFIG_PROMISCUOUS:
ret = rf2xx_promiscuous_set(dev, config->promiscuous);
break;
case IEEE802154_CONFIG_EVENT_HANDLER:
default:
break;
}
return ret;
}
static int power_on_and_setup(const struct device *dev)
{
const struct rf2xx_config *conf = dev->config;
struct rf2xx_context *ctx = dev->data;
uint8_t config;
rf2xx_iface_phy_rst(dev);
/* Sync transceiver state */
do {
rf2xx_iface_reg_write(dev, RF2XX_TRX_STATE_REG,
RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
} while (RF2XX_TRX_PHY_STATUS_TRX_OFF !=
(rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
RF2XX_TRX_PHY_STATUS_MASK));
/* get device identification */
ctx->trx_model = rf2xx_iface_reg_read(dev, RF2XX_PART_NUM_REG);
ctx->trx_version = rf2xx_iface_reg_read(dev, RF2XX_VERSION_NUM_REG);
/**
* Valid transceiver are:
* 231-Rev-A (Version 0x02)
* 232-Rev-A (Version 0x02)
* 233-Rev-A (Version 0x01) (Warning)
* 233-Rev-B (Version 0x02)
*/
if (ctx->trx_model != RF2XX_TRX_MODEL_231 &&
ctx->trx_model != RF2XX_TRX_MODEL_232 &&
ctx->trx_model != RF2XX_TRX_MODEL_233) {
LOG_DBG("Invalid or not supported transceiver");
return -ENODEV;
}
if (ctx->trx_version < 0x02) {
LOG_DBG("Transceiver is old and unstable release");
}
/* Set RSSI base */
if (ctx->trx_model == RF2XX_TRX_MODEL_233) {
ctx->trx_rssi_base = -94;
} else if (ctx->trx_model == RF2XX_TRX_MODEL_231) {
ctx->trx_rssi_base = -91;
} else {
ctx->trx_rssi_base = -90;
}
/* Configure PHY behaviour */
config = (1 << RF2XX_TX_AUTO_CRC_ON) |
(3 << RF2XX_SPI_CMD_MODE) |
(1 << RF2XX_IRQ_MASK_MODE);
rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_1_REG, config);
config = (1 << RF2XX_RX_SAFE_MODE);
if (ctx->trx_model != RF2XX_TRX_MODEL_232) {
config |= (1 << RF2XX_OQPSK_SCRAM_EN);
}
rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_2_REG, config);
ctx->tx_mode = IEEE802154_TX_MODE_CSMA_CA;
/* Configure INT behaviour */
config = (1 << RF2XX_RX_START) |
(1 << RF2XX_TRX_END);
rf2xx_iface_reg_write(dev, RF2XX_IRQ_MASK_REG, config);
gpio_init_callback(&ctx->irq_cb, trx_isr_handler, BIT(conf->irq_gpio.pin));
gpio_add_callback(conf->irq_gpio.port, &ctx->irq_cb);
return 0;
}
static inline int configure_gpios(const struct device *dev)
{
const struct rf2xx_config *conf = dev->config;
/* Chip IRQ line */
if (!device_is_ready(conf->irq_gpio.port)) {
LOG_ERR("IRQ GPIO device not ready");
return -ENODEV;
}
gpio_pin_configure_dt(&conf->irq_gpio, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&conf->irq_gpio,
GPIO_INT_EDGE_TO_ACTIVE);
/* Chip RESET line */
if (!device_is_ready(conf->reset_gpio.port)) {
LOG_ERR("RESET GPIO device not ready");
return -ENODEV;
}
gpio_pin_configure_dt(&conf->reset_gpio, GPIO_OUTPUT_INACTIVE);
/* Chip SLPTR line */
if (!device_is_ready(conf->slptr_gpio.port)) {
LOG_ERR("SLPTR GPIO device not ready");
return -ENODEV;
}
gpio_pin_configure_dt(&conf->slptr_gpio, GPIO_OUTPUT_INACTIVE);
/* Chip DIG2 line (Optional feature) */
if (conf->dig2_gpio.port != NULL) {
if (!device_is_ready(conf->dig2_gpio.port)) {
LOG_ERR("DIG2 GPIO device not ready");
return -ENODEV;
}
LOG_INF("Optional instance of %s device activated",
conf->dig2_gpio.port->name);
gpio_pin_configure_dt(&conf->dig2_gpio, GPIO_INPUT);
gpio_pin_interrupt_configure_dt(&conf->dig2_gpio,
GPIO_INT_EDGE_TO_ACTIVE);
}
/* Chip CLKM line (Optional feature) */
if (conf->clkm_gpio.port != NULL) {
if (!device_is_ready(conf->clkm_gpio.port)) {
LOG_ERR("CLKM GPIO device not ready");
return -ENODEV;
}
LOG_INF("Optional instance of %s device activated",
conf->clkm_gpio.port->name);
gpio_pin_configure_dt(&conf->clkm_gpio, GPIO_INPUT);
}
return 0;
}
static inline int configure_spi(const struct device *dev)
{
const struct rf2xx_config *conf = dev->config;
if (!spi_is_ready(&conf->spi)) {
LOG_ERR("SPI bus %s is not ready",
conf->spi.bus->name);
return -ENODEV;
}
return 0;
}
static int rf2xx_init(const struct device *dev)
{
struct rf2xx_context *ctx = dev->data;
const struct rf2xx_config *conf = dev->config;
char thread_name[20];
LOG_DBG("\nInitialize RF2XX Transceiver\n");
ctx->dev = dev;
k_sem_init(&ctx->trx_tx_sync, 0, 1);
k_sem_init(&ctx->trx_isr_lock, 0, 1);
if (configure_gpios(dev) != 0) {
LOG_ERR("Configuring GPIOS failed");
return -EIO;
}
if (configure_spi(dev) != 0) {
LOG_ERR("Configuring SPI failed");
return -EIO;
}
LOG_DBG("GPIO and SPI configured");
if (power_on_and_setup(dev) != 0) {
LOG_ERR("Configuring RF2XX failed");
return -EIO;
}
k_thread_create(&ctx->trx_thread,
ctx->trx_stack,
CONFIG_IEEE802154_RF2XX_RX_STACK_SIZE,
(k_thread_entry_t) rf2xx_thread_main,
ctx, NULL, NULL,
K_PRIO_COOP(2), 0, K_NO_WAIT);
snprintk(thread_name, sizeof(thread_name),
"rf2xx_trx [%d]", conf->inst);
k_thread_name_set(&ctx->trx_thread, thread_name);
return 0;
}
static void rf2xx_iface_init(struct net_if *iface)
{
const struct device *dev = net_if_get_device(iface);
struct rf2xx_context *ctx = dev->data;
uint8_t *mac = get_mac(dev);
net_if_set_link_addr(iface, mac, 8, NET_LINK_IEEE802154);
ctx->iface = iface;
ieee802154_init(iface);
}
static struct ieee802154_radio_api rf2xx_radio_api = {
.iface_api.init = rf2xx_iface_init,
.get_capabilities = rf2xx_get_capabilities,
.cca = rf2xx_cca,
.set_channel = rf2xx_set_channel,
.filter = rf2xx_filter,
.set_txpower = rf2xx_set_txpower,
.tx = rf2xx_tx,
.start = rf2xx_start,
.stop = rf2xx_stop,
.configure = rf2xx_configure,
};
#if !defined(CONFIG_IEEE802154_RAW_MODE)
#if defined(CONFIG_NET_L2_IEEE802154)
#define L2 IEEE802154_L2
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(IEEE802154_L2)
#define MTU RF2XX_MAX_PSDU_LENGTH
#elif defined(CONFIG_NET_L2_OPENTHREAD)
#define L2 OPENTHREAD_L2
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2)
#define MTU RF2XX_OT_PSDU_LENGTH
#endif
#endif /* CONFIG_IEEE802154_RAW_MODE */
#define DRV_INST_LOCAL_MAC_ADDRESS(n) \
UTIL_AND(DT_INST_NODE_HAS_PROP(n, local_mac_address), \
UTIL_AND(DT_INST_PROP_LEN(n, local_mac_address) == 8, \
DT_INST_PROP(n, local_mac_address)))
#define IEEE802154_RF2XX_DEVICE_CONFIG(n) \
static const struct rf2xx_config rf2xx_ctx_config_##n = { \
.inst = n, \
.has_mac = DT_INST_NODE_HAS_PROP(n, local_mac_address), \
.irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios), \
.reset_gpio = GPIO_DT_SPEC_INST_GET(n, reset_gpios), \
.slptr_gpio = GPIO_DT_SPEC_INST_GET(n, slptr_gpios), \
.dig2_gpio = GPIO_DT_SPEC_INST_GET_OR(n, dig2_gpios, {}), \
.clkm_gpio = GPIO_DT_SPEC_INST_GET_OR(n, clkm_gpios, {}), \
.spi = SPI_DT_SPEC_INST_GET(n, SPI_WORD_SET(8) | \
SPI_TRANSFER_MSB, 0), \
}
#define IEEE802154_RF2XX_DEVICE_DATA(n) \
static struct rf2xx_context rf2xx_ctx_data_##n = { \
.mac_addr = DRV_INST_LOCAL_MAC_ADDRESS(n) \
}
#define IEEE802154_RF2XX_RAW_DEVICE_INIT(n) \
DEVICE_DT_INST_DEFINE( \
n, \
&rf2xx_init, \
NULL, \
&rf2xx_ctx_data_##n, \
&rf2xx_ctx_config_##n, \
POST_KERNEL, \
CONFIG_IEEE802154_RF2XX_INIT_PRIO, \
&rf2xx_radio_api)
#define IEEE802154_RF2XX_NET_DEVICE_INIT(n) \
NET_DEVICE_DT_INST_DEFINE( \
n, \
&rf2xx_init, \
NULL, \
&rf2xx_ctx_data_##n, \
&rf2xx_ctx_config_##n, \
CONFIG_IEEE802154_RF2XX_INIT_PRIO, \
&rf2xx_radio_api, \
L2, \
L2_CTX_TYPE, \
MTU)
#define IEEE802154_RF2XX_INIT(inst) \
IEEE802154_RF2XX_DEVICE_CONFIG(inst); \
IEEE802154_RF2XX_DEVICE_DATA(inst); \
\
COND_CODE_1(CONFIG_IEEE802154_RAW_MODE, \
(IEEE802154_RF2XX_RAW_DEVICE_INIT(inst);), \
(IEEE802154_RF2XX_NET_DEVICE_INIT(inst);))
DT_INST_FOREACH_STATUS_OKAY(IEEE802154_RF2XX_INIT)