/* ieee802154_rf2xx.c - ATMEL RF2XX IEEE 802.15.4 Driver */

#define DT_DRV_COMPAT atmel_rf2xx

/*
 * Copyright (c) 2019-2020 Gerson Fernando Budke
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define LOG_MODULE_NAME ieee802154_rf2xx
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

#include <zephyr/kernel.h>
#include <zephyr/arch/cpu.h>
#include <zephyr/debug/stack.h>

#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_pkt.h>

#include <zephyr/sys/byteorder.h>
#include <string.h>
#include <zephyr/random/random.h>
#include <zephyr/linker/sections.h>
#include <zephyr/sys/atomic.h>

#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>

#include <zephyr/net/ieee802154_radio.h>

#include "ieee802154_rf2xx.h"
#include "ieee802154_rf2xx_regs.h"
#include "ieee802154_rf2xx_iface.h"

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

#define RF2XX_OT_PSDU_LENGTH              1280

#define RF2XX_ACK_FRAME_LEN               3
#define RF2XX_ACK_FRAME_TYPE              (2 << 0)
#define RF2XX_ACK_FRAME_PENDING_BIT       (1 << 4)
#define RF2XX_FRAME_CTRL_ACK_REQUEST_BIT  (1 << 5)

static uint8_t rf2xx_ack_psdu[RF2XX_ACK_FRAME_LEN] = { 0 };
static struct net_buf rf2xx_ack_frame = {
	.data  = rf2xx_ack_psdu,
	.size  = RF2XX_ACK_FRAME_LEN,
	.len   = RF2XX_ACK_FRAME_LEN,
	.__buf = rf2xx_ack_psdu,
	.frags = NULL,
};
static struct net_pkt rf2xx_ack_pkt = {
	.buffer = &rf2xx_ack_frame,
	.cb = {
		.lqi = 80,
		.rssi = -40,
	}
};
#endif /* CONFIG_NET_L2_OPENTHREAD */

/* Radio Transceiver ISR */
static inline void trx_isr_handler(const struct device *port,
				   struct gpio_callback *cb,
				   uint32_t pins)
{
	struct rf2xx_context *ctx = CONTAINER_OF(cb,
						 struct rf2xx_context,
						 irq_cb);

	ARG_UNUSED(port);
	ARG_UNUSED(pins);

	k_sem_give(&ctx->trx_isr_lock);
}

static void rf2xx_trx_set_state(const struct device *dev,
				enum rf2xx_trx_state_cmd_t state)
{
	do {
		rf2xx_iface_reg_write(dev, RF2XX_TRX_STATE_REG,
				      RF2XX_TRX_PHY_STATE_CMD_FORCE_TRX_OFF);
	} while (RF2XX_TRX_PHY_STATUS_TRX_OFF !=
		 (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
		  RF2XX_TRX_PHY_STATUS_MASK));

	do {
		rf2xx_iface_reg_write(dev, RF2XX_TRX_STATE_REG, state);
	} while (state !=
		 (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
		  RF2XX_TRX_PHY_STATUS_MASK));
}

static void rf2xx_trx_set_tx_state(const struct device *dev)
{
	uint8_t status;

	/**
	 * Ensures that RX automatically ACK will be sent when requested.
	 * Datasheet: Chapter 7.2.3 RX_AACK_ON – Receive with Automatic ACK
	 * Datasheet: Figure 7-13. Timing Example of an RX_AACK Transaction
	 * for Slotted Operation.
	 *
	 * This will create a spin lock that wait transceiver be free from
	 * current receive frame process
	 */
	do {
		status = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
			  RF2XX_TRX_PHY_STATUS_MASK);
	} while (status == RF2XX_TRX_PHY_STATUS_BUSY_RX_AACK ||
		 status == RF2XX_TRX_PHY_STATUS_STATE_TRANSITION);

	rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
	rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
	rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TX_ARET_ON);
}

static void rf2xx_trx_set_rx_state(const struct device *dev)
{
	rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
	rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
	/**
	 * Set extended RX mode
	 * Datasheet: chapter 7.2 Extended Operating Mode
	 */
	rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_RX_AACK_ON);
}

static void rf2xx_set_rssi_base(const struct device *dev, uint16_t channel)
{
	struct rf2xx_context *ctx = dev->data;
	int8_t base;

	if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915) {
		base = channel == 0
				? RF2XX_RSSI_BPSK_20
				: RF2XX_RSSI_BPSK_40;
	} else if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915) {
		base = channel == 0
				? RF2XX_RSSI_OQPSK_SIN_RC_100
				: RF2XX_RSSI_OQPSK_SIN_250;
	} else {
		base = RF2XX_RSSI_OQPSK_RC_250;
	}

	ctx->trx_rssi_base = base;
}

static void rf2xx_trx_rx(const struct device *dev)
{
	struct rf2xx_context *ctx = dev->data;
	struct net_pkt *pkt = NULL;
	uint8_t rx_buf[RX2XX_MAX_FRAME_SIZE];
	uint8_t pkt_len;
	uint8_t frame_len;
	uint8_t trac;

	/*
	 * The rf2xx frame buffer can have length > 128 bytes. The
	 * net_pkt_rx_alloc_with_buffer allocates max value of 128 bytes.
	 *
	 * This obligate the driver to have rx_buf statically allocated with
	 * RX2XX_MAX_FRAME_SIZE.
	 */
	if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
		pkt_len = ctx->rx_phr;
	} else {
		rf2xx_iface_frame_read(dev, rx_buf, RX2XX_FRAME_HEADER_SIZE);
		pkt_len = rx_buf[RX2XX_FRAME_PHR_INDEX];
	}

	if (!ctx->promiscuous && pkt_len < RX2XX_FRAME_MIN_PHR_SIZE) {
		LOG_ERR("Invalid RX frame length");
		return;
	}

	frame_len = RX2XX_FRAME_HEADER_SIZE + pkt_len +
		    RX2XX_FRAME_FOOTER_SIZE;

	rf2xx_iface_frame_read(dev, rx_buf, frame_len);

	if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
		trac = rx_buf[pkt_len + RX2XX_FRAME_TRAC_INDEX];
		trac = (trac >> RF2XX_RX_TRAC_STATUS) & RF2XX_RX_TRAC_BIT_MASK;

		ctx->pkt_ed = rx_buf[pkt_len + RX2XX_FRAME_ED_INDEX];
	} else {
		trac = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATE_REG)
			>> RF2XX_TRAC_STATUS) & RF2XX_TRAC_BIT_MASK;

		ctx->pkt_ed = (rf2xx_iface_reg_read(dev, RF2XX_PHY_RSSI_REG)
			       >> RF2XX_RSSI) & RF2XX_RSSI_MASK;
	}
	ctx->pkt_lqi = rx_buf[pkt_len + RX2XX_FRAME_LQI_INDEX];

	if (!ctx->promiscuous && trac == RF2XX_TRX_PHY_STATE_TRAC_INVALID) {
		LOG_ERR("Invalid RX frame");
		return;
	}

	if (!IS_ENABLED(CONFIG_IEEE802154_RAW_MODE) &&
	    !IS_ENABLED(CONFIG_NET_L2_OPENTHREAD) &&
	    pkt_len >= RX2XX_FRAME_FCS_LENGTH) {
		pkt_len -= RX2XX_FRAME_FCS_LENGTH;
	}

	pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, pkt_len,
					   AF_UNSPEC, 0, K_NO_WAIT);
	if (!pkt) {
		LOG_ERR("No RX buffer available");
		return;
	}

	memcpy(pkt->buffer->data, rx_buf + RX2XX_FRAME_HEADER_SIZE, pkt_len);
	net_buf_add(pkt->buffer, pkt_len);
	net_pkt_set_ieee802154_lqi(pkt, ctx->pkt_lqi);
	net_pkt_set_ieee802154_rssi_dbm(pkt, ctx->pkt_ed + ctx->trx_rssi_base);

	LOG_DBG("Caught a packet (%02X) (LQI: %02X, RSSI: %d, ED: %02X)",
		pkt_len, ctx->pkt_lqi, ctx->trx_rssi_base + ctx->pkt_ed,
		ctx->pkt_ed);

	if (net_recv_data(ctx->iface, pkt) < 0) {
		LOG_DBG("RX Packet dropped by NET stack");
		net_pkt_unref(pkt);
		return;
	}

	if (LOG_LEVEL >= LOG_LEVEL_DBG) {
		log_stack_usage(&ctx->trx_thread);
	}
}

static void rf2xx_process_rx_frame(const struct device *dev)
{
	struct rf2xx_context *ctx = dev->data;

	/*
	 * NOTE: In promiscuous mode invalid frames will be processed.
	 */

	if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
		rf2xx_trx_rx(dev);
	} else {
		/* Ensures that automatically ACK will be sent
		 * when requested
		 */
		while (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) ==
		       RF2XX_TRX_PHY_STATUS_BUSY_RX_AACK) {
			;
		}

		/* Set PLL_ON to avoid transceiver receive
		 * new data until finish reading process
		 */
		rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_PLL_ON);
		rf2xx_trx_rx(dev);
		rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_RX_AACK_ON);
	}
}

static void rf2xx_process_tx_frame(const struct device *dev)
{
	struct rf2xx_context *ctx = dev->data;

	ctx->trx_trac = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATE_REG) >>
			 RF2XX_TRAC_STATUS) & RF2XX_TRAC_BIT_MASK;
	k_sem_give(&ctx->trx_tx_sync);
	rf2xx_trx_set_rx_state(dev);
}

static void rf2xx_process_trx_end(const struct device *dev)
{
	uint8_t trx_status = (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
			   RF2XX_TRX_PHY_STATUS_MASK);

	if (trx_status == RF2XX_TRX_PHY_STATUS_TX_ARET_ON) {
		rf2xx_process_tx_frame(dev);
	} else {
		rf2xx_process_rx_frame(dev);
	}
}

static void rf2xx_thread_main(void *p1, void *p2, void *p3)
{
	ARG_UNUSED(p2);
	ARG_UNUSED(p3);

	struct rf2xx_context *ctx = p1;
	uint8_t isr_status;

	while (true) {
		k_sem_take(&ctx->trx_isr_lock, K_FOREVER);

		isr_status = rf2xx_iface_reg_read(ctx->dev,
						  RF2XX_IRQ_STATUS_REG);

		/*
		 *  IRQ_7 (BAT_LOW) Indicates a supply voltage below the
		 *    programmed threshold. 9.5.4
		 *  IRQ_6 (TRX_UR) Indicates a Frame Buffer access
		 *    violation. 9.3.3
		 *  IRQ_5 (AMI) Indicates address matching. 8.2
		 *  IRQ_4 (CCA_ED_DONE) Multi-functional interrupt:
		 *   1. AWAKE_END: 7.1.2.5
		 *      • Indicates finished transition to TRX_OFF state
		 *        from P_ON, SLEEP, DEEP_SLEEP, or RESET state.
		 *   2. CCA_ED_DONE: 8.5.4
		 *      • Indicates the end of a CCA or ED
		 *        measurement. 8.6.4
		 *  IRQ_3 (TRX_END)
		 *    RX: Indicates the completion of a frame
		 *      reception. 7.1.3
		 *    TX: Indicates the completion of a frame
		 *      transmission. 7.1.3
		 *  IRQ_2 (RX_START) Indicates the start of a PSDU
		 *    reception; the AT86RF233 state changed to BUSY_RX;
		 *    the PHR can be read from Frame Buffer. 7.1.3
		 *  IRQ_1 (PLL_UNLOCK) Indicates PLL unlock. If the radio
		 *    transceiver is in BUSY_TX / BUSY_TX_ARET state, the
		 *    PA is turned off immediately. 9.7.5
		 *  IRQ_0 (PLL_LOCK) Indicates PLL lock.
		 */
		if (isr_status & (1 << RF2XX_RX_START)) {
			if (ctx->trx_model != RF2XX_TRX_MODEL_231) {
				rf2xx_iface_sram_read(ctx->dev, 0,
						      &ctx->rx_phr, 1);
			}
		}
		if (isr_status & (1 << RF2XX_TRX_END)) {
			rf2xx_process_trx_end(ctx->dev);
		}
	}
}

static inline uint8_t *get_mac(const struct device *dev)
{
	const struct rf2xx_config *conf = dev->config;
	struct rf2xx_context *ctx = dev->data;

	if (!conf->has_mac) {
		sys_rand_get(ctx->mac_addr, sizeof(ctx->mac_addr));
	}

	/*
	 * Clear bit 0 to ensure it isn't a multicast address and set
	 * bit 1 to indicate address is locally administered and may
	 * not be globally unique.
	 */
	ctx->mac_addr[0] = (ctx->mac_addr[0] & ~0x01) | 0x02;

	return ctx->mac_addr;
}

static enum ieee802154_hw_caps rf2xx_get_capabilities(const struct device *dev)
{
	LOG_DBG("HW Caps");

	return IEEE802154_HW_FCS |
	       IEEE802154_HW_PROMISC |
	       IEEE802154_HW_FILTER |
	       IEEE802154_HW_CSMA |
	       IEEE802154_HW_RETRANSMISSION |
	       IEEE802154_HW_TX_RX_ACK |
	       IEEE802154_HW_RX_TX_ACK;
}

static int rf2xx_configure_sub_channel(const struct device *dev, uint16_t channel)
{
	struct rf2xx_context *ctx = dev->data;
	uint8_t reg;
	uint8_t cc_mask;

	if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915) {
		cc_mask = channel == 0
				   ? RF2XX_CC_BPSK_20
				   : RF2XX_CC_BPSK_40;
	} else if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915) {
		cc_mask = channel == 0
				   ? RF2XX_CC_OQPSK_SIN_RC_100
				   : RF2XX_CC_OQPSK_SIN_250;
	} else {
		cc_mask = RF2XX_CC_OQPSK_RC_250;
	}

	reg = rf2xx_iface_reg_read(dev, RF2XX_TRX_CTRL_2_REG)
	    & ~RF2XX_SUB_CHANNEL_MASK;
	rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_2_REG, reg | cc_mask);

	return 0;
}

static int rf2xx_configure_trx_path(const struct device *dev)
{
	struct rf2xx_context *ctx = dev->data;
	uint8_t reg;
	uint8_t gc_tx_offset;

	if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915) {
		gc_tx_offset = 0x03;
	} else {
		gc_tx_offset = 0x02;
	}

	reg = rf2xx_iface_reg_read(dev, RF2XX_RF_CTRL_0_REG)
	    & ~RF2XX_GC_TX_OFFS_MASK;
	rf2xx_iface_reg_write(dev, RF2XX_RF_CTRL_0_REG, reg | gc_tx_offset);

	return 0;
}

static int rf2xx_cca(const struct device *dev)
{
	ARG_UNUSED(dev);

	LOG_DBG("CCA");

	return 0;
}

static int rf2xx_set_channel(const struct device *dev, uint16_t channel)
{
	struct rf2xx_context *ctx = dev->data;
	uint8_t reg;

	LOG_DBG("Set Channel %d", channel);

	if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
		if ((ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915
		     || ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915)
		    && channel > 10) {
			LOG_ERR("Unsupported channel %u", channel);
			return channel > 26 ? -EINVAL : -ENOTSUP;
		}
		if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_FIVE_OQPSK_780 &&
		    channel > 3) {
			LOG_ERR("Unsupported channel %u", channel);
			return channel > 7 ? -EINVAL : -ENOTSUP;
		}

		rf2xx_configure_sub_channel(dev, channel);
		rf2xx_configure_trx_path(dev);
		rf2xx_set_rssi_base(dev, channel);
	} else {
		/* 2.4G O-QPSK, channel page zero */
		if (channel < 11 || channel > 26) {
			LOG_ERR("Unsupported channel %u", channel);
			return channel < 11 ? -ENOTSUP : -EINVAL;
		}
	}

	reg = rf2xx_iface_reg_read(dev, RF2XX_PHY_CC_CCA_REG) & ~0x1f;
	rf2xx_iface_reg_write(dev, RF2XX_PHY_CC_CCA_REG, reg | channel);

	return 0;
}

static int rf2xx_set_txpower(const struct device *dev, int16_t dbm)
{
	const struct rf2xx_config *conf = dev->config;
	struct rf2xx_context *ctx = dev->data;
	float min, max, step;
	uint8_t reg;
	uint8_t idx;
	uint8_t val;

	LOG_DBG("Try set Power to %d", dbm);

	/**
	 * if table size is equal 1 the code assumes a table was not defined. In
	 * this case the transceiver PHY_TX_PWR register will be set with value
	 * zero. This is a safe value for all variants and represents an output
	 * power above 0 dBm.
	 *
	 * Note: This is a special case too which avoid division by zero when
	 * computing the step variable.
	 */
	if (conf->tx_pwr_table_size == 1) {
		rf2xx_iface_reg_write(dev, RF2XX_PHY_TX_PWR_REG, 0);

		return 0;
	}

	min = conf->tx_pwr_min[1];
	if (conf->tx_pwr_min[0] == 0x01) {
		min *= -1.0f;
	}

	max = conf->tx_pwr_max[1];
	if (conf->tx_pwr_max[0] == 0x01) {
		min *= -1.0f;
	}

	step = (max - min) / ((float)conf->tx_pwr_table_size - 1.0f);

	if (step == 0.0f) {
		step = 1.0f;
	}

	LOG_DBG("Tx-power values: min %f, max %f, step %f, entries %d",
		(double)min, (double)max, (double)step, conf->tx_pwr_table_size);

	if (dbm < min) {
		LOG_INF("TX-power %d dBm below min of %f dBm, using %f dBm",
			dbm, (double)min, (double)max);
		dbm = min;
	} else if (dbm > max) {
		LOG_INF("TX-power %d dBm above max of %f dBm, using %f dBm",
			dbm, (double)min, (double)max);
		dbm = max;
	}

	idx = abs((int) (((float)(dbm - max) / step)));
	LOG_DBG("Tx-power idx: %d", idx);

	if (idx >= conf->tx_pwr_table_size) {
		idx = conf->tx_pwr_table_size - 1;
	}

	val = conf->tx_pwr_table[idx];

	if (ctx->trx_model != RF2XX_TRX_MODEL_212) {
		reg = rf2xx_iface_reg_read(dev, RF2XX_PHY_TX_PWR_REG) & 0xf0;
		val = reg + (val & 0x0f);
	}

	LOG_DBG("Tx-power normalized: %d dBm, PHY_TX_PWR 0x%02x, idx %d",
		dbm, val, idx);

	rf2xx_iface_reg_write(dev, RF2XX_PHY_TX_PWR_REG, val);

	return 0;
}

static int rf2xx_set_ieee_addr(const struct device *dev, bool set,
			       const uint8_t *ieee_addr)
{
	const uint8_t *ptr_to_reg = ieee_addr;

	LOG_DBG("IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
		ieee_addr[7], ieee_addr[6], ieee_addr[5], ieee_addr[4],
		ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]);

	if (set) {
		for (uint8_t i = 0; i < 8; i++, ptr_to_reg++) {
			rf2xx_iface_reg_write(dev, (RF2XX_IEEE_ADDR_0_REG + i),
					      *ptr_to_reg);
		}
	} else {
		for (uint8_t i = 0; i < 8; i++) {
			rf2xx_iface_reg_write(dev, (RF2XX_IEEE_ADDR_0_REG + i),
					      0);
		}
	}

	return 0;
}

static int rf2xx_set_short_addr(const struct device *dev, bool set,
				uint16_t short_addr)
{
	uint8_t short_addr_le[2] = { 0xFF, 0xFF };

	if (set) {
		sys_put_le16(short_addr, short_addr_le);
	}

	rf2xx_iface_reg_write(dev, RF2XX_SHORT_ADDR_0_REG, short_addr_le[0]);
	rf2xx_iface_reg_write(dev, RF2XX_SHORT_ADDR_1_REG, short_addr_le[1]);
	rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_0_REG,
			      short_addr_le[0] + short_addr_le[1]);

	LOG_DBG("Short Address: 0x%02X%02X", short_addr_le[1],
		short_addr_le[0]);

	return 0;
}

static int rf2xx_set_pan_id(const struct device *dev, bool set,
			    uint16_t pan_id)
{
	uint8_t pan_id_le[2] = { 0xFF, 0xFF };

	if (set) {
		sys_put_le16(pan_id, pan_id_le);
	}

	rf2xx_iface_reg_write(dev, RF2XX_PAN_ID_0_REG, pan_id_le[0]);
	rf2xx_iface_reg_write(dev, RF2XX_PAN_ID_1_REG, pan_id_le[1]);

	LOG_DBG("Pan Id: 0x%02X%02X", pan_id_le[1], pan_id_le[0]);

	return 0;
}

static int rf2xx_filter(const struct device *dev,
			bool set, enum ieee802154_filter_type type,
			const struct ieee802154_filter *filter)
{
	LOG_DBG("Applying filter %u", type);

	if (type == IEEE802154_FILTER_TYPE_IEEE_ADDR) {
		return rf2xx_set_ieee_addr(dev, set, filter->ieee_addr);
	} else if (type == IEEE802154_FILTER_TYPE_SHORT_ADDR) {
		return rf2xx_set_short_addr(dev, set, filter->short_addr);
	} else if (type == IEEE802154_FILTER_TYPE_PAN_ID) {
		return rf2xx_set_pan_id(dev, set, filter->pan_id);
	}

	return -ENOTSUP;
}

#if defined(CONFIG_NET_L2_OPENTHREAD)
static void rf2xx_handle_ack(struct rf2xx_context *ctx, struct net_buf *frag)
{
	if ((frag->data[0] & RF2XX_FRAME_CTRL_ACK_REQUEST_BIT) == 0) {
		return;
	}

	rf2xx_ack_psdu[0] = RF2XX_ACK_FRAME_TYPE;
	rf2xx_ack_psdu[2] = frag->data[2];

	if (ctx->trx_trac == RF2XX_TRX_PHY_STATE_TRAC_SUCCESS_DATA_PENDING) {
		rf2xx_ack_psdu[0] |= RF2XX_ACK_FRAME_PENDING_BIT;
	}

	net_pkt_cursor_init(&rf2xx_ack_pkt);

	if (ieee802154_handle_ack(ctx->iface, &rf2xx_ack_pkt) != NET_OK) {
		LOG_INF("ACK packet not handled.");
	}
}
#else
	#define rf2xx_handle_ack(...)
#endif

static int rf2xx_tx(const struct device *dev,
		    enum ieee802154_tx_mode mode,
		    struct net_pkt *pkt,
		    struct net_buf *frag)
{
	ARG_UNUSED(pkt);

	struct rf2xx_context *ctx = dev->data;
	int response = 0;

	LOG_DBG("TX");

	if (ctx->tx_mode != mode) {
		switch (mode) {
		case IEEE802154_TX_MODE_DIRECT:
			/* skip retries & csma/ca algorithm */
			rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_0_REG, 0x0E);
			break;
		case IEEE802154_TX_MODE_CSMA_CA:
			/* backoff maxBE = 5, minBE = 3 */
			rf2xx_iface_reg_write(dev, RF2XX_CSMA_BE_REG, 0x53);
			/* max frame retries = 3, csma/ca retries = 4 */
			rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_0_REG, 0x38);
			break;
		case IEEE802154_TX_MODE_CCA:
			/* backoff period = 0 */
			rf2xx_iface_reg_write(dev, RF2XX_CSMA_BE_REG, 0x00);
			/* no frame retries & no csma/ca retries */
			rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_0_REG, 0x00);
			break;
		case IEEE802154_TX_MODE_TXTIME:
		case IEEE802154_TX_MODE_TXTIME_CCA:
		default:
			NET_ERR("TX mode %d not supported", mode);
			return -ENOTSUP;
		}

		ctx->tx_mode = mode;
	}

	rf2xx_trx_set_tx_state(dev);
	rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);

	k_sem_reset(&ctx->trx_tx_sync);
	rf2xx_iface_frame_write(dev, frag->data, frag->len);
	rf2xx_iface_phy_tx_start(dev);
	k_sem_take(&ctx->trx_tx_sync, K_FOREVER);

	switch (ctx->trx_trac) {
	/* Channel is still busy after attempting MAX_CSMA_RETRIES of
	 * CSMA-CA
	 */
	case RF2XX_TRX_PHY_STATE_TRAC_CHANNEL_ACCESS_FAILED:
		response = -EBUSY;
		break;
	/* No acknowledgment frames were received during all retry
	 * attempts
	 */
	case RF2XX_TRX_PHY_STATE_TRAC_NO_ACK:
		response = -EAGAIN;
		break;
	/* Transaction not yet finished */
	case RF2XX_TRX_PHY_STATE_TRAC_INVALID:
		response = -EINTR;
		break;
	/* RF2XX_TRX_PHY_STATE_TRAC_SUCCESS:
	 *  The transaction was responded to by a valid ACK, or, if no
	 *  ACK is requested, after a successful frame transmission.
	 *
	 * RF2XX_TRX_PHY_STATE_TRAC_SUCCESS_DATA_PENDING:
	 * Equivalent to SUCCESS and indicating that the “Frame
	 * Pending” bit (see Section 8.1.2.2) of the received
	 * acknowledgment frame was set.
	 */
	default:
		rf2xx_handle_ack(ctx, frag);
		break;
	}

	return response;
}

static int rf2xx_start(const struct device *dev)
{
	const struct rf2xx_config *conf = dev->config;

	LOG_DBG("Start");

	rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
	rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);
	gpio_pin_interrupt_configure_dt(&conf->irq_gpio,
					GPIO_INT_EDGE_TO_ACTIVE);
	rf2xx_trx_set_rx_state(dev);

	return 0;
}

static int rf2xx_stop(const struct device *dev)
{
	const struct rf2xx_config *conf = dev->config;

	LOG_DBG("Stop");

	gpio_pin_interrupt_configure_dt(&conf->irq_gpio, GPIO_INT_DISABLE);
	rf2xx_trx_set_state(dev, RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
	rf2xx_iface_reg_read(dev, RF2XX_IRQ_STATUS_REG);

	return 0;
}

static int rf2xx_pan_coord_set(const struct device *dev, bool pan_coordinator)
{
	uint8_t reg;

	if (pan_coordinator) {
		reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
		reg |= (1 << RF2XX_AACK_I_AM_COORD);
		rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
	} else {
		reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
		reg &= ~(1 << RF2XX_AACK_I_AM_COORD);
		rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
	}

	return 0;
}

static int rf2xx_promiscuous_set(const struct device *dev, bool promiscuous)
{
	struct rf2xx_context *ctx = dev->data;
	uint8_t reg;

	ctx->promiscuous = promiscuous;

	if (promiscuous) {
		reg = rf2xx_iface_reg_read(dev, RF2XX_XAH_CTRL_1_REG);
		reg |= (1 << RF2XX_AACK_PROM_MODE);
		rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_1_REG, reg);

		reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
		reg |= (1 << RF2XX_AACK_DIS_ACK);
		rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
	} else {
		reg = rf2xx_iface_reg_read(dev, RF2XX_XAH_CTRL_1_REG);
		reg &= ~(1 << RF2XX_AACK_PROM_MODE);
		rf2xx_iface_reg_write(dev, RF2XX_XAH_CTRL_1_REG, reg);

		reg = rf2xx_iface_reg_read(dev, RF2XX_CSMA_SEED_1_REG);
		reg &= ~(1 << RF2XX_AACK_DIS_ACK);
		rf2xx_iface_reg_write(dev, RF2XX_CSMA_SEED_1_REG, reg);
	}

	return 0;
}

int rf2xx_configure(const struct device *dev,
		    enum ieee802154_config_type type,
		    const struct ieee802154_config *config)
{
	int ret = -EINVAL;

	LOG_DBG("Configure %d", type);

	switch (type) {
	case IEEE802154_CONFIG_AUTO_ACK_FPB:
	case IEEE802154_CONFIG_ACK_FPB:
		break;

	case IEEE802154_CONFIG_PAN_COORDINATOR:
		ret = rf2xx_pan_coord_set(dev, config->pan_coordinator);
		break;

	case IEEE802154_CONFIG_PROMISCUOUS:
		ret = rf2xx_promiscuous_set(dev, config->promiscuous);
		break;

	case IEEE802154_CONFIG_EVENT_HANDLER:
	default:
		break;
	}

	return ret;
}

static int rf2xx_attr_get(const struct device *dev, enum ieee802154_attr attr,
			   struct ieee802154_attr_value *value)
{
	struct rf2xx_context *ctx = dev->data;

	switch (attr) {
	case IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_PAGES:
		value->phy_supported_channel_pages = ctx->cc_page;
		return 0;

	case IEEE802154_ATTR_PHY_SUPPORTED_CHANNEL_RANGES:
		value->phy_supported_channels = &ctx->cc_channels;
		return 0;

	default:
		return -ENOENT;
	}
}

static int power_on_and_setup(const struct device *dev)
{
	const struct rf2xx_config *conf = dev->config;
	struct rf2xx_context *ctx = dev->data;
	uint8_t config;

	rf2xx_iface_phy_rst(dev);

	/* Sync transceiver state */
	do {
		rf2xx_iface_reg_write(dev, RF2XX_TRX_STATE_REG,
				      RF2XX_TRX_PHY_STATE_CMD_TRX_OFF);
	} while (RF2XX_TRX_PHY_STATUS_TRX_OFF !=
		 (rf2xx_iface_reg_read(dev, RF2XX_TRX_STATUS_REG) &
		  RF2XX_TRX_PHY_STATUS_MASK));

	/* get device identification */
	ctx->trx_model = rf2xx_iface_reg_read(dev, RF2XX_PART_NUM_REG);
	ctx->trx_version = rf2xx_iface_reg_read(dev, RF2XX_VERSION_NUM_REG);

	/**
	 * Valid transceiver are:
	 *  231-Rev-A (Version 0x02)
	 *  232-Rev-A (Version 0x02)
	 *  233-Rev-A (Version 0x01) (Warning)
	 *  233-Rev-B (Version 0x02)
	 */
	if (ctx->trx_model <= RF2XX_TRX_MODEL_230) {
		LOG_DBG("Invalid or not supported transceiver");
		return -ENODEV;
	}

	if (ctx->trx_model == RF2XX_TRX_MODEL_233 && ctx->trx_version == 0x01) {
		LOG_DBG("Transceiver is old and unstable release");
	}

	/* Set RSSI base */
	if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
		ctx->trx_rssi_base = -100;
	} else if (ctx->trx_model == RF2XX_TRX_MODEL_233) {
		ctx->trx_rssi_base = -94;
	} else if (ctx->trx_model == RF2XX_TRX_MODEL_231) {
		ctx->trx_rssi_base = -91;
	} else {
		ctx->trx_rssi_base = -90;
	}

	/* Disable All Features of TRX_CTRL_0 */
	config = 0;
	rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_0_REG, config);

	/* Configure PHY behaviour */
	config = (1 << RF2XX_TX_AUTO_CRC_ON) |
		 (3 << RF2XX_SPI_CMD_MODE) |
		 (1 << RF2XX_IRQ_MASK_MODE);
	rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_1_REG, config);

	config = (1 << RF2XX_RX_SAFE_MODE);
	if (ctx->trx_model != RF2XX_TRX_MODEL_232) {
		config |= (1 << RF2XX_OQPSK_SCRAM_EN);
	}
	rf2xx_iface_reg_write(dev, RF2XX_TRX_CTRL_2_REG, config);

	if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
		rf2xx_configure_trx_path(dev);
		rf2xx_iface_reg_write(dev, RF2XX_CC_CTRL_1_REG, 0);
	}

	ctx->tx_mode = IEEE802154_TX_MODE_CSMA_CA;
	ctx->promiscuous = false;

	/* Configure INT behaviour */
	config = (1 << RF2XX_RX_START) |
		 (1 << RF2XX_TRX_END);
	rf2xx_iface_reg_write(dev, RF2XX_IRQ_MASK_REG, config);

	gpio_init_callback(&ctx->irq_cb, trx_isr_handler,
			   BIT(conf->irq_gpio.pin));

	if (gpio_add_callback(conf->irq_gpio.port, &ctx->irq_cb) < 0) {
		LOG_ERR("Could not set IRQ callback.");
		return -ENXIO;
	}

	return 0;
}

static inline int configure_gpios(const struct device *dev)
{
	const struct rf2xx_config *conf = dev->config;

	/* Chip IRQ line */
	if (!gpio_is_ready_dt(&conf->irq_gpio)) {
		LOG_ERR("IRQ GPIO device not ready");
		return -ENODEV;
	}
	gpio_pin_configure_dt(&conf->irq_gpio, GPIO_INPUT);
	gpio_pin_interrupt_configure_dt(&conf->irq_gpio,
					GPIO_INT_EDGE_TO_ACTIVE);

	/* Chip RESET line */
	if (!gpio_is_ready_dt(&conf->reset_gpio)) {
		LOG_ERR("RESET GPIO device not ready");
		return -ENODEV;
	}
	gpio_pin_configure_dt(&conf->reset_gpio, GPIO_OUTPUT_INACTIVE);

	/* Chip SLPTR line */
	if (!gpio_is_ready_dt(&conf->slptr_gpio)) {
		LOG_ERR("SLPTR GPIO device not ready");
		return -ENODEV;
	}
	gpio_pin_configure_dt(&conf->slptr_gpio, GPIO_OUTPUT_INACTIVE);

	/* Chip DIG2 line (Optional feature) */
	if (conf->dig2_gpio.port != NULL) {
		if (!gpio_is_ready_dt(&conf->dig2_gpio)) {
			LOG_ERR("DIG2 GPIO device not ready");
			return -ENODEV;
		}
		LOG_INF("Optional instance of %s device activated",
			conf->dig2_gpio.port->name);
		gpio_pin_configure_dt(&conf->dig2_gpio, GPIO_INPUT);
		gpio_pin_interrupt_configure_dt(&conf->dig2_gpio,
						GPIO_INT_EDGE_TO_ACTIVE);
	}

	/* Chip CLKM line (Optional feature) */
	if (conf->clkm_gpio.port != NULL) {
		if (!gpio_is_ready_dt(&conf->clkm_gpio)) {
			LOG_ERR("CLKM GPIO device not ready");
			return -ENODEV;
		}
		LOG_INF("Optional instance of %s device activated",
			conf->clkm_gpio.port->name);
		gpio_pin_configure_dt(&conf->clkm_gpio, GPIO_INPUT);
	}

	return 0;
}

static inline int configure_spi(const struct device *dev)
{
	const struct rf2xx_config *conf = dev->config;

	if (!spi_is_ready_dt(&conf->spi)) {
		LOG_ERR("SPI bus %s is not ready",
			conf->spi.bus->name);
		return -ENODEV;
	}

	return 0;
}

static int rf2xx_init(const struct device *dev)
{
	struct rf2xx_context *ctx = dev->data;
	const struct rf2xx_config *conf = dev->config;
	char thread_name[20];

	LOG_DBG("\nInitialize RF2XX Transceiver\n");

	ctx->dev = dev;

	k_sem_init(&ctx->trx_tx_sync, 0, 1);
	k_sem_init(&ctx->trx_isr_lock, 0, 1);

	if (configure_gpios(dev) != 0) {
		LOG_ERR("Configuring GPIOS failed");
		return -EIO;
	}

	if (configure_spi(dev) != 0) {
		LOG_ERR("Configuring SPI failed");
		return -EIO;
	}

	LOG_DBG("GPIO and SPI configured");

	if (power_on_and_setup(dev) != 0) {
		LOG_ERR("Configuring RF2XX failed");
		return -EIO;
	}

	LOG_DBG("RADIO configured");

	k_thread_create(&ctx->trx_thread,
			ctx->trx_stack,
			CONFIG_IEEE802154_RF2XX_RX_STACK_SIZE,
			rf2xx_thread_main,
			ctx, NULL, NULL,
			K_PRIO_COOP(2), 0, K_NO_WAIT);

	snprintk(thread_name, sizeof(thread_name),
		 "rf2xx_trx [%d]", conf->inst);
	k_thread_name_set(&ctx->trx_thread, thread_name);

	LOG_DBG("Thread OK");

	return 0;
}

static void rf2xx_iface_init(struct net_if *iface)
{
	const struct device *dev = net_if_get_device(iface);
	struct rf2xx_context *ctx = dev->data;
	uint8_t *mac = get_mac(dev);

	net_if_set_link_addr(iface, mac, 8, NET_LINK_IEEE802154);

	ctx->iface = iface;

	if (ctx->trx_model == RF2XX_TRX_MODEL_212) {
		if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915 ||
		    ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_TWO_OQPSK_868_915) {
			ctx->cc_range.from_channel = 0U;
			ctx->cc_range.to_channel = 10U;
		} else if (ctx->cc_page == IEEE802154_ATTR_PHY_CHANNEL_PAGE_FIVE_OQPSK_780) {
			ctx->cc_range.from_channel = 0U;
			ctx->cc_range.to_channel = 3U;
		} else {
			__ASSERT(false, "Unsupported channel page %u.", ctx->cc_page);
		}
	} else {
		__ASSERT(ctx->cc_page ==
				 IEEE802154_ATTR_PHY_CHANNEL_PAGE_ZERO_OQPSK_2450_BPSK_868_915,
			 "Unsupported channel page %u.", ctx->cc_page);
		ctx->cc_range.from_channel = 11U;
		ctx->cc_range.to_channel = 26U;
	}

	ieee802154_init(iface);
}

static const struct ieee802154_radio_api rf2xx_radio_api = {
	.iface_api.init		= rf2xx_iface_init,

	.get_capabilities	= rf2xx_get_capabilities,
	.cca			= rf2xx_cca,
	.set_channel		= rf2xx_set_channel,
	.filter			= rf2xx_filter,
	.set_txpower		= rf2xx_set_txpower,
	.tx			= rf2xx_tx,
	.start			= rf2xx_start,
	.stop			= rf2xx_stop,
	.configure		= rf2xx_configure,
	.attr_get		= rf2xx_attr_get,
};

#if !defined(CONFIG_IEEE802154_RAW_MODE)
    #if defined(CONFIG_NET_L2_IEEE802154)
	#define L2 IEEE802154_L2
	#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(IEEE802154_L2)
	#define MTU RF2XX_MAX_PSDU_LENGTH
    #elif defined(CONFIG_NET_L2_OPENTHREAD)
	#define L2 OPENTHREAD_L2
	#define L2_CTX_TYPE NET_L2_GET_CTX_TYPE(OPENTHREAD_L2)
	#define MTU RF2XX_OT_PSDU_LENGTH
    #endif
#endif /* CONFIG_IEEE802154_RAW_MODE */

#define DRV_INST_LOCAL_MAC_ADDRESS(n)					\
	UTIL_AND(DT_INST_NODE_HAS_PROP(n, local_mac_address),		\
		 UTIL_AND(DT_INST_PROP_LEN(n, local_mac_address) == 8,	\
			  DT_INST_PROP(n, local_mac_address)))

#define IEEE802154_RF2XX_DEVICE_CONFIG(n)				  \
	BUILD_ASSERT(DT_INST_PROP_LEN(n, tx_pwr_min) == 2,		  \
	"rf2xx: Error TX-PWR-MIN len is different of two");		  \
	BUILD_ASSERT(DT_INST_PROP_LEN(n, tx_pwr_max) == 2,		  \
	"rf2xx: Error TX-PWR-MAX len is different of two");		  \
	BUILD_ASSERT(DT_INST_PROP_LEN(n, tx_pwr_table) != 0,		  \
	"rf2xx: Error TX-PWR-TABLE len must be greater than zero");	  \
	static const uint8_t rf2xx_pwr_table_##n[] =			  \
		DT_INST_PROP_OR(n, tx_pwr_table, 0);			  \
	static const struct rf2xx_config rf2xx_ctx_config_##n = {	  \
		.inst = n,						  \
		.has_mac = DT_INST_NODE_HAS_PROP(n, local_mac_address),   \
		.irq_gpio = GPIO_DT_SPEC_INST_GET(n, irq_gpios),	  \
		.reset_gpio = GPIO_DT_SPEC_INST_GET(n, reset_gpios),	  \
		.slptr_gpio = GPIO_DT_SPEC_INST_GET(n, slptr_gpios),	  \
		.dig2_gpio = GPIO_DT_SPEC_INST_GET_OR(n, dig2_gpios, {}), \
		.clkm_gpio = GPIO_DT_SPEC_INST_GET_OR(n, clkm_gpios, {}), \
		.spi = SPI_DT_SPEC_INST_GET(n, SPI_WORD_SET(8) |	  \
				 SPI_TRANSFER_MSB, 0),			  \
									  \
		.tx_pwr_min = DT_INST_PROP_OR(n, tx_pwr_min, 0),	  \
		.tx_pwr_max = DT_INST_PROP_OR(n, tx_pwr_max, 0),	  \
		.tx_pwr_table = rf2xx_pwr_table_##n,			  \
		.tx_pwr_table_size = DT_INST_PROP_LEN(n, tx_pwr_table),	  \
	}

#define IEEE802154_RF2XX_DEVICE_DATA(n)                                 \
	static struct rf2xx_context rf2xx_ctx_data_##n = {              \
		.mac_addr = { DRV_INST_LOCAL_MAC_ADDRESS(n) },          \
		.cc_page = BIT(DT_INST_ENUM_IDX_OR(n, channel_page, 0)),\
		.cc_channels = {                                        \
			.ranges = &rf2xx_ctx_data_##n.cc_range,         \
			.num_ranges = 1U,                               \
		}                                                       \
	}

#define IEEE802154_RF2XX_RAW_DEVICE_INIT(n)	   \
	DEVICE_DT_INST_DEFINE(			   \
		n,				   \
		&rf2xx_init,			   \
		NULL,				   \
		&rf2xx_ctx_data_##n,		   \
		&rf2xx_ctx_config_##n,		   \
		POST_KERNEL,			   \
		CONFIG_IEEE802154_RF2XX_INIT_PRIO, \
		&rf2xx_radio_api)

#define IEEE802154_RF2XX_NET_DEVICE_INIT(n)	   \
	NET_DEVICE_DT_INST_DEFINE(		   \
		n,				   \
		&rf2xx_init,			   \
		NULL,				   \
		&rf2xx_ctx_data_##n,		   \
		&rf2xx_ctx_config_##n,		   \
		CONFIG_IEEE802154_RF2XX_INIT_PRIO, \
		&rf2xx_radio_api,		   \
		L2,				   \
		L2_CTX_TYPE,			   \
		MTU)

#define IEEE802154_RF2XX_INIT(inst)				\
	IEEE802154_RF2XX_DEVICE_CONFIG(inst);			\
	IEEE802154_RF2XX_DEVICE_DATA(inst);			\
								\
	COND_CODE_1(CONFIG_IEEE802154_RAW_MODE,			\
		    (IEEE802154_RF2XX_RAW_DEVICE_INIT(inst);),	\
		    (IEEE802154_RF2XX_NET_DEVICE_INIT(inst);))

DT_INST_FOREACH_STATUS_OKAY(IEEE802154_RF2XX_INIT)
