text: /*
   * Copyright (c) 2018 Nordic Semiconductor ASA
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  /**
   * @file
   *   This file implements the OpenThread platform abstraction
   *   for radio communication.
   *
   */
  
  #define LOG_MODULE_NAME net_otPlat_radio
  
  #include <logging/log.h>
  LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
  
  #include <stdbool.h>
  #include <stddef.h>
  #include <stdint.h>
  #include <string.h>
  
  #include <kernel.h>
  #include <device.h>
  #include <net/ieee802154_radio.h>
  #include <net/net_pkt.h>
  #include <sys/__assert.h>
  
  #include <openthread/ip6.h>
  #include <openthread-system.h>
  #include <openthread/instance.h>
  #include <openthread/platform/radio.h>
  #include <openthread/platform/diag.h>
  #include <openthread/message.h>
  
  #include "platform-zephyr.h"
  
  #define SHORT_ADDRESS_SIZE 2
  
  #define FCS_SIZE 2
  #if defined(CONFIG_IEEE802154_2015)
  #define ACK_PKT_LENGTH 127
  #else
  #define ACK_PKT_LENGTH 5
  #endif
  
  #define FRAME_TYPE_MASK 0x07
  #define FRAME_TYPE_ACK 0x02
  
  #if IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE)
  #define OT_WORKER_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)
  #else
  #define OT_WORKER_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY)
  #endif
  
  enum pending_events {
  	PENDING_EVENT_FRAME_TO_SEND, /* There is a tx frame to send  */
  	PENDING_EVENT_FRAME_RECEIVED, /* Radio has received new frame */
  	PENDING_EVENT_RX_FAILED, /* The RX failed */
  	PENDING_EVENT_TX_STARTED, /* Radio has started transmitting */
  	PENDING_EVENT_TX_DONE, /* Radio transmission finished */
  	PENDING_EVENT_DETECT_ENERGY, /* Requested to start Energy Detection procedure */
  	PENDING_EVENT_DETECT_ENERGY_DONE, /* Energy Detection finished */
  	PENDING_EVENT_SLEEP, /* Sleep if idle */
  	PENDING_EVENT_COUNT /* Keep last */
  };
  
  K_SEM_DEFINE(radio_sem, 0, 1);
  
  static otRadioState sState = OT_RADIO_STATE_DISABLED;
  
  static otRadioFrame sTransmitFrame;
  static otRadioFrame ack_frame;
  static uint8_t ack_psdu[ACK_PKT_LENGTH];
  
  static struct net_pkt *tx_pkt;
  static struct net_buf *tx_payload;
  
  static const struct device *radio_dev;
  static struct ieee802154_radio_api *radio_api;
  
  static int8_t tx_power;
  static uint16_t channel;
  static bool promiscuous;
  
  static uint16_t energy_detection_time;
  static uint8_t energy_detection_channel;
  static int16_t energy_detected_value;
  
  ATOMIC_DEFINE(pending_events, PENDING_EVENT_COUNT);
  K_KERNEL_STACK_DEFINE(ot_task_stack,
  		      CONFIG_OPENTHREAD_RADIO_WORKQUEUE_STACK_SIZE);
  static struct k_work_q ot_work_q;
  static otError rx_result;
  static otError tx_result;
  
  K_FIFO_DEFINE(rx_pkt_fifo);
  K_FIFO_DEFINE(tx_pkt_fifo);
  
  static inline bool is_pending_event_set(enum pending_events event)
  {
  	return atomic_test_bit(pending_events, event);
  }
  
  static void set_pending_event(enum pending_events event)
  {
  	atomic_set_bit(pending_events, event);
  	otSysEventSignalPending();
  }
  
  static void reset_pending_event(enum pending_events event)
  {
  	atomic_clear_bit(pending_events, event);
  }
  
  static inline void clear_pending_events(void)
  {
  	atomic_clear(pending_events);
  }
  
  void energy_detected(const struct device *dev, int16_t max_ed)
  {
  	if (dev == radio_dev) {
  		energy_detected_value = max_ed;
  		set_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
  	}
  }
  
  enum net_verdict ieee802154_radio_handle_ack(struct net_if *iface,
  					     struct net_pkt *pkt)
  {
  	ARG_UNUSED(iface);
  
  	size_t ack_len = net_pkt_get_len(pkt);
  
  	if (ack_len > ACK_PKT_LENGTH) {
  		return NET_CONTINUE;
  	}
  
  	if ((*net_pkt_data(pkt) & FRAME_TYPE_MASK) != FRAME_TYPE_ACK) {
  		return NET_CONTINUE;
  	}
  
  	if (ack_frame.mLength != 0) {
  		LOG_ERR("Overwriting unhandled ACK frame.");
  	}
  
  	if (net_pkt_read(pkt, ack_psdu, ack_len) < 0) {
  		LOG_ERR("Failed to read ACK frame.");
  		return NET_CONTINUE;
  	}
  
  	ack_frame.mPsdu = ack_psdu;
  	ack_frame.mLength = ack_len;
  	ack_frame.mInfo.mRxInfo.mLqi = net_pkt_ieee802154_lqi(pkt);
  	ack_frame.mInfo.mRxInfo.mRssi = net_pkt_ieee802154_rssi(pkt);
  
  #if defined(CONFIG_NET_PKT_TIMESTAMP)
  	struct net_ptp_time *pkt_time = net_pkt_timestamp(pkt);
  
  	ack_frame.mInfo.mRxInfo.mTimestamp =
  		pkt_time->second * USEC_PER_SEC + pkt_time->nanosecond / NSEC_PER_USEC;
  #endif
  
  	return NET_OK;
  }
  
  void handle_radio_event(const struct device *dev, enum ieee802154_event evt,
  			void *event_params)
  {
  	ARG_UNUSED(event_params);
  
  	switch (evt) {
  	case IEEE802154_EVENT_TX_STARTED:
  		if (sState == OT_RADIO_STATE_TRANSMIT) {
  			set_pending_event(PENDING_EVENT_TX_STARTED);
  		}
  		break;
  	case IEEE802154_EVENT_RX_FAILED:
  		if (sState == OT_RADIO_STATE_RECEIVE) {
  			switch (*(enum ieee802154_rx_fail_reason *) event_params) {
  			case IEEE802154_RX_FAIL_NOT_RECEIVED:
  				rx_result = OT_ERROR_NO_FRAME_RECEIVED;
  				break;
  
  			case IEEE802154_RX_FAIL_INVALID_FCS:
  				rx_result = OT_ERROR_FCS;
  				break;
  
  			case IEEE802154_RX_FAIL_ADDR_FILTERED:
  				rx_result = OT_ERROR_DESTINATION_ADDRESS_FILTERED;
  				break;
  
  			case IEEE802154_RX_FAIL_OTHER:
  			default:
  				rx_result = OT_ERROR_FAILED;
  				break;
  			}
  			set_pending_event(PENDING_EVENT_RX_FAILED);
  		}
  		break;
  	case IEEE802154_EVENT_SLEEP:
  		set_pending_event(PENDING_EVENT_SLEEP);
  		break;
  	default:
  		/* do nothing - ignore event */
  		break;
  	}
  }
  
  static void dataInit(void)
  {
  	tx_pkt = net_pkt_alloc(K_NO_WAIT);
  	__ASSERT_NO_MSG(tx_pkt != NULL);
  
  	tx_payload = net_pkt_get_reserve_tx_data(K_NO_WAIT);
  	__ASSERT_NO_MSG(tx_payload != NULL);
  
  	net_pkt_append_buffer(tx_pkt, tx_payload);
  
  	sTransmitFrame.mPsdu = tx_payload->data;
  }
  
  void platformRadioInit(void)
  {
  	struct ieee802154_config cfg;
  
  	dataInit();
  
  	radio_dev = device_get_binding(CONFIG_NET_CONFIG_IEEE802154_DEV_NAME);
  	__ASSERT_NO_MSG(radio_dev != NULL);
  
  	radio_api = (struct ieee802154_radio_api *)radio_dev->api;
  	if (!radio_api) {
  		return;
  	}
  
  	k_work_queue_start(&ot_work_q, ot_task_stack,
  			   K_KERNEL_STACK_SIZEOF(ot_task_stack),
  			   OT_WORKER_PRIORITY, NULL);
  	k_thread_name_set(&ot_work_q.thread, "ot_radio_workq");
  
  	if ((radio_api->get_capabilities(radio_dev) &
  	     IEEE802154_HW_TX_RX_ACK) != IEEE802154_HW_TX_RX_ACK) {
  		LOG_ERR("Only radios with automatic ack handling "
  			"are currently supported");
  		k_panic();
  	}
  
  	cfg.event_handler = handle_radio_event;
  	radio_api->configure(radio_dev, IEEE802154_CONFIG_EVENT_HANDLER, &cfg);
  }
  
  void transmit_message(struct k_work *tx_job)
  {
  	ARG_UNUSED(tx_job);
  
  	tx_result = OT_ERROR_NONE;
  	/*
  	 * The payload is already in tx_payload->data,
  	 * but we need to set the length field
  	 * according to sTransmitFrame.length.
  	 * We subtract the FCS size as radio driver
  	 * adds CRC and increases frame length on its own.
  	 */
  	tx_payload->len = sTransmitFrame.mLength - FCS_SIZE;
  
  	channel = sTransmitFrame.mChannel;
  
  	radio_api->set_channel(radio_dev, sTransmitFrame.mChannel);
  	radio_api->set_txpower(radio_dev, tx_power);
  
  	net_pkt_set_ieee802154_frame_retry(tx_pkt,
  					   sTransmitFrame.mInfo.mTxInfo.mIsARetx);
  
  	if ((radio_api->get_capabilities(radio_dev) & IEEE802154_HW_TXTIME) &&
  	    (sTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)) {
  		uint64_t tx_at = sTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime +
  				 sTransmitFrame.mInfo.mTxInfo.mTxDelay;
  		net_pkt_set_txtime(tx_pkt, NSEC_PER_USEC * tx_at);
  		if (radio_api->tx(radio_dev, IEEE802154_TX_MODE_TXTIME_CCA,
  				  tx_pkt, tx_payload)) {
  			tx_result = OT_ERROR_INVALID_STATE;
  		}
  	} else if (sTransmitFrame.mInfo.mTxInfo.mCsmaCaEnabled) {
  		if (radio_api->get_capabilities(radio_dev) &
  		    IEEE802154_HW_CSMA) {
  			if (radio_api->tx(radio_dev, IEEE802154_TX_MODE_CSMA_CA,
  					  tx_pkt, tx_payload) != 0) {
  				tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
  			}
  		} else if (radio_api->cca(radio_dev) != 0 ||
  			   radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT,
  					 tx_pkt, tx_payload) != 0) {
  			tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
  		}
  	} else {
  		if (radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt, tx_payload)) {
  			tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
  		}
  	}
  
  	set_pending_event(PENDING_EVENT_TX_DONE);
  }
  
  static inline void handle_tx_done(otInstance *aInstance)
  {
  	if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) {
  		otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, tx_result);
  	} else {
  		if (sTransmitFrame.mPsdu[0] & IEEE802154_AR_FLAG_SET) {
  			if (ack_frame.mLength == 0) {
  				LOG_DBG("No ACK received.");
  				otPlatRadioTxDone(aInstance, &sTransmitFrame,
  						  NULL, OT_ERROR_NO_ACK);
  			} else {
  				otPlatRadioTxDone(aInstance, &sTransmitFrame,
  						  &ack_frame, tx_result);
  			}
  		} else {
  			otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, tx_result);
  		}
  		ack_frame.mLength = 0;
  	}
  }
  
  static void openthread_handle_received_frame(otInstance *instance,
  					     struct net_pkt *pkt)
  {
  	otRadioFrame recv_frame;
  
  	recv_frame.mPsdu = net_buf_frag_last(pkt->buffer)->data;
  	/* Length inc. CRC. */
  	recv_frame.mLength = net_buf_frags_len(pkt->buffer);
  	recv_frame.mChannel = platformRadioChannelGet(instance);
  	recv_frame.mInfo.mRxInfo.mLqi = net_pkt_ieee802154_lqi(pkt);
  	recv_frame.mInfo.mRxInfo.mRssi = net_pkt_ieee802154_rssi(pkt);
  	recv_frame.mInfo.mRxInfo.mAckedWithFramePending = net_pkt_ieee802154_ack_fpb(pkt);
  
  #if defined(CONFIG_NET_PKT_TIMESTAMP)
  	struct net_ptp_time *pkt_time = net_pkt_timestamp(pkt);
  
  	recv_frame.mInfo.mRxInfo.mTimestamp =
  		pkt_time->second * USEC_PER_SEC + pkt_time->nanosecond / NSEC_PER_USEC;
  #endif
  
  #if defined(CONFIG_IEEE802154_2015)
  	if (net_pkt_ieee802154_arb(pkt) && net_pkt_ieee802154_fv2015(pkt)) {
  		recv_frame.mInfo.mRxInfo.mAckedWithSecEnhAck =
  			net_pkt_ieee802154_ack_seb(pkt);
  		recv_frame.mInfo.mRxInfo.mAckFrameCounter =
  			net_pkt_ieee802154_ack_fc(pkt);
  		recv_frame.mInfo.mRxInfo.mAckKeyId =
  			net_pkt_ieee802154_ack_keyid(pkt);
  	}
  #endif
  
  	if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) {
  		otPlatDiagRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE);
  	} else {
  		otPlatRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE);
  	}
  
  	net_pkt_unref(pkt);
  }
  
  static void openthread_handle_frame_to_send(otInstance *instance,
  					    struct net_pkt *pkt)
  {
  	struct net_buf *buf;
  	otMessage *message;
  	otMessageSettings settings;
  
  	NET_DBG("Sending Ip6 packet to ot stack");
  
  	settings.mPriority = OT_MESSAGE_PRIORITY_NORMAL;
  	settings.mLinkSecurityEnabled = true;
  	message = otIp6NewMessage(instance, &settings);
  	if (message == NULL) {
  		goto exit;
  	}
  
  	for (buf = pkt->buffer; buf; buf = buf->frags) {
  		if (otMessageAppend(message, buf->data, buf->len) != OT_ERROR_NONE) {
  			NET_ERR("Error while appending to otMessage");
  			otMessageFree(message);
  			goto exit;
  		}
  	}
  
  	if (otIp6Send(instance, message) != OT_ERROR_NONE) {
  		NET_ERR("Error while calling otIp6Send");
  		goto exit;
  	}
  
  exit:
  	net_pkt_unref(pkt);
  }
  
  int notify_new_rx_frame(struct net_pkt *pkt)
  {
  	k_fifo_put(&rx_pkt_fifo, pkt);
  	set_pending_event(PENDING_EVENT_FRAME_RECEIVED);
  
  	return 0;
  }
  
  int notify_new_tx_frame(struct net_pkt *pkt)
  {
  	k_fifo_put(&tx_pkt_fifo, pkt);
  	set_pending_event(PENDING_EVENT_FRAME_TO_SEND);
  
  	return 0;
  }
  
  static int run_tx_task(otInstance *aInstance)
  {
  	static K_WORK_DEFINE(tx_job, transmit_message);
  
  	ARG_UNUSED(aInstance);
  
  	if (!k_work_is_pending(&tx_job)) {
  		sState = OT_RADIO_STATE_TRANSMIT;
  
  		k_work_submit_to_queue(&ot_work_q, &tx_job);
  		return 0;
  	} else {
  		return -EBUSY;
  	}
  }
  
  void platformRadioProcess(otInstance *aInstance)
  {
  	bool event_pending = false;
  
  	if (is_pending_event_set(PENDING_EVENT_FRAME_TO_SEND)) {
  		struct net_pkt *tx_pkt;
  
  		reset_pending_event(PENDING_EVENT_FRAME_TO_SEND);
  		while ((tx_pkt = (struct net_pkt *) k_fifo_get(&tx_pkt_fifo, K_NO_WAIT)) != NULL) {
  			if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR_RCP)) {
  				net_pkt_unref(tx_pkt);
  			} else {
  				openthread_handle_frame_to_send(aInstance, tx_pkt);
  			}
  		}
  	}
  
  	if (is_pending_event_set(PENDING_EVENT_FRAME_RECEIVED)) {
  		struct net_pkt *rx_pkt;
  
  		reset_pending_event(PENDING_EVENT_FRAME_RECEIVED);
  		while ((rx_pkt = (struct net_pkt *) k_fifo_get(&rx_pkt_fifo, K_NO_WAIT)) != NULL) {
  			openthread_handle_received_frame(aInstance, rx_pkt);
  		}
  	}
  
  	if (is_pending_event_set(PENDING_EVENT_RX_FAILED)) {
  		reset_pending_event(PENDING_EVENT_RX_FAILED);
  		if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) {
  			otPlatDiagRadioReceiveDone(aInstance, NULL, rx_result);
  		} else {
  			otPlatRadioReceiveDone(aInstance, NULL, rx_result);
  		}
  	}
  
  	if (is_pending_event_set(PENDING_EVENT_TX_STARTED)) {
  		reset_pending_event(PENDING_EVENT_TX_STARTED);
  		otPlatRadioTxStarted(aInstance, &sTransmitFrame);
  	}
  
  	if (is_pending_event_set(PENDING_EVENT_TX_DONE)) {
  		reset_pending_event(PENDING_EVENT_TX_DONE);
  
  		if (sState == OT_RADIO_STATE_TRANSMIT) {
  			sState = OT_RADIO_STATE_RECEIVE;
  			handle_tx_done(aInstance);
  		}
  	}
  
  	if (is_pending_event_set(PENDING_EVENT_SLEEP)) {
  		reset_pending_event(PENDING_EVENT_SLEEP);
  		ARG_UNUSED(otPlatRadioSleep(aInstance));
  	}
  
  	/* handle events that can't run during transmission */
  	if (sState != OT_RADIO_STATE_TRANSMIT) {
  		if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY)) {
  			radio_api->set_channel(radio_dev,
  					       energy_detection_channel);
  
  			if (!radio_api->ed_scan(radio_dev,
  						energy_detection_time,
  						energy_detected)) {
  				reset_pending_event(
  					PENDING_EVENT_DETECT_ENERGY);
  			} else {
  				event_pending = true;
  			}
  		}
  
  		if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY_DONE)) {
  			otPlatRadioEnergyScanDone(aInstance, (int8_t) energy_detected_value);
  			reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
  		}
  	}
  
  	if (event_pending) {
  		otSysEventSignalPending();
  	}
  }
  
  uint16_t platformRadioChannelGet(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	return channel;
  }
  
  void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
  {
  	ARG_UNUSED(aInstance);
  
  	radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_PAN_ID,
  			  (struct ieee802154_filter *) &aPanId);
  }
  
  void otPlatRadioSetExtendedAddress(otInstance *aInstance,
  				   const otExtAddress *aExtAddress)
  {
  	ARG_UNUSED(aInstance);
  
  	radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_IEEE_ADDR,
  			  (struct ieee802154_filter *) &aExtAddress);
  }
  
  void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddress)
  {
  	ARG_UNUSED(aInstance);
  
  	radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_SHORT_ADDR,
  			  (struct ieee802154_filter *) &aShortAddress);
  }
  
  bool otPlatRadioIsEnabled(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	return (sState != OT_RADIO_STATE_DISABLED) ? true : false;
  }
  
  otError otPlatRadioEnable(otInstance *aInstance)
  {
  	if (!otPlatRadioIsEnabled(aInstance)) {
  		sState = OT_RADIO_STATE_SLEEP;
  	}
  
  	return OT_ERROR_NONE;
  }
  
  otError otPlatRadioDisable(otInstance *aInstance)
  {
  	if (otPlatRadioIsEnabled(aInstance)) {
  		sState = OT_RADIO_STATE_DISABLED;
  	}
  
  	return OT_ERROR_NONE;
  }
  
  otError otPlatRadioSleep(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	otError error = OT_ERROR_INVALID_STATE;
  
  	if (sState == OT_RADIO_STATE_SLEEP ||
  	    sState == OT_RADIO_STATE_RECEIVE ||
  	    sState == OT_RADIO_STATE_TRANSMIT) {
  		error = OT_ERROR_NONE;
  		if (radio_api->stop(radio_dev)) {
  			sState = OT_RADIO_STATE_SLEEP;
  		}
  	}
  
  	return error;
  }
  
  otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
  {
  	ARG_UNUSED(aInstance);
  
  	channel = aChannel;
  
  	radio_api->set_channel(radio_dev, aChannel);
  	radio_api->set_txpower(radio_dev, tx_power);
  	radio_api->start(radio_dev);
  	sState = OT_RADIO_STATE_RECEIVE;
  
  	return OT_ERROR_NONE;
  }
  
  #if defined(CONFIG_OPENTHREAD_CSL_RECEIVER)
  otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel,
  			     uint32_t aStart, uint32_t aDuration)
  {
  	int result;
  
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = {
  		.rx_slot.channel = aChannel,
  		.rx_slot.start = aStart,
  		.rx_slot.duration = aDuration,
  	};
  
  	result = radio_api->configure(radio_dev, IEEE802154_CONFIG_RX_SLOT,
  				      &config);
  
  	return result ? OT_ERROR_FAILED : OT_ERROR_NONE;
  }
  #endif
  
  otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aPacket)
  {
  	otError error = OT_ERROR_INVALID_STATE;
  
  	ARG_UNUSED(aInstance);
  	ARG_UNUSED(aPacket);
  
  	__ASSERT_NO_MSG(aPacket == &sTransmitFrame);
  
  	enum ieee802154_hw_caps radio_caps;
  
  	radio_caps = radio_api->get_capabilities(radio_dev);
  
  	if ((sState == OT_RADIO_STATE_RECEIVE) || (radio_caps & IEEE802154_HW_SLEEP_TO_TX)) {
  		if (run_tx_task(aInstance) == 0) {
  			error = OT_ERROR_NONE;
  		}
  	}
  
  	return error;
  }
  
  otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	return &sTransmitFrame;
  }
  
  static void get_rssi_energy_detected(const struct device *dev, int16_t max_ed)
  {
  	ARG_UNUSED(dev);
  	energy_detected_value = max_ed;
  	k_sem_give(&radio_sem);
  }
  
  int8_t otPlatRadioGetRssi(otInstance *aInstance)
  {
  	int8_t ret_rssi = INT8_MAX;
  	int error = 0;
  	const uint16_t energy_detection_time = 1;
  	enum ieee802154_hw_caps radio_caps;
  	ARG_UNUSED(aInstance);
  
  	radio_caps = radio_api->get_capabilities(radio_dev);
  
  	if (!(radio_caps & IEEE802154_HW_ENERGY_SCAN)) {
  		/*
  		 * TODO: No API in Zephyr to get the RSSI
  		 * when IEEE802154_HW_ENERGY_SCAN is not available
  		 */
  		ret_rssi = 0;
  	} else {
  		/*
  		 * Blocking implementation of get RSSI
  		 * using no-blocking ed_scan
  		 */
  		error = radio_api->ed_scan(radio_dev, energy_detection_time,
  					   get_rssi_energy_detected);
  
  		if (error == 0) {
  			k_sem_take(&radio_sem, K_FOREVER);
  
  			ret_rssi = (int8_t)energy_detected_value;
  		}
  	}
  
  	return ret_rssi;
  }
  
  otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
  {
  	otRadioCaps caps = OT_RADIO_CAPS_NONE;
  
  	enum ieee802154_hw_caps radio_caps;
  	ARG_UNUSED(aInstance);
  	__ASSERT(radio_api,
  	    "platformRadioInit needs to be called prior to otPlatRadioGetCaps");
  
  	radio_caps = radio_api->get_capabilities(radio_dev);
  
  	if (radio_caps & IEEE802154_HW_ENERGY_SCAN) {
  		caps |= OT_RADIO_CAPS_ENERGY_SCAN;
  	}
  
  	if (radio_caps & IEEE802154_HW_CSMA) {
  		caps |= OT_RADIO_CAPS_CSMA_BACKOFF;
  	}
  
  	if (radio_caps & IEEE802154_HW_TX_RX_ACK) {
  		caps |= OT_RADIO_CAPS_ACK_TIMEOUT;
  	}
  
  	if (radio_caps & IEEE802154_HW_SLEEP_TO_TX) {
  		caps |= OT_RADIO_CAPS_SLEEP_TO_TX;
  	}
  
  #if defined(CONFIG_IEEE802154_2015)
  	if (radio_caps & IEEE802154_HW_TX_SEC) {
  		caps |= OT_RADIO_CAPS_TRANSMIT_SEC;
  	}
  #endif
  
  #if defined(CONFIG_NET_PKT_TXTIME)
  	if (radio_caps & IEEE802154_HW_TXTIME) {
  		caps |= OT_RADIO_CAPS_TRANSMIT_TIMING;
  	}
  #endif
  
  	if (radio_caps & IEEE802154_HW_RXTIME) {
  		caps |= OT_RADIO_CAPS_RECEIVE_TIMING;
  	}
  
  	return caps;
  }
  
  bool otPlatRadioGetPromiscuous(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	LOG_DBG("PromiscuousMode=%d", promiscuous ? 1 : 0);
  
  	return promiscuous;
  }
  
  void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
  {
  	struct ieee802154_config config = {
  		.promiscuous = aEnable
  	};
  
  	ARG_UNUSED(aInstance);
  
  	LOG_DBG("PromiscuousMode=%d", aEnable ? 1 : 0);
  
  	promiscuous = aEnable;
  	radio_api->configure(radio_dev, IEEE802154_CONFIG_PROMISCUOUS, &config);
  }
  
  otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel,
  			      uint16_t aScanDuration)
  {
  	energy_detection_time    = aScanDuration;
  	energy_detection_channel = aScanChannel;
  
  	if (radio_api->ed_scan == NULL) {
  		return OT_ERROR_NOT_IMPLEMENTED;
  	}
  
  	reset_pending_event(PENDING_EVENT_DETECT_ENERGY);
  	reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
  
  	radio_api->set_channel(radio_dev, aScanChannel);
  
  	if (radio_api->ed_scan(radio_dev, energy_detection_time, energy_detected) != 0) {
  		/*
  		 * OpenThread API does not accept failure of this function,
  		 * it can return 'No Error' or 'Not Implemented' error only.
  		 * If ed_scan start failed event is set to schedule the scan at
  		 * later time.
  		 */
  		LOG_ERR("Failed do start energy scan, scheduling for later");
  		set_pending_event(PENDING_EVENT_DETECT_ENERGY);
  	}
  
  	return OT_ERROR_NONE;
  }
  
  otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance,
  					       int8_t *aThreshold)
  {
  	OT_UNUSED_VARIABLE(aInstance);
  	OT_UNUSED_VARIABLE(aThreshold);
  
  	return OT_ERROR_NOT_IMPLEMENTED;
  }
  
  otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance,
  					       int8_t aThreshold)
  {
  	OT_UNUSED_VARIABLE(aInstance);
  	OT_UNUSED_VARIABLE(aThreshold);
  
  	return OT_ERROR_NOT_IMPLEMENTED;
  }
  
  void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
  {
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = {
  		.auto_ack_fpb.enabled = aEnable,
  		.auto_ack_fpb.mode = IEEE802154_FPB_ADDR_MATCH_THREAD,
  	};
  
  	(void)radio_api->configure(radio_dev, IEEE802154_CONFIG_AUTO_ACK_FPB,
  				   &config);
  }
  
  otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance,
  					 const uint16_t aShortAddress)
  {
  	ARG_UNUSED(aInstance);
  
  	uint8_t short_address[SHORT_ADDRESS_SIZE];
  	struct ieee802154_config config = {
  		.ack_fpb.enabled = true,
  		.ack_fpb.addr = short_address,
  		.ack_fpb.extended = false
  	};
  
  	sys_put_le16(aShortAddress, short_address);
  
  	if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
  				 &config) != 0) {
  		return OT_ERROR_NO_BUFS;
  	}
  
  	return OT_ERROR_NONE;
  }
  
  otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance,
  				       const otExtAddress *aExtAddress)
  {
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = {
  		.ack_fpb.enabled = true,
  		.ack_fpb.addr = (uint8_t *)aExtAddress->m8,
  		.ack_fpb.extended = true
  	};
  
  	if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
  				 &config) != 0) {
  		return OT_ERROR_NO_BUFS;
  	}
  
  	return OT_ERROR_NONE;
  }
  
  otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance,
  					   const uint16_t aShortAddress)
  {
  	ARG_UNUSED(aInstance);
  
  	uint8_t short_address[SHORT_ADDRESS_SIZE];
  	struct ieee802154_config config = {
  		.ack_fpb.enabled = false,
  		.ack_fpb.addr = short_address,
  		.ack_fpb.extended = false
  	};
  
  	sys_put_le16(aShortAddress, short_address);
  
  	if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
  				 &config) != 0) {
  		return OT_ERROR_NO_BUFS;
  	}
  
  	return OT_ERROR_NONE;
  }
  
  otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance,
  					 const otExtAddress *aExtAddress)
  {
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = {
  		.ack_fpb.enabled = false,
  		.ack_fpb.addr = (uint8_t *)aExtAddress->m8,
  		.ack_fpb.extended = true
  	};
  
  	if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
  				 &config) != 0) {
  		return OT_ERROR_NO_BUFS;
  	}
  
  	return OT_ERROR_NONE;
  }
  
  void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = {
  		.ack_fpb.enabled = false,
  		.ack_fpb.addr = NULL,
  		.ack_fpb.extended = false
  	};
  
  	(void)radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
  				   &config);
  }
  
  void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = {
  		.ack_fpb.enabled = false,
  		.ack_fpb.addr = NULL,
  		.ack_fpb.extended = true
  	};
  
  	(void)radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
  				   &config);
  }
  
  int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	return -100;
  }
  
  otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
  {
  	ARG_UNUSED(aInstance);
  
  	if (aPower == NULL) {
  		return OT_ERROR_INVALID_ARGS;
  	}
  
  	*aPower = tx_power;
  
  	return OT_ERROR_NONE;
  }
  
  otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
  {
  	ARG_UNUSED(aInstance);
  
  	tx_power = aPower;
  
  	return OT_ERROR_NONE;
  }
  
  #if defined(CONFIG_NET_PKT_TXTIME)
  uint64_t otPlatRadioGetNow(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	return radio_api->get_time(radio_dev);
  }
  #endif
  
  #if defined(CONFIG_IEEE802154_2015)
  void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode,
  			  uint8_t aKeyId, const otMacKey *aPrevKey,
  			  const otMacKey *aCurrKey, const otMacKey *aNextKey)
  {
  	ARG_UNUSED(aInstance);
  	__ASSERT_NO_MSG(aPrevKey != NULL && aCurrKey != NULL &&
  			aNextKey != NULL);
  
  	uint8_t key_id_mode = aKeyIdMode >> 3;
  
  	struct ieee802154_key keys[] = {
  		{
  			.key_id_mode = key_id_mode,
  			.key_index = aKeyId == 1 ? 0x80 : aKeyId - 1,
  			.key_value = (uint8_t *)aPrevKey->m8,
  			.frame_counter_per_key = false,
  		},
  		{
  			.key_id_mode = key_id_mode,
  			.key_index = aKeyId,
  			.key_value = (uint8_t *)aCurrKey->m8,
  			.frame_counter_per_key = false,
  		},
  		{
  			.key_id_mode = key_id_mode,
  			.key_index = aKeyId == 0x80 ? 1 : aKeyId + 1,
  			.key_value = (uint8_t *)aNextKey->m8,
  			.frame_counter_per_key = false,
  		},
  		{
  			.key_value = NULL,
  		},
  	};
  
  	struct ieee802154_config config = {
  		.mac_keys = keys
  	};
  
  	(void)radio_api->configure(radio_dev, IEEE802154_CONFIG_MAC_KEYS,
  				   &config);
  }
  
  void otPlatRadioSetMacFrameCounter(otInstance *aInstance,
  				   uint32_t aMacFrameCounter)
  {
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = { .frame_counter = aMacFrameCounter };
  
  	(void)radio_api->configure(radio_dev, IEEE802154_CONFIG_FRAME_COUNTER,
  				   &config);
  }
  #endif
  
  #if defined(CONFIG_OPENTHREAD_CSL_RECEIVER)
  otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr,
  			     const otExtAddress *aExtAddr)
  {
  	int result;
  	uint8_t ie_header[OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE];
  	struct ieee802154_config config;
  
  	ARG_UNUSED(aInstance);
  
  	ie_header[0] = CSL_IE_HEADER_BYTES_LO;
  	ie_header[1] = CSL_IE_HEADER_BYTES_HI;
  	/* Leave CSL Phase empty intentionally */
  	sys_put_le16(aCslPeriod, &ie_header[OT_IE_HEADER_SIZE + 2]);
  	config.ack_ie.data = ie_header;
  	config.ack_ie.data_len = OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE;
  	config.ack_ie.short_addr = aShortAddr;
  	config.ack_ie.ext_addr = aExtAddr->m8;
  	result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config);
  
  	config.csl_period = aCslPeriod;
  	result += radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_PERIOD, &config);
  
  	return result ? OT_ERROR_FAILED : OT_ERROR_NONE;
  }
  
  void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
  {
  	ARG_UNUSED(aInstance);
  
  	struct ieee802154_config config = { .csl_rx_time = aCslSampleTime };
  
  	(void)radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_RX_TIME, &config);
  }
  #endif /* CONFIG_OPENTHREAD_CSL_RECEIVER */
  
  uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
  {
  	ARG_UNUSED(aInstance);
  
  	return radio_api->get_sch_acc(radio_dev);
  }
  
  #if defined(CONFIG_OPENTHREAD_LINK_METRICS)
  /**
   * Header IE format - IEEE Std. 802.15.4-2015, 7.4.2.1 && 7.4.2.2
   *
   * +---------------------------------+----------------------+
   * | Length    | Element ID | Type=0 |      Vendor OUI      |
   * +-----------+------------+--------+----------------------+
   * |           Bytes: 0-1            |          2-4         |
   * +-----------+---------------------+----------------------+
   * | Bits: 0-6 |    7-14    |   15   | IE_VENDOR_THREAD_OUI |
   * +-----------+------------+--------+----------------------|
   *
   * Thread v1.2.1 Spec., 4.11.3.4.4.6
   * +---------------------------------+-------------------+------------------+
   * |                  Vendor Specific Information                           |
   * +---------------------------------+-------------------+------------------+
   * |                5                |         6         |   7 (optional)   |
   * +---------------------------------+-------------------+------------------+
   * | IE_VENDOR_THREAD_ACK_PROBING_ID | LINK_METRIC_TOKEN | LINK_METRIC_TOKEN|
   * |---------------------------------|-------------------|------------------|
   */
  static uint8_t set_vendor_ie_header_lm(bool lqi, bool link_margin, bool rssi, uint8_t *ie_header)
  {
  	/* Vendor-specific IE identifier */
  	const uint8_t ie_vendor_id = 0x00;
  	/* Thread Vendor-specific ACK Probing IE subtype ID */
  	const uint8_t ie_vendor_thread_ack_probing_id = 0x00;
  	/* Thread Vendor-specific IE OUI */
  	const uint32_t ie_vendor_thread_oui = 0xeab89b;
  	/* Thread Vendor-specific ACK Probing IE RSSI value placeholder */
  	const uint8_t ie_vendor_thread_rssi_token = 0x01;
  	/* Thread Vendor-specific ACK Probing IE Link margin value placeholder */
  	const uint8_t ie_vendor_thread_margin_token = 0x02;
  	/* Thread Vendor-specific ACK Probing IE LQI value placeholder */
  	const uint8_t ie_vendor_thread_lqi_token = 0x03;
  	const uint8_t ie_header_size = 2;
  	const uint8_t oui_size = 3;
  	const uint8_t sub_type = 1;
  	const uint8_t id_offset = 7;
  	const uint16_t id_mask = 0x00ff << id_offset;
  	const uint8_t type = 0x00;
  	const uint8_t type_offset = 7;
  	const uint8_t type_mask = 0x01 << type_offset;
  	const uint8_t length_mask = 0x7f;
  	uint8_t content_len;
  	uint16_t element_id = 0x0000;
  	uint8_t link_metrics_idx = 6;
  	uint8_t link_metrics_data_len = (uint8_t)lqi + (uint8_t)link_margin + (uint8_t)rssi;
  
  	__ASSERT(link_metrics_data_len <= 2, "Thread limits to 2 metrics at most");
  	__ASSERT(ie_header, "Invalid argument");
  
  	if (link_metrics_data_len == 0) {
  		return 0;
  	}
  
  	/* Set Element ID */
  	element_id = (((uint16_t)ie_vendor_id) << id_offset) & id_mask;
  	sys_put_le16(element_id, &ie_header[0]);
  
  	/* Set Length - number of octets in content field. */
  	content_len = oui_size + sub_type + link_metrics_data_len;
  	ie_header[0] = (ie_header[0] & ~length_mask) | (content_len & length_mask);
  
  	/* Set Type */
  	ie_header[1] = (ie_header[1] & ~type_mask) | (type & type_mask);
  
  	/* Set Vendor Oui */
  	sys_put_le24(ie_vendor_thread_oui, &ie_header[2]);
  
  	/* Set SubType */
  	ie_header[5] = ie_vendor_thread_ack_probing_id;
  
  	/* Set Link Metrics Tokens
  	 * TODO: Thread requires the order of requested metrics by the Link Metrics Initiator
  	 *       to be kept by the Link Metrics Subject in the ACKs.
  	 */
  	if (lqi) {
  		ie_header[link_metrics_idx++] = ie_vendor_thread_lqi_token;
  	}
  
  	if (link_margin) {
  		ie_header[link_metrics_idx++] = ie_vendor_thread_margin_token;
  	}
  
  	if (rssi) {
  		ie_header[link_metrics_idx++] = ie_vendor_thread_rssi_token;
  	}
  
  	return ie_header_size + content_len;
  }
  
  otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics,
  					  const otShortAddress aShortAddress,
  					  const otExtAddress *aExtAddress)
  {
  	int result;
  	uint8_t ie_header[OT_ACK_IE_MAX_SIZE];
  	uint16_t ie_header_len;
  	struct ieee802154_config config = {
  		.ack_ie.short_addr = aShortAddress,
  		.ack_ie.ext_addr = aExtAddress->m8,
  	};
  
  	ARG_UNUSED(aInstance);
  
  	ie_header_len = set_vendor_ie_header_lm(aLinkMetrics.mLqi, aLinkMetrics.mLinkMargin,
  						aLinkMetrics.mRssi, ie_header);
  	config.ack_ie.data = ie_header;
  	config.ack_ie.data_len = ie_header_len;
  	result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config);
  
  	return result ? OT_ERROR_FAILED : OT_ERROR_NONE;
  }
  #endif /* OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE */
  