You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
830 lines
19 KiB
830 lines
19 KiB
/* ieee802154_cc1200.c - TI CC1200 driver */ |
|
|
|
#define DT_DRV_COMPAT ti_cc1200 |
|
|
|
/* |
|
* Copyright (c) 2017 Intel Corporation. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define LOG_MODULE_NAME ieee802154_cc1200 |
|
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL |
|
|
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME); |
|
|
|
#include <errno.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/drivers/spi.h> |
|
#include <zephyr/drivers/gpio.h> |
|
|
|
#include <zephyr/net/ieee802154_radio.h> |
|
|
|
#include "ieee802154_cc1200.h" |
|
#include "ieee802154_cc1200_rf.h" |
|
|
|
/* ToDo: supporting 802.15.4g will require GPIO2 |
|
* used as CC1200_GPIO_SIG_RXFIFO_THR |
|
* |
|
* Note: GPIO3 is unused. |
|
*/ |
|
#define CC1200_IOCFG3 CC1200_GPIO_SIG_MARC_2PIN_STATUS_0 |
|
#define CC1200_IOCFG2 CC1200_GPIO_SIG_MARC_2PIN_STATUS_1 |
|
#define CC1200_IOCFG0 CC1200_GPIO_SIG_PKT_SYNC_RXTX |
|
|
|
/*********************** |
|
* Debugging functions * |
|
**********************/ |
|
static void cc1200_print_status(uint8_t status) |
|
{ |
|
if (status == CC1200_STATUS_IDLE) { |
|
LOG_DBG("Idling"); |
|
} else if (status == CC1200_STATUS_RX) { |
|
LOG_DBG("Receiving"); |
|
} else if (status == CC1200_STATUS_TX) { |
|
LOG_DBG("Transmitting"); |
|
} else if (status == CC1200_STATUS_FSTXON) { |
|
LOG_DBG("FS TX on"); |
|
} else if (status == CC1200_STATUS_CALIBRATE) { |
|
LOG_DBG("Calibrating"); |
|
} else if (status == CC1200_STATUS_SETTLING) { |
|
LOG_DBG("Settling"); |
|
} else if (status == CC1200_STATUS_RX_FIFO_ERROR) { |
|
LOG_DBG("RX FIFO error!"); |
|
} else if (status == CC1200_STATUS_TX_FIFO_ERROR) { |
|
LOG_DBG("TX FIFO error!"); |
|
} |
|
} |
|
|
|
/********************* |
|
* Generic functions * |
|
********************/ |
|
|
|
bool z_cc1200_access_reg(const struct device *dev, bool read, uint8_t addr, |
|
void *data, size_t length, bool extended, bool burst) |
|
{ |
|
const struct cc1200_config *config = dev->config; |
|
uint8_t cmd_buf[2]; |
|
const struct spi_buf buf[2] = { |
|
{ |
|
.buf = cmd_buf, |
|
.len = extended ? 2 : 1, |
|
}, |
|
{ |
|
.buf = data, |
|
.len = length, |
|
|
|
} |
|
}; |
|
struct spi_buf_set tx = { .buffers = buf }; |
|
|
|
cmd_buf[0] = 0U; |
|
|
|
if (burst) { |
|
cmd_buf[0] |= CC1200_ACCESS_BURST; |
|
} |
|
|
|
if (extended) { |
|
cmd_buf[0] |= CC1200_REG_EXTENDED_ADDRESS; |
|
cmd_buf[1] = addr; |
|
} else { |
|
cmd_buf[0] |= addr; |
|
} |
|
|
|
if (read) { |
|
const struct spi_buf_set rx = { |
|
.buffers = buf, |
|
.count = 2 |
|
}; |
|
|
|
cmd_buf[0] |= CC1200_ACCESS_RD; |
|
|
|
tx.count = 1; |
|
|
|
return (spi_transceive_dt(&config->bus, &tx, &rx) == 0); |
|
} |
|
|
|
/* CC1200_ACCESS_WR is 0 so no need to play with it */ |
|
tx.count = data ? 2 : 1; |
|
|
|
return (spi_write_dt(&config->bus, &tx) == 0); |
|
} |
|
|
|
static inline uint8_t *get_mac(const struct device *dev) |
|
{ |
|
struct cc1200_context *cc1200 = dev->data; |
|
|
|
#if defined(CONFIG_IEEE802154_CC1200_RANDOM_MAC) |
|
sys_rand_get(&cc1200->mac_addr[4], 4U); |
|
|
|
cc1200->mac_addr[7] = (cc1200->mac_addr[7] & ~0x01) | 0x02; |
|
#else |
|
cc1200->mac_addr[4] = CONFIG_IEEE802154_CC1200_MAC4; |
|
cc1200->mac_addr[5] = CONFIG_IEEE802154_CC1200_MAC5; |
|
cc1200->mac_addr[6] = CONFIG_IEEE802154_CC1200_MAC6; |
|
cc1200->mac_addr[7] = CONFIG_IEEE802154_CC1200_MAC7; |
|
#endif |
|
|
|
cc1200->mac_addr[0] = 0x00; |
|
cc1200->mac_addr[1] = 0x12; |
|
cc1200->mac_addr[2] = 0x4b; |
|
cc1200->mac_addr[3] = 0x00; |
|
|
|
return cc1200->mac_addr; |
|
} |
|
|
|
static uint8_t get_status(const struct device *dev) |
|
{ |
|
uint8_t val; |
|
|
|
if (z_cc1200_access_reg(dev, true, CC1200_INS_SNOP, |
|
&val, 1, false, false)) { |
|
/* See Section 3.1.2 */ |
|
return val & CC1200_STATUS_MASK; |
|
} |
|
|
|
/* We cannot get the status, so let's assume about readiness */ |
|
return CC1200_STATUS_CHIP_NOT_READY; |
|
} |
|
|
|
/****************** |
|
* GPIO functions * |
|
*****************/ |
|
|
|
static inline void gpio0_int_handler(const struct device *port, |
|
struct gpio_callback *cb, uint32_t pins) |
|
{ |
|
struct cc1200_context *cc1200 = |
|
CONTAINER_OF(cb, struct cc1200_context, rx_tx_cb); |
|
|
|
if (atomic_get(&cc1200->tx) == 1) { |
|
if (atomic_get(&cc1200->tx_start) == 0) { |
|
atomic_set(&cc1200->tx_start, 1); |
|
} else { |
|
atomic_set(&cc1200->tx, 0); |
|
} |
|
|
|
k_sem_give(&cc1200->tx_sync); |
|
} else { |
|
if (atomic_get(&cc1200->rx) == 1) { |
|
k_sem_give(&cc1200->rx_lock); |
|
atomic_set(&cc1200->rx, 0); |
|
} else { |
|
atomic_set(&cc1200->rx, 1); |
|
} |
|
} |
|
} |
|
|
|
static void enable_gpio0_interrupt(const struct device *dev, bool enable) |
|
{ |
|
const struct cc1200_config *cfg = dev->config; |
|
gpio_flags_t mode = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE; |
|
|
|
gpio_pin_interrupt_configure_dt(&cfg->interrupt, mode); |
|
} |
|
|
|
static int setup_gpio_callback(const struct device *dev) |
|
{ |
|
const struct cc1200_config *cfg = dev->config; |
|
struct cc1200_context *cc1200 = dev->data; |
|
|
|
gpio_init_callback(&cc1200->rx_tx_cb, gpio0_int_handler, BIT(cfg->interrupt.pin)); |
|
|
|
if (gpio_add_callback(cfg->interrupt.port, &cc1200->rx_tx_cb) != 0) { |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/**************** |
|
* RF functions * |
|
***************/ |
|
|
|
static uint8_t get_lo_divider(const struct device *dev) |
|
{ |
|
/* See Table 34 */ |
|
return FSD_BANDSELECT(read_reg_fs_cfg(dev)) << 1; |
|
} |
|
|
|
static bool write_reg_freq(const struct device *dev, uint32_t freq) |
|
{ |
|
uint8_t freq_data[3]; |
|
|
|
freq_data[0] = (uint8_t)((freq & 0x00FF0000) >> 16); |
|
freq_data[1] = (uint8_t)((freq & 0x0000FF00) >> 8); |
|
freq_data[2] = (uint8_t)(freq & 0x000000FF); |
|
|
|
return z_cc1200_access_reg(dev, false, CC1200_REG_FREQ2, |
|
freq_data, 3, true, true); |
|
} |
|
|
|
|
|
/* See Section 9.12 - RF programming |
|
* |
|
* The given formula in datasheet cannot be simply applied here, where CPU |
|
* limits us to unsigned integers of 32 bits. Instead, "slicing" it to |
|
* parts that fits in such limit is a solution which is applied below. |
|
* |
|
* The original formula being (freqoff is neglected): |
|
* Freq = ( RF * Lo_Div * 2^16 ) / Xtal |
|
* |
|
* RF and Xtal are, from here, expressed in KHz. |
|
* |
|
* It first calculates the targeted RF with given ChanCenterFreq0, channel |
|
* spacing and the channel number. |
|
* |
|
* The calculation will slice the targeted RF by multiple of 10: |
|
* 10^n where n is in [5, 3]. The rest, below 1000, is taken at once. |
|
* Let's take the 434000 KHz RF for instance: |
|
* it will be "sliced" in 3 parts: 400000, 30000, 4000. |
|
* Or the 169406 KHz RF, 4 parts: 100000, 60000, 9000, 406. |
|
* |
|
* This permits also to play with Xtal to keep the result big enough to avoid |
|
* losing precision. A factor - growing as much as Xtal decrease - is then |
|
* applied to get to the proper result. Which one is rounded to the nearest |
|
* integer, again to get a bit better precision. |
|
* |
|
* In the end, this algorithm below works for all the supported bands by CC1200. |
|
* User does not need to pass anything extra besides the nominal settings: no |
|
* pre-computed part or else. |
|
*/ |
|
static uint32_t rf_evaluate_freq_setting(const struct device *dev, uint32_t chan) |
|
{ |
|
struct cc1200_context *ctx = dev->data; |
|
uint32_t xtal = CONFIG_IEEE802154_CC1200_XOSC; |
|
uint32_t mult_10 = 100000U; |
|
uint32_t factor = 1U; |
|
uint32_t freq = 0U; |
|
uint32_t rf, lo_div; |
|
|
|
rf = ctx->rf_settings->chan_center_freq0 + |
|
((chan * (uint32_t)ctx->rf_settings->channel_spacing) / 10U); |
|
lo_div = get_lo_divider(dev); |
|
|
|
LOG_DBG("Calculating freq for %u KHz RF (%u)", rf, lo_div); |
|
|
|
while (rf > 0) { |
|
uint32_t hz, freq_tmp, rst; |
|
|
|
if (rf < 1000) { |
|
hz = rf; |
|
} else { |
|
hz = rf / mult_10; |
|
hz *= mult_10; |
|
} |
|
|
|
if (hz < 1000) { |
|
freq_tmp = (hz * lo_div * 65536U) / xtal; |
|
} else { |
|
freq_tmp = ((hz * lo_div) / xtal) * 65536U; |
|
} |
|
|
|
rst = freq_tmp % factor; |
|
freq_tmp /= factor; |
|
|
|
if (factor > 1 && (rst/(factor/10U)) > 5) { |
|
freq_tmp++; |
|
} |
|
|
|
freq += freq_tmp; |
|
|
|
factor *= 10U; |
|
mult_10 /= 10U; |
|
xtal /= 10U; |
|
rf -= hz; |
|
} |
|
|
|
LOG_DBG("FREQ is 0x%06X", freq); |
|
|
|
return freq; |
|
} |
|
|
|
static bool |
|
rf_install_settings(const struct device *dev, |
|
const struct cc1200_rf_registers_set *rf_settings) |
|
{ |
|
struct cc1200_context *cc1200 = dev->data; |
|
|
|
if (!z_cc1200_access_reg(dev, false, CC1200_REG_SYNC3, |
|
(void *)rf_settings->registers, |
|
CC1200_RF_NON_EXT_SPACE_REGS, false, true) || |
|
!z_cc1200_access_reg(dev, false, CC1200_REG_IF_MIX_CFG, |
|
(uint8_t *)rf_settings->registers |
|
+ CC1200_RF_NON_EXT_SPACE_REGS, |
|
CC1200_RF_EXT_SPACE_REGS, true, true) || |
|
!write_reg_pkt_len(dev, 0xFF)) { |
|
LOG_ERR("Could not install RF settings"); |
|
return false; |
|
} |
|
|
|
cc1200->rf_settings = rf_settings; |
|
|
|
return true; |
|
} |
|
|
|
static int rf_calibrate(const struct device *dev) |
|
{ |
|
if (!instruct_scal(dev)) { |
|
LOG_ERR("Could not calibrate RF"); |
|
return -EIO; |
|
} |
|
|
|
k_busy_wait(USEC_PER_MSEC * 5U); |
|
|
|
/* We need to re-enable RX as SCAL shuts off the freq synth */ |
|
if (!instruct_sidle(dev) || |
|
!instruct_sfrx(dev) || |
|
!instruct_srx(dev)) { |
|
LOG_ERR("Could not switch to RX"); |
|
return -EIO; |
|
} |
|
|
|
k_busy_wait(USEC_PER_MSEC * 10U); |
|
|
|
cc1200_print_status(get_status(dev)); |
|
|
|
return 0; |
|
} |
|
|
|
/**************** |
|
* TX functions * |
|
***************/ |
|
|
|
static inline bool write_txfifo(const struct device *dev, |
|
void *data, size_t length) |
|
{ |
|
return z_cc1200_access_reg(dev, false, |
|
CC1200_REG_TXFIFO, |
|
data, length, false, true); |
|
} |
|
|
|
/**************** |
|
* RX functions * |
|
***************/ |
|
|
|
static inline bool read_rxfifo(const struct device *dev, |
|
void *data, size_t length) |
|
{ |
|
return z_cc1200_access_reg(dev, true, |
|
CC1200_REG_RXFIFO, |
|
data, length, false, true); |
|
} |
|
|
|
static inline uint8_t get_packet_length(const struct device *dev) |
|
{ |
|
uint8_t len; |
|
|
|
if (z_cc1200_access_reg(dev, true, CC1200_REG_RXFIFO, |
|
&len, 1, false, true)) { |
|
return len; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static inline bool verify_rxfifo_validity(const struct device *dev, |
|
uint8_t pkt_len) |
|
{ |
|
/* packet should be at least 3 bytes as a ACK */ |
|
if (pkt_len < 3 || |
|
read_reg_num_rxbytes(dev) > (pkt_len + CC1200_FCS_LEN)) { |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
static inline bool read_rxfifo_content(const struct device *dev, |
|
struct net_buf *buf, uint8_t len) |
|
{ |
|
|
|
if (!read_rxfifo(dev, buf->data, len) || |
|
(get_status(dev) == CC1200_STATUS_RX_FIFO_ERROR)) { |
|
return false; |
|
} |
|
|
|
net_buf_add(buf, len); |
|
|
|
return true; |
|
} |
|
|
|
static inline bool verify_crc(const struct device *dev, struct net_pkt *pkt) |
|
{ |
|
uint8_t status[2]; |
|
int8_t rssi; |
|
|
|
if (!read_rxfifo(dev, status, 2)) { |
|
return false; |
|
} |
|
|
|
if (!(status[1] & CC1200_FCS_CRC_OK)) { |
|
return false; |
|
} |
|
|
|
rssi = (int8_t) status[0]; |
|
net_pkt_set_ieee802154_rssi_dbm( |
|
pkt, rssi == CC1200_INVALID_RSSI ? IEEE802154_MAC_RSSI_DBM_UNDEFINED : rssi); |
|
net_pkt_set_ieee802154_lqi(pkt, status[1] & CC1200_FCS_LQI_MASK); |
|
|
|
return true; |
|
} |
|
|
|
static void cc1200_rx(void *p1, void *p2, void *p3) |
|
{ |
|
ARG_UNUSED(p2); |
|
ARG_UNUSED(p3); |
|
|
|
const struct device *dev = p1; |
|
struct cc1200_context *cc1200 = dev->data; |
|
struct net_pkt *pkt; |
|
uint8_t pkt_len; |
|
|
|
while (1) { |
|
pkt = NULL; |
|
|
|
k_sem_take(&cc1200->rx_lock, K_FOREVER); |
|
|
|
if (get_status(dev) == CC1200_STATUS_RX_FIFO_ERROR) { |
|
LOG_ERR("Fifo error"); |
|
goto flush; |
|
} |
|
|
|
pkt_len = get_packet_length(dev); |
|
if (!verify_rxfifo_validity(dev, pkt_len)) { |
|
LOG_ERR("Invalid frame"); |
|
goto flush; |
|
} |
|
|
|
pkt = net_pkt_rx_alloc_with_buffer(cc1200->iface, pkt_len, |
|
AF_UNSPEC, 0, K_NO_WAIT); |
|
if (!pkt) { |
|
LOG_ERR("No free pkt available"); |
|
goto flush; |
|
} |
|
|
|
if (!read_rxfifo_content(dev, pkt->buffer, pkt_len)) { |
|
LOG_ERR("No content read"); |
|
goto flush; |
|
} |
|
|
|
if (!verify_crc(dev, pkt)) { |
|
LOG_ERR("Bad packet CRC"); |
|
goto out; |
|
} |
|
|
|
if (ieee802154_handle_ack(cc1200->iface, pkt) == NET_OK) { |
|
LOG_DBG("ACK packet handled"); |
|
goto out; |
|
} |
|
|
|
LOG_DBG("Caught a packet (%u)", pkt_len); |
|
|
|
if (net_recv_data(cc1200->iface, pkt) < 0) { |
|
LOG_DBG("Packet dropped by NET stack"); |
|
goto out; |
|
} |
|
|
|
log_stack_usage(&cc1200->rx_thread); |
|
continue; |
|
flush: |
|
LOG_DBG("Flushing RX"); |
|
instruct_sidle(dev); |
|
instruct_sfrx(dev); |
|
instruct_srx(dev); |
|
out: |
|
if (pkt) { |
|
net_pkt_unref(pkt); |
|
} |
|
|
|
} |
|
} |
|
|
|
|
|
/******************** |
|
* Radio device API * |
|
*******************/ |
|
static enum ieee802154_hw_caps cc1200_get_capabilities(const struct device *dev) |
|
{ |
|
return IEEE802154_HW_FCS; |
|
} |
|
|
|
static int cc1200_cca(const struct device *dev) |
|
{ |
|
struct cc1200_context *cc1200 = dev->data; |
|
|
|
if (atomic_get(&cc1200->rx) == 0) { |
|
uint8_t status = read_reg_rssi0(dev); |
|
|
|
if (!(status & CARRIER_SENSE) && |
|
(status & CARRIER_SENSE_VALID)) { |
|
return 0; |
|
} |
|
} |
|
|
|
LOG_WRN("Busy"); |
|
|
|
return -EBUSY; |
|
} |
|
|
|
static int cc1200_set_channel(const struct device *dev, uint16_t channel) |
|
{ |
|
struct cc1200_context *cc1200 = dev->data; |
|
uint32_t freq; |
|
|
|
/* As SUN FSK provides a host of configurations with extremely different |
|
* channel counts it doesn't make sense to validate (aka -EINVAL) a |
|
* global upper limit on the number of supported channels on this page. |
|
*/ |
|
if (channel > IEEE802154_CC1200_CHANNEL_LIMIT) { |
|
return -ENOTSUP; |
|
} |
|
|
|
/* Unlike usual 15.4 chips, cc1200 is closer to a bare metal radio modem |
|
* and thus does not provide any means to select a channel directly, but |
|
* requires instead that one calculates and configures the actual |
|
* targeted frequency for the requested channel. |
|
* |
|
* See rf_evaluate_freq_setting() above. |
|
*/ |
|
|
|
if (atomic_get(&cc1200->rx) != 0) { |
|
return -EIO; |
|
} |
|
|
|
freq = rf_evaluate_freq_setting(dev, channel); |
|
|
|
if (!write_reg_freq(dev, freq) || |
|
rf_calibrate(dev)) { |
|
LOG_ERR("Could not set channel %u", channel); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int cc1200_set_txpower(const struct device *dev, int16_t dbm) |
|
{ |
|
uint8_t pa_power_ramp; |
|
|
|
LOG_DBG("%d dbm", dbm); |
|
|
|
/* See Section 7.1 */ |
|
dbm = ((dbm + 18) * 2) - 1; |
|
if ((dbm <= 3) || (dbm >= 64)) { |
|
LOG_ERR("Unhandled value"); |
|
return -EINVAL; |
|
} |
|
|
|
pa_power_ramp = read_reg_pa_cfg1(dev) & ~PA_POWER_RAMP_MASK; |
|
pa_power_ramp |= ((uint8_t) dbm) & PA_POWER_RAMP_MASK; |
|
|
|
if (!write_reg_pa_cfg1(dev, pa_power_ramp)) { |
|
LOG_ERR("Could not proceed"); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int cc1200_tx(const struct device *dev, |
|
enum ieee802154_tx_mode mode, |
|
struct net_pkt *pkt, |
|
struct net_buf *frag) |
|
{ |
|
struct cc1200_context *cc1200 = dev->data; |
|
uint8_t *frame = frag->data; |
|
uint8_t len = frag->len; |
|
bool status = false; |
|
|
|
if (mode != IEEE802154_TX_MODE_DIRECT) { |
|
NET_ERR("TX mode %d not supported", mode); |
|
return -ENOTSUP; |
|
} |
|
|
|
LOG_DBG("%p (%u)", frag, len); |
|
|
|
/* ToDo: |
|
* Supporting 802.15.4g will require to loop in pkt's frags |
|
* depending on len value, this will also take more time. |
|
*/ |
|
|
|
if (!instruct_sidle(dev) || |
|
!instruct_sfrx(dev) || |
|
!instruct_sftx(dev) || |
|
!instruct_sfstxon(dev)) { |
|
LOG_ERR("Cannot switch to TX mode"); |
|
goto out; |
|
} |
|
|
|
if (!write_txfifo(dev, &len, CC1200_PHY_HDR_LEN) || |
|
!write_txfifo(dev, frame, len) || |
|
read_reg_num_txbytes(dev) != (len + CC1200_PHY_HDR_LEN)) { |
|
LOG_ERR("Cannot fill-in TX fifo"); |
|
goto out; |
|
} |
|
|
|
atomic_set(&cc1200->tx, 1); |
|
atomic_set(&cc1200->tx_start, 0); |
|
|
|
if (!instruct_stx(dev)) { |
|
LOG_ERR("Cannot start transmission"); |
|
goto out; |
|
} |
|
|
|
/* Wait for SYNC to be sent */ |
|
k_sem_take(&cc1200->tx_sync, K_MSEC(100)); |
|
if (atomic_get(&cc1200->tx_start) == 1) { |
|
/* Now wait for the packet to be fully sent */ |
|
k_sem_take(&cc1200->tx_sync, K_MSEC(100)); |
|
} |
|
|
|
out: |
|
cc1200_print_status(get_status(dev)); |
|
|
|
if (atomic_get(&cc1200->tx) == 1 && |
|
read_reg_num_txbytes(dev) != 0) { |
|
LOG_ERR("TX Failed"); |
|
|
|
atomic_set(&cc1200->tx_start, 0); |
|
instruct_sftx(dev); |
|
status = false; |
|
} else { |
|
status = true; |
|
} |
|
|
|
atomic_set(&cc1200->tx, 0); |
|
|
|
/* Get back to RX */ |
|
instruct_srx(dev); |
|
|
|
return status ? 0 : -EIO; |
|
} |
|
|
|
static int cc1200_start(const struct device *dev) |
|
{ |
|
if (!instruct_sidle(dev) || |
|
!instruct_sftx(dev) || |
|
!instruct_sfrx(dev) || |
|
rf_calibrate(dev)) { |
|
LOG_ERR("Could not proceed"); |
|
return -EIO; |
|
} |
|
|
|
enable_gpio0_interrupt(dev, true); |
|
|
|
cc1200_print_status(get_status(dev)); |
|
|
|
return 0; |
|
} |
|
|
|
static int cc1200_stop(const struct device *dev) |
|
{ |
|
enable_gpio0_interrupt(dev, false); |
|
|
|
if (!instruct_spwd(dev)) { |
|
LOG_ERR("Could not proceed"); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/* driver-allocated attribute memory - constant across all driver instances as |
|
* this driver's channel range is configured via a global KConfig setting. |
|
*/ |
|
IEEE802154_DEFINE_PHY_SUPPORTED_CHANNELS(drv_attr, 0, IEEE802154_CC1200_CHANNEL_LIMIT); |
|
|
|
static int cc1200_attr_get(const struct device *dev, enum ieee802154_attr attr, |
|
struct ieee802154_attr_value *value) |
|
{ |
|
ARG_UNUSED(dev); |
|
|
|
return ieee802154_attr_get_channel_page_and_range( |
|
attr, IEEE802154_ATTR_PHY_CHANNEL_PAGE_NINE_SUN_PREDEFINED, |
|
&drv_attr.phy_supported_channels, value); |
|
} |
|
|
|
/****************** |
|
* Initialization * |
|
*****************/ |
|
|
|
static int power_on_and_setup(const struct device *dev) |
|
{ |
|
if (!instruct_sres(dev)) { |
|
LOG_ERR("Cannot reset"); |
|
return -EIO; |
|
} |
|
|
|
if (!rf_install_settings(dev, &cc1200_rf_settings)) { |
|
return -EIO; |
|
} |
|
|
|
if (!write_reg_iocfg3(dev, CC1200_IOCFG3) || |
|
!write_reg_iocfg2(dev, CC1200_IOCFG2) || |
|
!write_reg_iocfg0(dev, CC1200_IOCFG0)) { |
|
LOG_ERR("Cannot configure GPIOs"); |
|
return -EIO; |
|
} |
|
|
|
if (setup_gpio_callback(dev) != 0) { |
|
return -EIO; |
|
} |
|
|
|
return rf_calibrate(dev); |
|
} |
|
|
|
static int cc1200_init(const struct device *dev) |
|
{ |
|
const struct cc1200_config *config = dev->config; |
|
struct cc1200_context *cc1200 = dev->data; |
|
|
|
atomic_set(&cc1200->tx, 0); |
|
atomic_set(&cc1200->tx_start, 0); |
|
atomic_set(&cc1200->rx, 0); |
|
k_sem_init(&cc1200->rx_lock, 0, 1); |
|
k_sem_init(&cc1200->tx_sync, 0, 1); |
|
|
|
/* Configure GPIOs */ |
|
if (!gpio_is_ready_dt(&config->interrupt)) { |
|
LOG_ERR("GPIO port %s is not ready", |
|
config->interrupt.port->name); |
|
return -ENODEV; |
|
} |
|
gpio_pin_configure_dt(&config->interrupt, GPIO_INPUT); |
|
|
|
if (!spi_is_ready_dt(&config->bus)) { |
|
LOG_ERR("SPI bus %s is not ready", config->bus.bus->name); |
|
return -ENODEV; |
|
} |
|
|
|
LOG_DBG("GPIO and SPI configured"); |
|
if (power_on_and_setup(dev) != 0) { |
|
LOG_ERR("Configuring CC1200 failed"); |
|
return -EIO; |
|
} |
|
|
|
k_thread_create(&cc1200->rx_thread, cc1200->rx_stack, |
|
CONFIG_IEEE802154_CC1200_RX_STACK_SIZE, |
|
cc1200_rx, |
|
(void *)dev, NULL, NULL, K_PRIO_COOP(2), 0, K_NO_WAIT); |
|
k_thread_name_set(&cc1200->rx_thread, "cc1200_rx"); |
|
|
|
LOG_INF("CC1200 initialized"); |
|
|
|
return 0; |
|
} |
|
|
|
static void cc1200_iface_init(struct net_if *iface) |
|
{ |
|
const struct device *dev = net_if_get_device(iface); |
|
struct cc1200_context *cc1200 = dev->data; |
|
uint8_t *mac = get_mac(dev); |
|
|
|
LOG_DBG(""); |
|
|
|
net_if_set_link_addr(iface, mac, 8, NET_LINK_IEEE802154); |
|
|
|
cc1200->iface = iface; |
|
|
|
ieee802154_init(iface); |
|
} |
|
|
|
static const struct cc1200_config cc1200_config = { |
|
.bus = SPI_DT_SPEC_INST_GET(0, SPI_WORD_SET(8), 0), |
|
.interrupt = GPIO_DT_SPEC_INST_GET(0, int_gpios) |
|
}; |
|
|
|
static struct cc1200_context cc1200_context_data; |
|
|
|
static const struct ieee802154_radio_api cc1200_radio_api = { |
|
.iface_api.init = cc1200_iface_init, |
|
|
|
.get_capabilities = cc1200_get_capabilities, |
|
.cca = cc1200_cca, |
|
.set_channel = cc1200_set_channel, |
|
.set_txpower = cc1200_set_txpower, |
|
.tx = cc1200_tx, |
|
.start = cc1200_start, |
|
.stop = cc1200_stop, |
|
.attr_get = cc1200_attr_get, |
|
}; |
|
|
|
NET_DEVICE_DT_INST_DEFINE(0, cc1200_init, NULL, &cc1200_context_data, |
|
&cc1200_config, CONFIG_IEEE802154_CC1200_INIT_PRIO, |
|
&cc1200_radio_api, IEEE802154_L2, |
|
NET_L2_GET_CTX_TYPE(IEEE802154_L2), 125);
|
|
|