1 /*
2 * Copyright (c) 2019 - 2023, 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 Long/Short Interframe Spacing handling procedure for the 802.15.4 driver.
38 *
39 */
40
41 #include "nrf_802154_ifs.h"
42
43 #include <assert.h>
44 #include <stdint.h>
45 #include <string.h>
46
47 #include "nrf_802154_pib.h"
48 #include "nrf_802154_request.h"
49 #include "mac_features/nrf_802154_frame_parser.h"
50 #include "nrf_802154_sl_timer.h"
51 #include "nrf_802154_sl_utils.h"
52 #include "nrf_802154_sl_atomics.h"
53
54 #if NRF_802154_IFS_ENABLED
55
56 /**
57 * @brief States of IFS handling procedure.
58 */
59 typedef enum
60 {
61 IFS_STATE_STOPPED = (1U << 0), ///< IFS procedure stopped.
62 IFS_STATE_ARMED = (1U << 1), ///< The @c m_timer is scheduled and waiting to fire.
63 IFS_STATE_FIRED = (1U << 2), ///< The @c m_timer fired and associated callback is in progress.
64 IFS_STATE_ABORTING = (1U << 3) ///< Abort request occurred. Resource release and notification are ongoing.
65 } ifs_state_t;
66
67 #define IFS_STATE_MASK ((ifs_state_t)(IFS_STATE_STOPPED | \
68 IFS_STATE_ARMED | \
69 IFS_STATE_FIRED | \
70 IFS_STATE_ABORTING))
71
72 static ifs_state_t m_state;
73
74 typedef struct
75 {
76 uint8_t * p_data;
77 nrf_802154_transmit_params_t params;
78 } ifs_operation_t;
79
80 static union
81 {
82 uint8_t sh[SHORT_ADDRESS_SIZE]; ///< Short address of the last frame transmitted.
83 uint8_t ext[EXTENDED_ADDRESS_SIZE]; ///< Extended address of the last frame transmitted.
84 } m_last_address;
85
86 static bool m_is_last_address_extended; ///< Whether the last transmitted frame had the extended address populated.
87 static uint64_t m_last_frame_timestamp; ///< Timestamp of the last transmitted frame (end of frame).
88 static uint8_t m_last_frame_length; ///< Length in bytes of the last transmitted frame.
89 static ifs_operation_t m_context; ///< Context passed to the timer.
90 static nrf_802154_sl_timer_t m_timer; ///< Interframe space timer.
91
92 /**
93 * Set state of IFS procedure.
94 *
95 * @param[in] expected_state Expected IFS state prior state transition.
96 * @param[in] new_state IFS procedure state to enter.
97 *
98 * @retval true Successfully set the new state.
99 * @retval false Failed to set the new state.
100 */
ifs_state_set(ifs_state_t expected_state,ifs_state_t new_state)101 static bool ifs_state_set(ifs_state_t expected_state, ifs_state_t new_state)
102 {
103 return nrf_802154_sl_atomic_cas_u8(&m_state, &expected_state, new_state);
104 }
105
ifs_state_is(ifs_state_t expected_state_mask)106 static bool ifs_state_is(ifs_state_t expected_state_mask)
107 {
108 return ((m_state & expected_state_mask) != 0);
109 }
110
ifs_tx_result_notify(bool result)111 static void ifs_tx_result_notify(bool result)
112 {
113 if (!result)
114 {
115 nrf_802154_transmit_done_metadata_t metadata = {};
116
117 metadata.frame_props = m_context.params.frame_props;
118 nrf_802154_notify_transmit_failed(m_context.p_data,
119 NRF_802154_TX_ERROR_TIMESLOT_DENIED,
120 &metadata);
121 }
122 }
123
callback_fired(nrf_802154_sl_timer_t * p_timer)124 static void callback_fired(nrf_802154_sl_timer_t * p_timer)
125 {
126 ifs_operation_t * p_ctx = (ifs_operation_t *)p_timer->user_data.p_pointer;
127
128 if (ifs_state_set(IFS_STATE_ARMED, IFS_STATE_FIRED))
129 {
130 nrf_802154_request_transmit(NRF_802154_TERM_NONE,
131 REQ_ORIG_IFS,
132 p_ctx->p_data,
133 &p_ctx->params,
134 ifs_tx_result_notify);
135
136 ifs_state_set(IFS_STATE_FIRED, IFS_STATE_STOPPED);
137 }
138 }
139
140 /**@brief Checks if the IFS is needed by comparing the addresses of the actual and the last frames. */
is_ifs_needed_by_address(const uint8_t * p_frame)141 static bool is_ifs_needed_by_address(const uint8_t * p_frame)
142 {
143 nrf_802154_frame_parser_data_t frame_data;
144 const uint8_t * addr;
145 bool is_extended;
146
147 bool result = nrf_802154_frame_parser_data_init(p_frame,
148 p_frame[PHR_OFFSET] + PHR_SIZE,
149 PARSE_LEVEL_ADDRESSING_END,
150 &frame_data);
151
152 if (result)
153 {
154 addr = nrf_802154_frame_parser_dst_addr_get(&frame_data);
155 is_extended = nrf_802154_frame_parser_dst_addr_is_extended(&frame_data);
156 }
157 else
158 {
159 addr = NULL;
160 is_extended = false;
161 }
162
163 if (!addr)
164 {
165 return true;
166 }
167
168 if (is_extended == m_is_last_address_extended)
169 {
170 uint8_t * last_addr = is_extended ? m_last_address.ext : m_last_address.sh;
171 size_t addr_len = is_extended ? EXTENDED_ADDRESS_SIZE : SHORT_ADDRESS_SIZE;
172
173 if (0 == memcmp(addr, last_addr, addr_len))
174 {
175 return true;
176 }
177 }
178
179 return false;
180 }
181
182 /**@brief Checks if the IFS is needed by measuring time between the actual and the last frames.
183 * Returns the needed ifs, 0 if none.
184 */
ifs_needed_by_time(uint64_t current_timestamp)185 static uint16_t ifs_needed_by_time(uint64_t current_timestamp)
186 {
187 if (!nrf_802154_sl_time64_is_in_future(m_last_frame_timestamp, current_timestamp))
188 {
189 /* Explicitly allow case where the timstamps are equal, i.e. we are running very fast. */
190 if (current_timestamp != m_last_frame_timestamp)
191 {
192 return 0;
193 }
194 }
195 uint16_t ifs_period;
196 uint64_t dt = current_timestamp - m_last_frame_timestamp;
197
198 if (m_last_frame_length > MAX_SIFS_FRAME_SIZE)
199 {
200 ifs_period = nrf_802154_pib_ifs_min_lifs_period_get();
201 }
202 else
203 {
204 ifs_period = nrf_802154_pib_ifs_min_sifs_period_get();
205 }
206
207 if (dt > ifs_period)
208 {
209 return 0;
210 }
211
212 return ifs_period;
213 }
214
nrf_802154_ifs_init(void)215 void nrf_802154_ifs_init(void)
216 {
217 m_state = IFS_STATE_STOPPED;
218 m_is_last_address_extended = false;
219 m_last_frame_timestamp = 0;
220 m_last_frame_length = 0;
221 m_context = (ifs_operation_t){ .p_data = NULL };
222
223 nrf_802154_sl_timer_init(&m_timer);
224 }
225
nrf_802154_ifs_deinit(void)226 void nrf_802154_ifs_deinit(void)
227 {
228 nrf_802154_sl_timer_deinit(&m_timer);
229 }
230
nrf_802154_ifs_pretransmission(uint8_t * p_frame,nrf_802154_transmit_params_t * p_params,nrf_802154_transmit_failed_notification_t notify_function)231 bool nrf_802154_ifs_pretransmission(
232 uint8_t * p_frame,
233 nrf_802154_transmit_params_t * p_params,
234 nrf_802154_transmit_failed_notification_t notify_function)
235 {
236 (void)notify_function;
237
238 nrf_802154_ifs_mode_t mode;
239
240 if (p_params->immediate)
241 {
242 return true;
243 }
244
245 mode = nrf_802154_pib_ifs_mode_get();
246
247 if (mode == NRF_802154_IFS_MODE_DISABLED)
248 {
249 // Functionality is disabled - skip the routine.
250 return true;
251 }
252
253 if (!m_last_frame_length)
254 {
255 // No frame was transmitted before - skip the routine.
256 return true;
257 }
258
259 if ((mode == NRF_802154_IFS_MODE_MATCHING_ADDRESSES) && !is_ifs_needed_by_address(p_frame))
260 {
261 return true;
262 }
263
264 uint64_t current_timestamp = nrf_802154_sl_timer_current_time_get();
265 uint32_t dt = ifs_needed_by_time(current_timestamp);
266
267 if (dt == 0)
268 {
269 return true;
270 }
271
272 if (!ifs_state_set(IFS_STATE_STOPPED, IFS_STATE_ARMED))
273 {
274 assert(false);
275 }
276 else
277 {
278 m_context.p_data = p_frame;
279 m_context.params.frame_props = p_params->frame_props;
280 m_context.params.cca = p_params->cca;
281 m_context.params.tx_power = p_params->tx_power;
282 m_context.params.immediate = true;
283 m_timer.trigger_time = m_last_frame_timestamp + dt;
284 m_timer.action_type = NRF_802154_SL_TIMER_ACTION_TYPE_CALLBACK;
285 m_timer.action.callback.callback = callback_fired;
286 m_timer.user_data.p_pointer = &m_context;
287
288 if (nrf_802154_sl_timer_add(&m_timer) != NRF_802154_SL_TIMER_RET_SUCCESS)
289 {
290 assert(false);
291 }
292 }
293
294 return false;
295 }
296
nrf_802154_ifs_transmitted_hook(const uint8_t * p_frame)297 void nrf_802154_ifs_transmitted_hook(const uint8_t * p_frame)
298 {
299 assert(p_frame[0] != 0U);
300
301 m_last_frame_timestamp = nrf_802154_sl_timer_current_time_get();
302
303 nrf_802154_frame_parser_data_t frame_data;
304 const uint8_t * addr;
305
306 bool result = nrf_802154_frame_parser_data_init(p_frame,
307 p_frame[PHR_OFFSET] + PHR_SIZE,
308 PARSE_LEVEL_ADDRESSING_END,
309 &frame_data);
310
311 if (result)
312 {
313 addr = nrf_802154_frame_parser_dst_addr_get(&frame_data);
314 m_is_last_address_extended = nrf_802154_frame_parser_dst_addr_is_extended(&frame_data);
315 }
316 else
317 {
318 addr = NULL;
319 m_is_last_address_extended = false;
320 }
321
322 if (!addr)
323 {
324 // If the transmitted frame has no address, we consider that enough time has passed so no IFS insertion will be needed.
325 m_last_frame_length = 0;
326 return;
327 }
328
329 if (m_is_last_address_extended)
330 {
331 memcpy(m_last_address.ext, addr, EXTENDED_ADDRESS_SIZE);
332 }
333 else
334 {
335 memcpy(m_last_address.sh, addr, SHORT_ADDRESS_SIZE);
336 }
337
338 m_last_frame_length = p_frame[0];
339 }
340
nrf_802154_ifs_abort(nrf_802154_term_t term_lvl,req_originator_t req_orig)341 bool nrf_802154_ifs_abort(nrf_802154_term_t term_lvl, req_originator_t req_orig)
342 {
343 bool result = true;
344
345 if (req_orig == REQ_ORIG_IFS)
346 {
347 // Ignore if self-request.
348 }
349 else
350 {
351 if (term_lvl >= NRF_802154_TERM_802154)
352 {
353 if (ifs_state_set(IFS_STATE_ARMED, IFS_STATE_ABORTING))
354 {
355 ifs_operation_t * p_op = (ifs_operation_t *)m_timer.user_data.p_pointer;
356
357 // The IFS was still waiting, so the transmission didn't occur
358 // at all. Notify with frame_props passed in nrf_802154_ifs_pretransmission hook
359 nrf_802154_transmit_done_metadata_t metadata = {};
360
361 metadata.frame_props = m_context.params.frame_props;
362 nrf_802154_notify_transmit_failed(p_op->p_data,
363 NRF_802154_TX_ERROR_ABORTED,
364 &metadata);
365 }
366
367 ifs_state_set(IFS_STATE_MASK, IFS_STATE_STOPPED);
368 }
369 else
370 {
371 result = !ifs_state_is(IFS_STATE_ARMED | IFS_STATE_FIRED);
372 }
373 }
374
375 return result;
376 }
377
378 #endif // NRF_802154_IFS_ENABLED
379