net: openthread: Tx started notification
OpenThread uses "tx started" notification to start ACK timeout if the driver does not support it. In order for this to be achieved tx task needs to run in parallel to the OpenThread task, otherwise it may delay the gap between cca and tx or run after transmission has already finished. Signed-off-by: Marek Porwisz <marek.porwisz@nordicsemi.no>
This commit is contained in:
parent
f6aa72a4d7
commit
19c8ff9f0a
1 changed files with 154 additions and 82 deletions
|
@ -42,8 +42,12 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
|
|||
#define FRAME_TYPE_MASK 0x07
|
||||
#define FRAME_TYPE_ACK 0x02
|
||||
|
||||
#define OT_WORKER_STACK_SIZE 512
|
||||
#define OT_WORKER_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)
|
||||
|
||||
enum pending_events {
|
||||
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.
|
||||
*/
|
||||
|
@ -74,7 +78,9 @@ static u8_t energy_detection_channel;
|
|||
static s16_t energy_detected_value;
|
||||
|
||||
ATOMIC_DEFINE(pending_events, PENDING_EVENT_COUNT);
|
||||
|
||||
K_THREAD_STACK_DEFINE(ot_task_stack, OT_WORKER_STACK_SIZE);
|
||||
static struct k_work_q ot_work_q;
|
||||
static otError tx_result;
|
||||
|
||||
static inline bool is_pending_event_set(enum pending_events event)
|
||||
{
|
||||
|
@ -137,6 +143,23 @@ enum net_verdict ieee802154_radio_handle_ack(struct net_if *iface,
|
|||
return NET_OK;
|
||||
}
|
||||
|
||||
void handle_radio_event(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;
|
||||
default:
|
||||
/* do nothing - ignore event */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dataInit(void)
|
||||
{
|
||||
tx_pkt = net_pkt_alloc(K_NO_WAIT);
|
||||
|
@ -152,6 +175,8 @@ static void dataInit(void)
|
|||
|
||||
void platformRadioInit(void)
|
||||
{
|
||||
struct ieee802154_config cfg;
|
||||
|
||||
dataInit();
|
||||
|
||||
radio_dev = device_get_binding(CONFIG_NET_CONFIG_IEEE802154_DEV_NAME);
|
||||
|
@ -162,101 +187,147 @@ void platformRadioInit(void)
|
|||
return;
|
||||
}
|
||||
|
||||
__ASSERT(radio_api->get_capabilities(radio_dev)
|
||||
& IEEE802154_HW_TX_RX_ACK,
|
||||
"Only radios with automatic ack handling "
|
||||
"are currently supported");
|
||||
k_work_q_start(&ot_work_q, ot_task_stack,
|
||||
K_THREAD_STACK_SIZEOF(ot_task_stack),
|
||||
OT_WORKER_PRIORITY);
|
||||
|
||||
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);
|
||||
|
||||
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(OPENTHREAD_ENABLE_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 int run_tx_task(otInstance *aInstance)
|
||||
{
|
||||
static struct k_work tx_job;
|
||||
|
||||
ARG_UNUSED(aInstance);
|
||||
|
||||
if (k_work_pending(&tx_job) == 0) {
|
||||
sState = OT_RADIO_STATE_TRANSMIT;
|
||||
|
||||
k_work_init(&tx_job, transmit_message);
|
||||
k_work_submit_to_queue(&ot_work_q, &tx_job);
|
||||
return 0;
|
||||
} else {
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
void platformRadioProcess(otInstance *aInstance)
|
||||
{
|
||||
bool event_pending = false;
|
||||
if (sState == OT_RADIO_STATE_TRANSMIT) {
|
||||
otError 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;
|
||||
if (is_pending_event_set(PENDING_EVENT_TX_STARTED)) {
|
||||
reset_pending_event(PENDING_EVENT_TX_STARTED);
|
||||
otPlatRadioTxStarted(aInstance, &sTransmitFrame);
|
||||
}
|
||||
|
||||
channel = sTransmitFrame.mChannel;
|
||||
if (is_pending_event_set(PENDING_EVENT_TX_DONE)) {
|
||||
reset_pending_event(PENDING_EVENT_TX_DONE);
|
||||
|
||||
radio_api->set_channel(radio_dev, sTransmitFrame.mChannel);
|
||||
radio_api->set_txpower(radio_dev, tx_power);
|
||||
|
||||
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) {
|
||||
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) {
|
||||
result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
|
||||
}
|
||||
} else {
|
||||
if (radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT,
|
||||
tx_pkt, tx_payload)) {
|
||||
result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
|
||||
}
|
||||
if (sState == OT_RADIO_STATE_TRANSMIT) {
|
||||
sState = OT_RADIO_STATE_RECEIVE;
|
||||
handle_tx_done(aInstance);
|
||||
}
|
||||
}
|
||||
|
||||
sState = OT_RADIO_STATE_RECEIVE;
|
||||
/* 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 (IS_ENABLED(OPENTHREAD_ENABLE_DIAG) && otPlatDiagModeGet()) {
|
||||
otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame,
|
||||
result);
|
||||
} else {
|
||||
if (sTransmitFrame.mPsdu[0] & IEEE802154_AR_FLAG_SET) {
|
||||
if (ack_frame.mLength == 0) {
|
||||
LOG_DBG("No ACK received.");
|
||||
|
||||
result = OT_ERROR_NO_ACK;
|
||||
|
||||
otPlatRadioTxDone(aInstance,
|
||||
&sTransmitFrame,
|
||||
NULL, result);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
otPlatRadioTxDone(aInstance, &sTransmitFrame,
|
||||
&ack_frame, result);
|
||||
|
||||
ack_frame.mLength = 0;
|
||||
if (!radio_api->ed_scan(radio_dev,
|
||||
energy_detection_time,
|
||||
energy_detected)) {
|
||||
reset_pending_event(
|
||||
PENDING_EVENT_DETECT_ENERGY);
|
||||
} else {
|
||||
otPlatRadioTxDone(aInstance, &sTransmitFrame,
|
||||
NULL, result);
|
||||
event_pending = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
(s8_t)energy_detected_value);
|
||||
reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY_DONE)) {
|
||||
otPlatRadioEnergyScanDone(aInstance,
|
||||
(s8_t)energy_detected_value);
|
||||
reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
|
||||
}
|
||||
|
||||
if (event_pending) {
|
||||
otSysEventSignalPending();
|
||||
}
|
||||
|
@ -360,9 +431,9 @@ otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aPacket)
|
|||
__ASSERT_NO_MSG(aPacket == &sTransmitFrame);
|
||||
|
||||
if (sState == OT_RADIO_STATE_RECEIVE) {
|
||||
error = OT_ERROR_NONE;
|
||||
sState = OT_RADIO_STATE_TRANSMIT;
|
||||
otSysEventSignalPending();
|
||||
if (run_tx_task(aInstance) == 0) {
|
||||
error = OT_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
|
@ -472,7 +543,8 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, u8_t aScanChannel,
|
|||
return OT_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
clear_pending_events();
|
||||
reset_pending_event(PENDING_EVENT_DETECT_ENERGY);
|
||||
reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
|
||||
|
||||
radio_api->set_channel(radio_dev, aScanChannel);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue