/*
 * 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"

#if defined(CONFIG_NET_L2_OPENTHREAD)
#include <zephyr/net/openthread.h>
#endif

/* 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 | IEEE802154_HW_ENERGY_SCAN;
}

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--];

			if (!IS_ENABLED(CONFIG_IEEE802154_L2_PKT_INCL_FCS)) {
				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 cmd_ieee_ed_scan_callback(RF_Handle aRfHandle,
				      RF_CmdHandle aRfCmdHandle,
				      RF_EventMask aRfEventMask)
{
	const struct device *const dev = DEVICE_DT_INST_GET(0);
	struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data;
	int maxRssi = IEEE802154_MAC_RSSI_DBM_UNDEFINED;

	if (drv_data->cmd_ieee_ed_scan.status != IEEE_DONE_OK) {
		LOG_DBG("ED Scan failed (%x)", drv_data->cmd_ieee_ed_scan.status);
	} else {
		maxRssi = drv_data->cmd_ieee_ed_scan.maxRssi;
	}

	drv_data->ed_scan_done_cb(dev, maxRssi);
}

int ieee802154_cc13xx_cc26xx_ed_scan(const struct device *dev,
				     uint16_t duration,
				     energy_scan_done_cb_t done_cb)
{
	int ret = 0;
	int channel;
	RF_EventMask reason;
	RF_ScheduleCmdParams sched_params = {
		.allowDelay = true,
	};
	struct ieee802154_cc13xx_cc26xx_data *drv_data = dev->data;

	channel = drv_data->cmd_ieee_rx.channel;

	drv_data->cmd_ieee_ed_scan.status = IDLE;
	drv_data->cmd_ieee_ed_scan.channel = channel;
	drv_data->cmd_ieee_ed_scan.endTime =
		duration * (CC13XX_CC26XX_RAT_CYCLES_PER_SECOND / 1000);
	drv_data->ed_scan_done_cb = done_cb;

	/* Abort FG and BG processes */
	if (ieee802154_cc13xx_cc26xx_stop(dev) < 0) {
		return -EIO;
	}

	/* Block TX while starting the ED scan */
	k_mutex_lock(&drv_data->tx_mutex, K_FOREVER);

	reason = RF_runScheduleCmd(drv_data->rf_handle,
		(RF_Op *)&drv_data->cmd_ieee_ed_scan, &sched_params,
		cmd_ieee_ed_scan_callback, RF_EventLastCmdDone);
	if ((reason & RF_EventLastCmdDone) == 0) {
		LOG_DBG("Failed to run command (0x%" PRIx64 ")",
			reason);
		ret = -EIO;
	}

	k_mutex_unlock(&drv_data->tx_mutex);
	return ret;
}

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,
	.ed_scan = ieee802154_cc13xx_cc26xx_ed_scan,
};

/** 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 = {
#if defined(CONFIG_NET_L2_OPENTHREAD)
			.bAcceptFt0Beacon = 1,
#else
			.bAcceptFt0Beacon = 0,
#endif
			.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 = {
#if defined(CONFIG_SOC_CC1352R) || defined(CONFIG_SOC_CC2652R) || \
	defined(CONFIG_SOC_CC1352R7) || defined(CONFIG_SOC_CC2652R7)
		.commandNo = CMD_RADIO_SETUP,
#elif defined(CONFIG_SOC_CC1352P) || defined(CONFIG_SOC_CC2652P) || \
	defined(CONFIG_SOC_CC1352P7) || defined(CONFIG_SOC_CC2652P7)
		.commandNo = CMD_RADIO_SETUP_PA,
#endif /* CONFIG_SOC_CCxx52x */
		.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
	},

	.cmd_ieee_ed_scan = {
		.commandNo = CMD_IEEE_ED_SCAN,
		.status = IDLE,
		.pNextOp = NULL,
		.startTrigger.triggerType = TRIG_NOW,
		.condition.rule = COND_NEVER,
		.endTrigger = {
			.triggerType = TRIG_REL_START,
			.pastTrig = 1,
		},
		.ccaRssiThr = CC13XX_CC26XX_RECEIVER_SENSITIVITY + 10,
		.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 = 1,
			.ccaCorrThr = 3,
		},
	},
};

#if defined(CONFIG_NET_L2_IEEE802154)
#define L2 IEEE802154_L2
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(IEEE802154_L2)
#define MTU IEEE802154_MTU
#elif defined(CONFIG_NET_L2_OPENTHREAD)
#define L2 OPENTHREAD_L2
#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2)
#define MTU 1280
#endif

#if defined(CONFIG_NET_L2_IEEE802154) || defined(CONFIG_NET_L2_PHY_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, L2,
			  L2_CTX_TYPE, 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
