1 /*
2  * Copyright (c) 2021, 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 the CSL IE injector for the 802.15.4 driver.
38  *
39  */
40 
41 #include "mac_features/nrf_802154_ie_writer.h"
42 
43 #include "mac_features/nrf_802154_frame_parser.h"
44 #include "mac_features/nrf_802154_delayed_trx.h"
45 #include "nrf_802154_core.h"
46 #include "nrf_802154_nrfx_addons.h"
47 #include "nrf_802154_tx_work_buffer.h"
48 #include "nrf_802154_utils_byteorder.h"
49 #include "nrf_802154_sl_timer.h"
50 
51 #include "nrf_802154_assert.h"
52 
53 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
54 #include "nrf_802154_bsim_utils.h"
55 #endif
56 
57 #if NRF_802154_IE_WRITER_ENABLED
58 
59 #define CSL_US_PER_UNIT (IE_CSL_SYMBOLS_PER_UNIT * PHY_US_PER_SYMBOL)
60 
61 typedef enum
62 {
63     IE_WRITER_RESET,
64     IE_WRITER_PREPARE,
65     IE_WRITER_COMMIT
66 } writer_state_t;
67 
68 static writer_state_t m_writer_state = IE_WRITER_RESET; ///< IE writer state
69 
70 #if NRF_802154_DELAYED_TRX_ENABLED
71 
72 static uint8_t * mp_csl_phase_addr;     ///< Cached CSL information element phase field address
73 static uint8_t * mp_csl_period_addr;    ///< Cached CSL information element period field address
74 static uint16_t  m_csl_period;          ///< CSL period value that will be injected to CSL information element
75 static uint64_t  m_csl_anchor_time;     ///< The anchor time based on which CSL window times are calculated
76 static bool      m_csl_anchor_time_set; ///< Information if CSL anchor time was set by the higher layer
77 
78 static uint8_t * mp_cst_phase_addr;     ///< Cached CST information element phase field address
79 static uint8_t * mp_cst_period_addr;    ///< Cached CST information element period field address
80 static uint16_t  m_cst_period;          ///< CST period value that will be injected to CST information element
81 static uint64_t  m_cst_anchor_time;     ///< The anchor time based on which CST window times are calculated
82 
83 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
84 static uint32_t m_csl_time_to_radio_address_us;
85 
86 #endif
87 
88 /** @brief Calulate CSL phase.
89  *
90  * @param[out]  p_csl_phase   Calculated CSL phase in units of 160us.
91  * @param[in]   csl_period    CSL period value.
92  * @param[in]   anchor_time   The anchor time based on which window times are calculated.
93  *
94  * @retval  true   The calculation was successful and @p p_csl_phase contains a valid CSL phase.
95  * @retval  false  The calculation failed and the value pointed to by @p p_csl_phase is undefined.
96  */
csl_phase_calc(uint32_t * p_csl_phase,uint16_t csl_period,uint64_t anchor_time)97 static bool csl_phase_calc(uint32_t * p_csl_phase, uint16_t csl_period, uint64_t anchor_time)
98 {
99     bool     result = false;
100     uint32_t us;
101 
102     if (m_csl_anchor_time_set)
103     {
104         result = (csl_period != 0);
105 
106         if (result)
107         {
108             /*
109              * This function is executed in the handler of RADIO.ADDRESS event. According to the IPS,
110              * in 802.15.4 transmit sequence RADIO.FRAMESTART event is triggered after the SHR is
111              * transmitted (nRF52840 PS v1.7 -- 6.20.12.6 Transmit sequence). However, RADIO.ADDRESS
112              * event is also triggered in 802.15.4 transmit sequence with a constant 32us offset.
113              * This handler is therefore expected to execute 32us before the SHR transmission ends.
114              *
115              * The IEEE 802.15.4-2015 specification is unclear about which point in time the CSL Phase
116              * should refer to. Following the latest developments in the Thread specification, here
117              * it is going to be calculated relative to the beginning of the MHR. This function
118              * executes approximately 2 * 32us = 64us before the first bit of MHR. The calculation
119              * below takes it into account by adding 64us to the current time.
120              */
121             uint64_t csl_ref_time_us = nrf_802154_sl_timer_current_time_get() + 64;
122             uint32_t csl_period_us   = csl_period * IE_CSL_SYMBOLS_PER_UNIT * PHY_US_PER_SYMBOL;
123 
124 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
125             /**
126              * In simulation this function is executed immediately after setting up radio ramp-up.
127              * The reference time calculated above must be increased with simulation-specific
128              * adjustments calculated earlier.
129              */
130             csl_ref_time_us += m_csl_time_to_radio_address_us;
131 #endif /* defined(CONFIG_SOC_SERIES_BSIM_NRFXX) */
132 
133             // Modulo of a negative number possibly will not be positive, so the below if-else clause is needed
134             if (csl_ref_time_us >= anchor_time)
135             {
136                 uint32_t time_from_previous_window =
137                     (uint32_t)((csl_ref_time_us - anchor_time) % csl_period_us);
138 
139                 us = csl_period_us - time_from_previous_window;
140             }
141             else
142             {
143                 us = (uint32_t)((anchor_time - csl_ref_time_us) % csl_period_us);
144             }
145         }
146     }
147     else
148     {
149         // Fallback to legacy method
150         result = nrf_802154_delayed_trx_nearest_drx_time_to_midpoint_get(&us);
151 
152 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
153         /**
154          * In simulation this function is executed immediately after setting up radio ramp-up.
155          * The time to midpoint calculated above must be decreased with simulation-specific
156          * adjustments calculated earlier.
157          */
158         us -= m_csl_time_to_radio_address_us;
159 #endif /* defined(CONFIG_SOC_SERIES_BSIM_NRFXX) */
160     }
161 
162     if (result)
163     {
164         // Round to the nearest integer when converting us to CSL units
165         uint32_t csl_phase = (us + (CSL_US_PER_UNIT >> 1)) / CSL_US_PER_UNIT;
166 
167         if (0 == csl_phase)
168         {
169             // If the phase was rounded down to 0, increase it by one period.
170             csl_phase = m_csl_period;
171         }
172 
173         *p_csl_phase = csl_phase;
174     }
175 
176     return result;
177 }
178 
179 /**
180  * @brief Writes CSL phase to previously set memory address.
181  *
182  * @param[inout]  p_written  Flag set to true if CSL IE was written. If CSL IE is not written, the flag
183  *                           is not modified.
184  */
csl_ie_write_commit(bool * p_written)185 static void csl_ie_write_commit(bool * p_written)
186 {
187     uint32_t csl_phase;
188 
189     if ((mp_csl_phase_addr == NULL) || (mp_csl_period_addr == NULL))
190     {
191         // CSL writer not armed. Nothing to be done.
192         return;
193     }
194 
195     if (csl_phase_calc(&csl_phase, m_csl_period, m_csl_anchor_time) == false)
196     {
197         // CSL Phase could not be determined. Do not write to the CSL IE.
198         return;
199     }
200 
201     if (csl_phase > IE_CSL_PERIOD_MAX)
202     {
203         // CSL phase exceeds the maximum value. Do not write to the CSL IE
204         return;
205     }
206 
207     host_16_to_little(csl_phase, mp_csl_phase_addr);
208     host_16_to_little(m_csl_period, mp_csl_period_addr);
209 
210     *p_written = true;
211 }
212 
213 /**
214  * @brief Finds and prepare memory address where CSL phase will be written.
215  *
216  * @param[in]  p_iterator  Information Element parser iterator.
217  *
218  * @retval  true  The write prepare operation to CSL IE was successful.
219  * @retval  false An improperly formatted CSL IE was detected.
220  */
csl_ie_write_prepare(const uint8_t * p_iterator)221 static bool csl_ie_write_prepare(const uint8_t * p_iterator)
222 {
223     NRF_802154_ASSERT(p_iterator != NULL);
224 
225     if (nrf_802154_frame_parser_ie_length_get(p_iterator) < IE_CSL_SIZE_MIN)
226     {
227         // The IE is too small to be a valid CSL IE.
228         return false;
229     }
230 
231     mp_csl_phase_addr  = (uint8_t *)nrf_802154_frame_parser_ie_content_address_get(p_iterator);
232     mp_csl_period_addr = mp_csl_phase_addr + sizeof(uint16_t);
233 
234     return true;
235 }
236 
237 /**
238  * @brief Resets CSL writer to pristine state.
239  */
csl_ie_write_reset(void)240 static void csl_ie_write_reset(void)
241 {
242     mp_csl_phase_addr  = NULL;
243     mp_csl_period_addr = NULL;
244 }
245 
246 /**
247  * @brief Writes CST phase to previously set memory address.
248  *
249  * Note: CST period value 0 is treated as a special value which makes the CST phase to be 0 as well.
250  *
251  * @param[inout]  p_written  Flag set to true if CST IE was written. If CST IE is not written, the flag
252  *                           is not modified.
253  */
cst_ie_write_commit(bool * p_written)254 static void cst_ie_write_commit(bool * p_written)
255 {
256     uint32_t cst_phase = 0;
257 
258     if ((mp_cst_phase_addr == NULL) || (mp_cst_period_addr == NULL))
259     {
260         // CST writer not armed. Nothing to be done.
261         return;
262     }
263 
264     if (m_cst_anchor_time == 0)
265     {
266         // CST parameters not configured.
267         return;
268     }
269 
270     if (m_cst_period > 0 && csl_phase_calc(&cst_phase, m_cst_period, m_cst_anchor_time) == false)
271     {
272         return;
273     }
274 
275     host_16_to_little(cst_phase, mp_cst_phase_addr);
276     host_16_to_little(m_cst_period, mp_cst_period_addr);
277 
278     *p_written = true;
279 }
280 
281 /**
282  * @brief Finds and prepare memory address where CST phase will be written.
283  *
284  * @param[in]  p_iterator  Information Element parser iterator.
285  *
286  * @retval  true  The write prepare operation to CST IE was successful.
287  * @retval  false An improperly formatted CST IE was detected.
288  */
cst_ie_write_prepare(const uint8_t * p_iterator)289 static bool cst_ie_write_prepare(const uint8_t * p_iterator)
290 {
291     NRF_802154_ASSERT(p_iterator != NULL);
292 
293     if (nrf_802154_frame_parser_ie_length_get(p_iterator) != IE_VENDOR_THREAD_CST_SIZE)
294     {
295         // The IE has unexpected size
296         return false;
297     }
298 
299     mp_cst_phase_addr = (uint8_t *)nrf_802154_frame_parser_ie_vendor_thread_data_addr_get(
300         p_iterator);
301     mp_cst_period_addr = mp_cst_phase_addr + sizeof(uint16_t);
302 
303     return true;
304 }
305 
306 /**
307  * @brief Resets CST writer to pristine state.
308  */
cst_ie_write_reset(void)309 static void cst_ie_write_reset(void)
310 {
311     mp_cst_phase_addr  = NULL;
312     mp_cst_period_addr = NULL;
313 }
314 
315 #else
316 
317 /**
318  * @brief Writes CSL phase to previously set memory address.
319  */
csl_ie_write_commit(bool * p_written)320 static void csl_ie_write_commit(bool * p_written)
321 {
322     // Intentionally empty
323     (void)p_written;
324 }
325 
326 /**
327  * @brief Finds and prepare memory address where CSL phase will be written.
328  *
329  * @param[in]  p_iterator  Information Element parser iterator.
330  *
331  * @retval  true  The write prepare operation to CSL IE was successful.
332  * @retval  false An improperly formatted CSL IE was detected.
333  */
csl_ie_write_prepare(const uint8_t * p_iterator)334 static bool csl_ie_write_prepare(const uint8_t * p_iterator)
335 {
336     // Intentionally empty
337     return true;
338 }
339 
340 /**
341  * @brief Resets CSL writer to pristine state.
342  */
csl_ie_write_reset(void)343 static void csl_ie_write_reset(void)
344 {
345     // Intentionally empty
346 }
347 
348 /**
349  * @brief Writes CST phase to previously set memory address.
350  *
351  * @param[inout]  p_written  Flag set to true if CST IE was written. If CST IE is not written, the flag
352  *                           is not modified.
353  */
cst_ie_write_commit(bool * p_written)354 static void cst_ie_write_commit(bool * p_written)
355 {
356     // Intentionally empty
357     (void)p_written;
358 }
359 
360 /**
361  * @brief Finds and prepare memory address where CST phase will be written.
362  *
363  * @param[in]  p_iterator  Information Element parser iterator.
364  *
365  * @retval  true  The write prepare operation to CST IE was successful.
366  * @retval  false An improperly formatted CST IE was detected.
367  */
cst_ie_write_prepare(const uint8_t * p_iterator)368 static bool cst_ie_write_prepare(const uint8_t * p_iterator)
369 {
370     // Intentionally empty
371     return true;
372 }
373 
374 /**
375  * @brief Resets CST writer to pristine state.
376  */
cst_ie_write_reset(void)377 static void cst_ie_write_reset(void)
378 {
379     // Intentionally empty
380 }
381 
382 #endif // NRF_802154_DELAYED_TRX_ENABLED
383 
384 static uint8_t * mp_lm_rssi_addr;   ///< Cached Link Metrics information element RSSI field address
385 static uint8_t * mp_lm_margin_addr;  ///< Cached Link Metrics information element link margin field address
386 static uint8_t * mp_lm_lqi_addr; ///< Cached Link Metrics information element LQI field address
387 
rssi_scale(int8_t rssi)388 static uint8_t rssi_scale(int8_t rssi)
389 {
390     int16_t rssi_lim;     ///< RSSI value after applying limits
391     int16_t intermediate; ///< RSSI value after casting to a wider type
392     uint8_t scaled;       ///< RSSI value after scaling
393 
394     // Apply lower limit
395     rssi_lim =
396         ((int16_t)rssi < IE_VENDOR_THREAD_RSSI_FLOOR) ? IE_VENDOR_THREAD_RSSI_FLOOR : (int16_t)rssi;
397     // Apply upper limit
398     rssi_lim = (rssi_lim > IE_VENDOR_THREAD_RSSI_CEIL) ? IE_VENDOR_THREAD_RSSI_CEIL : rssi_lim;
399 
400     // Cast to a wider type to avoid premature overflow
401     intermediate = (int16_t)rssi_lim - IE_VENDOR_THREAD_RSSI_FLOOR;
402     // Scale linearly to range 0 - UINT8_MAX
403     scaled = (uint8_t)((intermediate * (UINT8_MAX - 0)) /
404                        (IE_VENDOR_THREAD_RSSI_CEIL - IE_VENDOR_THREAD_RSSI_FLOOR));
405 
406     return scaled;
407 }
408 
margin_scale(int16_t margin)409 static uint8_t margin_scale(int16_t margin)
410 {
411     int16_t margin_lim; ///< Margin value after applying limits
412     uint8_t scaled;     ///< Margin value after scaling
413 
414     // Apply lower limit
415     margin_lim = (margin < IE_VENDOR_THREAD_MARGIN_FLOOR) ? IE_VENDOR_THREAD_MARGIN_FLOOR : margin;
416     // Apply upper limit
417     margin_lim =
418         (margin_lim > IE_VENDOR_THREAD_MARGIN_CEIL) ? IE_VENDOR_THREAD_MARGIN_CEIL : margin_lim;
419 
420     // Scale linearly to range 0 - UINT8_MAX
421     scaled = (uint8_t)(((margin_lim - IE_VENDOR_THREAD_MARGIN_FLOOR) * (UINT8_MAX - 0)) /
422                        (IE_VENDOR_THREAD_MARGIN_CEIL - IE_VENDOR_THREAD_MARGIN_FLOOR));
423 
424     return scaled;
425 }
426 
427 /**
428  * @brief Writes link metrics to previously prepared addresses in a frame.
429  *
430  * @param[inout]  p_written  Flag set to true if link metrics IE was written. If link metrics IE is
431  *                           not written, the flag is not modified.
432  */
link_metrics_ie_write_commit(bool * p_written)433 static void link_metrics_ie_write_commit(bool * p_written)
434 {
435     if ((mp_lm_rssi_addr != NULL) || (mp_lm_margin_addr != NULL))
436     {
437         int8_t rssi = (uint8_t)nrf_802154_core_last_frame_rssi_get();
438 
439         if (mp_lm_rssi_addr != NULL)
440         {
441             *mp_lm_rssi_addr = rssi_scale(rssi);
442             *p_written       = true;
443         }
444 
445         if (mp_lm_margin_addr != NULL)
446         {
447             *mp_lm_margin_addr = margin_scale((int16_t)rssi - ED_RSSIOFFS);
448             *p_written         = true;
449         }
450     }
451 
452     if (mp_lm_lqi_addr != NULL)
453     {
454         *mp_lm_lqi_addr = (uint8_t)nrf_802154_core_last_frame_lqi_get();
455         *p_written      = true;
456     }
457 }
458 
459 /**
460  * @brief Finds and prepare memory addresses where link metrics will be written.
461  *
462  * @param[in]  p_iterator  Information Element parser iterator.
463  *
464  * @retval  true  The write prepare operation for link metrics was successful.
465  * @retval  false An improperly formatted link metrics IE was detected.
466  */
link_metrics_ie_write_prepare(const uint8_t * p_iterator)467 static bool link_metrics_ie_write_prepare(const uint8_t * p_iterator)
468 {
469     NRF_802154_ASSERT(p_iterator != NULL);
470 
471     // Initialize the iterator at the start of IE content
472     uint8_t * p_content_iterator =
473         (uint8_t *)nrf_802154_frame_parser_ie_vendor_thread_data_addr_get(p_iterator);
474     uint8_t * ie_end = (uint8_t *)nrf_802154_frame_parser_ie_iterator_next(p_iterator);
475 
476     if (nrf_802154_frame_parser_ie_length_get(p_iterator) < IE_VENDOR_THREAD_ACK_SIZE_MIN ||
477         nrf_802154_frame_parser_ie_length_get(p_iterator) > IE_VENDOR_THREAD_ACK_SIZE_MAX)
478     {
479         return false;
480     }
481 
482     while (p_content_iterator != ie_end)
483     {
484         switch (*p_content_iterator)
485         {
486             case IE_VENDOR_THREAD_RSSI_TOKEN:
487                 if (mp_lm_rssi_addr != NULL)
488                 {
489                     return false;
490                 }
491                 mp_lm_rssi_addr = p_content_iterator;
492                 break;
493 
494             case IE_VENDOR_THREAD_MARGIN_TOKEN:
495                 if (mp_lm_margin_addr != NULL)
496                 {
497                     return false;
498                 }
499                 mp_lm_margin_addr = p_content_iterator;
500                 break;
501 
502             case IE_VENDOR_THREAD_LQI_TOKEN:
503                 if (mp_lm_lqi_addr != NULL)
504                 {
505                     return false;
506                 }
507                 mp_lm_lqi_addr = p_content_iterator;
508                 break;
509 
510             default:
511                 return false;
512         }
513 
514         p_content_iterator++;
515     }
516 
517     return true;
518 }
519 
520 /**
521  * @brief Resets the prepared addresses for injecting link metrics into a frame.
522  */
link_metrics_ie_write_reset(void)523 static void link_metrics_ie_write_reset(void)
524 {
525     mp_lm_rssi_addr   = NULL;
526     mp_lm_margin_addr = NULL;
527     mp_lm_lqi_addr    = NULL;
528 }
529 
530 /**
531  * @brief Performs IE write preparations.
532  *
533  * This function prepares the write operation for all recognized information elements and
534  * sets the state to IE_WRITER_PREPARE.
535  *
536  * If any of the information elements fails the boundary check or is not properly formatted,
537  * the writer state shall be set to IE_WRITER_RESET.
538  *
539  * @param[in]  p_ie_header  Pointer to the beginning of header IEs.
540  * @param[in]  p_end_addr   Pointer to the first invalid address after p_ie_header.
541  */
ie_writer_prepare(uint8_t * p_ie_header,const uint8_t * p_end_addr)542 static void ie_writer_prepare(uint8_t * p_ie_header, const uint8_t * p_end_addr)
543 {
544     NRF_802154_ASSERT(m_writer_state == IE_WRITER_RESET);
545     m_writer_state = IE_WRITER_PREPARE;
546 
547     const uint8_t * p_iterator = nrf_802154_frame_parser_header_ie_iterator_begin(p_ie_header);
548     bool            result     = true;
549 
550     while (nrf_802154_frame_parser_ie_iterator_end(p_iterator, p_end_addr) == false)
551     {
552         switch (nrf_802154_frame_parser_ie_id_get(p_iterator))
553         {
554             case IE_VENDOR_ID:
555                 if (nrf_802154_frame_parser_ie_length_get(p_iterator) >= IE_VENDOR_SIZE_MIN &&
556                     nrf_802154_frame_parser_ie_vendor_oui_get(p_iterator) == IE_VENDOR_THREAD_OUI &&
557                     nrf_802154_frame_parser_ie_length_get(p_iterator) >= IE_VENDOR_THREAD_SIZE_MIN)
558                 {
559                     switch (nrf_802154_frame_parser_ie_vendor_thread_subtype_get(p_iterator))
560                     {
561                         case IE_VENDOR_THREAD_ACK_PROBING_ID:
562                             result = link_metrics_ie_write_prepare(p_iterator);
563                             break;
564 
565                         case IE_VENDOR_THREAD_CST_ID:
566                             result = cst_ie_write_prepare(p_iterator);
567                             break;
568 
569                         default:
570                             break;
571                     }
572                 }
573                 break;
574 
575             case IE_CSL_ID:
576                 result = csl_ie_write_prepare(p_iterator);
577                 break;
578 
579             default:
580                 break;
581         }
582 
583         if (result == false)
584         {
585             nrf_802154_ie_writer_reset();
586             return;
587         }
588 
589         p_iterator = nrf_802154_frame_parser_ie_iterator_next(p_iterator);
590     }
591 }
592 
593 /**
594  * @brief Commits data to recognized information elements.
595  *
596  * @param[inout]  p_written  Flag set to true if IE was written. If IE was not written, the flag is
597  *                           not modified.
598  */
ie_writer_commit(bool * p_written)599 static void ie_writer_commit(bool * p_written)
600 {
601     NRF_802154_ASSERT(m_writer_state == IE_WRITER_PREPARE);
602     m_writer_state = IE_WRITER_COMMIT;
603 
604     csl_ie_write_commit(p_written);
605     cst_ie_write_commit(p_written);
606     link_metrics_ie_write_commit(p_written);
607 }
608 
nrf_802154_ie_writer_reset(void)609 void nrf_802154_ie_writer_reset(void)
610 {
611     m_writer_state = IE_WRITER_RESET;
612 
613     csl_ie_write_reset();
614     cst_ie_write_reset();
615     link_metrics_ie_write_reset();
616 }
617 
nrf_802154_ie_writer_prepare(uint8_t * p_ie_header,const uint8_t * p_end_addr)618 void nrf_802154_ie_writer_prepare(uint8_t * p_ie_header, const uint8_t * p_end_addr)
619 {
620     NRF_802154_ASSERT(p_ie_header != NULL);
621     NRF_802154_ASSERT(p_ie_header < p_end_addr);
622 
623     ie_writer_prepare(p_ie_header, p_end_addr);
624 }
625 
nrf_802154_ie_writer_tx_setup(uint8_t * p_frame,nrf_802154_transmit_params_t * p_params,nrf_802154_transmit_failed_notification_t notify_function)626 bool nrf_802154_ie_writer_tx_setup(
627     uint8_t                                 * p_frame,
628     nrf_802154_transmit_params_t            * p_params,
629     nrf_802154_transmit_failed_notification_t notify_function)
630 {
631     // The IE writer module can be in the IE_WRITER_PREPARE state if
632     // the previous transmission failed at an early stage.
633     // Reset it, to avoid data corruption in case this frame
634     // does not contain information elements. Otherwise, the
635     // IE writer would commit data in nrf_802154_ie_writer_tx_started_hook
636     // regardless if writing of IE elements is needed or not.
637     nrf_802154_ie_writer_reset();
638 
639     if (p_params->frame_props.dynamic_data_is_set)
640     {
641         // The dynamic data in the frame is already set. Pass.
642         return true;
643     }
644 
645     if ((p_frame[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK) == FRAME_TYPE_MULTIPURPOSE)
646     {
647         // Multipurpose frame parsing is not implemented, so skip IE writer.
648         return true;
649     }
650 
651     const uint8_t * p_mfr_addr;
652     uint8_t       * p_ie_header;
653 
654     nrf_802154_frame_parser_data_t frame_data;
655 
656     bool result = nrf_802154_frame_parser_data_init(p_frame,
657                                                     p_frame[PHR_OFFSET] + PHR_SIZE,
658                                                     PARSE_LEVEL_FULL,
659                                                     &frame_data);
660 
661     NRF_802154_ASSERT(result);
662     (void)result;
663 
664     p_ie_header = (uint8_t *)nrf_802154_frame_parser_ie_header_get(&frame_data);
665     p_mfr_addr  = nrf_802154_frame_parser_mfr_get(&frame_data);
666 
667     if (p_ie_header == NULL)
668     {
669         return true;
670     }
671 
672     nrf_802154_ie_writer_prepare(p_ie_header, p_mfr_addr);
673 
674     return true;
675 }
676 
nrf_802154_ie_writer_tx_started_hook(uint8_t * p_frame)677 bool nrf_802154_ie_writer_tx_started_hook(uint8_t * p_frame)
678 {
679     (void)p_frame;
680 
681     if (m_writer_state != IE_WRITER_PREPARE)
682     {
683         return true;
684     }
685 
686     bool written = false;
687 
688 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
689     nrf_802154_bsim_utils_core_hooks_adjustments_t adjustments;
690 
691     nrf_802154_bsim_utils_core_hooks_adjustments_get(&adjustments);
692 
693     m_csl_time_to_radio_address_us = adjustments.tx_started.time_to_radio_address_us;
694 #endif
695 
696     ie_writer_commit(&written);
697     nrf_802154_ie_writer_reset();
698 
699     if (written)
700     {
701         nrf_802154_tx_work_buffer_is_dynamic_data_updated_set();
702     }
703 
704     return true;
705 }
706 
nrf_802154_ie_writer_tx_ack_started_hook(uint8_t * p_ack)707 void nrf_802154_ie_writer_tx_ack_started_hook(uint8_t * p_ack)
708 {
709     (void)p_ack;
710 
711     if (m_writer_state != IE_WRITER_PREPARE)
712     {
713         return;
714     }
715 
716     bool written = false;
717 
718 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
719     nrf_802154_bsim_utils_core_hooks_adjustments_t adjustments;
720 
721     nrf_802154_bsim_utils_core_hooks_adjustments_get(&adjustments);
722 
723     m_csl_time_to_radio_address_us = adjustments.tx_ack_started.time_to_radio_address_us;
724 #endif
725 
726     ie_writer_commit(&written);
727     nrf_802154_ie_writer_reset();
728 
729     if (written)
730     {
731         nrf_802154_tx_work_buffer_is_dynamic_data_updated_set();
732     }
733 }
734 
735 #if NRF_802154_DELAYED_TRX_ENABLED
736 
nrf_802154_ie_writer_csl_period_set(uint16_t period)737 void nrf_802154_ie_writer_csl_period_set(uint16_t period)
738 {
739     m_csl_period = period;
740 }
741 
nrf_802154_ie_writer_csl_anchor_time_set(uint64_t anchor_time)742 void nrf_802154_ie_writer_csl_anchor_time_set(uint64_t anchor_time)
743 {
744     m_csl_anchor_time     = anchor_time;
745     m_csl_anchor_time_set = true;
746 }
747 
nrf_802154_ie_writer_cst_period_set(uint16_t period)748 void nrf_802154_ie_writer_cst_period_set(uint16_t period)
749 {
750     m_cst_period = period;
751 }
752 
nrf_802154_ie_writer_cst_anchor_time_set(uint64_t anchor_time)753 void nrf_802154_ie_writer_cst_anchor_time_set(uint64_t anchor_time)
754 {
755     m_cst_anchor_time = anchor_time;
756 }
757 
758 #endif // NRF_802154_DELAYED_TRX_ENABLED
759 
760 #endif // NRF_802154_IE_WRITER_ENABLED
761