| /* |
| * Copyright (c) 2019 Brett Witherspoon |
| * Copyright (c) 2020 Friedt Professional Engineering Services, Inc |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ti_cc13xx_cc26xx_ieee802154 |
| |
| #define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(ieee802154_cc13xx_cc26xx); |
| |
| #include <zephyr/device.h> |
| #include <errno.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/net/ieee802154_radio.h> |
| #include <zephyr/net/ieee802154.h> |
| #include <zephyr/net/net_pkt.h> |
| #include <zephyr/random/random.h> |
| #include <string.h> |
| #include <zephyr/sys/sys_io.h> |
| |
| #include <driverlib/rf_ieee_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.h" |
| |
| /* Overrides from SmartRF Studio 7 2.13.0 */ |
| static uint32_t overrides[] = { |
| /* DC/DC regulator: In Tx, use DCDCCTL5[3:0]=0x3 (DITHER_EN=0 and IPEAK=3). */ |
| 0x00F388D3, |
| /* Rx: Set LNA bias current offset to +15 to saturate trim to max (default: 0) */ |
| 0x000F8883, |
| 0xFFFFFFFF |
| }; |
| |
| /* 2.4 GHz power table */ |
| static const RF_TxPowerTable_Entry txPowerTable_2_4[] = { |
| {-20, RF_TxPowerTable_DEFAULT_PA_ENTRY(6, 3, 0, 2)}, |
| {-15, RF_TxPowerTable_DEFAULT_PA_ENTRY(10, 3, 0, 3)}, |
| {-10, RF_TxPowerTable_DEFAULT_PA_ENTRY(15, 3, 0, 5)}, |
| {-5, RF_TxPowerTable_DEFAULT_PA_ENTRY(22, 3, 0, 9)}, |
| {0, RF_TxPowerTable_DEFAULT_PA_ENTRY(19, 1, 0, 20)}, |
| {1, RF_TxPowerTable_DEFAULT_PA_ENTRY(22, 1, 0, 20)}, |
| {2, RF_TxPowerTable_DEFAULT_PA_ENTRY(25, 1, 0, 25)}, |
| {3, RF_TxPowerTable_DEFAULT_PA_ENTRY(29, 1, 0, 28)}, |
| {4, RF_TxPowerTable_DEFAULT_PA_ENTRY(35, 1, 0, 39)}, |
| {5, RF_TxPowerTable_DEFAULT_PA_ENTRY(23, 0, 0, 57)}, |
| RF_TxPowerTable_TERMINATION_ENTRY, |
| }; |
| |
| static void ieee802154_cc13xx_cc26xx_rx_done( |
| struct ieee802154_cc13xx_cc26xx_data *drv_data); |
| static int ieee802154_cc13xx_cc26xx_stop(const struct device *dev); |
| |
| /* TODO remove when rf driver bugfix is pulled in */ |
| static void update_saved_cmdhandle(RF_CmdHandle ch, RF_CmdHandle *saved) |
| { |
| *saved = MAX(ch, *saved); |
| } |
| |
| /* This is really the TX callback, because CSMA and TX are chained */ |
| static void cmd_ieee_csma_callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e) |
| { |
| ARG_UNUSED(h); |
| |
| const struct device *const dev = DEVICE_DT_INST_GET(0); |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| update_saved_cmdhandle(ch, (RF_CmdHandle *) &drv_data->saved_cmdhandle); |
| |
| LOG_DBG("e: 0x%" PRIx64, e); |
| |
| if (e & RF_EventInternalError) { |
| LOG_ERR("Internal error"); |
| } |
| } |
| |
| static void cmd_ieee_rx_callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e) |
| { |
| ARG_UNUSED(h); |
| |
| const struct device *const dev = DEVICE_DT_INST_GET(0); |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| update_saved_cmdhandle(ch, (RF_CmdHandle *) &drv_data->saved_cmdhandle); |
| |
| LOG_DBG("e: 0x%" PRIx64, e); |
| |
| if (e & RF_EventRxBufFull) { |
| LOG_WRN("RX buffer is full"); |
| } |
| |
| if (e & RF_EventInternalError) { |
| LOG_ERR("Internal error"); |
| } |
| |
| if (e & RF_EventRxEntryDone) { |
| ieee802154_cc13xx_cc26xx_rx_done(drv_data); |
| } |
| } |
| |
| 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_get_capabilities(const struct device *dev) |
| { |
| return IEEE802154_HW_FCS | IEEE802154_HW_FILTER | |
| IEEE802154_HW_RX_TX_ACK | IEEE802154_HW_TX_RX_ACK | IEEE802154_HW_CSMA | |
| IEEE802154_HW_RETRANSMISSION; |
| } |
| |
| static int ieee802154_cc13xx_cc26xx_cca(const struct device *dev) |
| { |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| RF_Stat status; |
| |
| status = RF_runImmediateCmd(drv_data->rf_handle, |
| (uint32_t *)&drv_data->cmd_ieee_cca_req); |
| if (status != RF_StatSuccess) { |
| LOG_ERR("Failed to request CCA (0x%x)", status); |
| return -EIO; |
| } |
| |
| switch (drv_data->cmd_ieee_cca_req.ccaInfo.ccaState) { |
| case 0: |
| return 0; |
| case 1: |
| return -EBUSY; |
| default: |
| return -EIO; |
| } |
| } |
| |
| static inline int ieee802154_cc13xx_cc26xx_channel_to_frequency( |
| uint16_t channel, uint16_t *frequency, uint16_t *fractFreq) |
| { |
| __ASSERT_NO_MSG(frequency != NULL); |
| __ASSERT_NO_MSG(fractFreq != NULL); |
| |
| /* See IEEE 802.15.4-2020, section 10.1.3.3. */ |
| if (channel >= 11 && channel <= 26) { |
| *frequency = 2405 + 5 * (channel - 11); |
| *fractFreq = 0; |
| return 0; |
| } else { |
| /* TODO: Support sub-GHz for CC13xx rather than having separate drivers */ |
| *frequency = 0; |
| *fractFreq = 0; |
| return channel < 11 ? -ENOTSUP : -EINVAL; |
| } |
| } |
| |
| static int ieee802154_cc13xx_cc26xx_set_channel(const struct device *dev, |
| uint16_t channel) |
| { |
| int ret; |
| RF_CmdHandle cmd_handle; |
| RF_EventMask reason; |
| uint16_t freq, fract; |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| ret = ieee802154_cc13xx_cc26xx_channel_to_frequency(channel, &freq, &fract); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* Abort FG and BG processes */ |
| if (ieee802154_cc13xx_cc26xx_stop(dev) < 0) { |
| ret = -EIO; |
| goto out; |
| } |
| |
| /* 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_ERR("Failed to set frequency: 0x%" PRIx64, reason); |
| ret = -EIO; |
| goto out; |
| } |
| |
| /* Run BG receive process on requested channel */ |
| drv_data->cmd_ieee_rx.status = IDLE; |
| drv_data->cmd_ieee_rx.channel = channel; |
| cmd_handle = RF_postCmd(drv_data->rf_handle, |
| (RF_Op *)&drv_data->cmd_ieee_rx, RF_PriorityNormal, |
| cmd_ieee_rx_callback, RF_EventRxEntryDone); |
| if (cmd_handle < 0) { |
| LOG_ERR("Failed to post RX command (%d)", cmd_handle); |
| ret = -EIO; |
| goto out; |
| } |
| |
| ret = 0; |
| |
| out: |
| k_mutex_unlock(&drv_data->tx_mutex); |
| return ret; |
| } |
| |
| /* TODO remove when rf driver bugfix is pulled in */ |
| static int ieee802154_cc13xx_cc26xx_reset_channel( |
| const struct device *dev) |
| { |
| uint8_t channel; |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| /* extract the channel from cmd_ieee_rx */ |
| channel = drv_data->cmd_ieee_rx.channel; |
| |
| __ASSERT_NO_MSG(11 <= channel && channel <= 26); |
| |
| LOG_DBG("re-setting channel to %u", channel); |
| |
| return ieee802154_cc13xx_cc26xx_set_channel(dev, channel); |
| } |
| |
| static int |
| ieee802154_cc13xx_cc26xx_filter(const struct device *dev, bool set, |
| enum ieee802154_filter_type type, |
| const struct ieee802154_filter *filter) |
| { |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| if (!set) { |
| return -ENOTSUP; |
| } |
| |
| if (type == IEEE802154_FILTER_TYPE_IEEE_ADDR) { |
| memcpy((uint8_t *)&drv_data->cmd_ieee_rx.localExtAddr, |
| filter->ieee_addr, |
| sizeof(drv_data->cmd_ieee_rx.localExtAddr)); |
| } else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) { |
| drv_data->cmd_ieee_rx.localShortAddr = filter->short_addr; |
| } else if (type == IEEE802154_FILTER_TYPE_PAN_ID) { |
| drv_data->cmd_ieee_rx.localPanID = filter->pan_id; |
| } else { |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| static int ieee802154_cc13xx_cc26xx_set_txpower(const struct device *dev, |
| int16_t dbm) |
| { |
| RF_Stat status; |
| const RF_TxPowerTable_Entry *table; |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| /* TODO Support sub-GHz for CC13xx */ |
| table = txPowerTable_2_4; |
| |
| RF_TxPowerTable_Value power_table_value = RF_TxPowerTable_findValue( |
| (RF_TxPowerTable_Entry *)table, dbm); |
| if (power_table_value.rawValue == RF_TxPowerTable_INVALID_VALUE) { |
| LOG_ERR("RF_TxPowerTable_findValue() failed"); |
| return -EINVAL; |
| } |
| |
| status = RF_setTxPower(drv_data->rf_handle, power_table_value); |
| if (status != RF_StatSuccess) { |
| LOG_ERR("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_tx(const struct device *dev, |
| enum ieee802154_tx_mode mode, |
| struct net_pkt *pkt, |
| struct net_buf *frag) |
| { |
| int r; |
| RF_EventMask reason; |
| RF_ScheduleCmdParams sched_params = { |
| .allowDelay = true, |
| }; |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| bool ack = ieee802154_is_ar_flag_set(frag); |
| int retry = CONFIG_IEEE802154_CC13XX_CC26XX_RADIO_TX_RETRIES; |
| |
| 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); |
| |
| /* Workaround for Issue #29418 where the driver stalls after |
| * wrapping around RF command handle 4096. This change |
| * effectively empties the RF command queue every ~4 minutes |
| * but otherwise causes the system to incur little overhead. |
| * A subsequent SimpleLink SDK release should resolve the issue. |
| */ |
| if (drv_data->saved_cmdhandle >= BIT(12) - 5) { |
| r = ieee802154_cc13xx_cc26xx_reset_channel(dev); |
| if (r < 0) { |
| goto out; |
| } |
| drv_data->saved_cmdhandle = -1; |
| } |
| |
| do { |
| |
| drv_data->cmd_ieee_csma.status = IDLE; |
| drv_data->cmd_ieee_csma.randomState = sys_rand32_get(); |
| |
| drv_data->cmd_ieee_tx.status = IDLE; |
| drv_data->cmd_ieee_tx.payloadLen = frag->len; |
| drv_data->cmd_ieee_tx.pPayload = frag->data; |
| drv_data->cmd_ieee_tx.condition.rule = |
| ack ? COND_STOP_ON_FALSE : COND_NEVER; |
| |
| if (ack) { |
| drv_data->cmd_ieee_rx_ack.status = IDLE; |
| drv_data->cmd_ieee_rx_ack.seqNo = frag->data[2]; |
| } |
| |
| reason = RF_runScheduleCmd(drv_data->rf_handle, |
| (RF_Op *)&drv_data->cmd_ieee_csma, &sched_params, |
| cmd_ieee_csma_callback, |
| RF_EventLastFGCmdDone | RF_EventLastCmdDone); |
| if ((reason & (RF_EventLastFGCmdDone | RF_EventLastCmdDone)) |
| == 0) { |
| LOG_DBG("Failed to run command (0x%" PRIx64 ")", |
| reason); |
| continue; |
| } |
| |
| if (drv_data->cmd_ieee_csma.status != IEEE_DONE_OK) { |
| /* TODO: According to IEEE 802.15.4 CSMA/CA failure |
| * fails TX immediately and should not trigger |
| * attempt (which is reserved for ACK timeouts). |
| */ |
| LOG_DBG("Channel access failure (0x%x)", |
| drv_data->cmd_ieee_csma.status); |
| continue; |
| } |
| |
| if (drv_data->cmd_ieee_tx.status != IEEE_DONE_OK) { |
| /* TODO: According to IEEE 802.15.4 transmission failure |
| * fails TX immediately and should not trigger |
| * attempt (which is reserved for ACK timeouts). |
| */ |
| LOG_DBG("Transmit failed (0x%x)", |
| drv_data->cmd_ieee_tx.status); |
| continue; |
| } |
| |
| if (!ack || drv_data->cmd_ieee_rx_ack.status == IEEE_DONE_ACK || |
| drv_data->cmd_ieee_rx_ack.status == IEEE_DONE_ACKPEND) { |
| r = 0; |
| goto out; |
| } |
| |
| LOG_DBG("No acknowledgment (0x%x)", |
| drv_data->cmd_ieee_rx_ack.status); |
| } while (retry-- > 0); |
| |
| LOG_DBG("Failed to TX"); |
| r = -EIO; |
| |
| out: |
| k_mutex_unlock(&drv_data->tx_mutex); |
| return r; |
| } |
| |
| static void ieee802154_cc13xx_cc26xx_rx_done( |
| struct ieee802154_cc13xx_cc26xx_data *drv_data) |
| { |
| struct net_pkt *pkt; |
| uint8_t len, seq, corr, lqi; |
| 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) { |
| /* rx_data contains length, psdu, fcs, rssi, corr */ |
| len = drv_data->rx_data[i][0]; |
| sdu = drv_data->rx_data[i] + 1; |
| seq = drv_data->rx_data[i][3]; |
| corr = drv_data->rx_data[i][len--] & 0x3F; |
| rssi = drv_data->rx_data[i][len--]; |
| |
| /* remove fcs as it is not expected by L2 |
| * But keep it for RAW mode |
| */ |
| if (IS_ENABLED(CONFIG_NET_L2_IEEE802154)) { |
| len -= 2; |
| } |
| |
| /* scale 6-bit corr to 8-bit lqi */ |
| lqi = corr << 2; |
| |
| LOG_DBG("Received: len = %u, seq = %u, rssi = %d, lqi = %u", |
| len, seq, rssi, lqi); |
| |
| 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; |
| |
| net_pkt_set_ieee802154_lqi(pkt, lqi); |
| net_pkt_set_ieee802154_rssi_dbm(pkt, |
| rssi == CC13XX_CC26XX_INVALID_RSSI |
| ? IEEE802154_MAC_RSSI_DBM_UNDEFINED |
| : 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_start(const struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| return 0; |
| } |
| |
| static int ieee802154_cc13xx_cc26xx_stop(const struct device *dev) |
| { |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| 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; |
| } |
| |
| /** |
| * Stops the sub-GHz interface and yields the radio (tells RF module to power |
| * down). |
| */ |
| static int ieee802154_cc13xx_cc26xx_stop_if(const struct device *dev) |
| { |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| int ret; |
| |
| ret = ieee802154_cc13xx_cc26xx_stop(dev); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| /* power down radio */ |
| RF_yield(drv_data->rf_handle); |
| return 0; |
| } |
| |
| static int |
| ieee802154_cc13xx_cc26xx_configure(const struct device *dev, |
| enum ieee802154_config_type type, |
| const struct ieee802154_config *config) |
| { |
| return -ENOTSUP; |
| } |
| |
| /* driver-allocated attribute memory - constant across all driver instances */ |
| IEEE802154_DEFINE_PHY_SUPPORTED_CHANNELS(drv_attr, 11, 26); |
| |
| static int ieee802154_cc13xx_cc26xx_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_ZERO_OQPSK_2450_BPSK_868_915, |
| &drv_attr.phy_supported_channels, value); |
| } |
| |
| static void ieee802154_cc13xx_cc26xx_data_init(const struct device *dev) |
| { |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| uint8_t *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); |
| } |
| |
| sys_memcpy_swap(&drv_data->mac, mac, sizeof(drv_data->mac)); |
| |
| /* Setup circular RX queue (TRM 25.3.2.7) */ |
| memset(&drv_data->rx_entry[0], 0, sizeof(drv_data->rx_entry[0])); |
| memset(&drv_data->rx_entry[1], 0, sizeof(drv_data->rx_entry[1])); |
| |
| drv_data->rx_entry[0].pNextEntry = (uint8_t *)&drv_data->rx_entry[1]; |
| drv_data->rx_entry[0].config.type = DATA_ENTRY_TYPE_PTR; |
| drv_data->rx_entry[0].config.lenSz = 1; |
| drv_data->rx_entry[0].length = sizeof(drv_data->rx_data[0]); |
| drv_data->rx_entry[0].pData = drv_data->rx_data[0]; |
| |
| drv_data->rx_entry[1].pNextEntry = (uint8_t *)&drv_data->rx_entry[0]; |
| drv_data->rx_entry[1].config.type = DATA_ENTRY_TYPE_PTR; |
| drv_data->rx_entry[1].config.lenSz = 1; |
| drv_data->rx_entry[1].length = sizeof(drv_data->rx_data[1]); |
| drv_data->rx_entry[1].pData = drv_data->rx_data[1]; |
| |
| drv_data->rx_queue.pCurrEntry = (uint8_t *)&drv_data->rx_entry[0]; |
| drv_data->rx_queue.pLastEntry = NULL; |
| |
| k_mutex_init(&drv_data->tx_mutex); |
| } |
| |
| static void ieee802154_cc13xx_cc26xx_iface_init(struct net_if *iface) |
| { |
| const struct device *dev = net_if_get_device(iface); |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| net_if_set_link_addr(iface, drv_data->mac, sizeof(drv_data->mac), |
| NET_LINK_IEEE802154); |
| |
| drv_data->iface = iface; |
| |
| ieee802154_init(iface); |
| } |
| |
| static const struct ieee802154_radio_api ieee802154_cc13xx_cc26xx_radio_api = { |
| .iface_api.init = ieee802154_cc13xx_cc26xx_iface_init, |
| |
| .get_capabilities = ieee802154_cc13xx_cc26xx_get_capabilities, |
| .cca = ieee802154_cc13xx_cc26xx_cca, |
| .set_channel = ieee802154_cc13xx_cc26xx_set_channel, |
| .filter = ieee802154_cc13xx_cc26xx_filter, |
| .set_txpower = ieee802154_cc13xx_cc26xx_set_txpower, |
| .tx = ieee802154_cc13xx_cc26xx_tx, |
| .start = ieee802154_cc13xx_cc26xx_start, |
| .stop = ieee802154_cc13xx_cc26xx_stop_if, |
| .configure = ieee802154_cc13xx_cc26xx_configure, |
| .attr_get = ieee802154_cc13xx_cc26xx_attr_get, |
| }; |
| |
| /** RF patches to use (note: RF core keeps a pointer to this, so no stack). */ |
| static RF_Mode rf_mode = { |
| .rfMode = RF_MODE_MULTIPLE, |
| .cpePatchFxn = &rf_patch_cpe_multi_protocol, |
| }; |
| |
| static int ieee802154_cc13xx_cc26xx_init(const struct device *dev) |
| { |
| RF_Params rf_params; |
| RF_EventMask reason; |
| struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; |
| |
| /* Initialize driver data */ |
| ieee802154_cc13xx_cc26xx_data_init(dev); |
| |
| /* 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_radio_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_data ieee802154_cc13xx_cc26xx_data = { |
| .cmd_fs = { |
| .commandNo = CMD_FS, |
| }, |
| |
| .cmd_ieee_cca_req = { |
| .commandNo = CMD_IEEE_CCA_REQ, |
| }, |
| |
| .cmd_ieee_rx = { |
| .commandNo = CMD_IEEE_RX, |
| .status = IDLE, |
| .pNextOp = NULL, |
| .startTrigger.triggerType = TRIG_NOW, |
| .condition.rule = COND_NEVER, |
| .channel = 0, |
| .rxConfig = { |
| .bAutoFlushCrc = 1, |
| .bAutoFlushIgn = 1, |
| .bIncludePhyHdr = 0, |
| .bIncludeCrc = 1, |
| .bAppendRssi = 1, |
| .bAppendCorrCrc = 1, |
| .bAppendSrcInd = 0, |
| .bAppendTimestamp = 0 |
| }, |
| .pRxQ = &ieee802154_cc13xx_cc26xx_data.rx_queue, |
| .pOutput = NULL, |
| .frameFiltOpt = { |
| .frameFiltEn = 1, |
| .frameFiltStop = 0, |
| .autoAckEn = 1, |
| .slottedAckEn = 0, |
| .autoPendEn = 0, |
| .defaultPend = 0, |
| .bPendDataReqOnly = 0, |
| .bPanCoord = 0, |
| .maxFrameVersion = 3, |
| .fcfReservedMask = 0, |
| .modifyFtFilter = 0, |
| .bStrictLenFilter = 1 |
| }, |
| .frameTypes = { |
| .bAcceptFt0Beacon = 0, |
| .bAcceptFt1Data = 1, |
| .bAcceptFt2Ack = 0, |
| .bAcceptFt3MacCmd = 1, |
| .bAcceptFt4Reserved = 0, |
| .bAcceptFt5Reserved = 0, |
| .bAcceptFt6Reserved = 0, |
| .bAcceptFt7Reserved = 0 |
| }, |
| .ccaOpt = { |
| #if IEEE802154_PHY_CCA_MODE == 1 |
| .ccaEnEnergy = 1, |
| .ccaEnCorr = 0, |
| #elif IEEE802154_PHY_CCA_MODE == 2 |
| .ccaEnEnergy = 0, |
| .ccaEnCorr = 1, |
| #elif IEEE802154_PHY_CCA_MODE == 3 |
| .ccaEnEnergy = 1, |
| .ccaEnCorr = 1, |
| #else |
| #error "Invalid CCA mode" |
| #endif |
| .ccaEnSync = 1, |
| .ccaSyncOp = 0, |
| .ccaCorrOp = 0, |
| .ccaCorrThr = 3, |
| }, |
| /* See IEEE 802.15.4-2006 6.9.9*/ |
| .ccaRssiThr = CC13XX_CC26XX_RECEIVER_SENSITIVITY + 10, |
| .numExtEntries = 0x00, |
| .numShortEntries = 0x00, |
| .pExtEntryList = NULL, |
| .pShortEntryList = NULL, |
| .localExtAddr = 0x0000000000000000, |
| .localShortAddr = 0x0000, |
| .localPanID = 0x0000, |
| .endTrigger.triggerType = TRIG_NEVER |
| }, |
| |
| .cmd_ieee_csma = { |
| .commandNo = CMD_IEEE_CSMA, |
| .status = IDLE, |
| .pNextOp = (rfc_radioOp_t *)&ieee802154_cc13xx_cc26xx_data.cmd_ieee_tx, |
| .startTrigger.triggerType = TRIG_NOW, |
| .condition.rule = COND_STOP_ON_FALSE, |
| .randomState = 0, |
| .macMaxBE = |
| CONFIG_IEEE802154_CC13XX_CC26XX_RADIO_CSMA_CA_MAX_BE, |
| .macMaxCSMABackoffs = |
| CONFIG_IEEE802154_CC13XX_CC26XX_RADIO_CSMA_CA_MAX_BO, |
| .csmaConfig = { |
| /* Initial value of CW for unslotted CSMA */ |
| .initCW = 1, |
| /* Unslotted CSMA for non-beacon enabled PAN */ |
| .bSlotted = 0, |
| /* RX stays on during CSMA backoffs */ |
| .rxOffMode = 0, |
| }, |
| .NB = 0, |
| .BE = CONFIG_IEEE802154_CC13XX_CC26XX_RADIO_CSMA_CA_MIN_BE, |
| .remainingPeriods = 0, |
| .endTrigger.triggerType = TRIG_NEVER, |
| }, |
| |
| .cmd_ieee_tx = { |
| .commandNo = CMD_IEEE_TX, |
| .status = IDLE, |
| .pNextOp = (rfc_radioOp_t *)&ieee802154_cc13xx_cc26xx_data.cmd_ieee_rx_ack, |
| .startTrigger.triggerType = TRIG_NOW, |
| .condition.rule = COND_NEVER, |
| .txOpt = { |
| /* Automatically insert PHY header */ |
| .bIncludePhyHdr = 0x0, |
| /* Automatically append CRC */ |
| .bIncludeCrc = 0x0, |
| /* Disable long frame testing */ |
| .payloadLenMsb = 0x0, |
| }, |
| .payloadLen = 0x0, |
| .pPayload = NULL, |
| }, |
| |
| .cmd_ieee_rx_ack = { |
| .commandNo = CMD_IEEE_RX_ACK, |
| .status = IDLE, |
| .pNextOp = NULL, |
| .startTrigger.triggerType = TRIG_NOW, |
| .condition.rule = COND_NEVER, |
| .seqNo = 0, |
| .endTrigger = { |
| .triggerType = TRIG_REL_START, |
| .pastTrig = 1, |
| }, |
| .endTime = IEEE802154_MAC_ACK_WAIT_DURATION * |
| CC13XX_CC26XX_RAT_CYCLES_PER_SECOND / |
| IEEE802154_2450MHZ_OQPSK_SYMBOLS_PER_SECOND, |
| }, |
| |
| .cmd_radio_setup = { |
| .commandNo = CMD_RADIO_SETUP, |
| .status = IDLE, |
| .pNextOp = NULL, |
| .startTrigger.triggerType = TRIG_NOW, |
| .condition.rule = COND_NEVER, |
| .mode = 0x01, /* IEEE 802.15.4 */ |
| .loDivider = 0x00, |
| .config = { |
| .frontEndMode = 0x0, |
| .biasMode = 0x0, |
| .analogCfgMode = 0x0, |
| .bNoFsPowerUp = 0x0, |
| }, |
| .txPower = 0x2853, /* 0 dBm */ |
| .pRegOverride = overrides |
| }, |
| }; |
| |
| #if defined(CONFIG_NET_L2_IEEE802154) |
| NET_DEVICE_DT_INST_DEFINE(0, ieee802154_cc13xx_cc26xx_init, NULL, |
| &ieee802154_cc13xx_cc26xx_data, NULL, |
| CONFIG_IEEE802154_CC13XX_CC26XX_INIT_PRIO, |
| &ieee802154_cc13xx_cc26xx_radio_api, IEEE802154_L2, |
| NET_L2_GET_CTX_TYPE(IEEE802154_L2), IEEE802154_MTU); |
| #else |
| DEVICE_DT_INST_DEFINE(0, ieee802154_cc13xx_cc26xx_init, NULL, |
| &ieee802154_cc13xx_cc26xx_data, NULL, POST_KERNEL, |
| CONFIG_IEEE802154_CC13XX_CC26XX_INIT_PRIO, |
| &ieee802154_cc13xx_cc26xx_radio_api); |
| #endif |