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