forked from openthread/openthread
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nrf_802154_csma_ca.c
262 lines (214 loc) · 7.8 KB
/
nrf_802154_csma_ca.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/* Copyright (c) 2017 - 2018, Nordic Semiconductor ASA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/**
* @file
* This file implements CSMA-CA procedure for the 802.15.4 driver.
*
*/
#define _POSIX_C_SOURCE // make sure that POSIX rand_r() is declared
#include "nrf_802154_csma_ca.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "nrf_802154_config.h"
#include "nrf_802154_const.h"
#include "nrf_802154_debug.h"
#include "nrf_802154_notification.h"
#include "nrf_802154_request.h"
#include "timer_scheduler/nrf_802154_timer_sched.h"
#if NRF_802154_CSMA_CA_ENABLED
static uint8_t m_nb; ///< The number of times the CSMA-CA algorithm was required to back off while attempting the current transmission.
static uint8_t m_be; ///< Backoff exponent, which is related to how many backoff periods a device shall wait before attempting to assess a channel.
static const uint8_t * mp_psdu; ///< Pointer to PSDU of the frame being transmitted.
static nrf_802154_timer_t m_timer; ///< Timer used to back off during CSMA-CA procedure.
static bool m_is_running; ///< Indicates if CSMA-CA procedure is running.
unsigned int nrf_802154_csma_ca_random_seed = 0; ///< Random seed used for backoff timeout calculation. NOTE: needs to be externally initialized!
/**
* @brief Perform appropriate actions for busy channel conditions.
*
* According to CSMA-CA description in 802.15.4 specification, when channel is busy NB and BE shall
* be incremented and the device shall wait random delay before next CCA procedure. If NB reaches
* macMaxCsmaBackoffs procedure fails.
*
* @retval true Procedure failed and TX failure should be notified to the next higher layer.
* @retval false Procedure is still ongoing and TX failure should be handled internally.
*/
static bool channel_busy(void);
/**
* @brief Check if CSMA-CA is ongoing.
*
* @retval true CSMA-CA is running.
* @retval false CSMA-CA is not running currently.
*/
static bool procedure_is_running(void)
{
return m_is_running;
}
/**
* @brief Stop CSMA-CA procedure.
*/
static void procedure_stop(void)
{
m_is_running = false;
}
/**
* Notify MAC layer that channel is busy if tx request failed and there are no retries left.
*
* @param[in] result Result of TX request.
*/
static void notify_busy_channel(bool result)
{
if (!result && (m_nb >= (NRF_802154_CSMA_CA_MAX_CSMA_BACKOFFS - 1)))
{
nrf_802154_notify_transmit_failed(mp_psdu, NRF_802154_TX_ERROR_BUSY_CHANNEL);
}
}
/**
* @brief Perform CCA procedure followed by frame transmission.
*
* If transmission is requested, CSMA-CA module waits for notification from the FSM module.
* If transmission request fails, CSMA-CA module performs procedure for busy channel condition
* @sa channel_busy().
*
* @param[in] p_context Unused variable passed from the Timer Scheduler module.
*/
static void frame_transmit(void * p_context)
{
(void)p_context;
nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_FRAME_TRANSMIT);
if (procedure_is_running())
{
if (!nrf_802154_request_transmit(NRF_802154_TERM_NONE,
REQ_ORIG_CSMA_CA,
mp_psdu,
true,
true,
notify_busy_channel))
{
(void)channel_busy();
}
}
nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_FRAME_TRANSMIT);
}
/**
* @brief Delay CCA procedure for random (2^BE - 1) unit backoff periods.
*/
static void random_backoff_start(void)
{
assert(nrf_802154_csma_ca_random_seed != 0);
uint8_t backoff_periods = rand_r(&nrf_802154_csma_ca_random_seed) % (1 << m_be);
m_timer.callback = frame_transmit;
m_timer.p_context = NULL;
m_timer.t0 = nrf_802154_timer_sched_time_get();
m_timer.dt = backoff_periods * UNIT_BACKOFF_PERIOD;
nrf_802154_timer_sched_add(&m_timer, false);
}
static bool channel_busy(void)
{
bool result = true;
if (procedure_is_running())
{
nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_CHANNEL_BUSY);
m_nb++;
if (m_be < NRF_802154_CSMA_CA_MAX_BE)
{
m_be++;
}
if (m_nb < NRF_802154_CSMA_CA_MAX_CSMA_BACKOFFS)
{
random_backoff_start();
result = false;
}
else
{
procedure_stop();
}
nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_CHANNEL_BUSY);
}
return result;
}
void nrf_802154_csma_ca_start(const uint8_t * p_data)
{
assert(!procedure_is_running());
mp_psdu = p_data;
m_nb = 0;
m_be = NRF_802154_CSMA_CA_MIN_BE;
m_is_running = true;
random_backoff_start();
}
bool nrf_802154_csma_ca_abort(nrf_802154_term_t term_lvl, req_originator_t req_orig)
{
bool result = false;
// Stop CSMA-CA only if request by the core or the higher layer.
if ((req_orig != REQ_ORIG_CORE) && (req_orig != REQ_ORIG_HIGHER_LAYER))
{
return true;
}
nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_ABORT);
if (term_lvl >= NRF_802154_TERM_802154)
{
// Stop CSMA-CA if termination level is high enough.
nrf_802154_timer_sched_remove(&m_timer);
procedure_stop();
result = true;
}
else if (!procedure_is_running())
{
// Return success in case procedure is already stopped.
result = true;
}
nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_ABORT);
return result;
}
bool nrf_802154_csma_ca_tx_failed_hook(const uint8_t * p_frame, nrf_802154_tx_error_t error)
{
(void)error;
bool result = true;
if (p_frame == mp_psdu)
{
nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_TX_FAILED);
result = channel_busy();
nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_TX_FAILED);
}
return result;
}
bool nrf_802154_csma_ca_tx_started_hook(const uint8_t * p_frame)
{
if (p_frame == mp_psdu)
{
nrf_802154_log(EVENT_TRACE_ENTER, FUNCTION_CSMA_TX_STARTED);
assert(!nrf_802154_timer_sched_is_running(&m_timer));
procedure_stop();
nrf_802154_log(EVENT_TRACE_EXIT, FUNCTION_CSMA_TX_STARTED);
}
return true;
}
#endif // NRF_802154_CSMA_CA_ENABLED