1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 * This file implements the OpenThread platform abstraction
10 * for radio communication.
11 *
12 */
13
14 #define LOG_MODULE_NAME net_otPlat_radio
15
16 #include <logging/log.h>
17 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
18
19 #include <stdbool.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <string.h>
23
24 #include <kernel.h>
25 #include <device.h>
26 #include <net/ieee802154_radio.h>
27 #include <net/net_pkt.h>
28 #include <sys/__assert.h>
29
30 #include <openthread/ip6.h>
31 #include <openthread-system.h>
32 #include <openthread/instance.h>
33 #include <openthread/platform/radio.h>
34 #include <openthread/platform/diag.h>
35 #include <openthread/message.h>
36
37 #include "platform-zephyr.h"
38
39 #define SHORT_ADDRESS_SIZE 2
40
41 #define FCS_SIZE 2
42 #if defined(CONFIG_IEEE802154_2015)
43 #define ACK_PKT_LENGTH 127
44 #else
45 #define ACK_PKT_LENGTH 5
46 #endif
47
48 #define FRAME_TYPE_MASK 0x07
49 #define FRAME_TYPE_ACK 0x02
50
51 #if IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE)
52 #define OT_WORKER_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)
53 #else
54 #define OT_WORKER_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY)
55 #endif
56
57 enum pending_events {
58 PENDING_EVENT_FRAME_TO_SEND, /* There is a tx frame to send */
59 PENDING_EVENT_FRAME_RECEIVED, /* Radio has received new frame */
60 PENDING_EVENT_RX_FAILED, /* The RX failed */
61 PENDING_EVENT_TX_STARTED, /* Radio has started transmitting */
62 PENDING_EVENT_TX_DONE, /* Radio transmission finished */
63 PENDING_EVENT_DETECT_ENERGY, /* Requested to start Energy Detection procedure */
64 PENDING_EVENT_DETECT_ENERGY_DONE, /* Energy Detection finished */
65 PENDING_EVENT_SLEEP, /* Sleep if idle */
66 PENDING_EVENT_COUNT /* Keep last */
67 };
68
69 K_SEM_DEFINE(radio_sem, 0, 1);
70
71 static otRadioState sState = OT_RADIO_STATE_DISABLED;
72
73 static otRadioFrame sTransmitFrame;
74 static otRadioFrame ack_frame;
75 static uint8_t ack_psdu[ACK_PKT_LENGTH];
76
77 static struct net_pkt *tx_pkt;
78 static struct net_buf *tx_payload;
79
80 static const struct device *radio_dev;
81 static struct ieee802154_radio_api *radio_api;
82
83 static int8_t tx_power;
84 static uint16_t channel;
85 static bool promiscuous;
86
87 static uint16_t energy_detection_time;
88 static uint8_t energy_detection_channel;
89 static int16_t energy_detected_value;
90
91 ATOMIC_DEFINE(pending_events, PENDING_EVENT_COUNT);
92 K_KERNEL_STACK_DEFINE(ot_task_stack,
93 CONFIG_OPENTHREAD_RADIO_WORKQUEUE_STACK_SIZE);
94 static struct k_work_q ot_work_q;
95 static otError rx_result;
96 static otError tx_result;
97
98 K_FIFO_DEFINE(rx_pkt_fifo);
99 K_FIFO_DEFINE(tx_pkt_fifo);
100
is_pending_event_set(enum pending_events event)101 static inline bool is_pending_event_set(enum pending_events event)
102 {
103 return atomic_test_bit(pending_events, event);
104 }
105
set_pending_event(enum pending_events event)106 static void set_pending_event(enum pending_events event)
107 {
108 atomic_set_bit(pending_events, event);
109 otSysEventSignalPending();
110 }
111
reset_pending_event(enum pending_events event)112 static void reset_pending_event(enum pending_events event)
113 {
114 atomic_clear_bit(pending_events, event);
115 }
116
clear_pending_events(void)117 static inline void clear_pending_events(void)
118 {
119 atomic_clear(pending_events);
120 }
121
energy_detected(const struct device * dev,int16_t max_ed)122 void energy_detected(const struct device *dev, int16_t max_ed)
123 {
124 if (dev == radio_dev) {
125 energy_detected_value = max_ed;
126 set_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
127 }
128 }
129
ieee802154_radio_handle_ack(struct net_if * iface,struct net_pkt * pkt)130 enum net_verdict ieee802154_radio_handle_ack(struct net_if *iface,
131 struct net_pkt *pkt)
132 {
133 ARG_UNUSED(iface);
134
135 size_t ack_len = net_pkt_get_len(pkt);
136
137 if (ack_len > ACK_PKT_LENGTH) {
138 return NET_CONTINUE;
139 }
140
141 if ((*net_pkt_data(pkt) & FRAME_TYPE_MASK) != FRAME_TYPE_ACK) {
142 return NET_CONTINUE;
143 }
144
145 if (ack_frame.mLength != 0) {
146 LOG_ERR("Overwriting unhandled ACK frame.");
147 }
148
149 if (net_pkt_read(pkt, ack_psdu, ack_len) < 0) {
150 LOG_ERR("Failed to read ACK frame.");
151 return NET_CONTINUE;
152 }
153
154 ack_frame.mPsdu = ack_psdu;
155 ack_frame.mLength = ack_len;
156 ack_frame.mInfo.mRxInfo.mLqi = net_pkt_ieee802154_lqi(pkt);
157 ack_frame.mInfo.mRxInfo.mRssi = net_pkt_ieee802154_rssi(pkt);
158
159 #if defined(CONFIG_NET_PKT_TIMESTAMP)
160 struct net_ptp_time *pkt_time = net_pkt_timestamp(pkt);
161
162 ack_frame.mInfo.mRxInfo.mTimestamp =
163 pkt_time->second * USEC_PER_SEC + pkt_time->nanosecond / NSEC_PER_USEC;
164 #endif
165
166 return NET_OK;
167 }
168
handle_radio_event(const struct device * dev,enum ieee802154_event evt,void * event_params)169 void handle_radio_event(const struct device *dev, enum ieee802154_event evt,
170 void *event_params)
171 {
172 ARG_UNUSED(event_params);
173
174 switch (evt) {
175 case IEEE802154_EVENT_TX_STARTED:
176 if (sState == OT_RADIO_STATE_TRANSMIT) {
177 set_pending_event(PENDING_EVENT_TX_STARTED);
178 }
179 break;
180 case IEEE802154_EVENT_RX_FAILED:
181 if (sState == OT_RADIO_STATE_RECEIVE) {
182 switch (*(enum ieee802154_rx_fail_reason *) event_params) {
183 case IEEE802154_RX_FAIL_NOT_RECEIVED:
184 rx_result = OT_ERROR_NO_FRAME_RECEIVED;
185 break;
186
187 case IEEE802154_RX_FAIL_INVALID_FCS:
188 rx_result = OT_ERROR_FCS;
189 break;
190
191 case IEEE802154_RX_FAIL_ADDR_FILTERED:
192 rx_result = OT_ERROR_DESTINATION_ADDRESS_FILTERED;
193 break;
194
195 case IEEE802154_RX_FAIL_OTHER:
196 default:
197 rx_result = OT_ERROR_FAILED;
198 break;
199 }
200 set_pending_event(PENDING_EVENT_RX_FAILED);
201 }
202 break;
203 case IEEE802154_EVENT_SLEEP:
204 set_pending_event(PENDING_EVENT_SLEEP);
205 break;
206 default:
207 /* do nothing - ignore event */
208 break;
209 }
210 }
211
dataInit(void)212 static void dataInit(void)
213 {
214 tx_pkt = net_pkt_alloc(K_NO_WAIT);
215 __ASSERT_NO_MSG(tx_pkt != NULL);
216
217 tx_payload = net_pkt_get_reserve_tx_data(K_NO_WAIT);
218 __ASSERT_NO_MSG(tx_payload != NULL);
219
220 net_pkt_append_buffer(tx_pkt, tx_payload);
221
222 sTransmitFrame.mPsdu = tx_payload->data;
223 }
224
platformRadioInit(void)225 void platformRadioInit(void)
226 {
227 struct ieee802154_config cfg;
228
229 dataInit();
230
231 radio_dev = device_get_binding(CONFIG_NET_CONFIG_IEEE802154_DEV_NAME);
232 __ASSERT_NO_MSG(radio_dev != NULL);
233
234 radio_api = (struct ieee802154_radio_api *)radio_dev->api;
235 if (!radio_api) {
236 return;
237 }
238
239 k_work_queue_start(&ot_work_q, ot_task_stack,
240 K_KERNEL_STACK_SIZEOF(ot_task_stack),
241 OT_WORKER_PRIORITY, NULL);
242 k_thread_name_set(&ot_work_q.thread, "ot_radio_workq");
243
244 if ((radio_api->get_capabilities(radio_dev) &
245 IEEE802154_HW_TX_RX_ACK) != IEEE802154_HW_TX_RX_ACK) {
246 LOG_ERR("Only radios with automatic ack handling "
247 "are currently supported");
248 k_panic();
249 }
250
251 cfg.event_handler = handle_radio_event;
252 radio_api->configure(radio_dev, IEEE802154_CONFIG_EVENT_HANDLER, &cfg);
253 }
254
transmit_message(struct k_work * tx_job)255 void transmit_message(struct k_work *tx_job)
256 {
257 int tx_err;
258
259 ARG_UNUSED(tx_job);
260
261 /*
262 * The payload is already in tx_payload->data,
263 * but we need to set the length field
264 * according to sTransmitFrame.length.
265 * We subtract the FCS size as radio driver
266 * adds CRC and increases frame length on its own.
267 */
268 tx_payload->len = sTransmitFrame.mLength - FCS_SIZE;
269
270 channel = sTransmitFrame.mChannel;
271
272 radio_api->set_channel(radio_dev, sTransmitFrame.mChannel);
273 radio_api->set_txpower(radio_dev, tx_power);
274
275 net_pkt_set_ieee802154_frame_secured(tx_pkt,
276 sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed);
277 net_pkt_set_ieee802154_mac_hdr_rdy(tx_pkt, sTransmitFrame.mInfo.mTxInfo.mIsHeaderUpdated);
278
279 if ((radio_api->get_capabilities(radio_dev) & IEEE802154_HW_TXTIME) &&
280 (sTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)) {
281 uint64_t tx_at = sTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime +
282 sTransmitFrame.mInfo.mTxInfo.mTxDelay;
283 net_pkt_set_txtime(tx_pkt, NSEC_PER_USEC * tx_at);
284 tx_err =
285 radio_api->tx(radio_dev, IEEE802154_TX_MODE_TXTIME_CCA, tx_pkt, tx_payload);
286 } else if (sTransmitFrame.mInfo.mTxInfo.mCsmaCaEnabled) {
287 if (radio_api->get_capabilities(radio_dev) & IEEE802154_HW_CSMA) {
288 tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_CSMA_CA, tx_pkt,
289 tx_payload);
290 } else {
291 tx_err = radio_api->cca(radio_dev);
292 if (tx_err == 0) {
293 tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt,
294 tx_payload);
295 }
296 }
297 } else {
298 tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt, tx_payload);
299 }
300
301 /*
302 * OpenThread handles the following errors:
303 * - OT_ERROR_NONE
304 * - OT_ERROR_NO_ACK
305 * - OT_ERROR_CHANNEL_ACCESS_FAILURE
306 * - OT_ERROR_ABORT
307 * Any other error passed to `otPlatRadioTxDone` will result in assertion.
308 */
309 switch (tx_err) {
310 case 0:
311 tx_result = OT_ERROR_NONE;
312 break;
313 case -ENOMSG:
314 tx_result = OT_ERROR_NO_ACK;
315 break;
316 case -EBUSY:
317 tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
318 break;
319 case -EIO:
320 tx_result = OT_ERROR_ABORT;
321 break;
322 default:
323 tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
324 break;
325 }
326
327 set_pending_event(PENDING_EVENT_TX_DONE);
328 }
329
handle_tx_done(otInstance * aInstance)330 static inline void handle_tx_done(otInstance *aInstance)
331 {
332 sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed =
333 net_pkt_ieee802154_frame_secured(tx_pkt);
334 sTransmitFrame.mInfo.mTxInfo.mIsHeaderUpdated = net_pkt_ieee802154_mac_hdr_rdy(tx_pkt);
335
336 if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) {
337 otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, tx_result);
338 } else {
339 if (sTransmitFrame.mPsdu[0] & IEEE802154_AR_FLAG_SET) {
340 if (ack_frame.mLength == 0) {
341 LOG_DBG("No ACK received.");
342 otPlatRadioTxDone(aInstance, &sTransmitFrame,
343 NULL, OT_ERROR_NO_ACK);
344 } else {
345 otPlatRadioTxDone(aInstance, &sTransmitFrame,
346 &ack_frame, tx_result);
347 }
348 } else {
349 otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, tx_result);
350 }
351 ack_frame.mLength = 0;
352 }
353 }
354
openthread_handle_received_frame(otInstance * instance,struct net_pkt * pkt)355 static void openthread_handle_received_frame(otInstance *instance,
356 struct net_pkt *pkt)
357 {
358 otRadioFrame recv_frame;
359
360 recv_frame.mPsdu = net_buf_frag_last(pkt->buffer)->data;
361 /* Length inc. CRC. */
362 recv_frame.mLength = net_buf_frags_len(pkt->buffer);
363 recv_frame.mChannel = platformRadioChannelGet(instance);
364 recv_frame.mInfo.mRxInfo.mLqi = net_pkt_ieee802154_lqi(pkt);
365 recv_frame.mInfo.mRxInfo.mRssi = net_pkt_ieee802154_rssi(pkt);
366 recv_frame.mInfo.mRxInfo.mAckedWithFramePending = net_pkt_ieee802154_ack_fpb(pkt);
367
368 #if defined(CONFIG_NET_PKT_TIMESTAMP)
369 struct net_ptp_time *pkt_time = net_pkt_timestamp(pkt);
370
371 recv_frame.mInfo.mRxInfo.mTimestamp =
372 pkt_time->second * USEC_PER_SEC + pkt_time->nanosecond / NSEC_PER_USEC;
373 #endif
374
375 #if defined(CONFIG_IEEE802154_2015)
376 if (net_pkt_ieee802154_arb(pkt) && net_pkt_ieee802154_fv2015(pkt)) {
377 recv_frame.mInfo.mRxInfo.mAckedWithSecEnhAck =
378 net_pkt_ieee802154_ack_seb(pkt);
379 recv_frame.mInfo.mRxInfo.mAckFrameCounter =
380 net_pkt_ieee802154_ack_fc(pkt);
381 recv_frame.mInfo.mRxInfo.mAckKeyId =
382 net_pkt_ieee802154_ack_keyid(pkt);
383 }
384 #endif
385
386 if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) {
387 otPlatDiagRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE);
388 } else {
389 otPlatRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE);
390 }
391
392 net_pkt_unref(pkt);
393 }
394
openthread_handle_frame_to_send(otInstance * instance,struct net_pkt * pkt)395 static void openthread_handle_frame_to_send(otInstance *instance,
396 struct net_pkt *pkt)
397 {
398 struct net_buf *buf;
399 otMessage *message;
400 otMessageSettings settings;
401
402 NET_DBG("Sending Ip6 packet to ot stack");
403
404 settings.mPriority = OT_MESSAGE_PRIORITY_NORMAL;
405 settings.mLinkSecurityEnabled = true;
406 message = otIp6NewMessage(instance, &settings);
407 if (message == NULL) {
408 goto exit;
409 }
410
411 for (buf = pkt->buffer; buf; buf = buf->frags) {
412 if (otMessageAppend(message, buf->data, buf->len) != OT_ERROR_NONE) {
413 NET_ERR("Error while appending to otMessage");
414 otMessageFree(message);
415 goto exit;
416 }
417 }
418
419 if (otIp6Send(instance, message) != OT_ERROR_NONE) {
420 NET_ERR("Error while calling otIp6Send");
421 goto exit;
422 }
423
424 exit:
425 net_pkt_unref(pkt);
426 }
427
notify_new_rx_frame(struct net_pkt * pkt)428 int notify_new_rx_frame(struct net_pkt *pkt)
429 {
430 k_fifo_put(&rx_pkt_fifo, pkt);
431 set_pending_event(PENDING_EVENT_FRAME_RECEIVED);
432
433 return 0;
434 }
435
notify_new_tx_frame(struct net_pkt * pkt)436 int notify_new_tx_frame(struct net_pkt *pkt)
437 {
438 k_fifo_put(&tx_pkt_fifo, pkt);
439 set_pending_event(PENDING_EVENT_FRAME_TO_SEND);
440
441 return 0;
442 }
443
run_tx_task(otInstance * aInstance)444 static int run_tx_task(otInstance *aInstance)
445 {
446 static K_WORK_DEFINE(tx_job, transmit_message);
447
448 ARG_UNUSED(aInstance);
449
450 if (!k_work_is_pending(&tx_job)) {
451 sState = OT_RADIO_STATE_TRANSMIT;
452
453 k_work_submit_to_queue(&ot_work_q, &tx_job);
454 return 0;
455 } else {
456 return -EBUSY;
457 }
458 }
459
platformRadioProcess(otInstance * aInstance)460 void platformRadioProcess(otInstance *aInstance)
461 {
462 bool event_pending = false;
463
464 if (is_pending_event_set(PENDING_EVENT_FRAME_TO_SEND)) {
465 struct net_pkt *tx_pkt;
466
467 reset_pending_event(PENDING_EVENT_FRAME_TO_SEND);
468 while ((tx_pkt = (struct net_pkt *) k_fifo_get(&tx_pkt_fifo, K_NO_WAIT)) != NULL) {
469 if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR_RCP)) {
470 net_pkt_unref(tx_pkt);
471 } else {
472 openthread_handle_frame_to_send(aInstance, tx_pkt);
473 }
474 }
475 }
476
477 if (is_pending_event_set(PENDING_EVENT_FRAME_RECEIVED)) {
478 struct net_pkt *rx_pkt;
479
480 reset_pending_event(PENDING_EVENT_FRAME_RECEIVED);
481 while ((rx_pkt = (struct net_pkt *) k_fifo_get(&rx_pkt_fifo, K_NO_WAIT)) != NULL) {
482 openthread_handle_received_frame(aInstance, rx_pkt);
483 }
484 }
485
486 if (is_pending_event_set(PENDING_EVENT_RX_FAILED)) {
487 reset_pending_event(PENDING_EVENT_RX_FAILED);
488 if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) {
489 otPlatDiagRadioReceiveDone(aInstance, NULL, rx_result);
490 } else {
491 otPlatRadioReceiveDone(aInstance, NULL, rx_result);
492 }
493 }
494
495 if (is_pending_event_set(PENDING_EVENT_TX_STARTED)) {
496 reset_pending_event(PENDING_EVENT_TX_STARTED);
497 otPlatRadioTxStarted(aInstance, &sTransmitFrame);
498 }
499
500 if (is_pending_event_set(PENDING_EVENT_TX_DONE)) {
501 reset_pending_event(PENDING_EVENT_TX_DONE);
502
503 if (sState == OT_RADIO_STATE_TRANSMIT) {
504 sState = OT_RADIO_STATE_RECEIVE;
505 handle_tx_done(aInstance);
506 }
507 }
508
509 if (is_pending_event_set(PENDING_EVENT_SLEEP)) {
510 reset_pending_event(PENDING_EVENT_SLEEP);
511 ARG_UNUSED(otPlatRadioSleep(aInstance));
512 }
513
514 /* handle events that can't run during transmission */
515 if (sState != OT_RADIO_STATE_TRANSMIT) {
516 if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY)) {
517 radio_api->set_channel(radio_dev,
518 energy_detection_channel);
519
520 if (!radio_api->ed_scan(radio_dev,
521 energy_detection_time,
522 energy_detected)) {
523 reset_pending_event(
524 PENDING_EVENT_DETECT_ENERGY);
525 } else {
526 event_pending = true;
527 }
528 }
529
530 if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY_DONE)) {
531 otPlatRadioEnergyScanDone(aInstance, (int8_t) energy_detected_value);
532 reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
533 }
534 }
535
536 if (event_pending) {
537 otSysEventSignalPending();
538 }
539 }
540
platformRadioChannelGet(otInstance * aInstance)541 uint16_t platformRadioChannelGet(otInstance *aInstance)
542 {
543 ARG_UNUSED(aInstance);
544
545 return channel;
546 }
547
otPlatRadioSetPanId(otInstance * aInstance,uint16_t aPanId)548 void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
549 {
550 ARG_UNUSED(aInstance);
551
552 radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_PAN_ID,
553 (struct ieee802154_filter *) &aPanId);
554 }
555
otPlatRadioSetExtendedAddress(otInstance * aInstance,const otExtAddress * aExtAddress)556 void otPlatRadioSetExtendedAddress(otInstance *aInstance,
557 const otExtAddress *aExtAddress)
558 {
559 ARG_UNUSED(aInstance);
560
561 radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_IEEE_ADDR,
562 (struct ieee802154_filter *) &aExtAddress);
563 }
564
otPlatRadioSetShortAddress(otInstance * aInstance,uint16_t aShortAddress)565 void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddress)
566 {
567 ARG_UNUSED(aInstance);
568
569 radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_SHORT_ADDR,
570 (struct ieee802154_filter *) &aShortAddress);
571 }
572
otPlatRadioIsEnabled(otInstance * aInstance)573 bool otPlatRadioIsEnabled(otInstance *aInstance)
574 {
575 ARG_UNUSED(aInstance);
576
577 return (sState != OT_RADIO_STATE_DISABLED) ? true : false;
578 }
579
otPlatRadioEnable(otInstance * aInstance)580 otError otPlatRadioEnable(otInstance *aInstance)
581 {
582 if (!otPlatRadioIsEnabled(aInstance)) {
583 sState = OT_RADIO_STATE_SLEEP;
584 }
585
586 return OT_ERROR_NONE;
587 }
588
otPlatRadioDisable(otInstance * aInstance)589 otError otPlatRadioDisable(otInstance *aInstance)
590 {
591 if (otPlatRadioIsEnabled(aInstance)) {
592 sState = OT_RADIO_STATE_DISABLED;
593 }
594
595 return OT_ERROR_NONE;
596 }
597
otPlatRadioSleep(otInstance * aInstance)598 otError otPlatRadioSleep(otInstance *aInstance)
599 {
600 ARG_UNUSED(aInstance);
601
602 otError error = OT_ERROR_INVALID_STATE;
603
604 if (sState == OT_RADIO_STATE_SLEEP ||
605 sState == OT_RADIO_STATE_RECEIVE ||
606 sState == OT_RADIO_STATE_TRANSMIT) {
607 error = OT_ERROR_NONE;
608 if (radio_api->stop(radio_dev)) {
609 sState = OT_RADIO_STATE_SLEEP;
610 }
611 }
612
613 return error;
614 }
615
otPlatRadioReceive(otInstance * aInstance,uint8_t aChannel)616 otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
617 {
618 ARG_UNUSED(aInstance);
619
620 channel = aChannel;
621
622 radio_api->set_channel(radio_dev, aChannel);
623 radio_api->set_txpower(radio_dev, tx_power);
624 radio_api->start(radio_dev);
625 sState = OT_RADIO_STATE_RECEIVE;
626
627 return OT_ERROR_NONE;
628 }
629
630 #if defined(CONFIG_OPENTHREAD_CSL_RECEIVER)
otPlatRadioReceiveAt(otInstance * aInstance,uint8_t aChannel,uint32_t aStart,uint32_t aDuration)631 otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel,
632 uint32_t aStart, uint32_t aDuration)
633 {
634 int result;
635
636 ARG_UNUSED(aInstance);
637
638 struct ieee802154_config config = {
639 .rx_slot.channel = aChannel,
640 .rx_slot.start = aStart,
641 .rx_slot.duration = aDuration,
642 };
643
644 result = radio_api->configure(radio_dev, IEEE802154_CONFIG_RX_SLOT,
645 &config);
646
647 return result ? OT_ERROR_FAILED : OT_ERROR_NONE;
648 }
649 #endif
650
otPlatRadioTransmit(otInstance * aInstance,otRadioFrame * aPacket)651 otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aPacket)
652 {
653 otError error = OT_ERROR_INVALID_STATE;
654
655 ARG_UNUSED(aInstance);
656 ARG_UNUSED(aPacket);
657
658 __ASSERT_NO_MSG(aPacket == &sTransmitFrame);
659
660 enum ieee802154_hw_caps radio_caps;
661
662 radio_caps = radio_api->get_capabilities(radio_dev);
663
664 if ((sState == OT_RADIO_STATE_RECEIVE) || (radio_caps & IEEE802154_HW_SLEEP_TO_TX)) {
665 if (run_tx_task(aInstance) == 0) {
666 error = OT_ERROR_NONE;
667 }
668 }
669
670 return error;
671 }
672
otPlatRadioGetTransmitBuffer(otInstance * aInstance)673 otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
674 {
675 ARG_UNUSED(aInstance);
676
677 return &sTransmitFrame;
678 }
679
get_rssi_energy_detected(const struct device * dev,int16_t max_ed)680 static void get_rssi_energy_detected(const struct device *dev, int16_t max_ed)
681 {
682 ARG_UNUSED(dev);
683 energy_detected_value = max_ed;
684 k_sem_give(&radio_sem);
685 }
686
otPlatRadioGetRssi(otInstance * aInstance)687 int8_t otPlatRadioGetRssi(otInstance *aInstance)
688 {
689 int8_t ret_rssi = INT8_MAX;
690 int error = 0;
691 const uint16_t energy_detection_time = 1;
692 enum ieee802154_hw_caps radio_caps;
693 ARG_UNUSED(aInstance);
694
695 radio_caps = radio_api->get_capabilities(radio_dev);
696
697 if (!(radio_caps & IEEE802154_HW_ENERGY_SCAN)) {
698 /*
699 * TODO: No API in Zephyr to get the RSSI
700 * when IEEE802154_HW_ENERGY_SCAN is not available
701 */
702 ret_rssi = 0;
703 } else {
704 /*
705 * Blocking implementation of get RSSI
706 * using no-blocking ed_scan
707 */
708 error = radio_api->ed_scan(radio_dev, energy_detection_time,
709 get_rssi_energy_detected);
710
711 if (error == 0) {
712 k_sem_take(&radio_sem, K_FOREVER);
713
714 ret_rssi = (int8_t)energy_detected_value;
715 }
716 }
717
718 return ret_rssi;
719 }
720
otPlatRadioGetCaps(otInstance * aInstance)721 otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
722 {
723 otRadioCaps caps = OT_RADIO_CAPS_NONE;
724
725 enum ieee802154_hw_caps radio_caps;
726 ARG_UNUSED(aInstance);
727 __ASSERT(radio_api,
728 "platformRadioInit needs to be called prior to otPlatRadioGetCaps");
729
730 radio_caps = radio_api->get_capabilities(radio_dev);
731
732 if (radio_caps & IEEE802154_HW_ENERGY_SCAN) {
733 caps |= OT_RADIO_CAPS_ENERGY_SCAN;
734 }
735
736 if (radio_caps & IEEE802154_HW_CSMA) {
737 caps |= OT_RADIO_CAPS_CSMA_BACKOFF;
738 }
739
740 if (radio_caps & IEEE802154_HW_TX_RX_ACK) {
741 caps |= OT_RADIO_CAPS_ACK_TIMEOUT;
742 }
743
744 if (radio_caps & IEEE802154_HW_SLEEP_TO_TX) {
745 caps |= OT_RADIO_CAPS_SLEEP_TO_TX;
746 }
747
748 #if defined(CONFIG_IEEE802154_2015)
749 if (radio_caps & IEEE802154_HW_TX_SEC) {
750 caps |= OT_RADIO_CAPS_TRANSMIT_SEC;
751 }
752 #endif
753
754 #if defined(CONFIG_NET_PKT_TXTIME)
755 if (radio_caps & IEEE802154_HW_TXTIME) {
756 caps |= OT_RADIO_CAPS_TRANSMIT_TIMING;
757 }
758 #endif
759
760 if (radio_caps & IEEE802154_HW_RXTIME) {
761 caps |= OT_RADIO_CAPS_RECEIVE_TIMING;
762 }
763
764 return caps;
765 }
766
otPlatRadioGetPromiscuous(otInstance * aInstance)767 bool otPlatRadioGetPromiscuous(otInstance *aInstance)
768 {
769 ARG_UNUSED(aInstance);
770
771 LOG_DBG("PromiscuousMode=%d", promiscuous ? 1 : 0);
772
773 return promiscuous;
774 }
775
otPlatRadioSetPromiscuous(otInstance * aInstance,bool aEnable)776 void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
777 {
778 struct ieee802154_config config = {
779 .promiscuous = aEnable
780 };
781
782 ARG_UNUSED(aInstance);
783
784 LOG_DBG("PromiscuousMode=%d", aEnable ? 1 : 0);
785
786 promiscuous = aEnable;
787 radio_api->configure(radio_dev, IEEE802154_CONFIG_PROMISCUOUS, &config);
788 }
789
otPlatRadioEnergyScan(otInstance * aInstance,uint8_t aScanChannel,uint16_t aScanDuration)790 otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel,
791 uint16_t aScanDuration)
792 {
793 energy_detection_time = aScanDuration;
794 energy_detection_channel = aScanChannel;
795
796 if (radio_api->ed_scan == NULL) {
797 return OT_ERROR_NOT_IMPLEMENTED;
798 }
799
800 reset_pending_event(PENDING_EVENT_DETECT_ENERGY);
801 reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE);
802
803 radio_api->set_channel(radio_dev, aScanChannel);
804
805 if (radio_api->ed_scan(radio_dev, energy_detection_time, energy_detected) != 0) {
806 /*
807 * OpenThread API does not accept failure of this function,
808 * it can return 'No Error' or 'Not Implemented' error only.
809 * If ed_scan start failed event is set to schedule the scan at
810 * later time.
811 */
812 LOG_ERR("Failed do start energy scan, scheduling for later");
813 set_pending_event(PENDING_EVENT_DETECT_ENERGY);
814 }
815
816 return OT_ERROR_NONE;
817 }
818
otPlatRadioGetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t * aThreshold)819 otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance,
820 int8_t *aThreshold)
821 {
822 OT_UNUSED_VARIABLE(aInstance);
823 OT_UNUSED_VARIABLE(aThreshold);
824
825 return OT_ERROR_NOT_IMPLEMENTED;
826 }
827
otPlatRadioSetCcaEnergyDetectThreshold(otInstance * aInstance,int8_t aThreshold)828 otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance,
829 int8_t aThreshold)
830 {
831 OT_UNUSED_VARIABLE(aInstance);
832 OT_UNUSED_VARIABLE(aThreshold);
833
834 return OT_ERROR_NOT_IMPLEMENTED;
835 }
836
otPlatRadioEnableSrcMatch(otInstance * aInstance,bool aEnable)837 void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
838 {
839 ARG_UNUSED(aInstance);
840
841 struct ieee802154_config config = {
842 .auto_ack_fpb.enabled = aEnable,
843 .auto_ack_fpb.mode = IEEE802154_FPB_ADDR_MATCH_THREAD,
844 };
845
846 (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_AUTO_ACK_FPB,
847 &config);
848 }
849
otPlatRadioAddSrcMatchShortEntry(otInstance * aInstance,const uint16_t aShortAddress)850 otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance,
851 const uint16_t aShortAddress)
852 {
853 ARG_UNUSED(aInstance);
854
855 uint8_t short_address[SHORT_ADDRESS_SIZE];
856 struct ieee802154_config config = {
857 .ack_fpb.enabled = true,
858 .ack_fpb.addr = short_address,
859 .ack_fpb.extended = false
860 };
861
862 sys_put_le16(aShortAddress, short_address);
863
864 if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
865 &config) != 0) {
866 return OT_ERROR_NO_BUFS;
867 }
868
869 return OT_ERROR_NONE;
870 }
871
otPlatRadioAddSrcMatchExtEntry(otInstance * aInstance,const otExtAddress * aExtAddress)872 otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance,
873 const otExtAddress *aExtAddress)
874 {
875 ARG_UNUSED(aInstance);
876
877 struct ieee802154_config config = {
878 .ack_fpb.enabled = true,
879 .ack_fpb.addr = (uint8_t *)aExtAddress->m8,
880 .ack_fpb.extended = true
881 };
882
883 if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
884 &config) != 0) {
885 return OT_ERROR_NO_BUFS;
886 }
887
888 return OT_ERROR_NONE;
889 }
890
otPlatRadioClearSrcMatchShortEntry(otInstance * aInstance,const uint16_t aShortAddress)891 otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance,
892 const uint16_t aShortAddress)
893 {
894 ARG_UNUSED(aInstance);
895
896 uint8_t short_address[SHORT_ADDRESS_SIZE];
897 struct ieee802154_config config = {
898 .ack_fpb.enabled = false,
899 .ack_fpb.addr = short_address,
900 .ack_fpb.extended = false
901 };
902
903 sys_put_le16(aShortAddress, short_address);
904
905 if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
906 &config) != 0) {
907 return OT_ERROR_NO_BUFS;
908 }
909
910 return OT_ERROR_NONE;
911 }
912
otPlatRadioClearSrcMatchExtEntry(otInstance * aInstance,const otExtAddress * aExtAddress)913 otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance,
914 const otExtAddress *aExtAddress)
915 {
916 ARG_UNUSED(aInstance);
917
918 struct ieee802154_config config = {
919 .ack_fpb.enabled = false,
920 .ack_fpb.addr = (uint8_t *)aExtAddress->m8,
921 .ack_fpb.extended = true
922 };
923
924 if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
925 &config) != 0) {
926 return OT_ERROR_NO_BUFS;
927 }
928
929 return OT_ERROR_NONE;
930 }
931
otPlatRadioClearSrcMatchShortEntries(otInstance * aInstance)932 void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
933 {
934 ARG_UNUSED(aInstance);
935
936 struct ieee802154_config config = {
937 .ack_fpb.enabled = false,
938 .ack_fpb.addr = NULL,
939 .ack_fpb.extended = false
940 };
941
942 (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
943 &config);
944 }
945
otPlatRadioClearSrcMatchExtEntries(otInstance * aInstance)946 void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
947 {
948 ARG_UNUSED(aInstance);
949
950 struct ieee802154_config config = {
951 .ack_fpb.enabled = false,
952 .ack_fpb.addr = NULL,
953 .ack_fpb.extended = true
954 };
955
956 (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB,
957 &config);
958 }
959
otPlatRadioGetReceiveSensitivity(otInstance * aInstance)960 int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
961 {
962 ARG_UNUSED(aInstance);
963
964 return -100;
965 }
966
otPlatRadioGetTransmitPower(otInstance * aInstance,int8_t * aPower)967 otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
968 {
969 ARG_UNUSED(aInstance);
970
971 if (aPower == NULL) {
972 return OT_ERROR_INVALID_ARGS;
973 }
974
975 *aPower = tx_power;
976
977 return OT_ERROR_NONE;
978 }
979
otPlatRadioSetTransmitPower(otInstance * aInstance,int8_t aPower)980 otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
981 {
982 ARG_UNUSED(aInstance);
983
984 tx_power = aPower;
985
986 return OT_ERROR_NONE;
987 }
988
otPlatTimeGet(void)989 uint64_t otPlatTimeGet(void)
990 {
991 if (radio_api == NULL || radio_api->get_time == NULL) {
992 return k_ticks_to_us_floor64(k_uptime_ticks());
993 } else {
994 return radio_api->get_time(radio_dev);
995 }
996 }
997
998 #if defined(CONFIG_NET_PKT_TXTIME)
otPlatRadioGetNow(otInstance * aInstance)999 uint64_t otPlatRadioGetNow(otInstance *aInstance)
1000 {
1001 ARG_UNUSED(aInstance);
1002
1003 return otPlatTimeGet();
1004 }
1005 #endif
1006
1007 #if defined(CONFIG_IEEE802154_2015)
otPlatRadioSetMacKey(otInstance * aInstance,uint8_t aKeyIdMode,uint8_t aKeyId,const otMacKey * aPrevKey,const otMacKey * aCurrKey,const otMacKey * aNextKey)1008 void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode,
1009 uint8_t aKeyId, const otMacKey *aPrevKey,
1010 const otMacKey *aCurrKey, const otMacKey *aNextKey)
1011 {
1012 ARG_UNUSED(aInstance);
1013 __ASSERT_NO_MSG(aPrevKey != NULL && aCurrKey != NULL &&
1014 aNextKey != NULL);
1015
1016 uint8_t key_id_mode = aKeyIdMode >> 3;
1017
1018 struct ieee802154_key keys[] = {
1019 {
1020 .key_id_mode = key_id_mode,
1021 .key_index = aKeyId == 1 ? 0x80 : aKeyId - 1,
1022 .key_value = (uint8_t *)aPrevKey->m8,
1023 .frame_counter_per_key = false,
1024 },
1025 {
1026 .key_id_mode = key_id_mode,
1027 .key_index = aKeyId,
1028 .key_value = (uint8_t *)aCurrKey->m8,
1029 .frame_counter_per_key = false,
1030 },
1031 {
1032 .key_id_mode = key_id_mode,
1033 .key_index = aKeyId == 0x80 ? 1 : aKeyId + 1,
1034 .key_value = (uint8_t *)aNextKey->m8,
1035 .frame_counter_per_key = false,
1036 },
1037 {
1038 .key_value = NULL,
1039 },
1040 };
1041
1042 struct ieee802154_config config = {
1043 .mac_keys = keys
1044 };
1045
1046 (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_MAC_KEYS,
1047 &config);
1048 }
1049
otPlatRadioSetMacFrameCounter(otInstance * aInstance,uint32_t aMacFrameCounter)1050 void otPlatRadioSetMacFrameCounter(otInstance *aInstance,
1051 uint32_t aMacFrameCounter)
1052 {
1053 ARG_UNUSED(aInstance);
1054
1055 struct ieee802154_config config = { .frame_counter = aMacFrameCounter };
1056
1057 (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_FRAME_COUNTER,
1058 &config);
1059 }
1060 #endif
1061
1062 #if defined(CONFIG_OPENTHREAD_CSL_RECEIVER)
otPlatRadioEnableCsl(otInstance * aInstance,uint32_t aCslPeriod,otShortAddress aShortAddr,const otExtAddress * aExtAddr)1063 otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr,
1064 const otExtAddress *aExtAddr)
1065 {
1066 int result;
1067 uint8_t ie_header[OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE];
1068 struct ieee802154_config config;
1069
1070 ARG_UNUSED(aInstance);
1071
1072 ie_header[0] = CSL_IE_HEADER_BYTES_LO;
1073 ie_header[1] = CSL_IE_HEADER_BYTES_HI;
1074 /* Leave CSL Phase empty intentionally */
1075 sys_put_le16(aCslPeriod, &ie_header[OT_IE_HEADER_SIZE + 2]);
1076 config.ack_ie.data = ie_header;
1077 config.ack_ie.short_addr = aShortAddr;
1078 config.ack_ie.ext_addr = aExtAddr->m8;
1079
1080 if (aCslPeriod > 0) {
1081 config.ack_ie.data_len = OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE;
1082 } else {
1083 config.ack_ie.data_len = 0;
1084 }
1085 result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config);
1086
1087 config.csl_period = aCslPeriod;
1088 result += radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_PERIOD, &config);
1089
1090 return result ? OT_ERROR_FAILED : OT_ERROR_NONE;
1091 }
1092
otPlatRadioUpdateCslSampleTime(otInstance * aInstance,uint32_t aCslSampleTime)1093 void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
1094 {
1095 ARG_UNUSED(aInstance);
1096
1097 struct ieee802154_config config = { .csl_rx_time = aCslSampleTime };
1098
1099 (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_RX_TIME, &config);
1100 }
1101 #endif /* CONFIG_OPENTHREAD_CSL_RECEIVER */
1102
otPlatRadioGetCslAccuracy(otInstance * aInstance)1103 uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
1104 {
1105 ARG_UNUSED(aInstance);
1106
1107 return radio_api->get_sch_acc(radio_dev);
1108 }
1109
1110 #if defined(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT)
1111 /**
1112 * Header IE format - IEEE Std. 802.15.4-2015, 7.4.2.1 && 7.4.2.2
1113 *
1114 * +---------------------------------+----------------------+
1115 * | Length | Element ID | Type=0 | Vendor OUI |
1116 * +-----------+------------+--------+----------------------+
1117 * | Bytes: 0-1 | 2-4 |
1118 * +-----------+---------------------+----------------------+
1119 * | Bits: 0-6 | 7-14 | 15 | IE_VENDOR_THREAD_OUI |
1120 * +-----------+------------+--------+----------------------|
1121 *
1122 * Thread v1.2.1 Spec., 4.11.3.4.4.6
1123 * +---------------------------------+-------------------+------------------+
1124 * | Vendor Specific Information |
1125 * +---------------------------------+-------------------+------------------+
1126 * | 5 | 6 | 7 (optional) |
1127 * +---------------------------------+-------------------+------------------+
1128 * | IE_VENDOR_THREAD_ACK_PROBING_ID | LINK_METRIC_TOKEN | LINK_METRIC_TOKEN|
1129 * |---------------------------------|-------------------|------------------|
1130 */
set_vendor_ie_header_lm(bool lqi,bool link_margin,bool rssi,uint8_t * ie_header)1131 static uint8_t set_vendor_ie_header_lm(bool lqi, bool link_margin, bool rssi, uint8_t *ie_header)
1132 {
1133 /* Vendor-specific IE identifier */
1134 const uint8_t ie_vendor_id = 0x00;
1135 /* Thread Vendor-specific ACK Probing IE subtype ID */
1136 const uint8_t ie_vendor_thread_ack_probing_id = 0x00;
1137 /* Thread Vendor-specific IE OUI */
1138 const uint32_t ie_vendor_thread_oui = 0xeab89b;
1139 /* Thread Vendor-specific ACK Probing IE RSSI value placeholder */
1140 const uint8_t ie_vendor_thread_rssi_token = 0x01;
1141 /* Thread Vendor-specific ACK Probing IE Link margin value placeholder */
1142 const uint8_t ie_vendor_thread_margin_token = 0x02;
1143 /* Thread Vendor-specific ACK Probing IE LQI value placeholder */
1144 const uint8_t ie_vendor_thread_lqi_token = 0x03;
1145 const uint8_t ie_header_size = 2;
1146 const uint8_t oui_size = 3;
1147 const uint8_t sub_type = 1;
1148 const uint8_t id_offset = 7;
1149 const uint16_t id_mask = 0x00ff << id_offset;
1150 const uint8_t type = 0x00;
1151 const uint8_t type_offset = 7;
1152 const uint8_t type_mask = 0x01 << type_offset;
1153 const uint8_t length_mask = 0x7f;
1154 uint8_t content_len;
1155 uint16_t element_id = 0x0000;
1156 uint8_t link_metrics_idx = 6;
1157 uint8_t link_metrics_data_len = (uint8_t)lqi + (uint8_t)link_margin + (uint8_t)rssi;
1158
1159 __ASSERT(link_metrics_data_len <= 2, "Thread limits to 2 metrics at most");
1160 __ASSERT(ie_header, "Invalid argument");
1161
1162 if (link_metrics_data_len == 0) {
1163 return 0;
1164 }
1165
1166 /* Set Element ID */
1167 element_id = (((uint16_t)ie_vendor_id) << id_offset) & id_mask;
1168 sys_put_le16(element_id, &ie_header[0]);
1169
1170 /* Set Length - number of octets in content field. */
1171 content_len = oui_size + sub_type + link_metrics_data_len;
1172 ie_header[0] = (ie_header[0] & ~length_mask) | (content_len & length_mask);
1173
1174 /* Set Type */
1175 ie_header[1] = (ie_header[1] & ~type_mask) | (type & type_mask);
1176
1177 /* Set Vendor Oui */
1178 sys_put_le24(ie_vendor_thread_oui, &ie_header[2]);
1179
1180 /* Set SubType */
1181 ie_header[5] = ie_vendor_thread_ack_probing_id;
1182
1183 /* Set Link Metrics Tokens
1184 * TODO: Thread requires the order of requested metrics by the Link Metrics Initiator
1185 * to be kept by the Link Metrics Subject in the ACKs.
1186 */
1187 if (lqi) {
1188 ie_header[link_metrics_idx++] = ie_vendor_thread_lqi_token;
1189 }
1190
1191 if (link_margin) {
1192 ie_header[link_metrics_idx++] = ie_vendor_thread_margin_token;
1193 }
1194
1195 if (rssi) {
1196 ie_header[link_metrics_idx++] = ie_vendor_thread_rssi_token;
1197 }
1198
1199 return ie_header_size + content_len;
1200 }
1201
otPlatRadioConfigureEnhAckProbing(otInstance * aInstance,otLinkMetrics aLinkMetrics,const otShortAddress aShortAddress,const otExtAddress * aExtAddress)1202 otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics,
1203 const otShortAddress aShortAddress,
1204 const otExtAddress *aExtAddress)
1205 {
1206 int result;
1207 uint8_t ie_header[OT_ACK_IE_MAX_SIZE];
1208 uint16_t ie_header_len;
1209 struct ieee802154_config config = {
1210 .ack_ie.short_addr = aShortAddress,
1211 .ack_ie.ext_addr = aExtAddress->m8,
1212 };
1213
1214 ARG_UNUSED(aInstance);
1215
1216 ie_header_len = set_vendor_ie_header_lm(aLinkMetrics.mLqi, aLinkMetrics.mLinkMargin,
1217 aLinkMetrics.mRssi, ie_header);
1218 config.ack_ie.data = ie_header;
1219 config.ack_ie.data_len = ie_header_len;
1220 result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config);
1221
1222 return result ? OT_ERROR_FAILED : OT_ERROR_NONE;
1223 }
1224
1225 #endif /* CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT */
1226