1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/cc110x/cc1100-csmaca-mac.c

217 lines
7.5 KiB
C
Raw Normal View History

/******************************************************************************
Copyright 2008, Freie Universitaet Berlin (FUB). All rights reserved.
These sources were developed at the Freie Universitaet Berlin, Computer Systems
and Telematics group (http://cst.mi.fu-berlin.de).
-------------------------------------------------------------------------------
This file is part of FeuerWare.
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
FeuerWare is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see http://www.gnu.org/licenses/ .
--------------------------------------------------------------------------------
For further information and questions please use the web site
http://scatterweb.mi.fu-berlin.de
and the mailinglist (subscription via web site)
scatterweb@lists.spline.inf.fu-berlin.de
*******************************************************************************/
/**
* @file
* @ingroup dev_cc110x
* @brief ScatterWeb MSB-A2 mac-layer
*
* @author Freie Universität Berlin, Computer Systems & Telematics, FeuerWhere project
* @author Thomas Hillebrandt <hillebra@inf.fu-berlin.de>
* @author Heiko Will <hwill@inf.fu-berlin.de>
* @version $Revision: 2128 $
*
* @note $Id: cc1100-csmaca-mac.c 2128 2010-05-12 12:07:59Z hillebra $
*/
#include <stdio.h>
//#include <stdlib.h>
#include <string.h>
#include "cc1100.h"
#include "cc1100_phy.h"
#include "cc1100-csmaca-mac.h"
#include "protocol-multiplex.h"
#include "hwtimer.h"
/*---------------------------------------------------------------------------*/
#define COLLISION_STATE_INITIAL 1
#define COLLISION_STATE_MEASURE 2
#define COLLISION_STATE_KEEP 3
static uint32_t send_csmaca_calls = 0;
static uint32_t send_csmaca_calls_cs_timeout = 0;
static int collision_count;
static double collisions_per_sec = 0;
static int collision_state = COLLISION_STATE_INITIAL;
static uint64_t collision_measurement_start = 0;
volatile static int cs_hwtimer_id = -1;
volatile static int cs_timeout_flag = 0;
/*---------------------------------------------------------------------------*/
static void cs_timeout_cb(void* ptr)
{
cs_timeout_flag = 1;
}
/*---------------------------------------------------------------------------*/
int cc1100_send_csmaca(radio_address_t address, protocol_t protocol, int priority, char *payload, int payload_len)
{
uint16_t min_window_size;
uint16_t max_window_size;
uint16_t difs;
uint16_t slottime;
switch (priority)
{
case PRIORITY_ALARM:
min_window_size = PRIO_ALARM_MIN_WINDOW_SIZE;
max_window_size = PRIO_ALARM_MAX_WINDOW_SIZE;
difs = PRIO_ALARM_DIFS;
slottime = PRIO_ALARM_SLOTTIME;
break;
case PRIORITY_WARNING:
min_window_size = PRIO_WARN_MIN_WINDOW_SIZE;
max_window_size = PRIO_WARN_MAX_WINDOW_SIZE;
difs = PRIO_WARN_DIFS;
slottime = PRIO_WARN_SLOTTIME;
break;
default:
min_window_size = PRIO_DATA_MIN_WINDOW_SIZE;
max_window_size = PRIO_DATA_MAX_WINDOW_SIZE;
difs = PRIO_DATA_DIFS;
slottime = PRIO_DATA_SLOTTIME;
}
// Calculate collisions per second
if (collision_state == COLLISION_STATE_INITIAL) {
collision_measurement_start = swtimer_now();
collision_count = 0;
collisions_per_sec = 0;
collision_state = COLLISION_STATE_MEASURE;
} else if (collision_state == COLLISION_STATE_MEASURE) {
uint64_t timespan = swtimer_now() - collision_measurement_start;
if (timespan > 1000000) {
collisions_per_sec = (collision_count * 1000000) / (double) timespan;
if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) {
collision_measurement_start = swtimer_now();
collision_state = COLLISION_STATE_KEEP;
} else if (collisions_per_sec > 2.2) {
collision_measurement_start = swtimer_now();
collision_state = COLLISION_STATE_KEEP;
} else {
collision_state = COLLISION_STATE_INITIAL;
}
}
} else if (collision_state == COLLISION_STATE_KEEP) {
uint64_t timespan = swtimer_now() - collision_measurement_start;
if (timespan > 5000000) {
collision_state = COLLISION_STATE_INITIAL;
}
}
// Adjust initial window size according to collision rate
if (collisions_per_sec > 0.5 && collisions_per_sec <= 2.2) {
min_window_size *= 2;
} else if (collisions_per_sec > 2.2) {
min_window_size *= 4;
}
uint16_t windowSize = min_window_size; // Start with window size of PRIO_XXX_MIN_WINDOW_SIZE
uint16_t backoff = 0; // Backoff between 1 and windowSize
uint32_t total; // Holds the total wait time before send try
uint32_t cs_timeout; // Current carrier sense timeout value
if (protocol == 0)
{
return RADIO_INVALID_PARAM; // Not allowed, protocol id must be greater zero
}
cc1100_phy_mutex_lock(); // Lock radio for exclusive access
// Get carrier sense timeout based on overall error rate till now
send_csmaca_calls++;
int fail_percentage = (send_csmaca_calls_cs_timeout * 100) / send_csmaca_calls;
if (fail_percentage == 0) fail_percentage = 1;
cs_timeout = CARRIER_SENSE_TIMEOUT / fail_percentage;
if (cs_timeout < CARRIER_SENSE_TIMEOUT_MIN) cs_timeout = CARRIER_SENSE_TIMEOUT_MIN;
cc1100_cs_init(); // Initialize carrier sensing
window:
if (backoff != 0) goto cycle; // If backoff was 0
windowSize *= 2; // ...double the current window size
if (windowSize > max_window_size)
{
windowSize = max_window_size; // This is the maximum size allowed
}
backoff = rand() % windowSize; // ...and choose new backoff
if (backoff < 0) backoff *= -1;
backoff += (uint16_t) 1;
cycle:
cs_timeout_flag = 0; // Carrier sense timeout flag
cs_hwtimer_id = hwtimer_set(cs_timeout, // Set hwtimer to set CS timeout flag
cs_timeout_cb, NULL);
while (cc1100_cs_read()) // Wait until air is free
{
if (cs_timeout_flag)
{
send_csmaca_calls_cs_timeout++;
#ifndef CSMACA_MAC_AGGRESSIVE_MODE
cc1100_phy_mutex_unlock();
cc1100_go_after_tx(); // Go from RX to default mode
return RADIO_CS_TIMEOUT; // Return immediately
#endif
#ifdef CSMACA_MAC_AGGRESSIVE_MODE
goto send; // Send anyway
#endif
}
}
hwtimer_remove(cs_hwtimer_id); // Remove hwtimer
cc1100_cs_write_cca(1); // Air is free now
cc1100_cs_set_enabled(true);
if (cc1100_cs_read()) goto window; // GDO0 triggers on rising edge, so
// test once after interrupt is enabled
if (backoff > 0) backoff--; // Decrement backoff counter
total = slottime; // Calculate total wait time
total *= (uint32_t)backoff; // Slot vector set
total += difs; // ...and standard DIFS wait time
cs_timeout_flag = 0; // Carrier sense timeout flag
cs_hwtimer_id = hwtimer_set(total, // Set hwtimer to set CS timeout flag
cs_timeout_cb, NULL);
while (!cs_timeout_flag
|| !cc1100_cs_read_cca()) // Wait until timeout is finished
{
if (cc1100_cs_read_cca() == 0) // Is the air still free?
{
hwtimer_remove(cs_hwtimer_id);
goto window; // No. Go back to new wait period.
}
}
cc1100_cs_set_enabled(false);
#ifdef CSMACA_MAC_AGGRESSIVE_MODE
send:
#endif
int res = cc1100_send(address, protocol, priority, payload, payload_len);
if (res < 0) {
collision_count++;
}
return res;
}