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