|  | /* | 
|  | * 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/rand32.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_2_4_GHZ | | 
|  | IEEE802154_HW_FILTER | IEEE802154_HW_TX_RX_ACK | | 
|  | IEEE802154_HW_CSMA; | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | if (channel >= IEEE802154_2_4_GHZ_CHANNEL_MIN | 
|  | && 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 int ieee802154_cc13xx_cc26xx_set_channel(const struct device *dev, | 
|  | uint16_t channel) | 
|  | { | 
|  | int r; | 
|  | RF_CmdHandle cmd_handle; | 
|  | RF_EventMask reason; | 
|  | uint16_t freq, fract; | 
|  | struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data; | 
|  |  | 
|  | /* TODO Support sub-GHz for CC13xx */ | 
|  | if (channel < 11 || channel > 26) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | r = ieee802154_cc13xx_cc26xx_channel_to_frequency( | 
|  | channel, &freq, &fract); | 
|  | if (r < 0) { | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* Abort FG and BG processes */ | 
|  | if (ieee802154_cc13xx_cc26xx_stop(dev) < 0) { | 
|  | r = -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); | 
|  | r = -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); | 
|  | r = -EIO; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | r = 0; | 
|  |  | 
|  | out: | 
|  | k_mutex_unlock(&drv_data->tx_mutex); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | /* 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) { | 
|  | LOG_DBG("Channel access failure (0x%x)", | 
|  | drv_data->cmd_ieee_csma.status); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (drv_data->cmd_ieee_tx.status != IEEE_DONE_OK) { | 
|  | 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 inline uint8_t ieee802154_cc13xx_cc26xx_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_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( | 
|  | pkt, | 
|  | ieee802154_cc13xx_cc26xx_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_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; | 
|  | } | 
|  |  | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | memcpy(&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 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, | 
|  | }; | 
|  |  | 
|  | /** 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_clear_rx = { | 
|  | .commandNo = CMD_CLEAR_RX, | 
|  | .pQueue = &ieee802154_cc13xx_cc26xx_data.rx_queue, | 
|  | }, | 
|  |  | 
|  | .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_set_tx_power = { | 
|  | .commandNo = CMD_SET_TX_POWER | 
|  | }, | 
|  |  | 
|  | .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 |