1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/link_layer/csma_sender/csma_sender.c
2016-06-05 13:57:26 +02:00

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;
}