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 <chrisfriedt@gmail.com>
This commit is contained in:
Christopher Friedt 2020-11-03 13:24:35 -05:00 committed by Jukka Rissanen
commit ec0e737b0c
10 changed files with 958 additions and 8 deletions

View file

@ -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)

View file

@ -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

View file

@ -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;
}

View file

@ -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 <logging/log.h>
LOG_MODULE_REGISTER(ieee802154_cc13xx_cc26xx_subg);
#include <device.h>
#include <errno.h>
#include <sys/byteorder.h>
#include <net/ieee802154_radio.h>
#include <net/ieee802154.h>
#include <net/net_pkt.h>
#include <random/rand32.h>
#include <string.h>
#include <sys/sys_io.h>
#include <driverlib/rf_mailbox.h>
#include <driverlib/rf_prop_mailbox.h>
#include <driverlib/rfc.h>
#include <inc/hw_ccfg.h>
#include <inc/hw_fcfg1.h>
#include <rf_patches/rf_patch_cpe_multi_protocol.h>
#include <ti/drivers/rf/RF.h>
#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);

View file

@ -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 <kernel.h>
#include <net/net_if.h>
#include <ti/drivers/rf/RF.h>
#include <driverlib/rf_common_cmd.h>
#include <driverlib/rf_data_entry.h>
#include <driverlib/rf_ieee_cmd.h>
#include <driverlib/rf_prop_cmd.h>
#include <driverlib/rf_mailbox.h>
/* 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_ */

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,