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 ACK timeout procedure for the 802.15.4 driver.
38  *
39  */
40 
41 #define NRF_802154_MODULE_ID NRF_802154_DRV_MODULE_ID_ACK_TIMEOUT
42 
43 #include "nrf_802154_ack_timeout.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_notification.h"
51 #include "nrf_802154_procedures_duration.h"
52 #include "nrf_802154_request.h"
53 #include "nrf_802154_sl_timer.h"
54 
55 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
56 #include "nrf_802154_bsim_utils.h"
57 #endif
58 
59 #if NRF_802154_ACK_TIMEOUT_ENABLED
60 
61 #define RETRY_DELAY     500     ///< Procedure is delayed by this time if it cannot be performed at the moment [us].
62 #define MAX_RETRY_DELAY 1000000 ///< Maximum allowed delay of procedure retry [us].
63 
64 static void timeout_timer_retry(void);
65 
66 static uint32_t              m_timeout = NRF_802154_PRECISE_ACK_TIMEOUT_DEFAULT_TIMEOUT; ///< ACK timeout in us.
67 static nrf_802154_sl_timer_t m_timer;                                                    ///< Timer used to notify when the ACK frame is not received for too long.
68 static uint32_t              m_dt;                                                       ///< Length [us] of the originally scheduled timeout plus any retry extensions used.
69 static volatile bool         m_procedure_is_active;
70 static uint8_t             * mp_frame;
71 
timeout_timer_fired(nrf_802154_sl_timer_t * p_timer)72 static void timeout_timer_fired(nrf_802154_sl_timer_t * p_timer)
73 {
74     nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
75 
76     (void)p_timer;
77 
78     if (m_procedure_is_active)
79     {
80         nrf_802154_ack_timeout_handle_params_t param = {0};
81 
82         param.p_frame = mp_frame;
83 
84         if (nrf_802154_request_ack_timeout_handle(&param))
85         {
86             m_procedure_is_active = false;
87         }
88         else
89         {
90             timeout_timer_retry();
91         }
92     }
93 
94     nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
95 }
96 
timeout_timer_retry(void)97 static void timeout_timer_retry(void)
98 {
99     m_dt += RETRY_DELAY;
100     NRF_802154_ASSERT(m_dt <= MAX_RETRY_DELAY);
101 
102     m_timer.trigger_time += RETRY_DELAY;
103 
104     nrf_802154_sl_timer_ret_t ret;
105 
106     ret = nrf_802154_sl_timer_add(&m_timer);
107     NRF_802154_ASSERT(ret == NRF_802154_SL_TIMER_RET_SUCCESS);
108     (void)ret;
109 }
110 
timeout_timer_start(void)111 static void timeout_timer_start(void)
112 {
113     uint64_t                  now = nrf_802154_sl_timer_current_time_get();
114     nrf_802154_sl_timer_ret_t ret;
115 
116     m_dt = m_timeout + IMM_ACK_DURATION + nrf_802154_frame_duration_get(mp_frame[0], false, true);
117 #if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
118     /**
119      * In simulation, this function is executed immediately after setting up Tx ramp-up instead of
120      * handler of RADIO.ADDRESS event. The timeout must be increased with simulation-specific
121      * adjustments calculated earlier.
122      */
123     nrf_802154_bsim_utils_core_hooks_adjustments_t adjustments;
124 
125     nrf_802154_bsim_utils_core_hooks_adjustments_get(&adjustments);
126 
127     m_dt += adjustments.tx_started.time_to_radio_address_us;
128 #endif
129 
130     m_timer.action_type              = NRF_802154_SL_TIMER_ACTION_TYPE_CALLBACK;
131     m_timer.action.callback.callback = timeout_timer_fired;
132     m_timer.trigger_time             = now + m_dt;
133 
134     m_procedure_is_active = true;
135 
136     ret = nrf_802154_sl_timer_add(&m_timer);
137     NRF_802154_ASSERT(ret == NRF_802154_SL_TIMER_RET_SUCCESS);
138     (void)ret;
139 }
140 
timeout_timer_stop(void)141 static void timeout_timer_stop(void)
142 {
143     m_procedure_is_active = false;
144 
145     // To make sure `timeout_timer_fired()` detects that procedure is being stopped if it preempts
146     // this function.
147     __DMB();
148 
149     (void)nrf_802154_sl_timer_remove(&m_timer);
150 }
151 
nrf_802154_ack_timeout_init(void)152 void nrf_802154_ack_timeout_init(void)
153 {
154     m_timeout             = NRF_802154_PRECISE_ACK_TIMEOUT_DEFAULT_TIMEOUT;
155     m_dt                  = 0;
156     m_procedure_is_active = false;
157     mp_frame              = NULL;
158 
159     nrf_802154_sl_timer_init(&m_timer);
160 }
161 
nrf_802154_ack_timeout_deinit(void)162 void nrf_802154_ack_timeout_deinit(void)
163 {
164     nrf_802154_sl_timer_deinit(&m_timer);
165 }
166 
nrf_802154_ack_timeout_time_set(uint32_t time)167 void nrf_802154_ack_timeout_time_set(uint32_t time)
168 {
169     m_timeout = time;
170 }
171 
nrf_802154_ack_timeout_tx_started_hook(uint8_t * p_frame)172 bool nrf_802154_ack_timeout_tx_started_hook(uint8_t * p_frame)
173 {
174     mp_frame = p_frame;
175     timeout_timer_start();
176 
177     return true;
178 }
179 
nrf_802154_ack_timeout_abort(nrf_802154_term_t term_lvl,req_originator_t req_orig)180 bool nrf_802154_ack_timeout_abort(nrf_802154_term_t term_lvl, req_originator_t req_orig)
181 {
182     bool result;
183 
184     if (!m_procedure_is_active || req_orig == REQ_ORIG_ACK_TIMEOUT)
185     {
186         // Ignore if procedure is not running or self-request.
187         result = true;
188     }
189     else if (term_lvl >= NRF_802154_TERM_802154)
190     {
191         // Stop procedure only if termination level is high enough.
192         timeout_timer_stop();
193 
194         result = true;
195     }
196     else
197     {
198         result = false;
199     }
200 
201     return result;
202 }
203 
nrf_802154_ack_timeout_transmitted_hook(const uint8_t * p_frame)204 void nrf_802154_ack_timeout_transmitted_hook(const uint8_t * p_frame)
205 {
206     NRF_802154_ASSERT((p_frame == mp_frame) || (!m_procedure_is_active));
207 
208     timeout_timer_stop();
209 }
210 
nrf_802154_ack_timeout_rx_ack_started_hook(void)211 void nrf_802154_ack_timeout_rx_ack_started_hook(void)
212 {
213     NRF_802154_ASSERT(m_procedure_is_active);
214 
215     timeout_timer_stop();
216 }
217 
nrf_802154_ack_timeout_tx_failed_hook(uint8_t * p_frame,nrf_802154_tx_error_t error)218 bool nrf_802154_ack_timeout_tx_failed_hook(uint8_t * p_frame, nrf_802154_tx_error_t error)
219 {
220     (void)error;
221     NRF_802154_ASSERT((p_frame == mp_frame) || (!m_procedure_is_active));
222 
223     timeout_timer_stop();
224 
225     return true;
226 }
227 
228 #endif // NRF_802154_ACK_TIMEOUT_ENABLED
229