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(¶m))
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