|  | /* 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/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, | 
|  | .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 == RF2XX_TRX_CC_PAGE_0) { | 
|  | base = channel == 0 | 
|  | ? RF2XX_RSSI_BPSK_20 | 
|  | : RF2XX_RSSI_BPSK_40; | 
|  | } else if (ctx->cc_page == RF2XX_TRX_CC_PAGE_2) { | 
|  | 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 (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_rx_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_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("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 *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); | 
|  | } | 
|  | } | 
|  | 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) | 
|  | { | 
|  | struct rf2xx_context *ctx = dev->data; | 
|  |  | 
|  | 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 | | 
|  | (ctx->trx_model == RF2XX_TRX_MODEL_212 | 
|  | ? IEEE802154_HW_SUB_GHZ | 
|  | : IEEE802154_HW_2_4_GHZ); | 
|  | } | 
|  |  | 
|  | 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 == RF2XX_TRX_CC_PAGE_0) { | 
|  | cc_mask = channel == 0 | 
|  | ? RF2XX_CC_BPSK_20 | 
|  | : RF2XX_CC_BPSK_40; | 
|  | } else if (ctx->cc_page == RF2XX_TRX_CC_PAGE_2) { | 
|  | 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 == RF2XX_TRX_CC_PAGE_0) { | 
|  | 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 == RF2XX_TRX_CC_PAGE_0 | 
|  | || ctx->cc_page == RF2XX_TRX_CC_PAGE_2) | 
|  | && channel > 10) { | 
|  | LOG_ERR("Unsupported channel %u", channel); | 
|  | return -EINVAL; | 
|  | } | 
|  | if (ctx->cc_page == RF2XX_TRX_CC_PAGE_5 && channel > 3) { | 
|  | LOG_ERR("Unsupported channel %u", channel); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | rf2xx_configure_sub_channel(dev, channel); | 
|  | rf2xx_configure_trx_path(dev); | 
|  | rf2xx_set_rssi_base(dev, channel); | 
|  | } else { | 
|  | 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) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | 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; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | uint16_t rf2xx_get_subgiga_channel_count(const struct device *dev) | 
|  | { | 
|  | struct rf2xx_context *ctx = dev->data; | 
|  |  | 
|  | return ctx->cc_page == RF2XX_TRX_CC_PAGE_5 ? 4 : 11; | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | /* 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 (!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_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, | 
|  | (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); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | 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, | 
|  | .get_subg_channel_count	= rf2xx_get_subgiga_channel_count, | 
|  | }; | 
|  |  | 
|  | #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 = DT_INST_ENUM_IDX_OR(n, channel_page, 0),	\ | 
|  | } | 
|  |  | 
|  | #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) |