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