Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 75 additions & 4 deletions drivers/at86rf2xx/at86rf2xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "at86rf2xx_registers.h"
#include "at86rf2xx_internal.h"
#include "at86rf2xx_netdev.h"
#include "at86rf2xx_csma.h"

#define ENABLE_DEBUG (0)
#include "debug.h"
Expand All @@ -47,6 +48,10 @@ void at86rf2xx_setup(at86rf2xx_t *dev, const at86rf2xx_params_t *params)
/* radio state is P_ON when first powered-on */
dev->state = AT86RF2XX_STATE_P_ON;
dev->pending_tx = 0;

#ifdef AT86RF2XX_SOFTWARE_CSMA
dev->pending_irq = 0;
#endif
}

void at86rf2xx_reset(at86rf2xx_t *dev)
Expand Down Expand Up @@ -112,6 +117,13 @@ void at86rf2xx_reset(at86rf2xx_t *dev)
tmp |= (AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__OFF);
at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_0, tmp);

#ifdef AT86RF2XX_SOFTWARE_CSMA
at86rf2xx_set_option(dev, AT86RF2XX_OPT_CSMA, true);
at86rf2xx_set_max_retries(dev, 0);
at86rf2xx_set_csma_max_retries(dev, 0);
at86rf2xx_set_csma_backoff_exp(dev, 0, 0);
#endif

/* enable interrupts */
at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK,
AT86RF2XX_IRQ_STATUS_MASK__TRX_END);
Expand All @@ -131,31 +143,90 @@ size_t at86rf2xx_send(at86rf2xx_t *dev, uint8_t *data, size_t len)
DEBUG("[at86rf2xx] Error: data to send exceeds max packet size\n");
return 0;
}
at86rf2xx_tx_prepare(dev);
#ifdef AT86RF2XX_SOFTWARE_CSMA
at86rf2xx_csma_load(dev, data, len, 0);
at86rf2xx_csma_send(dev, 0);
#else
bool success = at86rf2xx_tx_prepare(dev);
if (!success) {
return 0;
}
at86rf2xx_tx_load(dev, data, len, 0);
at86rf2xx_tx_exec(dev);
#endif
return len;
}

void at86rf2xx_tx_prepare(at86rf2xx_t *dev)
bool at86rf2xx_tx_prepare(at86rf2xx_t *dev)
{
uint8_t state;

dev->pending_tx++;

/* make sure ongoing transmissions are finished */
#ifdef AT86RF2XX_SOFTWARE_CSMA
state = at86rf2xx_get_status(dev);
if (state == AT86RF2XX_STATE_BUSY_RX_AACK ||
state == AT86RF2XX_STATE_BUSY_TX_ARET) {
/* We should NOT send right now. This CSMA attempt should fail. */
dev->pending_tx--;
return false;
}

int irq_state = irq_disable();
if (dev->pending_irq != 0) {
irq_restore(irq_state);
dev->pending_tx--;
/*
* If there are pending interrupts, we don't want to send just yet.
* What if the interrupt is for a received packet?
* Instead, we just return false. When we handle the interrupt, we will
* check for a received packet, and then attempt this CSMA probe again.
*/
return false;
}
irq_restore(irq_state);

at86rf2xx_set_state(dev, AT86RF2XX_STATE_TX_ARET_ON);
if (at86rf2xx_get_status(dev) != AT86RF2XX_STATE_TX_ARET_ON) {
/*
* This means we transitioned to a busy state at the very last
* minute. Fail this CSMA attempt.
*/
dev->pending_tx--;
return false;
}

/*
* Check for pending interrupts one last time. After this, no receive
* interrupt can happen, because we are in TX_ARET_ON.
*/
irq_state = irq_disable();
if (dev->pending_irq != 0) {
irq_restore(irq_state);
at86rf2xx_set_state(dev, state);
dev->pending_tx--;
/*
* As above, we return false if there is a pending interrupt.
*/
return false;
}
irq_restore(irq_state);
#else
do {
state = at86rf2xx_get_status(dev);
} while (state == AT86RF2XX_STATE_BUSY_RX_AACK ||
state == AT86RF2XX_STATE_BUSY_TX_ARET);

at86rf2xx_set_state(dev, AT86RF2XX_STATE_TX_ARET_ON);
#endif

if (state != AT86RF2XX_STATE_TX_ARET_ON) {
dev->idle_state = state;
}

at86rf2xx_set_state(dev, AT86RF2XX_STATE_TX_ARET_ON);

dev->tx_frame_len = IEEE802154_FCS_LEN;
return true;
}

size_t at86rf2xx_tx_load(at86rf2xx_t *dev, uint8_t *data,
Expand Down
205 changes: 205 additions & 0 deletions drivers/at86rf2xx/at86rf2xx_csma.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* Copyright (C) 2018 University of California, Berkeley
*
* 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 drivers_at86rf2xx
* @{
*
* @file
* @brief CSMA Implementation for AT86RF2xx without Deaf Listening
*
* @author Sam Kumar <samkumar@cs.berkeley.edu>
*
* @}
*/

#include "net/gnrc.h"
#include "net/gnrc/netdev.h"
#include "net/netdev.h"
#include "random.h"
#include "xtimer.h"

#include "at86rf2xx.h"
#include "at86rf2xx_csma.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

#ifndef AT86RF2XX_SOFTWARE_CSMA_MIN_BACKOFF_EXP
#define AT86RF2XX_SOFTWARE_CSMA_MIN_BACKOFF_EXP 3
#endif

#ifndef AT86RF2XX_SOFTWARE_CSMA_MAX_BACKOFF_EXP
#define AT86RF2XX_SOFTWARE_CSMA_MAX_BACKOFF_EXP 5
#endif

#ifndef AT86RF2XX_SOFTWARE_CSMA_MAX_TRIES
#define AT86RF2XX_SOFTWARE_CSMA_MAX_TRIES 5
#endif

#ifndef AT86RF2XX_SOFTWARE_CSMA_BACKOFF_MICROS
#define AT86RF2XX_SOFTWARE_CSMA_BACKOFF_MICROS 320
#endif

#ifndef AT86RF2XX_SOFTWARE_LINK_MAX_TRIES
#define AT86RF2XX_SOFTWARE_LINK_MAX_TRIES 5
#endif

#ifndef AT86RF2XX_SOFTWARE_LINK_RETRY_DELAY_MICROS
#define AT86RF2XX_SOFTWARE_LINK_RETRY_DELAY_MICROS 10000
#endif

#ifdef AT86RF2XX_SOFTWARE_CSMA

bool at86rf2xx_csma_probe_and_send_if_pending(at86rf2xx_t* dev) {
if (dev->csma_should_probe_and_send) {
dev->csma_should_probe_and_send = false;


bool success = at86rf2xx_tx_prepare(dev);
if (!success) {
dev->csma_should_probe_and_send = true;
return false;
}
at86rf2xx_tx_load(dev, dev->csma_buf, dev->csma_buf_len, 0);
at86rf2xx_tx_exec(dev);
}

return true;
}

bool at86rf2xx_csma_clear_pending(at86rf2xx_t* dev) {
bool pending = dev->csma_should_probe_and_send;
dev->csma_should_probe_and_send = false;
return pending;
}

static void try_send_packet(void* arg) {
at86rf2xx_t* dev = arg;
dev->csma_should_probe_and_send = true;

int state = irq_disable();
dev->pending_irq++;
irq_restore(state);

/*
* arg is actually of the type at86rf2xx_t*, but at86rf2xx_t actually
* "inherits" netdev_t, so this is OK.
*/
netdev_t* ndev = arg;

if (ndev->event_callback) {
ndev->event_callback(ndev, NETDEV_EVENT_ISR);
}
}

static void csma_backoff_and_send(at86rf2xx_t* dev, uint32_t additional_delay_micros) {
uint8_t csma_try_index = AT86RF2XX_SOFTWARE_CSMA_MAX_TRIES - dev->csma_num_tries_left;
uint8_t be = AT86RF2XX_SOFTWARE_CSMA_MIN_BACKOFF_EXP + csma_try_index;
if (be > AT86RF2XX_SOFTWARE_CSMA_MAX_BACKOFF_EXP) {
be = AT86RF2XX_SOFTWARE_CSMA_MAX_BACKOFF_EXP;
}
uint32_t max_possible_csma_backoff = ((uint32_t) AT86RF2XX_SOFTWARE_CSMA_BACKOFF_MICROS) << be;
if (max_possible_csma_backoff == 0 && additional_delay_micros == 0) {
try_send_packet(dev);
} else {
uint32_t csma_backoff_micros = random_uint32_range(0, max_possible_csma_backoff);
uint32_t total_delay = csma_backoff_micros + additional_delay_micros;
xtimer_set(&dev->csma_backoff_timer, total_delay);
}
}

int at86rf2xx_csma_load(at86rf2xx_t* dev, uint8_t* frame, size_t frame_len, size_t offset) {
assert(!dev->csma_in_progress);

if (offset + frame_len > AT86RF2XX_MAX_PKT_LENGTH) {
return -EOVERFLOW;
}

memcpy(&dev->csma_buf[offset], frame, frame_len);
dev->csma_buf_len = offset + frame_len;
return 0;
}

void perform_link_try_with_csma(at86rf2xx_t* dev, uint32_t additional_delay_micros) {
assert(!dev->csma_in_progress);

dev->csma_num_tries_left = AT86RF2XX_SOFTWARE_CSMA_MAX_TRIES;
dev->csma_in_progress = true;
dev->csma_should_probe_and_send = false;

csma_backoff_and_send(dev, additional_delay_micros);
}

void at86rf2xx_csma_send(at86rf2xx_t* dev, uint8_t num_link_tries) {
assert(!dev->link_tx_in_progress);
if (num_link_tries == 0) {
num_link_tries = AT86RF2XX_SOFTWARE_LINK_MAX_TRIES;
}

dev->csma_backoff_timer.arg = dev;
dev->link_tx_num_tries_left = num_link_tries;
dev->link_tx_in_progress = true;

perform_link_try_with_csma(dev, 0);
}

void at86rf2xx_csma_csma_try_succeeded(at86rf2xx_t* dev) {
assert(!dev->csma_should_probe_and_send);
dev->csma_in_progress = false;
dev->csma_num_tries_left = 0;
}

bool at86rf2xx_csma_csma_try_failed(at86rf2xx_t* dev) {
assert(!dev->csma_should_probe_and_send);
dev->csma_num_tries_left--;
if (dev->csma_num_tries_left == 0) {
dev->csma_in_progress = false;

// Don't try again; inform upper layer of channel access failure.
return false;
}

// Perform the next CSMA retry after a backoff.
csma_backoff_and_send(dev, 0);
return true;
}

void at86rf2xx_csma_link_tx_try_succeeded(at86rf2xx_t* dev) {
assert(!dev->csma_in_progress);
assert(dev->link_tx_in_progress);
dev->link_tx_in_progress = false;
dev->link_tx_num_tries_left = 0;
}

bool at86rf2xx_csma_link_tx_try_failed(at86rf2xx_t* dev) {
assert(!dev->csma_in_progress);
assert(dev->link_tx_in_progress);
dev->link_tx_num_tries_left--;
if (dev->link_tx_num_tries_left == 0) {
dev->link_tx_in_progress = false;

// Don't try again; inform upper layer that transmission failed.
return false;
}

// Perform the next link retransmission.
perform_link_try_with_csma(dev, AT86RF2XX_SOFTWARE_LINK_RETRY_DELAY_MICROS);
return true;
}

void at86rf2xx_csma_init(at86rf2xx_t* dev) {
memset(&dev->csma_backoff_timer, 0x00, sizeof(dev->csma_backoff_timer));
dev->csma_backoff_timer.callback = try_send_packet;
dev->csma_in_progress = false;
dev->csma_num_tries_left = 0;
dev->link_tx_in_progress = false;
dev->link_tx_num_tries_left = 0;
}

#endif
Loading