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