<commit_before>/*
 * 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)
{
	int tx_err;

	ARG_UNUSED(tx_job);

	/*
	 * 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);

	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);
		tx_err =
			radio_api->tx(radio_dev, IEEE802154_TX_MODE_TXTIME_CCA, tx_pkt, tx_payload);
	} else if (sTransmitFrame.mInfo.mTxInfo.mCsmaCaEnabled) {
		if (radio_api->get_capabilities(radio_dev) & IEEE802154_HW_CSMA) {
			tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_CSMA_CA, tx_pkt,
					       tx_payload);
		} else {
			tx_err = radio_api->cca(radio_dev);
			if (tx_err == 0) {
				tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt,
						       tx_payload);
			}
		}
	} else {
		tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt, tx_payload);
	}

	/*
	 * OpenThread handles the following errors:
	 * - OT_ERROR_NONE
	 * - OT_ERROR_NO_ACK
	 * - OT_ERROR_CHANNEL_ACCESS_FAILURE
	 * - OT_ERROR_ABORT
	 * Any other error passed to `otPlatRadioTxDone` will result in assertion.
	 */
	switch (tx_err) {
	case 0:
		tx_result = OT_ERROR_NONE;
		break;
	case -ENOMSG:
		tx_result = OT_ERROR_NO_ACK;
		break;
	case -EBUSY:
		tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
		break;
	case -EIO:
		tx_result = OT_ERROR_ABORT;
		break;
	default:
		tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
		break;
	}

	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;<commit_msg>net: openthread: propagate security and header updated flags<commit_after>/*
 * 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)
{
	int tx_err;

	ARG_UNUSED(tx_job);

	/*
	 * 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_secured(tx_pkt,
					     sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed);
	net_pkt_set_ieee802154_mac_hdr_rdy(tx_pkt, sTransmitFrame.mInfo.mTxInfo.mIsHeaderUpdated);

	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);
		tx_err =
			radio_api->tx(radio_dev, IEEE802154_TX_MODE_TXTIME_CCA, tx_pkt, tx_payload);
	} else if (sTransmitFrame.mInfo.mTxInfo.mCsmaCaEnabled) {
		if (radio_api->get_capabilities(radio_dev) & IEEE802154_HW_CSMA) {
			tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_CSMA_CA, tx_pkt,
					       tx_payload);
		} else {
			tx_err = radio_api->cca(radio_dev);
			if (tx_err == 0) {
				tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt,
						       tx_payload);
			}
		}
	} else {
		tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt, tx_payload);
	}

	/*
	 * OpenThread handles the following errors:
	 * - OT_ERROR_NONE
	 * - OT_ERROR_NO_ACK
	 * - OT_ERROR_CHANNEL_ACCESS_FAILURE
	 * - OT_ERROR_ABORT
	 * Any other error passed to `otPlatRadioTxDone` will result in assertion.
	 */
	switch (tx_err) {
	case 0:
		tx_result = OT_ERROR_NONE;
		break;
	case -ENOMSG:
		tx_result = OT_ERROR_NO_ACK;
		break;
	case -EBUSY:
		tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
		break;
	case -EIO:
		tx_result = OT_ERROR_ABORT;
		break;
	default:
		tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
		break;
	}

	set_pending_event(PENDING_EVENT_TX_DONE);
}

static inline void handle_tx_done(otInstance *aInstance)
{
	sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed =
		net_pkt_ieee802154_frame_secured(tx_pkt);
	sTransmitFrame.mInfo.mTxInfo.mIsHeaderUpdated = net_pkt_ieee802154_mac_hdr_rdy(tx_pkt);

	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;
{'fraction_non_alphanumeric': 0.09362780194966129, 'fraction_numerical': 0.02500413063832131, 'mean_word_length': 5.45273631840796, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5820149', 'n_tokens_mistral': 7891, 'n_tokens_neox': 7148, 'n_words': 1232}