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