zephyr/drivers/ieee802154/ieee802154_rf2xx.c

1185 lines
32 KiB
C
Raw Permalink Normal View History

/* 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 <stdlib.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/random.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,
net: pkt: decouple from ieee802154 internals This change implements part two of the program laid out in the TSCH RFC, see #50336#issuecomment-1250250154 : > Consolidate IEEE 802.15.4 options in net_pkt This change improves decoupling of generic net core code from IEEE 802.15.4 internals. It also simplifies IEEE 802.15.4 attribute cloning and thereby makes it easier to maintain and less error prone (and probably even faster as individual bits are no longer copied over separately). This enables us to extend and design IEEE 802.15.4 L2 attributes inside the package in isolation from the net core code which will no longer have to be changed when introducing changes or additions to the flags. This flexibility will be built upon in later change sets to model the IEEE 802.15.4 attributes closer to the spec. The solution is inspired by Linux's sk_buff->cb attribute which addresses the same concern as the attribute introduced in this change set: https://elixir.bootlin.com/linux/v6.0.1/source/include/linux/skbuff.h#L871 As the inline comment says: The cb attribute can be made a union or even a uint8[something] in the future, if further L2s need a control block, too. Right now such full indirection would make the code overly abstract, so I chose to compromise with maintainability in mind. Care has been taken to ensure that this changes does not introduce additional padding into the net package. To maintain zero-padding, future changes to the net packet struct will have to ensure that the IEEE 802.15.4 struct is 4-byte aligned (iff the IEEE 802.15.4 struct continues with max uint32_t scalar members) which is no deviation from the previous implementation. Signed-off-by: Florian Grandel <jerico.dev@gmail.com>
2022-10-12 18:19:54 +02:00
.cb = {
.lqi = 80,
.rssi = -40,
}
};
#endif /* CONFIG_NET_L2_OPENTHREAD */
/* 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_set_rssi_base(const struct device *dev, uint16_t channel)
{
struct rf2xx_context *ctx = dev->data;
int8_t base;
if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915) {
base = channel == 0
? RF2XX_RSSI_BPSK_20
: RF2XX_RSSI_BPSK_40;
} else if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915) {
base = channel == 0
? RF2XX_RSSI_OQPSK_SIN_RC_100
: RF2XX_RSSI_OQPSK_SIN_250;
} else {
base = RF2XX_RSSI_OQPSK_RC_250;
}
ctx->trx_rssi_base = base;
}
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_rx_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 (!ctx->promiscuous && 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);
if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
trac = rx_buf[pkt_len + RX2XX_FRAME_TRAC_INDEX];
trac = (trac >> RF2XX_RX_TRAC_STATUS) & RF2XX_RX_TRAC_BIT_MASK;
ctx->pkt_ed = rx_buf[pkt_len + RX2XX_FRAME_ED_INDEX];
} else {
trac = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATE_REG)
>> RF2XX_TRAC_STATUS) & RF2XX_TRAC_BIT_MASK;
ctx->pkt_ed = (rf2xx_iface_reg_read(dev, RF2XX_PHY_RSSI_REG)
>> RF2XX_RSSI) & RF2XX_RSSI_MASK;
}
ctx->pkt_lqi = rx_buf[pkt_len + RX2XX_FRAME_LQI_INDEX];
if (!ctx->promiscuous && trac == RF2XX_TRX_PHY_STATE_TRAC_INVALID) {
LOG_ERR("Invalid RX frame");
return;
}
if (!IS_ENABLED(CONFIG_IEEE802154_RAW_MODE) &&
!IS_ENABLED(CONFIG_NET_L2_OPENTHREAD) &&
pkt_len >= RX2XX_FRAME_FCS_LENGTH) {
pkt_len -= RX2XX_FRAME_FCS_LENGTH;
}
pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, pkt_len,
AF_UNSPEC, 0, K_NO_WAIT);
if (!pkt) {
LOG_ERR("No RX buffer 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_dbm(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("RX 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) & RF2XX_TRAC_BIT_MASK;
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 *p1, void *p2, void *p3)
{
ARG_UNUSED(p2);
ARG_UNUSED(p3);
struct rf2xx_context *ctx = p1;
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);
}
}
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;
if (!conf->has_mac) {
sys_rand_get(ctx->mac_addr, sizeof(ctx->mac_addr));
}
/*
* 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)
{
LOG_DBG("HW Caps");
return IEEE802154_HW_FCS |
IEEE802154_HW_PROMISC |
IEEE802154_HW_FILTER |
IEEE802154_HW_CSMA |
IEEE802154_HW_RETRANSMISSION |
IEEE802154_HW_TX_RX_ACK |
IEEE802154_HW_RX_TX_ACK;
}
static int rf2xx_configure_sub_channel(const struct device *dev, uint16_t channel)
{
struct rf2xx_context *ctx = dev->data;
uint8_t reg;
uint8_t cc_mask;
if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915) {
cc_mask = channel == 0
? RF2XX_CC_BPSK_20
: RF2XX_CC_BPSK_40;
} else if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915) {
cc_mask = channel == 0
? RF2XX_CC_OQPSK_SIN_RC_100
: RF2XX_CC_OQPSK_SIN_250;
} else {
cc_mask = RF2XX_CC_OQPSK_RC_250;
}
reg = rf2xx_iface_reg_read(dev, RF2XX_TRX_CTRL_2_REG)
& ~RF2XX_SUB_CHANNEL_MASK;
rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_2_REG, reg | cc_mask);
return 0;
}
static int rf2xx_configure_trx_path(const struct device *dev)
{
struct rf2xx_context *ctx = dev->data;
uint8_t reg;
uint8_t gc_tx_offset;
if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915) {
gc_tx_offset = 0x03;
} else {
gc_tx_offset = 0x02;
}
reg = rf2xx_iface_reg_read(dev, RF2XX_RF_CTRL_0_REG)
& ~RF2XX_GC_TX_OFFS_MASK;
rf2xx_iface_reg_write(dev, RF2XX_RF_CTRL_0_REG, reg | gc_tx_offset);
return 0;
}
static int rf2xx_cca(const struct device *dev)
{
ARG_UNUSED(dev);
LOG_DBG("CCA");
return 0;
}
static int rf2xx_set_channel(const struct device *dev, uint16_t channel)
{
struct rf2xx_context *ctx = dev->data;
uint8_t reg;
LOG_DBG("Set Channel %d", channel);
if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
if ((ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915
|| ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915)
&& channel > 10) {
LOG_ERR("Unsupported channel %u", channel);
return channel > 26 ? -EINVAL : -ENOTSUP;
}
if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_FIVE_OQPSK_780 &&
channel > 3) {
LOG_ERR("Unsupported channel %u", channel);
return channel > 7 ? -EINVAL : -ENOTSUP;
}
rf2xx_configure_sub_channel(dev, channel);
rf2xx_configure_trx_path(dev);
rf2xx_set_rssi_base(dev, channel);
} else {
/* 2.4G O-QPSK, channel page zero */
if (channel < 11 || channel > 26) {
LOG_ERR("Unsupported channel %u", channel);
return channel < 11 ? -ENOTSUP : -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)
{
const struct rf2xx_config *conf = dev->config;
struct rf2xx_context *ctx = dev->data;
float min, max, step;
uint8_t reg;
uint8_t idx;
uint8_t val;
LOG_DBG("Try set Power to %d", dbm);
/**
* if table size is equal 1 the code assumes a table was not defined. In
* this case the transceiver PHY_TX_PWR register will be set with value
* zero. This is a safe value for all variants and represents an output
* power above 0 dBm.
*
* Note: This is a special case too which avoid division by zero when
* computing the step variable.
*/
if (conf->tx_pwr_table_size == 1) {
rf2xx_iface_reg_write(dev, RF2XX_PHY_TX_PWR_REG, 0);
return 0;
}
min = conf->tx_pwr_min[1];
if (conf->tx_pwr_min[0] == 0x01) {
min *= -1.0f;
}
max = conf->tx_pwr_max[1];
if (conf->tx_pwr_max[0] == 0x01) {
min *= -1.0f;
}
step = (max - min) / ((float)conf->tx_pwr_table_size - 1.0f);
if (step == 0.0f) {
step = 1.0f;
}
LOG_DBG("Tx-power values: min %f, max %f, step %f, entries %d",
(double)min, (double)max, (double)step, conf->tx_pwr_table_size);
if (dbm < min) {
LOG_INF("TX-power %d dBm below min of %f dBm, using %f dBm",
dbm, (double)min, (double)max);
dbm = min;
} else if (dbm > max) {
LOG_INF("TX-power %d dBm above max of %f dBm, using %f dBm",
dbm, (double)min, (double)max);
dbm = max;
}
idx = abs((int) (((float)(dbm - max) / step)));
LOG_DBG("Tx-power idx: %d", idx);
if (idx >= conf->tx_pwr_table_size) {
idx = conf->tx_pwr_table_size - 1;
}
val = conf->tx_pwr_table[idx];
if (ctx->trx_model != RF2XX_TRX_MODEL_212) {
reg = rf2xx_iface_reg_read(dev, RF2XX_PHY_TX_PWR_REG) & 0xf0;
val = reg + (val & 0x0f);
}
LOG_DBG("Tx-power normalized: %d dBm, PHY_TX_PWR 0x%02x, idx %d",
dbm, val, idx);
rf2xx_iface_reg_write(dev, RF2XX_PHY_TX_PWR_REG, val);
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_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;
LOG_DBG("TX");
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;
LOG_DBG("Start");
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;
LOG_DBG("Stop");
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)
{
struct rf2xx_context *ctx = dev->data;
uint8_t reg;
ctx->promiscuous = promiscuous;
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;
LOG_DBG("Configure %d", type);
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 rf2xx_attr_get(const struct device *dev, enum ieee802154_attr attr,
struct ieee802154_attr_value *value)
{
struct rf2xx_context *ctx = dev->data;
switch (attr) {
case IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_PAGES:
value->phy_supported_channel_pages = ctx->cc_page;
return 0;
case IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_RANGES:
value->phy_supported_channels = &ctx->cc_channels;
return 0;
default:
return -ENOENT;
}
}
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_230) {
LOG_DBG("Invalid or not supported transceiver");
return -ENODEV;
}
if (ctx->trx_model == RF2XX_TRX_MODEL_233 && ctx->trx_version == 0x01) {
LOG_DBG("Transceiver is old and unstable release");
}
/* Set RSSI base */
if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
ctx->trx_rssi_base = -100;
} else 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;
}
/* Disable All Features of TRX_CTRL_0 */
config = 0;
rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_0_REG, config);
/* 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);
if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
rf2xx_configure_trx_path(dev);
rf2xx_iface_reg_write(dev, RF2XX_CC_CTRL_1_REG, 0);
}
ctx->tx_mode = IEEE802154_TX_MODE_CSMA_CA;
ctx->promiscuous = false;
/* 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));
if (gpio_add_callback(conf->irq_gpio.port, &ctx->irq_cb) < 0) {
LOG_ERR("Could not set IRQ callback.");
return -ENXIO;
}
return 0;
}
static inline int configure_gpios(const struct device *dev)
{
const struct rf2xx_config *conf = dev->config;
/* Chip IRQ line */
if (!gpio_is_ready_dt(&conf->irq_gpio)) {
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 (!gpio_is_ready_dt(&conf->reset_gpio)) {
LOG_ERR("RESET GPIO device not ready");
return -ENODEV;
}
gpio_pin_configure_dt(&conf->reset_gpio, GPIO_OUTPUT_INACTIVE);
/* Chip SLPTR line */
if (!gpio_is_ready_dt(&conf->slptr_gpio)) {
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 (!gpio_is_ready_dt(&conf->dig2_gpio)) {
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 (!gpio_is_ready_dt(&conf->clkm_gpio)) {
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_dt(&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;
}
LOG_DBG("RADIO configured");
k_thread_create(&ctx->trx_thread,
ctx->trx_stack,
CONFIG_IEEE802154_RF2XX_RX_STACK_SIZE,
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);
LOG_DBG("Thread OK");
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;
if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915 ||
ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915) {
ctx->cc_range.from_channel = 0U;
ctx->cc_range.to_channel = 10U;
} else if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_FIVE_OQPSK_780) {
ctx->cc_range.from_channel = 0U;
ctx->cc_range.to_channel = 3U;
} else {
__ASSERT(false, "Unsupported channel page %u.", ctx->cc_page);
}
} else {
__ASSERT(ctx->cc_page ==
IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915,
"Unsupported channel page %u.", ctx->cc_page);
ctx->cc_range.from_channel = 11U;
ctx->cc_range.to_channel = 26U;
}
ieee802154_init(iface);
}
static const 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,
.attr_get = rf2xx_attr_get,
};
#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) \
BUILD_ASSERT(DT_INST_PROP_LEN(n, tx_pwr_min) == 2, \
"rf2xx: Error TX-PWR-MIN len is different of two"); \
BUILD_ASSERT(DT_INST_PROP_LEN(n, tx_pwr_max) == 2, \
"rf2xx: Error TX-PWR-MAX len is different of two"); \
BUILD_ASSERT(DT_INST_PROP_LEN(n, tx_pwr_table) != 0, \
"rf2xx: Error TX-PWR-TABLE len must be greater than zero"); \
static const uint8_t rf2xx_pwr_table_##n[] = \
DT_INST_PROP_OR(n, tx_pwr_table, 0); \
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), \
\
.tx_pwr_min = DT_INST_PROP_OR(n, tx_pwr_min, 0), \
.tx_pwr_max = DT_INST_PROP_OR(n, tx_pwr_max, 0), \
.tx_pwr_table = rf2xx_pwr_table_##n, \
.tx_pwr_table_size = DT_INST_PROP_LEN(n, tx_pwr_table), \
}
#define IEEE802154_RF2XX_DEVICE_DATA(n) \
static struct rf2xx_context rf2xx_ctx_data_##n = { \
.mac_addr = { DRV_INST_LOCAL_MAC_ADDRESS(n) }, \
.cc_page = BIT(DT_INST_ENUM_IDX_OR(n, channel_page, 0)),\
.cc_channels = { \
.ranges = &rf2xx_ctx_data_##n.cc_range, \
.num_ranges = 1U, \
} \
}
#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)