From 51d5bbec9cccfcd21cacd723b24be80d49d26333 Mon Sep 17 00:00:00 2001 From: Christopher Friedt Date: Tue, 3 Nov 2020 13:24:35 -0500 Subject: [PATCH] drivers: ieee802154: cc13xx_cc26xx: sub-ghz support This change adds IEEE 802.15.4g (Sub GHz) support for the cc1352r. The 2.4 GHz radio and the Sub GHz radio are capable of operating simultaneously. Fixes #26315 Signed-off-by: Christopher Friedt --- drivers/ieee802154/CMakeLists.txt | 1 + drivers/ieee802154/Kconfig.cc13xx_cc26xx | 34 + drivers/ieee802154/ieee802154_cc13xx_cc26xx.c | 8 +- .../ieee802154_cc13xx_cc26xx_subg.c | 754 ++++++++++++++++++ .../ieee802154_cc13xx_cc26xx_subg.h | 104 +++ include/net/ieee802154_radio.h | 20 +- .../echo_client/overlay-802154-subg.conf | 18 + .../echo_server/overlay-802154-subg.conf | 16 + .../cc13x2_cc26x2/Kconfig.defconfig.series | 7 +- soc/arm/ti_simplelink/cc13x2_cc26x2/power.c | 4 +- 10 files changed, 958 insertions(+), 8 deletions(-) create mode 100644 drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.c create mode 100644 drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.h create mode 100644 samples/net/sockets/echo_client/overlay-802154-subg.conf create mode 100644 samples/net/sockets/echo_server/overlay-802154-subg.conf diff --git a/drivers/ieee802154/CMakeLists.txt b/drivers/ieee802154/CMakeLists.txt index c4f4eb730c94..87311d9c4838 100644 --- a/drivers/ieee802154/CMakeLists.txt +++ b/drivers/ieee802154/CMakeLists.txt @@ -7,6 +7,7 @@ zephyr_sources_ifdef(CONFIG_IEEE802154_MCR20A ieee802154_mcr20a.c) zephyr_sources_ifdef(CONFIG_IEEE802154_NRF5 ieee802154_nrf5.c) zephyr_sources_ifdef(CONFIG_IEEE802154_CC1200 ieee802154_cc1200.c) zephyr_sources_ifdef(CONFIG_IEEE802154_CC13XX_CC26XX ieee802154_cc13xx_cc26xx.c) +zephyr_sources_ifdef(CONFIG_IEEE802154_CC13XX_CC26XX_SUB_GHZ ieee802154_cc13xx_cc26xx_subg.c) zephyr_sources_ifdef(CONFIG_IEEE802154_RF2XX ieee802154_rf2xx.c) zephyr_sources_ifdef(CONFIG_IEEE802154_RF2XX ieee802154_rf2xx_iface.c) zephyr_sources_ifdef(CONFIG_IEEE802154_DW1000 ieee802154_dw1000.c) diff --git a/drivers/ieee802154/Kconfig.cc13xx_cc26xx b/drivers/ieee802154/Kconfig.cc13xx_cc26xx index d8acc409eb3a..f8b6b59cb0d7 100644 --- a/drivers/ieee802154/Kconfig.cc13xx_cc26xx +++ b/drivers/ieee802154/Kconfig.cc13xx_cc26xx @@ -21,3 +21,37 @@ config IEEE802154_CC13XX_CC26XX_INIT_PRIO Set the initialization priority number. endif # IEEE802154_CC13XX_CC26XX + +menuconfig IEEE802154_CC13XX_CC26XX_SUB_GHZ + bool "TI CC13xx / CC26xx IEEE 802.15.4g driver support" + select NET_L2_IEEE802154_SUB_GHZ + +if IEEE802154_CC13XX_CC26XX_SUB_GHZ + +config IEEE802154_CC13XX_CC26XX_SUB_GHZ_DRV_NAME + string "TI CC13xx / CC26xx IEEE 802.15.4g driver's name" + default "IEEE802154_1" + help + This option sets the driver name. + +config IEEE802154_CC13XX_CC26XX_SUB_GHZ_NUM_RX_BUF + int "TI CC13xx / CC26xx IEEE 802.15.4g receive buffer count" + default 2 + help + This option allows the user to configure the number of + receive buffers. + +config IEEE802154_CC13XX_CC26XX_SUB_GHZ_CS_THRESHOLD + int "TI CC13xx / CC26xx IEEE 802.15.4g Carrier Sense Threshold in dBm" + default -70 + help + This option sets RSSI threshold for carrier sense in the CSMA/CA + algorithm. + +config IEEE802154_CC13XX_CC26XX_SUB_GHZ_INIT_PRIO + int "TI CC13xx / CC26xx IEEE 802.15.4g initialization priority" + default 80 + help + Set the initialization priority number. + +endif # IEEE802154_CC13XX_CC26XX_SUB_GHZ diff --git a/drivers/ieee802154/ieee802154_cc13xx_cc26xx.c b/drivers/ieee802154/ieee802154_cc13xx_cc26xx.c index 1a95651e98f9..76c85712fb84 100644 --- a/drivers/ieee802154/ieee802154_cc13xx_cc26xx.c +++ b/drivers/ieee802154/ieee802154_cc13xx_cc26xx.c @@ -447,10 +447,12 @@ static int ieee802154_cc13xx_cc26xx_stop(const struct device *dev) RF_Stat status; - status = RF_flushCmd(drv_data->rf_handle, RF_CMDHANDLE_FLUSH_ALL, RF_ABORT_PREEMPTION); - if (!(status == RF_StatCmdDoneSuccess || status == RF_StatSuccess + status = RF_flushCmd(drv_data->rf_handle, RF_CMDHANDLE_FLUSH_ALL, 0); + if (!(status == RF_StatCmdDoneSuccess + || status == RF_StatSuccess + || status == RF_StatRadioInactiveError || status == RF_StatInvalidParamsError)) { - LOG_ERR("Failed to abort radio operations (%d)", status); + LOG_DBG("Failed to abort radio operations (%d)", status); return -EIO; } diff --git a/drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.c b/drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.c new file mode 100644 index 000000000000..aed8bf8fdf12 --- /dev/null +++ b/drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.c @@ -0,0 +1,754 @@ +/* + * Copyright (c) 2020 Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL +#include +LOG_MODULE_REGISTER(ieee802154_cc13xx_cc26xx_subg); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "ieee802154_cc13xx_cc26xx_subg.h" + +static void ieee802154_cc13xx_cc26xx_subg_rx_done( + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data); +static void ieee802154_cc13xx_cc26xx_subg_data_init( + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data); +static int ieee802154_cc13xx_cc26xx_subg_stop( + const struct device *dev); +static int ieee802154_cc13xx_cc26xx_subg_rx( + const struct device *dev); +static void ieee802154_cc13xx_cc26xx_subg_setup_rx_buffers( + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data); + +DEVICE_DECLARE(ieee802154_cc13xx_cc26xx_subg); + +/* Overrides from SmartRF Studio 7 2.18.0 */ +static uint32_t overrides_sub_ghz[] = { + /* DC/DC regulator: In Tx, use DCDCCTL5[3:0]=0x7 (DITHER_EN=0 and IPEAK=7). */ + (uint32_t)0x00F788D3, + /* Set RF_FSCA.ANADIV.DIV_SEL_BIAS = 1. Bits [0:16, 24, 30] are don't care.. */ + (uint32_t)0x4001405D, + /* Set RF_FSCA.ANADIV.DIV_SEL_BIAS = 1. Bits [0:16, 24, 30] are don't care.. */ + (uint32_t)0x08141131, + /* Tx: Configure PA ramp time, PACTL2.RC=0x3 (in ADI0, set PACTL2[4:3]=0x3) */ + ADI_2HALFREG_OVERRIDE(0, 16, 0x8, 0x8, 17, 0x1, 0x1), + /* Tx: Configure PA ramping, set wait time before turning off (0x1A ticks of 16/24 us = 17.3 us). */ + HW_REG_OVERRIDE(0x6028, 0x001A), + /* Rx: Set AGC reference level to 0x16 (default: 0x2E) */ + HW_REG_OVERRIDE(0x609C, 0x0016), + /* Rx: Set RSSI offset to adjust reported RSSI by -1 dB (default: -2), trimmed for external bias and differential configuration */ + (uint32_t)0x000188A3, + /* Rx: Set anti-aliasing filter bandwidth to 0x8 (in ADI0, set IFAMPCTL3[7:4]=0x8) */ + ADI_HALFREG_OVERRIDE(0, 61, 0xF, 0x8), + /* Tx: Set PA trim to max to maximize its output power (in ADI0, set PACTL0=0xF8) */ + ADI_REG_OVERRIDE(0, 12, 0xF8), + (uint32_t)0xFFFFFFFF +}; + +/* Sub GHz power table */ +static const RF_TxPowerTable_Entry txPowerTable_sub_ghz[] = { + { -20, RF_TxPowerTable_DEFAULT_PA_ENTRY(0, 3, 0, 2) }, + { -15, RF_TxPowerTable_DEFAULT_PA_ENTRY(1, 3, 0, 3) }, + { -10, RF_TxPowerTable_DEFAULT_PA_ENTRY(2, 3, 0, 5) }, + { -5, RF_TxPowerTable_DEFAULT_PA_ENTRY(4, 3, 0, 5) }, + { 0, RF_TxPowerTable_DEFAULT_PA_ENTRY(8, 3, 0, 8) }, + { 1, RF_TxPowerTable_DEFAULT_PA_ENTRY(9, 3, 0, 9) }, + { 2, RF_TxPowerTable_DEFAULT_PA_ENTRY(10, 3, 0, 9) }, + { 3, RF_TxPowerTable_DEFAULT_PA_ENTRY(11, 3, 0, 10) }, + { 4, RF_TxPowerTable_DEFAULT_PA_ENTRY(13, 3, 0, 11) }, + { 5, RF_TxPowerTable_DEFAULT_PA_ENTRY(14, 3, 0, 14) }, + { 6, RF_TxPowerTable_DEFAULT_PA_ENTRY(17, 3, 0, 16) }, + { 7, RF_TxPowerTable_DEFAULT_PA_ENTRY(20, 3, 0, 19) }, + { 8, RF_TxPowerTable_DEFAULT_PA_ENTRY(24, 3, 0, 22) }, + { 9, RF_TxPowerTable_DEFAULT_PA_ENTRY(28, 3, 0, 31) }, + { 10, RF_TxPowerTable_DEFAULT_PA_ENTRY(18, 2, 0, 31) }, + { 11, RF_TxPowerTable_DEFAULT_PA_ENTRY(26, 2, 0, 51) }, + { 12, RF_TxPowerTable_DEFAULT_PA_ENTRY(16, 0, 0, 82) }, + { 13, RF_TxPowerTable_DEFAULT_PA_ENTRY(36, 0, 0, 89) }, + { 14, RF_TxPowerTable_DEFAULT_PA_ENTRY(63, 0, 1, 0) }, + RF_TxPowerTable_TERMINATION_ENTRY +}; + +static inline int ieee802154_cc13xx_cc26xx_subg_channel_to_frequency( + uint16_t channel, uint16_t *frequency, uint16_t *fractFreq) +{ + __ASSERT_NO_MSG(frequency != NULL); + __ASSERT_NO_MSG(fractFreq != NULL); + + if (channel == IEEE802154_SUB_GHZ_CHANNEL_MIN) { + *frequency = 868; + /* + * uint16_t fractional part of 868.3 MHz + * equivalent to (0.3 * 1000 * BIT(16)) / 1000, rounded up + */ + *fractFreq = 0x4ccd; + } else if (1 <= channel && channel <= IEEE802154_SUB_GHZ_CHANNEL_MAX) { + *frequency = 906 + 2 * (channel - 1); + *fractFreq = 0; + } else if (IEEE802154_2_4_GHZ_CHANNEL_MIN <= channel + && channel <= IEEE802154_2_4_GHZ_CHANNEL_MAX) { + *frequency = 2405 + 5 * (channel - IEEE802154_2_4_GHZ_CHANNEL_MIN); + *fractFreq = 0; + } else { + *frequency = 0; + *fractFreq = 0; + return -EINVAL; + } + + return 0; +} + +static inline bool is_subghz(uint16_t channel) +{ + return (channel <= IEEE802154_SUB_GHZ_CHANNEL_MAX); +} + +static inline struct ieee802154_cc13xx_cc26xx_subg_data * +get_dev_data(const struct device *dev) +{ + return dev->data; +} + +static void cmd_prop_tx_adv_callback(RF_Handle h, RF_CmdHandle ch, + RF_EventMask e) +{ + const struct device *dev = + &DEVICE_NAME_GET(ieee802154_cc13xx_cc26xx_subg); + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + RF_Op *op = RF_getCmdOp(h, ch); + + LOG_DBG("ch: %u cmd: %04x cs st: %04x tx st: %04x e: 0x%" PRIx64, ch, + op->commandNo, op->status, drv_data->cmd_prop_tx_adv.status, e); +} + +static void cmd_prop_rx_adv_callback(RF_Handle h, RF_CmdHandle ch, + RF_EventMask e) +{ + const struct device *dev = + &DEVICE_NAME_GET(ieee802154_cc13xx_cc26xx_subg); + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + RF_Op *op = RF_getCmdOp(h, ch); + + LOG_DBG("ch: %u cmd: %04x st: %04x e: 0x%" PRIx64, ch, + op->commandNo, op->status, e); + + if (e & RF_EventRxEntryDone) { + ieee802154_cc13xx_cc26xx_subg_rx_done(drv_data); + } + + if (op->status == PROP_ERROR_RXBUF + || op->status == PROP_ERROR_RXFULL + || op->status == PROP_ERROR_RXOVF) { + LOG_DBG("RX Error %x", op->status); + /* Restart RX */ + (void)ieee802154_cc13xx_cc26xx_subg_rx(dev); + } +} + +static void client_error_callback(RF_Handle h, RF_CmdHandle ch, + RF_EventMask e) +{ + ARG_UNUSED(h); + ARG_UNUSED(ch); + LOG_DBG("e: 0x%" PRIx64, e); +} + +static void client_event_callback(RF_Handle h, RF_ClientEvent event, + void *arg) +{ + ARG_UNUSED(h); + LOG_DBG("event: %d arg: %p", event, arg); +} + +static enum ieee802154_hw_caps +ieee802154_cc13xx_cc26xx_subg_get_capabilities(const struct device *dev) +{ + /* TODO: enable IEEE802154_HW_FILTER */ + return IEEE802154_HW_FCS | IEEE802154_HW_CSMA + | IEEE802154_HW_SUB_GHZ; +} + +static int ieee802154_cc13xx_cc26xx_subg_cca(const struct device *dev) +{ + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + RF_Stat status; + + drv_data->cmd_prop_cs.status = IDLE; + drv_data->cmd_prop_cs.pNextOp = NULL; + drv_data->cmd_prop_cs.condition.rule = COND_NEVER; + + status = RF_runImmediateCmd(drv_data->rf_handle, + (uint32_t *)&drv_data->cmd_prop_cs); + if (status != RF_StatSuccess) { + LOG_ERR("Failed to request CCA (0x%x)", status); + return -EIO; + } + + switch (drv_data->cmd_prop_cs.status) { + case PROP_DONE_OK: + return 0; + case PROP_DONE_BUSY: + return -EBUSY; + default: + return -EIO; + } +} + +static int ieee802154_cc13xx_cc26xx_subg_rx(const struct device *dev) +{ + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + RF_CmdHandle cmd_handle; + + /* Set all RX entries to empty */ + ieee802154_cc13xx_cc26xx_subg_setup_rx_buffers(drv_data); + + drv_data->cmd_prop_rx_adv.status = IDLE; + cmd_handle = RF_postCmd(drv_data->rf_handle, + (RF_Op *)&drv_data->cmd_prop_rx_adv, RF_PriorityNormal, + cmd_prop_rx_adv_callback, RF_EventRxEntryDone); + if (cmd_handle < 0) { + LOG_DBG("Failed to post RX command (%d)", cmd_handle); + return -EIO; + } + + return 0; +} + +static int ieee802154_cc13xx_cc26xx_subg_set_channel( + const struct device *dev, uint16_t channel) +{ + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + RF_EventMask reason; + uint16_t freq, fract; + int r; + + if (!is_subghz(channel)) { + return -EINVAL; + } + + r = ieee802154_cc13xx_cc26xx_subg_channel_to_frequency( + channel, &freq, &fract); + if (r < 0) { + return -EINVAL; + } + + /* Abort FG and BG processes */ + if (ieee802154_cc13xx_cc26xx_subg_stop(dev) < 0) { + return -EIO; + } + + /* Block TX while changing channel */ + k_mutex_lock(&drv_data->tx_mutex, K_FOREVER); + + /* Set the frequency */ + drv_data->cmd_fs.status = IDLE; + drv_data->cmd_fs.frequency = freq; + drv_data->cmd_fs.fractFreq = fract; + reason = RF_runCmd(drv_data->rf_handle, (RF_Op *)&drv_data->cmd_fs, + RF_PriorityNormal, NULL, 0); + if (reason != RF_EventLastCmdDone) { + LOG_DBG("Failed to set frequency: 0x%" PRIx64, reason); + r = -EIO; + goto out; + } + + /* Run BG receive process on requested channel */ + r = ieee802154_cc13xx_cc26xx_subg_rx(dev); + +out: + k_mutex_unlock(&drv_data->tx_mutex); + return r; +} + +static int +ieee802154_cc13xx_cc26xx_subg_filter(const struct device *dev, bool set, + enum ieee802154_filter_type type, + const struct ieee802154_filter *filter) +{ + ARG_UNUSED(dev); + ARG_UNUSED(set); + ARG_UNUSED(type); + ARG_UNUSED(filter); + return -ENOTSUP; +} + +static int ieee802154_cc13xx_cc26xx_subg_set_txpower( + const struct device *dev, int16_t dbm) +{ + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = get_dev_data(dev); + RF_Stat status; + + RF_TxPowerTable_Value power_table_value = RF_TxPowerTable_findValue( + (RF_TxPowerTable_Entry *)txPowerTable_sub_ghz, dbm); + + if (power_table_value.rawValue == RF_TxPowerTable_INVALID_VALUE) { + LOG_DBG("RF_TxPowerTable_findValue() failed"); + return -EINVAL; + } + + status = RF_setTxPower(drv_data->rf_handle, power_table_value); + if (status != RF_StatSuccess) { + LOG_DBG("RF_setTxPower() failed: %d", status); + return -EIO; + } + + return 0; +} + +/* See IEEE 802.15.4 section 6.2.5.1 and TRM section 25.5.4.3 */ +static int ieee802154_cc13xx_cc26xx_subg_tx(const struct device *dev, + enum ieee802154_tx_mode mode, + struct net_pkt *pkt, + struct net_buf *frag) +{ + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + int retry = CONFIG_NET_L2_IEEE802154_RADIO_TX_RETRIES; + RF_EventMask reason; + int r; + + if (mode != IEEE802154_TX_MODE_CSMA_CA) { + NET_ERR("TX mode %d not supported", mode); + return -ENOTSUP; + } + + k_mutex_lock(&drv_data->tx_mutex, K_FOREVER); + + /* Prepend data with the SUN FSK PHY header */ + drv_data->tx_data[0] = frag->len + IEEE802154_SUN_PHY_FSK_PHR_LEN; + /* 20.2.2 PHR field format. 802.15.4-2015 */ + drv_data->tx_data[1] = 0; + drv_data->tx_data[1] |= BIT(3); /* FCS Type: 2-octet FCS */ + drv_data->tx_data[1] |= BIT(4); /* DW: Enable Data Whitening */ + memcpy(&drv_data->tx_data[IEEE802154_SUN_PHY_FSK_PHR_LEN], + frag->data, frag->len); + + /* Chain commands */ + drv_data->cmd_prop_cs.pNextOp = + (rfc_radioOp_t *) &drv_data->cmd_prop_tx_adv; + drv_data->cmd_prop_cs.condition.rule = COND_STOP_ON_TRUE; + + /* Set TX data */ + drv_data->cmd_prop_tx_adv.pktLen = frag->len + + IEEE802154_SUN_PHY_FSK_PHR_LEN; + drv_data->cmd_prop_tx_adv.pPkt = drv_data->tx_data; + + /* Abort FG and BG processes */ + r = ieee802154_cc13xx_cc26xx_subg_stop(dev); + if (r < 0) { + r = -EIO; + goto out; + } + + do { + /* Reset command status */ + drv_data->cmd_prop_cs.status = IDLE; + drv_data->cmd_prop_tx_adv.status = IDLE; + + reason = RF_runCmd(drv_data->rf_handle, + (RF_Op *)&drv_data->cmd_prop_cs, + RF_PriorityNormal, cmd_prop_tx_adv_callback, + RF_EventLastCmdDone); + if ((reason & RF_EventLastCmdDone) == 0) { + LOG_DBG("Failed to run command (%" PRIx64 ")", reason); + r = -EIO; + goto out; + } + + if (drv_data->cmd_prop_cs.status != PROP_DONE_IDLE) { + LOG_DBG("Channel access failure (0x%x)", + drv_data->cmd_prop_cs.status); + /* Collision Avoidance is a WIP + * Currently, we just wait a random amount of us in the + * range [0,256) but k_busy_wait() is fairly inaccurate in + * practice. Future revisions may attempt to use the RAdio + * Timer (RAT) to measure this somewhat more precisely. + */ + k_busy_wait(sys_rand32_get() & 0xff); + continue; + } + + if (drv_data->cmd_prop_tx_adv.status != PROP_DONE_OK) { + LOG_DBG("Transmit failed (0x%x)", + drv_data->cmd_prop_tx_adv.status); + continue; + } + + /* TODO: handle RX acknowledgment */ + r = 0; + goto out; + + } while (retry-- > 0); + + LOG_DBG("Failed to TX"); + r = -EIO; + +out: + (void)ieee802154_cc13xx_cc26xx_subg_rx(dev); + k_mutex_unlock(&drv_data->tx_mutex); + return r; +} + +static inline uint8_t ieee802154_cc13xx_cc26xx_subg_convert_rssi( + int8_t rssi) +{ + if (rssi > CC13XX_CC26XX_RECEIVER_SENSITIVITY + + CC13XX_CC26XX_RSSI_DYNAMIC_RANGE) { + rssi = CC13XX_CC26XX_RECEIVER_SENSITIVITY + + CC13XX_CC26XX_RSSI_DYNAMIC_RANGE; + } else if (rssi < CC13XX_CC26XX_RECEIVER_SENSITIVITY) { + rssi = CC13XX_CC26XX_RECEIVER_SENSITIVITY; + } + + return (255 * (rssi - CC13XX_CC26XX_RECEIVER_SENSITIVITY)) / + CC13XX_CC26XX_RSSI_DYNAMIC_RANGE; +} + +static void ieee802154_cc13xx_cc26xx_subg_rx_done( + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data) +{ + struct net_pkt *pkt; + uint8_t len; + int8_t rssi; + uint8_t *sdu; + + for (int i = 0; i < CC13XX_CC26XX_NUM_RX_BUF; i++) { + if (drv_data->rx_entry[i].status == DATA_ENTRY_FINISHED) { + len = drv_data->rx_data[i][1]; + sdu = &drv_data->rx_data[i][3]; + rssi = sdu[len - 2]; + len -= 2; + + LOG_DBG("Received: len = %u, rssi = %d", len, rssi); + + pkt = net_pkt_rx_alloc_with_buffer( + drv_data->iface, len, AF_UNSPEC, 0, K_NO_WAIT); + if (!pkt) { + LOG_WRN("Cannot allocate packet"); + continue; + } + + if (net_pkt_write(pkt, sdu, len)) { + LOG_WRN("Cannot write packet"); + net_pkt_unref(pkt); + continue; + } + + drv_data->rx_entry[i].status = DATA_ENTRY_PENDING; + + /* TODO determine LQI in PROP mode */ + net_pkt_set_ieee802154_lqi(pkt, 0xff); + net_pkt_set_ieee802154_rssi( + pkt, + ieee802154_cc13xx_cc26xx_subg_convert_rssi(rssi)); + + if (net_recv_data(drv_data->iface, pkt)) { + LOG_WRN("Packet dropped"); + net_pkt_unref(pkt); + } + + } else if (drv_data->rx_entry[i].status == + DATA_ENTRY_UNFINISHED) { + LOG_WRN("Frame not finished"); + drv_data->rx_entry[i].status = DATA_ENTRY_PENDING; + } + } +} + +static int ieee802154_cc13xx_cc26xx_subg_start(const struct device *dev) +{ + ARG_UNUSED(dev); + return 0; +} + +static int ieee802154_cc13xx_cc26xx_subg_stop(const struct device *dev) +{ + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + RF_Stat status; + + status = RF_flushCmd(drv_data->rf_handle, RF_CMDHANDLE_FLUSH_ALL, 0); + if (!(status == RF_StatCmdDoneSuccess + || status == RF_StatSuccess + || status == RF_StatRadioInactiveError + || status == RF_StatInvalidParamsError)) { + LOG_DBG("Failed to abort radio operations (%d)", status); + return -EIO; + } + + return 0; +} + +static int +ieee802154_cc13xx_cc26xx_subg_configure(const struct device *dev, + enum ieee802154_config_type type, + const struct ieee802154_config *config) +{ + return -ENOTSUP; +} + +uint16_t ieee802154_cc13xx_cc26xx_subg_get_subg_channel_count( + const struct device *dev) +{ + ARG_UNUSED(dev); + + /* IEEE 802.15.4 SubGHz channels range from 0 to 10*/ + return 11; +} + +static void ieee802154_cc13xx_cc26xx_subg_setup_rx_buffers( + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data) +{ + for (size_t i = 0; i < CC13XX_CC26XX_NUM_RX_BUF; ++i) { + memset(&drv_data->rx_entry[i], 0, sizeof(drv_data->rx_entry[i])); + + if (i < CC13XX_CC26XX_NUM_RX_BUF - 1) { + drv_data->rx_entry[i].pNextEntry = + (uint8_t *) &drv_data->rx_entry[i + 1]; + } else { + drv_data->rx_entry[i].pNextEntry = + (uint8_t *) &drv_data->rx_entry[0]; + } + + drv_data->rx_entry[i].config.type = DATA_ENTRY_TYPE_PTR; + drv_data->rx_entry[i].config.lenSz = 1; + drv_data->rx_entry[i].length = sizeof(drv_data->rx_data[0]); + drv_data->rx_entry[i].pData = drv_data->rx_data[i]; + } + + drv_data->rx_queue.pCurrEntry = (uint8_t *)&drv_data->rx_entry[0]; + drv_data->rx_queue.pLastEntry = NULL; +} + +static void ieee802154_cc13xx_cc26xx_subg_data_init( + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data) +{ + uint8_t *mac; + + /* FIXME do multi-protocol devices need more than one IEEE MAC? */ + if (sys_read32(CCFG_BASE + CCFG_O_IEEE_MAC_0) != 0xFFFFFFFF && + sys_read32(CCFG_BASE + CCFG_O_IEEE_MAC_1) != 0xFFFFFFFF) { + mac = (uint8_t *)(CCFG_BASE + CCFG_O_IEEE_MAC_0); + } else { + mac = (uint8_t *)(FCFG1_BASE + FCFG1_O_MAC_15_4_0); + } + + memcpy(&drv_data->mac, mac, sizeof(drv_data->mac)); + + /* Setup circular RX queue (TRM 25.3.2.7) */ + ieee802154_cc13xx_cc26xx_subg_setup_rx_buffers(drv_data); + + k_mutex_init(&drv_data->tx_mutex); +} + +static void ieee802154_cc13xx_cc26xx_subg_iface_init(struct net_if *iface) +{ + const struct device *dev = net_if_get_device(iface); + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + + net_if_set_link_addr(iface, drv_data->mac, sizeof(drv_data->mac), + NET_LINK_IEEE802154); + + drv_data->iface = iface; + + ieee802154_init(iface); +} + +static struct ieee802154_radio_api + ieee802154_cc13xx_cc26xx_subg_radio_api = { + .iface_api.init = ieee802154_cc13xx_cc26xx_subg_iface_init, + + .get_capabilities = ieee802154_cc13xx_cc26xx_subg_get_capabilities, + .cca = ieee802154_cc13xx_cc26xx_subg_cca, + .set_channel = ieee802154_cc13xx_cc26xx_subg_set_channel, + .filter = ieee802154_cc13xx_cc26xx_subg_filter, + .set_txpower = ieee802154_cc13xx_cc26xx_subg_set_txpower, + .tx = ieee802154_cc13xx_cc26xx_subg_tx, + .start = ieee802154_cc13xx_cc26xx_subg_start, + .stop = ieee802154_cc13xx_cc26xx_subg_stop, + .configure = ieee802154_cc13xx_cc26xx_subg_configure, + .get_subg_channel_count = + ieee802154_cc13xx_cc26xx_subg_get_subg_channel_count, +}; + +static int ieee802154_cc13xx_cc26xx_subg_init(const struct device *dev) +{ + RF_Params rf_params; + RF_EventMask reason; + RF_Mode rf_mode = { + .rfMode = RF_MODE_MULTIPLE, + .cpePatchFxn = &rf_patch_cpe_multi_protocol, + }; + struct ieee802154_cc13xx_cc26xx_subg_data *drv_data = + get_dev_data(dev); + + /* Initialize driver data */ + ieee802154_cc13xx_cc26xx_subg_data_init(drv_data); + + /* Setup radio */ + RF_Params_init(&rf_params); + rf_params.pErrCb = client_error_callback; + rf_params.pClientEventCb = client_event_callback; + + drv_data->rf_handle = RF_open(&drv_data->rf_object, + &rf_mode, (RF_RadioSetup *)&drv_data->cmd_prop_radio_div_setup, + &rf_params); + if (drv_data->rf_handle == NULL) { + LOG_ERR("RF_open() failed"); + return -EIO; + } + + /* + * Run CMD_FS with frequency 0 to ensure RF_currClient is not NULL. + * RF_currClient is a static variable in the TI RF Driver library. + * If this is not done, then even CMD_ABORT fails. + */ + drv_data->cmd_fs.status = IDLE; + drv_data->cmd_fs.pNextOp = NULL; + drv_data->cmd_fs.condition.rule = COND_NEVER; + drv_data->cmd_fs.synthConf.bTxMode = false; + drv_data->cmd_fs.frequency = 0; + drv_data->cmd_fs.fractFreq = 0; + + reason = RF_runCmd(drv_data->rf_handle, (RF_Op *)&drv_data->cmd_fs, + RF_PriorityNormal, NULL, 0); + if (reason != RF_EventLastCmdDone) { + LOG_ERR("Failed to set frequency: 0x%" PRIx64, reason); + return -EIO; + } + + return 0; +} + +static struct ieee802154_cc13xx_cc26xx_subg_data + ieee802154_cc13xx_cc26xx_subg_data = { + .cmd_set_tx_power = { + .commandNo = CMD_SET_TX_POWER + }, + + /* Common Radio Commands */ + .cmd_clear_rx = { + .commandNo = CMD_CLEAR_RX, + .pQueue = &ieee802154_cc13xx_cc26xx_subg_data.rx_queue, + }, + + /* Sub-GHz Radio Commands */ + .cmd_prop_radio_div_setup = { + .commandNo = CMD_PROP_RADIO_DIV_SETUP, + .condition.rule = COND_NEVER, + .modulation.modType = 1, /* FSK */ + .modulation.deviation = 200, + .symbolRate.preScale = 15, + .symbolRate.rateWord = 131072, + .rxBw = 0x59, /* 310.8 kHz */ + .preamConf.nPreamBytes = 7, + .formatConf.nSwBits = 24, /* 24-bit of syncword */ + .formatConf.bMsbFirst = true, + .formatConf.whitenMode = 7, + .config.biasMode = true, + .formatConf.bMsbFirst = true, + .txPower = 0x013f, /* from Smart RF Studio */ + .centerFreq = 915, + .intFreq = 0x0999, + .loDivider = 5, + .pRegOverride = overrides_sub_ghz, + }, + + .cmd_fs = { + .commandNo = CMD_FS, + .condition.rule = COND_NEVER, + }, + + .cmd_prop_rx_adv = { + .commandNo = CMD_PROP_RX_ADV, + .condition.rule = COND_NEVER, + .pktConf = { + .bRepeatOk = true, + .bRepeatNok = true, + .bUseCrc = true, + .filterOp = true, + }, + .rxConf = { + .bAutoFlushIgnored = true, + .bAutoFlushCrcErr = true, + .bIncludeHdr = true, + .bAppendRssi = true, + }, + /* Preamble & SFD for 2-FSK SUN PHY. 802.15.4-2015, 20.2.1 */ + .syncWord0 = 0x0055904E, + .maxPktLen = IEEE802154_MAX_PHY_PACKET_SIZE, + .hdrConf = { + .numHdrBits = 16, + .numLenBits = 11, + }, + .lenOffset = -4, + .endTrigger.triggerType = TRIG_NEVER, + .pQueue = &ieee802154_cc13xx_cc26xx_subg_data.rx_queue, + .pOutput = + (uint8_t *) &ieee802154_cc13xx_cc26xx_subg_data + .cmd_prop_rx_adv_output, + }, + + .cmd_prop_cs = { + .commandNo = CMD_PROP_CS, + .startTrigger.pastTrig = true, + .condition.rule = COND_NEVER, + .csConf.bEnaRssi = true, + .csConf.busyOp = true, + .csConf.idleOp = true, + .rssiThr = CONFIG_IEEE802154_CC13XX_CC26XX_SUB_GHZ_CS_THRESHOLD, + .corrPeriod = 640, /* Filler, used for correlation only */ + .corrConfig.numCorrInv = 0x03, + .csEndTrigger.triggerType = TRIG_REL_START, + /* 8 symbol periods. 802.15.4-2015 Table 11.1 */ + .csEndTime = 5000, + }, + + .cmd_prop_tx_adv = { + .commandNo = CMD_PROP_TX_ADV, + .startTrigger.triggerType = TRIG_NOW, + .startTrigger.pastTrig = true, + .condition.rule = COND_NEVER, + .pktConf.bUseCrc = true, + /* PHR field format. 802.15.4-2015, 20.2.2 */ + .numHdrBits = 16, + .preTrigger.triggerType = TRIG_REL_START, + .preTrigger.pastTrig = true, + /* Preamble & SFD for 2-FSK SUN PHY. 802.15.4-2015, 20.2.1 */ + .syncWord = 0x0055904E, + }, +}; + +NET_DEVICE_INIT(ieee802154_cc13xx_cc26xx_subg, + CONFIG_IEEE802154_CC13XX_CC26XX_SUB_GHZ_DRV_NAME, + ieee802154_cc13xx_cc26xx_subg_init, device_pm_control_nop, + &ieee802154_cc13xx_cc26xx_subg_data, NULL, + CONFIG_IEEE802154_CC13XX_CC26XX_SUB_GHZ_INIT_PRIO, + &ieee802154_cc13xx_cc26xx_subg_radio_api, IEEE802154_L2, + NET_L2_GET_CTX_TYPE(IEEE802154_L2), IEEE802154_MTU); diff --git a/drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.h b/drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.h new file mode 100644 index 000000000000..699c97f5fb56 --- /dev/null +++ b/drivers/ieee802154/ieee802154_cc13xx_cc26xx_subg.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019 Brett Witherspoon + * Copyright (c) 2020 Friedt Professional Engineering Services, Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_IEEE802154_IEEE802154_CC13XX_CC26XX_SUBG_H_ +#define ZEPHYR_DRIVERS_IEEE802154_IEEE802154_CC13XX_CC26XX_SUBG_H_ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +/* See IEEE 802.15.4-2015 20.2.2 */ +#define IEEE802154_SUN_PHY_FSK_PHR_LEN 2 + +/* IEEE 802.15.4-2015 915 MHz 2FSK PHY symbol rate (20.6.3) */ +#define IEEE802154_SUN_PHY_2FSK_200K_SYMBOLS_PER_SECOND 200000 + +/* IEEE 802.15.4-2006 PHY constants (6.4.1) */ +#define IEEE802154_MAX_PHY_PACKET_SIZE 127 +#define IEEE802154_TURNAROUND_TIME 12 + +/* IEEE 802.15.4-2006 PHY PIB attributes (6.4.2) */ +#define IEEE802154_PHY_CCA_MODE 1 +#define IEEE802154_PHY_SHR_DURATION 2 +#define IEEE802154_PHY_SYMBOLS_PER_OCTET 8 + +/* IEEE 802.15.4-2006 MAC constants (7.4.1) */ +#define IEEE802154_UNIT_BACKOFF_PERIOD 20 + +/* ACK is 2 bytes for PHY header + 2 bytes MAC header + 2 bytes MAC footer */ +#define IEEE802154_ACK_FRAME_OCTETS 6 + +/* IEEE 802.15.4-2006 MAC PIB attributes (7.4.2) + * + * The macAckWaitDuration attribute does not include aUnitBackoffPeriod for + * non-beacon enabled PANs (See IEEE 802.15.4-2006 7.5.6.4.2) + */ +#define IEEE802154_MAC_ACK_WAIT_DURATION \ + (IEEE802154_TURNAROUND_TIME + IEEE802154_PHY_SHR_DURATION + \ + IEEE802154_ACK_FRAME_OCTETS * IEEE802154_PHY_SYMBOLS_PER_OCTET) + +/* Reserve two bytes for 16-bit CRC */ +#define IEEE802154_MTU (IEEE802154_MAX_PHY_PACKET_SIZE - 2) + +#define CC13XX_CC26XX_RAT_CYCLES_PER_SECOND 4000000 + +#define CC13XX_CC26XX_NUM_RX_BUF \ + CONFIG_IEEE802154_CC13XX_CC26XX_SUB_GHZ_NUM_RX_BUF + +/* + * Two additional bytes for the PHR in PROP mode + * One additional byte for RSSI value from CPE + */ +#define CC13XX_CC26XX_RX_BUF_SIZE (IEEE802154_MAX_PHY_PACKET_SIZE \ + + IEEE802154_SUN_PHY_FSK_PHR_LEN + 1) + +/* + * Two additional bytes for the SUN FSK PHY HDR + * (See IEEE 802.15.4-2015 20.2.2) + */ +#define CC13XX_CC26XX_TX_BUF_SIZE \ + (IEEE802154_MAX_PHY_PACKET_SIZE + IEEE802154_SUN_PHY_FSK_PHR_LEN) + +#define CC13XX_CC26XX_RECEIVER_SENSITIVITY -100 +#define CC13XX_CC26XX_RSSI_DYNAMIC_RANGE 95 + +struct ieee802154_cc13xx_cc26xx_subg_data { + RF_Handle rf_handle; + RF_Object rf_object; + + struct net_if *iface; + uint8_t mac[8]; + + struct k_mutex tx_mutex; + + dataQueue_t rx_queue; + rfc_dataEntryPointer_t rx_entry[CC13XX_CC26XX_NUM_RX_BUF]; + uint8_t rx_data[CC13XX_CC26XX_NUM_RX_BUF][CC13XX_CC26XX_RX_BUF_SIZE]; + uint8_t tx_data[CC13XX_CC26XX_TX_BUF_SIZE]; + + /* Common Radio Commands */ + volatile rfc_CMD_CLEAR_RX_t cmd_clear_rx; + volatile rfc_CMD_SET_TX_POWER_t cmd_set_tx_power; + volatile rfc_CMD_FS_t cmd_fs; + + /* Sub-GHz Radio Commands */ + volatile rfc_CMD_PROP_RADIO_DIV_SETUP_t cmd_prop_radio_div_setup; + volatile rfc_CMD_PROP_RX_ADV_t cmd_prop_rx_adv; + volatile rfc_CMD_PROP_TX_ADV_t cmd_prop_tx_adv; + volatile rfc_propRxOutput_t cmd_prop_rx_adv_output; + volatile rfc_CMD_PROP_CS_t cmd_prop_cs; +}; + +#endif /* ZEPHYR_DRIVERS_IEEE802154_IEEE802154_CC13XX_CC26XX_SUBG_H_ */ diff --git a/include/net/ieee802154_radio.h b/include/net/ieee802154_radio.h index 8132a53c351a..48225df7fa5b 100644 --- a/include/net/ieee802154_radio.h +++ b/include/net/ieee802154_radio.h @@ -26,6 +26,24 @@ extern "C" { * @{ */ +/** + * @brief IEEE 802.15.4 Channel assignments + * + * Channel numbering for 868 MHz, 915 MHz, and 2450 MHz bands. + * + * - Channel 0 is for 868.3 MHz. + * - Channels 1-10 are for 906 to 924 MHz with 2 MHz channel spacing. + * - Channels 11-26 are for 2405 to 2530 MHz with 5 MHz channel spacing. + * + * For more information, please refer to 802.15.4-2015 Section 10.1.2.2. + */ +enum ieee802154_channel { + IEEE802154_SUB_GHZ_CHANNEL_MIN = 0, + IEEE802154_SUB_GHZ_CHANNEL_MAX = 10, + IEEE802154_2_4_GHZ_CHANNEL_MIN = 11, + IEEE802154_2_4_GHZ_CHANNEL_MAX = 26, +}; + enum ieee802154_hw_caps { IEEE802154_HW_FCS = BIT(0), /* Frame Check-Sum supported */ IEEE802154_HW_PROMISC = BIT(1), /* Promiscuous mode supported */ @@ -209,10 +227,8 @@ struct ieee802154_radio_api { enum ieee802154_config_type type, const struct ieee802154_config *config); -#ifdef CONFIG_NET_L2_IEEE802154_SUB_GHZ /** Get the available amount of Sub-GHz channels */ uint16_t (*get_subg_channel_count)(const struct device *dev); -#endif /* CONFIG_NET_L2_IEEE802154_SUB_GHZ */ /** Run an energy detection scan. * Note: channel must be set prior to request this function. diff --git a/samples/net/sockets/echo_client/overlay-802154-subg.conf b/samples/net/sockets/echo_client/overlay-802154-subg.conf new file mode 100644 index 000000000000..e529a4c8f145 --- /dev/null +++ b/samples/net/sockets/echo_client/overlay-802154-subg.conf @@ -0,0 +1,18 @@ +CONFIG_BT=n + +# Disable TCP and IPv4 (TCP disabled to avoid heavy traffic) +CONFIG_NET_TCP=n +CONFIG_NET_IPV4=n + +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="" +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::2" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::1" + +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_L2_IEEE802154_SHELL=y +CONFIG_NET_L2_IEEE802154_LOG_LEVEL_INF=y + +CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1 diff --git a/samples/net/sockets/echo_server/overlay-802154-subg.conf b/samples/net/sockets/echo_server/overlay-802154-subg.conf new file mode 100644 index 000000000000..cda923ec621e --- /dev/null +++ b/samples/net/sockets/echo_server/overlay-802154-subg.conf @@ -0,0 +1,16 @@ +CONFIG_BT=n + +# Disable TCP and IPv4 (TCP disabled to avoid heavy traffic) +CONFIG_NET_TCP=n +CONFIG_NET_IPV4=n + +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="" + +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_L2_IEEE802154_SHELL=y +CONFIG_NET_L2_IEEE802154_LOG_LEVEL_INF=y + +CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1 diff --git a/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series b/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series index 7cb6f934664e..b7ccba7d6ad0 100644 --- a/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series +++ b/soc/arm/ti_simplelink/cc13x2_cc26x2/Kconfig.defconfig.series @@ -56,7 +56,12 @@ config IEEE802154_CC13XX_CC26XX # required for linking with PowerCC26X2_config in # soc/arm/ti_simplelink/cc13x2_cc26x2/power.c select SYS_POWER_MANAGEMENT - select SYS_POWER_SLEEP_STATES + +config IEEE802154_CC13XX_CC26XX_SUB_GHZ + default y + # required for linking with PowerCC26X2_config in + # soc/arm/ti_simplelink/cc13x2_cc26x2/power.c + select SYS_POWER_MANAGEMENT config NET_CONFIG_IEEE802154_DEV_NAME default IEEE802154_CC13XX_CC26XX_DRV_NAME diff --git a/soc/arm/ti_simplelink/cc13x2_cc26x2/power.c b/soc/arm/ti_simplelink/cc13x2_cc26x2/power.c index 9867fd417377..326c6c261b56 100644 --- a/soc/arm/ti_simplelink/cc13x2_cc26x2/power.c +++ b/soc/arm/ti_simplelink/cc13x2_cc26x2/power.c @@ -25,8 +25,8 @@ LOG_MODULE_REGISTER(soc); const PowerCC26X2_Config PowerCC26X2_config = { #if defined(CONFIG_IEEE802154_CC13XX_CC26XX) \ - || defined(CONFIG_BLE_CC13XX_CC26XX) - /* TODO: check for IEEE802154_CC13XX_CC26XX_SUB_GHZ */ + || defined(CONFIG_BLE_CC13XX_CC26XX) \ + || defined(CONFIG_IEEE802154_CC13XX_CC26XX_SUB_GHZ) .policyInitFxn = NULL, .policyFxn = NULL, .calibrateFxn = &PowerCC26XX_calibrate,