Loading...
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 | /* 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.
*
*/
#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.
/**
* @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)
{
uint8_t backoff_periods = rand() % (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
|