1 /*
2 * Copyright (c) 2018, Nordic Semiconductor ASA
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 /**
36 * @file
37 * This file implements delayed transmission and reception features.
38 *
39 */
40
41 #define NRF_802154_MODULE_ID NRF_802154_DRV_MODULE_ID_DELAYED_TRX
42
43 #include "nrf_802154_delayed_trx.h"
44
45 #include "nrf_802154_assert.h"
46 #include <stdbool.h>
47 #include <stdint.h>
48
49 #include "../nrf_802154_debug.h"
50 #include "nrf_802154_config.h"
51 #include "nrf_802154_const.h"
52 #include "nrf_802154_frame_parser.h"
53 #include "nrf_802154_notification.h"
54 #include "nrf_802154_pib.h"
55 #include "nrf_802154_procedures_duration.h"
56 #include "nrf_802154_queue.h"
57 #include "nrf_802154_request.h"
58 #include "nrf_802154_utils.h"
59 #include "nrf_802154_tx_power.h"
60 #include "rsch/nrf_802154_rsch.h"
61 #include "nrf_802154_sl_timer.h"
62 #include "nrf_802154_sl_utils.h"
63 #include "nrf_802154_sl_atomics.h"
64
65 #ifdef NRF_802154_USE_INTERNAL_INCLUDES
66 #include "nrf_802154_delayed_trx_internal.h"
67 #endif
68
69 #if NRF_802154_DELAYED_TRX_ENABLED
70
71 #if defined(NRF52_SERIES)
72 #define TX_SETUP_TIME_MAX 270u ///< Maximum time needed to prepare TX procedure [us]. It does not include TX ramp-up time.
73 #define RX_SETUP_TIME_MAX 270u ///< Maximum time needed to prepare RX procedure [us]. It does not include RX ramp-up time.
74 #elif defined(NRF53_SERIES)
75 #define TX_SETUP_TIME_MAX 360u ///< Maximum time needed to prepare TX procedure [us]. It does not include TX ramp-up time.
76 #define RX_SETUP_TIME_MAX 290u ///< Maximum time needed to prepare RX procedure [us]. It does not include RX ramp-up time.
77 #elif defined(NRF54L_SERIES)
78 #define TX_SETUP_TIME_MAX 600u ///< Maximum time needed to prepare TX procedure [us]. It does not include TX ramp-up time.
79 #define RX_SETUP_TIME_MAX 600u ///< Maximum time needed to prepare RX procedure [us]. It does not include RX ramp-up time.
80 #elif defined(NRF54H_SERIES)
81 #ifndef TX_SETUP_TIME_MAX
82 #define TX_SETUP_TIME_MAX 400u ///< Maximum time needed to prepare TX procedure [us]. It does not include TX ramp-up time.
83 #endif
84 #ifndef RX_SETUP_TIME_MAX
85 #define RX_SETUP_TIME_MAX 400u ///< Maximum time needed to prepare RX procedure [us]. It does not include RX ramp-up time.
86 #endif
87 #endif
88
89 /**
90 * @brief States of delayed operations.
91 */
92 typedef enum
93 {
94 DELAYED_TRX_OP_STATE_STOPPED = (1 << 0), ///< Delayed operation stopped.
95 DELAYED_TRX_OP_STATE_PENDING = (1 << 1), ///< Delayed operation scheduled and waiting for timeslot.
96 DELAYED_TRX_OP_STATE_ONGOING = (1 << 2), ///< Delayed operation ongoing (during timeslot).
97 DELAYED_TRX_OP_STATE_ALLOWED_MSK = ((1 << 3) - 1) ///< Mask of allowed delayed operation states.
98 } delayed_trx_op_state_t;
99
100 /**
101 * @brief RX delayed operation frame data.
102 */
103 typedef struct
104 {
105 uint64_t sof_timestamp; ///< Timestamp of last start of frame notification received in RX window.
106 uint8_t psdu_length; ///< Length in bytes of the frame to be received in RX window.
107 bool ack_requested; ///< Flag indicating if Ack for the frame to be received in RX window is requested.
108 } delayed_rx_frame_data_t;
109
110 /**
111 * @brief RX delayed operation data.
112 */
113 typedef struct
114 {
115 nrf_802154_sl_timer_t timeout_timer; ///< Timer for delayed RX timeout handling.
116 uint32_t timeout_length; ///< Requested length [us] of RX window plus RX_RAMP_UP_TIME.
117 volatile delayed_rx_frame_data_t extension_frame; ///< Data of frame that caused extension of RX window.
118 uint8_t channel; ///< Channel number on which reception should be performed.
119 } dly_rx_data_t;
120
121 /**
122 * @brief TX delayed operation data.
123 */
124 typedef struct
125 {
126 uint8_t * p_data; ///< Pointer to a buffer containing PHR and PSDU of the frame requested to be transmitted.
127 nrf_802154_transmit_params_t params; ///< Transmission parameters.
128
129 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
130 uint64_t time; ///< Target time of the first bit of the frame.
131
132 #endif
133 } dly_tx_data_t;
134
135 /**
136 * @brief Delayed operation data.
137 */
138 typedef struct
139 {
140 delayed_trx_op_state_t state; ///< State of the delayed timeslot.
141 rsch_dly_ts_id_t id; ///< Identifier of the delayed timeslot.
142 rsch_dly_ts_op_t op; ///< Type of delayed operation to be performed.
143
144 union
145 {
146 dly_tx_data_t tx; ///< Data specific for delayed transmission.
147 dly_rx_data_t rx; ///< Data specific for delayed reception.
148 };
149 } dly_op_data_t;
150
151 /**
152 * @brief Array of slots for RX delayed operations.
153 */
154 static dly_op_data_t m_dly_rx_data[NRF_802154_RSCH_DLY_TS_OP_DRX_SLOTS];
155
156 /**
157 * @brief Array of slots for TX delayed operations.
158 */
159 static dly_op_data_t m_dly_tx_data[NRF_802154_RSCH_DLY_TS_OP_DTX_SLOTS];
160
161 /**
162 * @brief Queue of RX delayed operations IDs to be processed.
163 */
164 static nrf_802154_queue_t m_dly_rx_id_q;
165
166 /**
167 * @brief Storage for RX delayed operations ID queue.
168 */
169 static dly_op_data_t * m_dly_rx_id_q_mem[NRF_802154_RSCH_DLY_TS_OP_DRX_SLOTS];
170
171 /**
172 * @brief Search for a RX delayed operation with given ID.
173 *
174 * @param[in] id Identifier to search for.
175 *
176 * @return Pointer to matching slot.
177 */
dly_rx_data_by_id_search(rsch_dly_ts_id_t id)178 static dly_op_data_t * dly_rx_data_by_id_search(rsch_dly_ts_id_t id)
179 {
180 dly_op_data_t * p_dly_op_data = NULL;
181
182 for (uint32_t i = 0; i < sizeof(m_dly_rx_data) / sizeof(m_dly_rx_data[0]); i++)
183 {
184 if (m_dly_rx_data[i].id == id)
185 {
186 // Slot with a matching identifier found
187 if ((p_dly_op_data == NULL))
188 {
189 // It's the first matching slot found
190 p_dly_op_data = &m_dly_rx_data[i];
191 }
192 else
193 {
194 // There's already been a matching slot. We expect all active slots to have unique
195 // IDs and all inactive slots to have their IDs set to NRF_802154_RESERVED_INVALID_ID.
196 // If we ended up here then either we're searching for invalid ID or the IDs assigned
197 // to active slots aren't unique. Either way - assert.
198 NRF_802154_ASSERT(false);
199 }
200 }
201 }
202
203 return p_dly_op_data;
204 }
205
206 /**
207 * Set state of a delayed operation.
208 *
209 * @param[in] p_dly_op_data Data of the delayed operation.
210 * @param[in] expected_state Expected delayed operation state prior to state transition.
211 * @param[in] new_state Delayed operation state to enter.
212 *
213 * @retval true Successfully set the new state.
214 * @retval false Failed to set the new state.
215 */
dly_op_state_set(dly_op_data_t * p_dly_op_data,delayed_trx_op_state_t expected_state,delayed_trx_op_state_t new_state)216 static bool dly_op_state_set(dly_op_data_t * p_dly_op_data,
217 delayed_trx_op_state_t expected_state,
218 delayed_trx_op_state_t new_state)
219 {
220 bool result = false;
221
222 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
223
224 switch (p_dly_op_data->op)
225 {
226 case RSCH_DLY_TS_OP_DTX:
227 case RSCH_DLY_TS_OP_DRX:
228 {
229 result = nrf_802154_sl_atomic_cas_u8(
230 (uint8_t *)&p_dly_op_data->state,
231 (uint8_t *)&expected_state,
232 new_state);
233
234 if (result)
235 {
236 nrf_802154_log_local_event(NRF_802154_LOG_VERBOSITY_LOW,
237 NRF_802154_LOG_LOCAL_EVENT_ID_DELAYED_TRX__SET_STATE,
238 (uint32_t)new_state);
239 }
240 }
241 break;
242
243 default:
244 {
245 NRF_802154_ASSERT(false);
246 }
247 }
248
249 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
250
251 return result;
252 }
253
254 /**
255 * @brief Search for a TX delayed operation with given ID.
256 *
257 * @param[in] id Identifier to search for.
258 *
259 * @return Pointer to matching slot.
260 */
dly_tx_data_by_id_search(rsch_dly_ts_id_t id)261 static dly_op_data_t * dly_tx_data_by_id_search(rsch_dly_ts_id_t id)
262 {
263 // Note that this function only supports a single slot.
264 if (id == m_dly_tx_data[0].id)
265 {
266 return &m_dly_tx_data[0];
267 }
268
269 return NULL;
270 }
271
272 /**
273 * @brief Retrieve an available slot from a pool.
274 *
275 * @param[inout] p_dly_op_data_pool Pool of slots to allocate from.
276 * @param[in] pool_len Number of elements in the pool.
277 *
278 * @return Pointer to an available slot.
279 */
available_dly_ts_slot_from_pool_get(dly_op_data_t * p_dly_op_data_pool,uint32_t pool_len)280 static dly_op_data_t * available_dly_ts_slot_from_pool_get(
281 dly_op_data_t * p_dly_op_data_pool, uint32_t pool_len)
282 {
283 for (uint32_t i = 0; i < pool_len; i++)
284 {
285 if (dly_op_state_set(&p_dly_op_data_pool[i],
286 DELAYED_TRX_OP_STATE_STOPPED,
287 DELAYED_TRX_OP_STATE_PENDING))
288 {
289 return &p_dly_op_data_pool[i];
290 }
291 }
292
293 return NULL;
294 }
295
296 /**
297 * @brief Get a slot for TX delayed operation.
298 *
299 * @return Pointer to a slot.
300 */
available_dly_tx_slot_get(void)301 static dly_op_data_t * available_dly_tx_slot_get(void)
302 {
303 return available_dly_ts_slot_from_pool_get(
304 m_dly_tx_data,
305 sizeof(m_dly_tx_data) / sizeof(m_dly_tx_data[0]));
306 }
307
308 /**
309 * @brief Get a slot for RX delayed operation.
310 *
311 * @return Pointer to a slot.
312 */
available_dly_rx_slot_get(void)313 static dly_op_data_t * available_dly_rx_slot_get(void)
314 {
315 return available_dly_ts_slot_from_pool_get(
316 m_dly_rx_data,
317 sizeof(m_dly_rx_data) / sizeof(m_dly_rx_data[0]));
318 }
319
320 /**
321 * @brief Get an ongoing RX delayed operation slot.
322 *
323 * @return Pointer to a slot or NULL if no ongoing RX delayed operations exist at the moment.
324 */
ongoing_dly_rx_slot_get(void)325 static dly_op_data_t * ongoing_dly_rx_slot_get(void)
326 {
327 dly_op_data_t * p_dly_op_data = NULL;
328
329 for (uint32_t i = 0; i < sizeof(m_dly_rx_data) / sizeof(m_dly_rx_data[0]); i++)
330 {
331 if (m_dly_rx_data[i].state == DELAYED_TRX_OP_STATE_ONGOING)
332 {
333 p_dly_op_data = &m_dly_rx_data[i];
334 }
335 }
336
337 return p_dly_op_data;
338 }
339
dly_ts_slot_release(dly_op_data_t * p_dly_op_data,bool handler)340 static bool dly_ts_slot_release(dly_op_data_t * p_dly_op_data, bool handler)
341 {
342 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
343
344 bool result;
345
346 result = nrf_802154_rsch_delayed_timeslot_cancel(p_dly_op_data->id, handler);
347
348 if (result)
349 {
350 p_dly_op_data->id = NRF_802154_RESERVED_INVALID_ID;
351 }
352
353 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
354
355 return result;
356 }
357
358 /**
359 * @brief Atomically push an ID to RX delayed operation ID queue.
360 *
361 * @param[in] id Identifier to be pushed to the queue.
362 */
dly_rx_data_atomically_push(dly_op_data_t * p_dly_op_data)363 static void dly_rx_data_atomically_push(dly_op_data_t * p_dly_op_data)
364 {
365 nrf_802154_mcu_critical_state_t mcu_cs;
366
367 nrf_802154_mcu_critical_enter(mcu_cs);
368
369 NRF_802154_ASSERT(!nrf_802154_queue_is_full(&m_dly_rx_id_q));
370
371 dly_op_data_t ** pp_op = (dly_op_data_t **)nrf_802154_queue_push_begin(&m_dly_rx_id_q);
372
373 *pp_op = p_dly_op_data;
374
375 nrf_802154_queue_push_commit(&m_dly_rx_id_q);
376
377 nrf_802154_mcu_critical_exit(mcu_cs);
378 }
379
380 /**
381 * @brief Atomically pop an ID from RX delayed operation ID queue.
382 *
383 * @param[out] p_id Pointer to identifier popped from the queue.
384 */
dly_rx_data_atomically_pop(void)385 static dly_op_data_t * dly_rx_data_atomically_pop(void)
386 {
387 nrf_802154_mcu_critical_state_t mcu_cs;
388
389 nrf_802154_mcu_critical_enter(mcu_cs);
390
391 dly_op_data_t ** pp_op = (dly_op_data_t **)nrf_802154_queue_pop_begin(&m_dly_rx_id_q);
392
393 nrf_802154_queue_pop_commit(&m_dly_rx_id_q);
394
395 nrf_802154_mcu_critical_exit(mcu_cs);
396
397 return *pp_op;
398 }
399
400 /**
401 * Start delayed operation.
402 *
403 * @param[in] p_dly_ts_param Parameters of the requested delayed timeslot.
404 * @param[inout] p_dly_op_data Data of the delayed operation.
405 */
dly_op_request(const rsch_dly_ts_param_t * p_dly_ts_param,dly_op_data_t * p_dly_op_data)406 static bool dly_op_request(const rsch_dly_ts_param_t * p_dly_ts_param,
407 dly_op_data_t * p_dly_op_data)
408 {
409 bool result = nrf_802154_rsch_delayed_timeslot_request(p_dly_ts_param);
410
411 if (!result)
412 {
413 p_dly_op_data->id = NRF_802154_RESERVED_INVALID_ID;
414
415 // Release the delayed operation slot immediately in case of failure.
416 bool state_set = dly_op_state_set(p_dly_op_data,
417 DELAYED_TRX_OP_STATE_PENDING,
418 DELAYED_TRX_OP_STATE_STOPPED);
419
420 NRF_802154_ASSERT(state_set);
421 (void)state_set;
422 }
423
424 return result;
425 }
426
427 /**
428 * Notify MAC layer that no frame was received before timeout.
429 *
430 * @param[in] p_context Not used.
431 */
notify_rx_timeout(nrf_802154_sl_timer_t * p_timer)432 static void notify_rx_timeout(nrf_802154_sl_timer_t * p_timer)
433 {
434 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
435
436 dly_op_data_t * p_dly_op_data = (dly_op_data_t *)(p_timer->user_data.p_pointer);
437 uint64_t now = nrf_802154_sl_timer_current_time_get();
438 uint64_t sof_timestamp = p_dly_op_data->rx.extension_frame.sof_timestamp;
439
440 // Make sure that the timestamp has been latched safely. If frame reception preempts the code
441 // after executing this line, the RX window will not be extended.
442 __DMB();
443 uint8_t psdu_length = p_dly_op_data->rx.extension_frame.psdu_length;
444 bool ack_requested = p_dly_op_data->rx.extension_frame.ack_requested;
445 uint32_t frame_length = nrf_802154_rx_duration_get(psdu_length, ack_requested);
446
447 if (nrf_802154_sl_time64_is_in_future(now, sof_timestamp + frame_length))
448 {
449 // @TODO protect against infinite extensions - allow only one timer extension
450 p_dly_op_data->rx.timeout_timer.trigger_time = sof_timestamp + frame_length;
451 p_dly_op_data->rx.timeout_length = frame_length;
452
453 nrf_802154_sl_timer_ret_t ret;
454
455 ret = nrf_802154_sl_timer_add(&p_dly_op_data->rx.timeout_timer);
456 NRF_802154_ASSERT(ret == NRF_802154_SL_TIMER_RET_SUCCESS);
457 (void)ret;
458 }
459 else
460 {
461 bool notified = nrf_802154_notify_receive_failed(
462 NRF_802154_RX_ERROR_DELAYED_TIMEOUT,
463 p_dly_op_data->id,
464 false);
465
466 // It should always be possible to notify DRX result
467 NRF_802154_ASSERT(notified);
468 (void)notified;
469
470 p_dly_op_data->id = NRF_802154_RESERVED_INVALID_ID;
471
472 bool result = dly_op_state_set(p_dly_op_data,
473 DELAYED_TRX_OP_STATE_ONGOING,
474 DELAYED_TRX_OP_STATE_STOPPED);
475
476 NRF_802154_ASSERT(result);
477 (void)result;
478
479 if (!nrf_802154_pib_rx_on_when_idle_get())
480 {
481 (void)nrf_802154_request_sleep(NRF_802154_TERM_NONE);
482 }
483 }
484
485 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
486 }
487
488 /**
489 * Transmit request result callback.
490 *
491 * @param[in] result Result of TX request.
492 */
dly_tx_result_notify(bool result)493 static void dly_tx_result_notify(bool result)
494 {
495 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
496
497 // Currently there's only a single delayed transmission possible at a time
498 dly_op_data_t * p_dly_op_data = dly_tx_data_by_id_search(NRF_802154_RESERVED_DTX_ID);
499
500 NRF_802154_ASSERT(p_dly_op_data != NULL);
501
502 if (!result)
503 {
504 // core rejected attempt, use my current frame_props
505 nrf_802154_transmit_done_metadata_t metadata = {};
506
507 metadata.frame_props = p_dly_op_data->tx.params.frame_props;
508 nrf_802154_notify_transmit_failed(p_dly_op_data->tx.p_data,
509 NRF_802154_TX_ERROR_TIMESLOT_DENIED,
510 &metadata);
511 }
512
513 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
514 }
515
dly_rx_all_ongoing_abort(void)516 static void dly_rx_all_ongoing_abort(void)
517 {
518 nrf_802154_sl_timer_ret_t ret;
519 dly_op_data_t * p_dly_op_data;
520 bool result;
521
522 for (int i = 0; i < sizeof(m_dly_rx_data) / sizeof(m_dly_rx_data[0]); i++)
523 {
524 p_dly_op_data = &m_dly_rx_data[i];
525
526 ret = nrf_802154_sl_timer_remove(&p_dly_op_data->rx.timeout_timer);
527
528 if (ret != NRF_802154_SL_TIMER_RET_SUCCESS)
529 {
530 // If the timer is ongoing, the timer either fired or is about to fire.
531 // Cleanup and notification will be performed by the timer callback.
532 continue;
533 }
534
535 bool notified = nrf_802154_notify_receive_failed(NRF_802154_RX_ERROR_DELAYED_ABORTED,
536 p_dly_op_data->id,
537 false);
538
539 // It should always be possible to notify DRX result
540 NRF_802154_ASSERT(notified);
541 (void)notified;
542
543 p_dly_op_data->id = NRF_802154_RESERVED_INVALID_ID;
544
545 result = dly_op_state_set(p_dly_op_data,
546 DELAYED_TRX_OP_STATE_ONGOING,
547 DELAYED_TRX_OP_STATE_STOPPED);
548 NRF_802154_ASSERT(result);
549 (void)result;
550 }
551 }
552
553 /**
554 * Receive request result callback.
555 *
556 * @param[in] result Result of RX request.
557 */
dly_rx_result_notify(bool result)558 static void dly_rx_result_notify(bool result)
559 {
560 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
561
562 dly_op_data_t * p_dly_op_data = dly_rx_data_atomically_pop();
563
564 if (p_dly_op_data == NULL)
565 {
566 NRF_802154_ASSERT(false);
567 return;
568 }
569
570 if (result)
571 {
572 uint64_t now;
573
574 dly_rx_all_ongoing_abort();
575
576 now = nrf_802154_sl_timer_current_time_get();
577 uint32_t tout_len = p_dly_op_data->rx.timeout_length;
578
579 p_dly_op_data->rx.timeout_timer.trigger_time = now + tout_len;
580 p_dly_op_data->rx.timeout_timer.user_data.p_pointer = p_dly_op_data;
581 p_dly_op_data->rx.timeout_timer.action_type =
582 NRF_802154_SL_TIMER_ACTION_TYPE_CALLBACK;
583
584 p_dly_op_data->rx.extension_frame.sof_timestamp = now;
585 p_dly_op_data->rx.extension_frame.psdu_length = 0;
586 p_dly_op_data->rx.extension_frame.ack_requested = false;
587
588 nrf_802154_sl_timer_ret_t ret;
589
590 ret = nrf_802154_sl_timer_add(&p_dly_op_data->rx.timeout_timer);
591 NRF_802154_ASSERT(ret == NRF_802154_SL_TIMER_RET_SUCCESS);
592 (void)ret;
593 }
594 else
595 {
596 bool notified = nrf_802154_notify_receive_failed(
597 NRF_802154_RX_ERROR_DELAYED_TIMESLOT_DENIED,
598 p_dly_op_data->id,
599 false);
600
601 // It should always be possible to notify DRX result
602 NRF_802154_ASSERT(notified);
603 (void)notified;
604 }
605
606 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
607 }
608
transmit_attempt(dly_op_data_t * p_dly_op_data)609 static void transmit_attempt(dly_op_data_t * p_dly_op_data)
610 {
611 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
612
613 // No need to enqueue transmit attempts. Proceed to transmission immediately
614 (void)nrf_802154_request_transmit(NRF_802154_TERM_802154,
615 REQ_ORIG_DELAYED_TRX,
616 p_dly_op_data->tx.p_data,
617 &p_dly_op_data->tx.params,
618 dly_tx_result_notify);
619
620 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
621 }
622
receive_attempt(dly_op_data_t * p_dly_op_data)623 static bool receive_attempt(dly_op_data_t * p_dly_op_data)
624 {
625 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
626
627 bool result = false;
628 bool channel_update_success = true;
629
630 // This function is expected to result in calling @ref dly_rx_result_notify.
631 // In order for that function to differentiate between different delayed RX
632 // windows, we atomically insert the ID of the current delayed RX into a FIFO queue.
633 dly_rx_data_atomically_push(p_dly_op_data);
634
635 if (nrf_802154_pib_channel_get() != p_dly_op_data->rx.channel)
636 {
637 nrf_802154_pib_channel_set(p_dly_op_data->rx.channel);
638 channel_update_success = nrf_802154_request_channel_update(REQ_ORIG_DELAYED_TRX);
639 }
640
641 if (channel_update_success)
642 {
643 result = nrf_802154_request_receive(NRF_802154_TERM_802154,
644 REQ_ORIG_DELAYED_TRX,
645 dly_rx_result_notify,
646 true,
647 p_dly_op_data->id);
648 }
649 else
650 {
651 dly_rx_result_notify(result);
652 }
653
654 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
655
656 return result;
657 }
658
659 /**
660 * Notify that the previously requested delayed TX timeslot has started just now.
661 *
662 * @param[in] dly_ts_id ID of the started timeslot.
663 */
tx_timeslot_started_callback(rsch_dly_ts_id_t dly_ts_id)664 static void tx_timeslot_started_callback(rsch_dly_ts_id_t dly_ts_id)
665 {
666 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
667
668 dly_op_data_t * p_dly_op_data = dly_tx_data_by_id_search(dly_ts_id);
669
670 NRF_802154_ASSERT(p_dly_op_data != NULL);
671
672 bool result = dly_op_state_set(p_dly_op_data,
673 DELAYED_TRX_OP_STATE_PENDING,
674 DELAYED_TRX_OP_STATE_ONGOING);
675
676 NRF_802154_ASSERT(result);
677
678 transmit_attempt(p_dly_op_data);
679
680 result = dly_ts_slot_release(p_dly_op_data, true);
681 NRF_802154_ASSERT(result);
682
683 result = dly_op_state_set(p_dly_op_data,
684 DELAYED_TRX_OP_STATE_ONGOING,
685 DELAYED_TRX_OP_STATE_STOPPED);
686 NRF_802154_ASSERT(result);
687 (void)result;
688
689 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
690 }
691
692 /**
693 * Notify that the previously requested delayed RX timeslot has started just now.
694 *
695 * @param[in] dly_ts_id ID of the started timeslot.
696 */
rx_timeslot_started_callback(rsch_dly_ts_id_t dly_ts_id)697 static void rx_timeslot_started_callback(rsch_dly_ts_id_t dly_ts_id)
698 {
699 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
700
701 dly_op_data_t * p_dly_op_data = dly_rx_data_by_id_search(dly_ts_id);
702
703 if (p_dly_op_data == NULL)
704 {
705 /* The DRX was canceled. */
706 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
707 return;
708 }
709
710 bool attempt_success = false;
711 bool result = dly_op_state_set(p_dly_op_data,
712 DELAYED_TRX_OP_STATE_PENDING,
713 DELAYED_TRX_OP_STATE_ONGOING);
714
715 if (result)
716 {
717 attempt_success = receive_attempt(p_dly_op_data);
718 }
719
720 result = nrf_802154_rsch_delayed_timeslot_cancel(dly_ts_id, true);
721 NRF_802154_ASSERT(result);
722
723 if (!attempt_success)
724 {
725 p_dly_op_data->id = NRF_802154_RESERVED_INVALID_ID;
726
727 result = dly_op_state_set(p_dly_op_data,
728 DELAYED_TRX_OP_STATE_ONGOING,
729 DELAYED_TRX_OP_STATE_STOPPED);
730 NRF_802154_ASSERT(result);
731 }
732
733 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
734 }
735
736 #ifdef TEST
737 #include "string.h"
nrf_802154_delayed_trx_module_reset(void)738 void nrf_802154_delayed_trx_module_reset(void)
739 {
740 memset(m_dly_rx_data, 0, sizeof(m_dly_rx_data));
741 memset(m_dly_tx_data, 0, sizeof(m_dly_tx_data));
742 memset(&m_dly_rx_id_q, 0, sizeof(m_dly_rx_id_q));
743 memset(m_dly_rx_id_q_mem, 0, sizeof(m_dly_rx_id_q_mem));
744 }
745
746 #endif // TEST
747
nrf_802154_delayed_trx_init(void)748 void nrf_802154_delayed_trx_init(void)
749 {
750 nrf_802154_queue_init(&m_dly_rx_id_q,
751 m_dly_rx_id_q_mem,
752 sizeof(m_dly_rx_id_q_mem),
753 sizeof(m_dly_rx_id_q_mem[0]));
754
755 for (uint32_t i = 0; i < sizeof(m_dly_rx_data) / sizeof(m_dly_rx_data[0]); i++)
756 {
757 m_dly_rx_data[i].state = DELAYED_TRX_OP_STATE_STOPPED;
758 m_dly_rx_data[i].id = NRF_802154_RESERVED_INVALID_ID;
759 nrf_802154_sl_timer_init(&m_dly_rx_data[i].rx.timeout_timer);
760 }
761
762 for (uint32_t i = 0; i < sizeof(m_dly_tx_data) / sizeof(m_dly_tx_data[0]); i++)
763 {
764 m_dly_tx_data[i].state = DELAYED_TRX_OP_STATE_STOPPED;
765 m_dly_tx_data[i].id = NRF_802154_RESERVED_INVALID_ID;
766 }
767 }
768
nrf_802154_delayed_trx_deinit(void)769 void nrf_802154_delayed_trx_deinit(void)
770 {
771 for (uint32_t i = 0; i < sizeof(m_dly_rx_data) / sizeof(m_dly_rx_data[0]); i++)
772 {
773 nrf_802154_sl_timer_deinit(&m_dly_rx_data[i].rx.timeout_timer);
774 }
775 }
776
nrf_802154_delayed_trx_transmit(uint8_t * p_data,uint64_t tx_time,const nrf_802154_transmit_at_metadata_t * p_metadata)777 bool nrf_802154_delayed_trx_transmit(uint8_t * p_data,
778 uint64_t tx_time,
779 const nrf_802154_transmit_at_metadata_t * p_metadata)
780 {
781 dly_op_data_t * p_dly_tx_data = available_dly_tx_slot_get();
782 bool result = false;
783
784 if (p_dly_tx_data != NULL)
785 {
786 tx_time -= TX_SETUP_TIME_MAX;
787 tx_time -= TX_RAMP_UP_TIME;
788
789 if (p_metadata->cca)
790 {
791 tx_time -= nrf_802154_cca_before_tx_duration_get();
792 }
793
794 p_dly_tx_data->op = RSCH_DLY_TS_OP_DTX;
795
796 p_dly_tx_data->tx.p_data = p_data;
797 p_dly_tx_data->tx.params.frame_props = p_metadata->frame_props;
798 (void)nrf_802154_tx_power_convert_metadata_to_tx_power_split(p_metadata->channel,
799 p_metadata->tx_power,
800 &p_dly_tx_data->tx.params.tx_power);
801 p_dly_tx_data->tx.params.cca = p_metadata->cca;
802 p_dly_tx_data->tx.params.immediate = true;
803 p_dly_tx_data->tx.params.extra_cca_attempts = p_metadata->extra_cca_attempts;
804 p_dly_tx_data->tx.params.channel = p_metadata->channel;
805 p_dly_tx_data->id = NRF_802154_RESERVED_DTX_ID;
806
807 rsch_dly_ts_param_t dly_ts_param =
808 {
809 .trigger_time = tx_time,
810 .ppi_trigger_en = true,
811 .ppi_trigger_dly = TX_SETUP_TIME_MAX,
812 .prio = RSCH_PRIO_TX,
813 .op = RSCH_DLY_TS_OP_DTX,
814 .type = RSCH_DLY_TS_TYPE_PRECISE,
815 .started_callback = tx_timeslot_started_callback,
816 .id = NRF_802154_RESERVED_DTX_ID,
817 };
818
819 result = dly_op_request(&dly_ts_param, p_dly_tx_data);
820 }
821
822 return result;
823 }
824
nrf_802154_delayed_trx_receive(uint64_t rx_time,uint32_t timeout,uint8_t channel,uint32_t id)825 bool nrf_802154_delayed_trx_receive(uint64_t rx_time,
826 uint32_t timeout,
827 uint8_t channel,
828 uint32_t id)
829 {
830 if (dly_rx_data_by_id_search(id) != NULL)
831 {
832 /* DRX with given id is already present. */
833 return false;
834 }
835
836 dly_op_data_t * p_dly_rx_data = available_dly_rx_slot_get();
837 bool result = false;
838
839 if (p_dly_rx_data != NULL)
840 {
841 rx_time -= RX_SETUP_TIME_MAX;
842 rx_time -= RX_RAMP_UP_TIME;
843
844 p_dly_rx_data->op = RSCH_DLY_TS_OP_DRX;
845
846 p_dly_rx_data->rx.timeout_length = timeout + RX_RAMP_UP_TIME +
847 RX_SETUP_TIME_MAX;
848 p_dly_rx_data->rx.timeout_timer.action.callback.callback = notify_rx_timeout;
849
850 p_dly_rx_data->rx.channel = channel;
851 p_dly_rx_data->id = id;
852
853 rsch_dly_ts_param_t dly_ts_param =
854 {
855 .trigger_time = rx_time,
856 .ppi_trigger_en = true,
857 .ppi_trigger_dly = RX_SETUP_TIME_MAX,
858 .prio = RSCH_PRIO_IDLE_LISTENING,
859 .op = RSCH_DLY_TS_OP_DRX,
860 .type = RSCH_DLY_TS_TYPE_PRECISE,
861 .started_callback = rx_timeslot_started_callback,
862 .id = id,
863 };
864
865 result = dly_op_request(&dly_ts_param, p_dly_rx_data);
866 }
867
868 return result;
869 }
870
nrf_802154_delayed_trx_transmit_cancel(void)871 bool nrf_802154_delayed_trx_transmit_cancel(void)
872 {
873 // This function does not provide any ID because it assumes that only a single delayed
874 // transmission can be scheduled at a time. Therefore use the first (and only) entry
875 // of m_dly_tx_data
876 dly_op_data_t * p_dly_op_data = &m_dly_tx_data[0];
877 bool result = false;
878
879 if (dly_ts_slot_release(p_dly_op_data, false))
880 {
881 result = dly_op_state_set(p_dly_op_data,
882 DELAYED_TRX_OP_STATE_PENDING,
883 DELAYED_TRX_OP_STATE_STOPPED);
884
885 NRF_802154_ASSERT(result);
886 }
887
888 return result;
889 }
890
nrf_802154_delayed_trx_receive_cancel(uint32_t id)891 bool nrf_802154_delayed_trx_receive_cancel(uint32_t id)
892 {
893 dly_op_data_t * p_dly_op_data = dly_rx_data_by_id_search(id);
894 bool stopped = false;
895
896 if (p_dly_op_data == NULL)
897 {
898 // Delayed receive window with provided ID could not be found.
899 return false;
900 }
901
902 bool result = nrf_802154_rsch_delayed_timeslot_cancel(id, false);
903 bool was_running = false;
904
905 nrf_802154_sl_timer_ret_t ret = nrf_802154_sl_timer_remove(&p_dly_op_data->rx.timeout_timer);
906
907 was_running = (ret == NRF_802154_SL_TIMER_RET_SUCCESS);
908
909 if (result || was_running)
910 {
911 p_dly_op_data->id = NRF_802154_RESERVED_INVALID_ID;
912 stopped = true;
913
914 nrf_802154_sl_atomic_store_u8((uint8_t *)&p_dly_op_data->state,
915 DELAYED_TRX_OP_STATE_STOPPED);
916 }
917
918 return stopped;
919 }
920
nrf_802154_delayed_trx_abort(nrf_802154_term_t term_lvl,req_originator_t req_orig)921 bool nrf_802154_delayed_trx_abort(nrf_802154_term_t term_lvl, req_originator_t req_orig)
922 {
923 nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_HIGH);
924
925 if (ongoing_dly_rx_slot_get() == NULL)
926 {
927 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
928 return true;
929 }
930
931 if (term_lvl < NRF_802154_TERM_802154)
932 {
933 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
934 return false;
935 }
936
937 dly_rx_all_ongoing_abort();
938
939 nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_HIGH);
940 return true;
941 }
942
nrf_802154_delayed_trx_rx_started_hook(const uint8_t * p_frame)943 void nrf_802154_delayed_trx_rx_started_hook(const uint8_t * p_frame)
944 {
945 dly_op_data_t * p_dly_op_data = ongoing_dly_rx_slot_get();
946 nrf_802154_frame_parser_data_t frame_data;
947
948 bool result = nrf_802154_frame_parser_data_init(p_frame,
949 p_frame[PHR_OFFSET] + PHR_SIZE,
950 PARSE_LEVEL_FCF_OFFSETS,
951 &frame_data);
952
953 if ((result) && (p_dly_op_data != NULL))
954 {
955 p_dly_op_data->rx.extension_frame.sof_timestamp = nrf_802154_sl_timer_current_time_get();
956 p_dly_op_data->rx.extension_frame.psdu_length = p_frame[PHR_OFFSET];
957 p_dly_op_data->rx.extension_frame.ack_requested = nrf_802154_frame_parser_ar_bit_is_set(
958 &frame_data);
959 }
960 }
961
nrf_802154_delayed_trx_nearest_drx_time_to_midpoint_get(uint32_t * p_drx_time_to_midpoint)962 bool nrf_802154_delayed_trx_nearest_drx_time_to_midpoint_get(uint32_t * p_drx_time_to_midpoint)
963 {
964 bool result = false;
965 uint32_t min_time_to_start = 0xffffffff;
966 uint64_t drx_time_to_start = UINT64_C(0xffffffff);
967 uint32_t drx_time_to_midpoint;
968 uint32_t drx_window_duration_time;
969
970 for (int i = 0; i < sizeof(m_dly_rx_data) / sizeof(m_dly_rx_data[0]); i++)
971 {
972 if (m_dly_rx_data[i].state != DELAYED_TRX_OP_STATE_PENDING)
973 {
974 continue;
975 }
976
977 result = nrf_802154_rsch_delayed_timeslot_time_to_start_get(m_dly_rx_data[i].id,
978 &drx_time_to_start);
979 drx_time_to_start += RX_SETUP_TIME_MAX + RX_RAMP_UP_TIME;
980
981 if (result)
982 {
983 min_time_to_start = drx_time_to_start < min_time_to_start ?
984 (uint32_t)drx_time_to_start : min_time_to_start;
985 drx_window_duration_time = m_dly_rx_data[i].rx.timeout_length -
986 (RX_SETUP_TIME_MAX + RX_RAMP_UP_TIME);
987 drx_time_to_midpoint = min_time_to_start + drx_window_duration_time / 2;
988 }
989 }
990
991 if (result)
992 {
993 *p_drx_time_to_midpoint = drx_time_to_midpoint;
994 }
995
996 return result;
997 }
998
999 #endif // NRF_802154_DELAYED_TRX_ENABLED
1000