mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
251 lines
7.1 KiB
C
251 lines
7.1 KiB
C
/*
|
|
* Copyright (C) 2015 INRIA
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @{
|
|
* @ingroup net_csma_sender
|
|
* @file
|
|
* @brief Implementation of the CSMA/CA helper
|
|
*
|
|
* @author Kévin Roussel <Kevin.Roussel@inria.fr>
|
|
* @}
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "xtimer.h"
|
|
#include "random.h"
|
|
#include "net/netdev2.h"
|
|
#include "net/netopt.h"
|
|
|
|
#include "net/csma_sender.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
#if ENABLE_DEBUG
|
|
/* For PRIu16 etc. */
|
|
#include <inttypes.h>
|
|
#endif
|
|
|
|
|
|
/** @brief Current value for mac_min_be parameter */
|
|
static uint8_t mac_min_be = CSMA_SENDER_MIN_BE_DEFAULT;
|
|
|
|
/** @brief Current value for mac_max_be parameter */
|
|
static uint8_t mac_max_be = CSMA_SENDER_MAX_BE_DEFAULT;
|
|
|
|
/** @brief Current value for mac_max_csma_backoffs parameter */
|
|
static uint8_t mac_max_csma_backoffs = CSMA_SENDER_MAX_BACKOFFS_DEFAULT;
|
|
|
|
|
|
/*--------------------- "INTERNAL" UTILITY FUNCTIONS ---------------------*/
|
|
|
|
/**
|
|
* @brief choose an adequate random backoff period in microseconds,
|
|
* from the given Backoff Exponent
|
|
*
|
|
* @param[in] be Backoff Exponent for the computation of period
|
|
*
|
|
* @return An adequate random backoff exponent in microseconds
|
|
*/
|
|
static inline uint32_t choose_backoff_period(int be)
|
|
{
|
|
if (be < mac_min_be) {
|
|
be = mac_min_be;
|
|
}
|
|
if (be > mac_max_be) {
|
|
be = mac_max_be;
|
|
}
|
|
uint32_t max_backoff = ((1 << be) - 1) * CSMA_SENDER_BACKOFF_PERIOD_UNIT;
|
|
|
|
uint32_t period = random_uint32() % max_backoff;
|
|
if (period < CSMA_SENDER_BACKOFF_PERIOD_UNIT) {
|
|
period = CSMA_SENDER_BACKOFF_PERIOD_UNIT;
|
|
}
|
|
|
|
return period;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Perform a CCA and send the given packet if medium is available
|
|
*
|
|
* @param[in] device netdev device, needs to be already initialized
|
|
* @param[in] vector pointer to the data
|
|
* @param[in] count number of elements in @p vector
|
|
*
|
|
* @return the return value of device driver's
|
|
* netdev2_driver_t::send() function if medium was
|
|
* available
|
|
* @return -ECANCELED if an internal driver error occurred
|
|
* @return -EBUSY if radio medium was not available
|
|
* to send the given data
|
|
*/
|
|
static int send_if_cca(netdev2_t *device, struct iovec *vector, unsigned count)
|
|
{
|
|
netopt_enable_t hwfeat;
|
|
|
|
/* perform a CCA */
|
|
DEBUG("csma: Checking radio medium availability...\n");
|
|
int res = device->driver->get(device,
|
|
NETOPT_IS_CHANNEL_CLR,
|
|
(void *) &hwfeat,
|
|
sizeof(netopt_enable_t));
|
|
if (res < 0) {
|
|
/* normally impossible: we got a big internal problem! */
|
|
DEBUG("csma: !!! DEVICE DRIVER FAILURE! TRANSMISSION ABORTED!\n");
|
|
return -ECANCELED;
|
|
}
|
|
|
|
/* if medium is clear, send the packet and return */
|
|
if (hwfeat == NETOPT_ENABLE) {
|
|
DEBUG("csma: Radio medium available: sending packet.\n");
|
|
return device->driver->send(device, vector, count);
|
|
}
|
|
|
|
/* if we arrive here, medium was not available for transmission */
|
|
DEBUG("csma: Radio medium busy.\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
/*------------------------- "EXPORTED" FUNCTIONS -------------------------*/
|
|
|
|
void csma_sender_set_min_be(uint8_t val)
|
|
{
|
|
mac_min_be = val;
|
|
}
|
|
|
|
void csma_sender_set_max_be(uint8_t val)
|
|
{
|
|
mac_max_be = val;
|
|
}
|
|
|
|
void csma_sender_set_max_backoffs(uint8_t val)
|
|
{
|
|
mac_max_csma_backoffs = val;
|
|
}
|
|
|
|
|
|
int csma_sender_csma_ca_send(netdev2_t *dev, struct iovec *vector,
|
|
unsigned count)
|
|
{
|
|
netopt_enable_t hwfeat;
|
|
|
|
/* Does the transceiver do automatic CSMA/CA when sending? */
|
|
int res = dev->driver->get(dev,
|
|
NETOPT_CSMA,
|
|
(void *) &hwfeat,
|
|
sizeof(netopt_enable_t));
|
|
bool ok = false;
|
|
|
|
switch (res) {
|
|
case -ENODEV:
|
|
/* invalid device pointer given */
|
|
return -ENODEV;
|
|
case -ENOTSUP:
|
|
/* device doesn't make auto-CSMA/CA */
|
|
break;
|
|
case -EOVERFLOW: /* (normally impossible...*/
|
|
case -ECANCELED:
|
|
DEBUG("csma: !!! DEVICE DRIVER FAILURE! TRANSMISSION ABORTED!\n");
|
|
/* internal driver error! */
|
|
return -ECANCELED;
|
|
default:
|
|
ok = (hwfeat == NETOPT_ENABLE);
|
|
}
|
|
|
|
if (ok) {
|
|
/* device does CSMA/CA all by itself: let it do its job */
|
|
DEBUG("csma: Network device does hardware CSMA/CA\n");
|
|
return dev->driver->send(dev, vector, count);
|
|
}
|
|
|
|
/* if we arrive here, then we must perform the CSMA/CA procedure
|
|
ourselves by software */
|
|
random_init(xtimer_now());
|
|
DEBUG("csma: Starting software CSMA/CA....\n");
|
|
|
|
int nb = 0, be = mac_min_be;
|
|
|
|
while (nb <= mac_max_csma_backoffs) {
|
|
/* delay for an adequate random backoff period */
|
|
uint32_t bp = choose_backoff_period(be);
|
|
xtimer_usleep(bp);
|
|
|
|
/* try to send after a CCA */
|
|
res = send_if_cca(dev, vector, count);
|
|
if (res >= 0) {
|
|
/* TX done */
|
|
return res;
|
|
}
|
|
else if (res != -EBUSY) {
|
|
/* something has gone wrong, return the error code */
|
|
return res;
|
|
}
|
|
|
|
/* medium is busy: increment CSMA counters */
|
|
DEBUG("csma: Radio medium busy.\n");
|
|
be++;
|
|
if (be > mac_max_be) {
|
|
be = mac_max_be;
|
|
}
|
|
nb++;
|
|
/* ... and try again if we have no exceeded the retry limit */
|
|
}
|
|
|
|
/* if we arrive here, medium was never available for transmission */
|
|
DEBUG("csma: Software CSMA/CA failure: medium never available.\n");
|
|
return -EBUSY;
|
|
}
|
|
|
|
|
|
int csma_sender_cca_send(netdev2_t *dev, struct iovec *vector, unsigned count)
|
|
{
|
|
netopt_enable_t hwfeat;
|
|
|
|
/* Does the transceiver do automatic CCA before sending? */
|
|
int res = dev->driver->get(dev,
|
|
NETOPT_AUTOCCA,
|
|
(void *) &hwfeat,
|
|
sizeof(netopt_enable_t));
|
|
bool ok = false;
|
|
|
|
switch (res) {
|
|
case -ENODEV:
|
|
/* invalid device pointer given */
|
|
return -ENODEV;
|
|
case -ENOTSUP:
|
|
/* device doesn't make auto-CCA */
|
|
break;
|
|
case -EOVERFLOW: /* (normally impossible...*/
|
|
case -ECANCELED:
|
|
/* internal driver error! */
|
|
DEBUG("csma: !!! DEVICE DRIVER FAILURE! TRANSMISSION ABORTED!\n");
|
|
return -ECANCELED;
|
|
default:
|
|
ok = (hwfeat == NETOPT_ENABLE);
|
|
}
|
|
|
|
if (ok) {
|
|
/* device does auto-CCA: let him do its job */
|
|
DEBUG("csma: Network device does auto-CCA checking.\n");
|
|
return dev->driver->send(dev, vector, count);
|
|
}
|
|
|
|
/* if we arrive here, we must do CCA ourselves to see if radio medium
|
|
is clear before sending */
|
|
res = send_if_cca(dev, vector, count);
|
|
if (res == -EBUSY) {
|
|
DEBUG("csma: Transmission cancelled!\n");
|
|
}
|
|
|
|
return res;
|
|
}
|