2010-09-22 15:10:42 +02:00
/******************************************************************************
2013-08-16 10:20:23 +02:00
* Copyright 2008 , Freie Universitaet Berlin ( FUB ) . All rights reserved .
*
* These sources were developed at the Freie Universitaet Berlin , Computer Systems
2010-09-22 15:10:42 +02:00
and Telematics group ( http : //cst.mi.fu-berlin.de).
2013-08-16 10:20:23 +02:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* This file is part of RIOT .
*
2014-07-31 19:45:27 +02:00
* 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 .
2013-08-16 10:20:23 +02:00
*
2010-09-22 15:10:42 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
2014-07-31 20:49:35 +02:00
* @ ingroup dev_cc110x
2010-09-22 15:10:42 +02:00
* @ {
*/
/**
* @ file
* @ internal
2014-07-31 20:49:35 +02:00
* @ brief TI Chipcon CC110x physical radio driver
2010-09-22 15:10:42 +02:00
*
2014-07-31 20:49:35 +02:00
* @ author Thomas Hillebrandt < hillebra @ inf . fu - berlin . de >
* @ author Heiko Will < hwill @ inf . fu - berlin . de >
2013-06-21 22:36:48 +02:00
* @ author Oliver Hahm < oliver . hahm @ inria . fr >
2010-09-22 15:10:42 +02:00
* @ version $ Revision : 2130 $
*
2014-07-31 20:49:35 +02:00
* @ note $ Id : cc1100_phy . c 2130 2010 - 05 - 12 13 : 19 : 07 Z hillebra $
2010-09-22 15:10:42 +02:00
*/
# include <stdio.h>
# include <string.h>
# include "hwtimer.h"
2013-12-16 17:54:58 +01:00
# include "vtimer.h"
2010-09-22 15:10:42 +02:00
# include "cc1100.h"
# include "cc1100_spi.h"
# include "cc1100_phy.h"
# include "cc1100-defaultSettings.h"
2013-10-28 16:14:52 +01:00
# include "protocol-multiplex.h"
2010-09-22 15:10:42 +02:00
# include "kernel.h"
# include "thread.h"
# include "mutex.h"
# include "msg.h"
# include "debug.h"
2010-11-09 16:59:55 +01:00
# define PRIORITY_CC1100 PRIORITY_MAIN-1
2010-11-04 14:24:06 +01:00
2010-09-22 15:10:42 +02:00
# define MSG_POLL 12346
2014-07-31 20:49:35 +02:00
# define FLAGS_IDENTIFICATION (0x01) ///< Bit mask for reading the identification out of the flags field
# define R_FLAGS_PROTOCOL(x) ((x & 0x0E)>>1) ///< Macro for reading the protocol out of the flags field
# define W_FLAGS_PROTOCOL(x) ((x<<1) & 0x0E) ///< Macro for writing the protocol in the flags field
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
/* RX/TX buffer data structures */
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
2013-06-21 22:36:48 +02:00
typedef struct {
cc1100_packet_layer0_t packet ;
packet_info_t info ;
2010-09-22 15:10:42 +02:00
} rx_buffer_t ;
2014-07-31 20:49:35 +02:00
# define RX_BUFF_SIZE (10) ///< Size of RX queue
static volatile uint8_t rx_buffer_head ; ///< RX queue head
static volatile uint8_t rx_buffer_tail ; ///< RX queue tail
static volatile uint8_t rx_buffer_size ; ///< RX queue size
static rx_buffer_t rx_buffer [ RX_BUFF_SIZE ] ; ///< RX buffer
static cc1100_packet_layer0_t tx_buffer ; ///< TX buffer (for one packet)
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
/* Process/Event management data structures */
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
# define MAX_PACKET_HANDLERS (5)
2010-09-22 15:10:42 +02:00
static packet_monitor_t packet_monitor ;
static handler_entry_t handlers [ MAX_PACKET_HANDLERS ] ;
static const pm_table_t handler_table ;
static const char * cc1100_event_handler_name = " cc1100_event_handler " ;
2014-07-17 12:27:29 +02:00
static mutex_t cc1100_mutex = MUTEX_INIT ;
2014-07-06 22:57:56 +02:00
volatile kernel_pid_t cc1100_mutex_pid ;
2012-03-02 20:22:22 +01:00
static vtimer_t cc1100_watch_dog ;
static timex_t cc1100_watch_dog_period ;
2010-09-22 15:10:42 +02:00
2014-07-06 22:57:56 +02:00
static kernel_pid_t cc1100_event_handler_pid ;
2014-03-04 20:20:01 +01:00
static void * cc1100_event_handler_function ( void * ) ;
2010-10-25 15:40:01 +02:00
static char event_handler_stack [ KERNEL_CONF_STACKSIZE_MAIN ] ;
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
/* Sequence number buffer management data structures */
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
/**
* @ name Sequence Buffer
* @ {
*/
2014-07-31 20:49:35 +02:00
# define MAX_SEQ_BUFFER_SIZE (20) ///< Maximum size of the sequence number buffer
2010-09-22 15:10:42 +02:00
2013-06-21 22:36:48 +02:00
typedef struct {
2014-07-31 20:49:35 +02:00
uint64_t m_ticks ; ///< 64-bit timestamp
uint8_t source ; ///< Source address
uint8_t identification ; ///< Identification (1-bit)
2011-03-08 11:43:21 +01:00
} seq_buffer_entry_t ;
2010-09-22 15:10:42 +02:00
2013-06-21 22:36:48 +02:00
//* Sequence number buffer for this layer */
2011-03-08 11:43:21 +01:00
static seq_buffer_entry_t seq_buffer [ MAX_SEQ_BUFFER_SIZE ] ;
2010-09-22 15:10:42 +02:00
2013-06-21 22:36:48 +02:00
//* Next position to enter a new value into ::seqBuffer */
2010-09-22 15:10:42 +02:00
static uint8_t seq_buffer_pos = 0 ;
/**
2014-07-31 20:49:35 +02:00
* @ brief Last sequence number this node has seen
2010-09-22 15:10:42 +02:00
*
2014-07-31 20:49:35 +02:00
* @ note ( phySrc + flags . identification ) - for speedup in ISR .
2010-09-22 15:10:42 +02:00
*/
static volatile uint16_t last_seq_num = 0 ;
/** @} */
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
// WOR configuration data structures
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
# define EVENT0_MAX (60493) ///< Maximum RX polling interval in milliseconds
# define WOR_RES_SWITCH (1891) ///< Switching point value in milliseconds between
///< WOR_RES = 0 and WOR_RES = 1
# define DUTY_CYCLE_SIZE (7) ///< Length of duty cycle array
2010-09-22 15:10:42 +02:00
2014-07-31 20:49:35 +02:00
cc1100_wor_config_t cc1100_wor_config ; ///< CC1100 WOR configuration
2010-09-22 15:10:42 +02:00
2014-07-31 20:49:35 +02:00
uint16_t cc1100_burst_count ; ///< Burst count, number of packets in a burst transfer
uint8_t cc1100_retransmission_count_uc ; ///< Number of retransmissions for unicast
uint8_t cc1100_retransmission_count_bc ; ///< Number of retransmissions for broadcast
2010-09-22 15:10:42 +02:00
2014-07-31 20:49:35 +02:00
static const double duty_cycle [ 2 ] [ DUTY_CYCLE_SIZE ] = { ///< Duty cycle values from AN047
2013-06-21 22:36:48 +02:00
{ 12.5 , 6.25 , 3.125 , 1.563 , 0.781 , 0.391 , 0.195 } ,
{ 1.95 , 0.9765 , 0.4883 , 0.2441 , 0.1221 , 0.061035 , 0.030518 }
2010-09-22 15:10:42 +02:00
} ;
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
// Data structures for statistic
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
cc1100_statistic_t cc1100_statistic ;
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
// Initialization of physical layer
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
2014-05-07 12:36:32 +02:00
void cc1100_phy_init ( void )
2010-09-22 15:10:42 +02:00
{
2013-06-21 22:36:48 +02:00
int i ;
rx_buffer_head = 0 ;
rx_buffer_tail = 0 ;
rx_buffer_size = 0 ;
/* Initialize RX-Buffer (clear content) */
2013-06-24 22:37:35 +02:00
for ( i = 0 ; i < RX_BUFF_SIZE ; i + + ) {
2013-06-21 22:36:48 +02:00
rx_buffer - > packet . length = 0 ;
}
/* Initialize handler table & packet monitor */
packet_monitor = NULL ;
pm_init_table ( ( pm_table_t * ) & handler_table , MAX_PACKET_HANDLERS , handlers ) ;
/* Clear sequence number buffer */
memset ( seq_buffer , 0 , sizeof ( seq_buffer_entry_t ) * MAX_SEQ_BUFFER_SIZE ) ;
/* Initialize mutex */
2014-08-06 09:27:23 +02:00
cc1100_mutex_pid = KERNEL_PID_UNDEF ;
2013-06-21 22:36:48 +02:00
/* Allocate event numbers and start cc1100 event process */
cc1100_event_handler_pid = thread_create ( event_handler_stack , sizeof ( event_handler_stack ) , PRIORITY_CC1100 , CREATE_STACKTEST ,
2014-03-04 20:20:01 +01:00
cc1100_event_handler_function , NULL , cc1100_event_handler_name ) ;
2013-06-21 22:36:48 +02:00
/* Active watchdog for the first time */
2013-06-24 22:37:35 +02:00
if ( radio_mode = = CC1100_MODE_CONSTANT_RX ) {
2013-12-19 19:44:27 +01:00
cc1100_watch_dog_period = timex_set ( CC1100_WATCHDOG_PERIOD , 0 ) ;
2013-06-21 22:36:48 +02:00
2013-12-19 19:44:27 +01:00
if ( timex_cmp ( cc1100_watch_dog_period , timex_set ( 0 , 0 ) ) ! = 0 ) {
vtimer_set_msg ( & cc1100_watch_dog , cc1100_watch_dog_period , cc1100_event_handler_pid , NULL ) ;
2013-06-21 22:36:48 +02:00
}
}
2010-09-22 15:10:42 +02:00
}
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
/* CC1100 mutual exclusion */
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
void cc1100_phy_mutex_lock ( void )
{
2014-04-10 22:28:35 +02:00
if ( sched_active_thread - > pid ! = cc1100_mutex_pid ) {
2013-06-21 22:36:48 +02:00
mutex_lock ( & cc1100_mutex ) ;
2014-04-10 22:28:35 +02:00
cc1100_mutex_pid = sched_active_thread - > pid ;
2013-06-21 22:36:48 +02:00
}
2010-09-22 15:10:42 +02:00
}
void cc1100_phy_mutex_unlock ( void )
{
2014-08-06 09:27:23 +02:00
cc1100_mutex_pid = KERNEL_PID_UNDEF ;
2013-08-04 04:06:31 +02:00
mutex_unlock ( & cc1100_mutex ) ;
2010-09-22 15:10:42 +02:00
}
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
// Statistical functions
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
void cc1100_reset_statistic ( void )
{
2013-06-21 22:36:48 +02:00
cc1100_statistic . packets_in_up = 0 ;
cc1100_statistic . acks_send = 0 ;
cc1100_statistic . packets_out_acked = 0 ;
cc1100_statistic . packets_in = 0 ;
cc1100_statistic . packets_out = 0 ;
cc1100_statistic . packets_out_broadcast = 0 ;
cc1100_statistic . raw_packets_out_acked = 0 ;
cc1100_statistic . raw_packets_out = 0 ;
cc1100_statistic . packets_in_dups = 0 ;
cc1100_statistic . packets_in_crc_fail = 0 ;
cc1100_statistic . packets_in_while_tx = 0 ;
cc1100_statistic . rx_buffer_max = 0 ;
cc1100_statistic . watch_dog_resets = 0 ;
2010-09-22 15:10:42 +02:00
}
void cc1100_print_statistic ( void )
{
2013-06-21 22:36:48 +02:00
printf ( " \n Statistic on CC1100 interface \n \n " ) ;
printf ( " Total packets send on layer 0.5 (broadcast): %lu \n " , cc1100_statistic . packets_out_broadcast ) ;
printf ( " Total packets send on layer 0.5 (unicast): %lu \n " , cc1100_statistic . packets_out ) ;
printf ( " Total packets Acked on layer 0.5: %lu (%.2f%%) \n " , cc1100_statistic . packets_out_acked , cc1100_statistic . packets_out_acked * ( 100.0f / ( float ) cc1100_statistic . packets_out ) ) ;
printf ( " Total packets send on layer 0: %lu \n " , cc1100_statistic . raw_packets_out ) ;
printf ( " Total packets send on layer 0 w. Ack on Layer 0.5: %lu (Avg. Ack after: %lu packets) \n " , cc1100_statistic . raw_packets_out_acked , cc1100_statistic . raw_packets_out_acked / cc1100_statistic . packets_out_acked ) ;
printf ( " Burst count on this node: %i (%.2f%%) \n " , cc1100_burst_count , ( 100 / ( float ) cc1100_burst_count ) * ( cc1100_statistic . raw_packets_out_acked / ( float ) cc1100_statistic . packets_out_acked ) ) ;
printf ( " Total packets In on layer 0: %lu \n " , cc1100_statistic . packets_in ) ;
printf ( " Duped packets In on layer 0: %lu \n " , cc1100_statistic . packets_in_dups ) ;
printf ( " Corrupted packets In on layer 0: %lu \n " , cc1100_statistic . packets_in_crc_fail ) ;
printf ( " Packets In on layer 0 while in TX: %lu \n " , cc1100_statistic . packets_in_while_tx ) ;
printf ( " Total packets In and up to layer 1: %lu (%.2f%%) \n " , cc1100_statistic . packets_in_up , cc1100_statistic . packets_in_up * ( 100.0f / ( float ) cc1100_statistic . packets_in ) ) ;
printf ( " Total Acks send on layer 0.5: %lu \n " , cc1100_statistic . acks_send ) ;
printf ( " RX Buffer max: %lu (now: %u) \n " , cc1100_statistic . rx_buffer_max , rx_buffer_size ) ;
printf ( " State machine resets by cc1100 watchdog: %lu \n " , cc1100_statistic . watch_dog_resets ) ;
2010-09-22 15:10:42 +02:00
}
void cc1100_print_config ( void )
{
2013-06-21 22:36:48 +02:00
char buf [ 8 ] ;
printf ( " Current radio mode: %s \r \n " , cc1100_mode_to_text ( radio_mode ) ) ;
printf ( " Current radio state: %s \r \n " , cc1100_state_to_text ( radio_state ) ) ;
printf ( " Current MARC state: %s \r \n " , cc1100_get_marc_state ( ) ) ;
printf ( " Current channel number: %u \r \n " , cc1100_get_channel ( ) ) ;
printf ( " Burst count: %u packet(s) \r \n " , cc1100_burst_count ) ;
printf ( " Retransmissions (unicast): %u - if no ACK \r \n " , cc1100_retransmission_count_uc ) ;
printf ( " Retransmissions (broadcast): %u - always \r \n " , cc1100_retransmission_count_bc ) ;
printf ( " Output power setting: %s \r \n " , cc1100_get_output_power ( buf ) ) ;
2013-06-24 22:37:35 +02:00
if ( radio_mode = = CC1100_MODE_WOR ) {
2013-06-21 22:36:48 +02:00
printf ( " RX polling interval: %u ms \r \n " , cc1100_wor_config . rx_interval ) ;
printf ( " WOR receive time: 0x%.2X (%f ms) \r \n " , cc1100_wor_config . rx_time_reg ,
cc1100_wor_config . rx_time_ms ) ;
printf ( " CC1100 WOREVT0 register: 0x%.2X \r \n " , cc1100_wor_config . wor_evt_0 ) ;
printf ( " CC1100 WOREVT1 register: 0x%.2X \r \n " , cc1100_wor_config . wor_evt_1 ) ;
printf ( " CC1100 WOR_CTRL register: 0x%.2X \r \n " , cc1100_wor_config . wor_ctrl ) ;
printf ( " CC1100 MAN_WOR flag: %u \r \n " , rflags . MAN_WOR ) ;
}
2010-09-22 15:10:42 +02:00
}
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
// Change of RX polling interval (T_EVENT0)
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
inline uint16_t iround ( double d )
{
2013-11-21 14:18:54 +01:00
return ( uint16_t ) ( d + 0.5 ) ;
2010-09-22 15:10:42 +02:00
}
int cc1100_phy_calc_wor_settings ( uint16_t millis )
{
2013-06-21 22:36:48 +02:00
/* Get packet interval as milliseconds */
double t_packet_interval = ( double ) ( ( T_PACKET_INTERVAL ) / 1000.0 ) ;
/* Calculate minimal T_EVENT0:
2014-02-11 18:15:43 +01:00
2013-06-21 22:36:48 +02:00
( 1 ) t_rx_time > t_packet_interval
( 2 ) t_rx_time = T_EVENT0 / 2 ^ ( RX_TIME + 3 + WOR_RES )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
with RX_TIME = 0 & & WOR_RES = 0 = > event0_min > t_packet_interval * 8
2014-02-11 18:15:43 +01:00
2013-06-21 22:36:48 +02:00
t_packet_interval = 3.8 ms ( @ 400 kbit / s )
2014-02-11 18:15:43 +01:00
2013-06-21 22:36:48 +02:00
= > event0_min = Math . ceil ( 3.8 * 8 ) + 10 */
uint16_t event0_min = ( uint16_t ) ( t_packet_interval * 8 ) + 1 + 10 ;
/* Check if given value is in allowed range */
2013-06-24 22:37:35 +02:00
if ( millis < event0_min | | millis > EVENT0_MAX ) {
2013-06-21 22:36:48 +02:00
return - 1 ;
}
/* Time resolution for EVENT0 and other WOR parameters, */
/* possible values are 0 and 1 if WOR is used */
uint8_t wor_res = millis < WOR_RES_SWITCH ? 0 : 1 ;
/* Calculate new value for EVENT0 */
double tmp = ( millis * 26 ) / ( double ) 750 ;
2013-06-24 22:37:35 +02:00
if ( wor_res = = 1 ) {
2013-06-21 22:36:48 +02:00
tmp / = 32 ;
}
tmp * = 1000 ;
uint16_t event0 = ( uint16_t ) iround ( tmp ) ;
/* Calculate all possible RX timeouts */
int i ;
double rx_timeouts [ DUTY_CYCLE_SIZE ] ;
2013-06-24 22:37:35 +02:00
for ( i = 0 ; i < DUTY_CYCLE_SIZE ; i + + ) {
2013-06-21 22:36:48 +02:00
rx_timeouts [ i ] = ( millis * duty_cycle [ wor_res ] [ i ] ) / 100 ;
}
/* Calculate index for optimal rx_timeout (MCSM2.RX_TIME) (if possible) */
int idx = - 1 ;
2013-06-24 22:37:35 +02:00
for ( i = DUTY_CYCLE_SIZE - 1 ; i > = 0 ; i - - ) {
if ( rx_timeouts [ i ] > t_packet_interval ) {
2013-06-21 22:36:48 +02:00
idx = i ;
break ;
}
}
/* If no index found, exit here (configuration with given value is not possible) */
2013-06-24 22:37:35 +02:00
if ( idx = = - 1 ) {
2013-06-21 22:36:48 +02:00
return - 1 ;
}
/* Calculate burst count (secure burst calculation with 8 extra packets) */
int burst_count = ( int ) iround ( millis / t_packet_interval ) + 8 ;
/* All calculations successful, now its safe to store */
/* final configuration values in global WOR configuration */
cc1100_wor_config . rx_interval = millis ;
cc1100_wor_config . wor_ctrl = ( wor_res = = 0 ) ? 0x78 : 0x79 ;
cc1100_wor_config . wor_evt_0 = ( uint8_t ) event0 ;
cc1100_wor_config . wor_evt_1 = ( uint8_t ) ( event0 > > 8 ) ;
cc1100_wor_config . rx_time_reg = idx ;
cc1100_wor_config . rx_time_ms = rx_timeouts [ idx ] ;
/* If successful, return number of packets in a burst transfer */
return burst_count ;
2010-09-22 15:10:42 +02:00
}
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
// Sequence number buffer management
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
static bool contains_seq_entry ( uint8_t src , uint8_t id )
{
2013-06-21 22:36:48 +02:00
int i ;
uint32_t cmp ;
timex_t now_timex ;
vtimer_now ( & now_timex ) ;
2013-06-24 22:37:35 +02:00
for ( i = 0 ; i < MAX_SEQ_BUFFER_SIZE ; i + + ) {
if ( ( seq_buffer [ i ] . source = = src ) & & ( seq_buffer [ i ] . identification = = id ) ) {
2013-06-21 22:36:48 +02:00
/* Check if time stamp is OK */
cmp = ( radio_mode = = CC1100_MODE_WOR ) ? cc1100_wor_config . rx_interval : 16000 ; /* constant RX ~16ms */
2013-12-19 19:44:27 +01:00
if ( ( timex_uint64 ( now_timex ) - seq_buffer [ i ] . m_ticks < cmp ) ) {
2013-06-21 22:36:48 +02:00
return true ;
}
else {
seq_buffer [ i ] . source = 0 ; /* Reset */
}
}
}
return false ;
2010-09-22 15:10:42 +02:00
}
static void add_seq_entry ( uint8_t src , uint8_t id )
{
2013-06-21 22:36:48 +02:00
/* Remove all entries with given source to avoid short time overflow
* of one bit counter ( of the source node ) . So a valid packet would get
* lost ( especially important in constant RX mode ) . */
int i ;
2013-06-24 22:37:35 +02:00
for ( i = 0 ; i < MAX_SEQ_BUFFER_SIZE ; i + + ) {
if ( seq_buffer [ i ] . source = = src ) {
2013-06-21 22:36:48 +02:00
seq_buffer [ i ] . source = 0 ; /* Reset */
}
}
/* Add new entry */
seq_buffer [ seq_buffer_pos ] . source = src ;
seq_buffer [ seq_buffer_pos ] . identification = id ;
timex_t now ;
vtimer_now ( & now ) ;
2013-12-19 19:44:27 +01:00
seq_buffer [ seq_buffer_pos ] . m_ticks = timex_uint64 ( now ) ;
2010-09-22 15:10:42 +02:00
2013-06-21 22:36:48 +02:00
/* Store 16 bit sequence number of layer 0 for speedup */
last_seq_num = src ;
last_seq_num < < = 8 ;
last_seq_num + = id ;
seq_buffer_pos + + ;
2010-09-22 15:10:42 +02:00
2013-06-24 22:37:35 +02:00
if ( seq_buffer_pos = = MAX_SEQ_BUFFER_SIZE ) {
2013-06-21 22:36:48 +02:00
seq_buffer_pos = 0 ;
}
2010-09-22 15:10:42 +02:00
}
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
/* CC1100 physical layer send functions */
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
static void send_link_level_ack ( uint8_t dest )
{
2014-07-31 20:49:35 +02:00
uint8_t oldState = radio_state ; /* Save old state */
cc1100_packet_layer0_t ack ; /* Local packet, don't overwrite */
2013-06-21 22:36:48 +02:00
2014-07-31 20:49:35 +02:00
radio_state = RADIO_SEND_ACK ; /* Set state to "Sending ACK" */
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x08 ) ; /* Turn off FS-Autocal */
cc1100_spi_write_reg ( CC1100_MCSM1 , 0x00 ) ; /* TX_OFFMODE = IDLE */
ack . length = 3 ; /* possible packet in txBuffer!*/
2013-06-21 22:36:48 +02:00
ack . address = dest ;
ack . phy_src = rflags . RSSI ;
ack . flags = ( LAYER_1_PROTOCOL_LL_ACK < < 1 ) ;
2014-07-31 20:49:35 +02:00
cc1100_send_raw ( ( uint8_t * ) & ack , /* IDLE -> TX (88.4 us) */
2013-06-21 22:36:48 +02:00
ack . length + 1 ) ;
2014-07-31 20:49:35 +02:00
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x18 ) ; /* Turn on FS-Autocal */
cc1100_spi_write_reg ( CC1100_MCSM1 , 0x03 ) ; /* TX_OFFMODE = RX */
radio_state = oldState ; /* Restore state */
2013-06-21 22:36:48 +02:00
cc1100_statistic . acks_send + + ;
2010-09-22 15:10:42 +02:00
}
static bool send_burst ( cc1100_packet_layer0_t * packet , uint8_t retries , uint8_t rtc )
{
2013-06-21 22:36:48 +02:00
int i ;
radio_state = RADIO_SEND_BURST ;
rflags . LL_ACK = false ;
2013-06-24 22:37:35 +02:00
for ( i = 1 ; i < = cc1100_burst_count ; i + + ) {
2013-06-21 22:36:48 +02:00
/*
* Number of bytes to send is :
* length of phy payload ( packet - > length )
* + size of length field ( 1 byte )
*/
extern unsigned long hwtimer_now ( void ) ;
timer_tick_t t = hwtimer_now ( ) + RTIMER_TICKS ( T_PACKET_INTERVAL ) ;
2014-07-31 20:49:35 +02:00
cc1100_send_raw ( ( uint8_t * ) packet , packet - > length + 1 ) ; /* RX -> TX (9.6 us) */
2013-06-21 22:36:48 +02:00
cc1100_statistic . raw_packets_out + + ;
/* Delay until predefined "send" interval has passed */
timer_tick_t now = hwtimer_now ( ) ;
2013-06-24 22:37:35 +02:00
if ( t > now ) {
2013-06-21 22:36:48 +02:00
hwtimer_wait ( t - now ) ;
}
/**
* After sending the packet the CC1100 goes automatically
* into RX mode ( 21.5 us ) ( listening for an ACK ) .
* Do not interrupt burst if send to broadcast address ( a node may
* have the broadcast address at startup and would stop the burst
* by sending an ACK ) .
*/
2013-06-24 22:37:35 +02:00
if ( rflags . LL_ACK & & packet - > address ! = CC1100_BROADCAST_ADDRESS ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . raw_packets_out_acked + = i ;
break ;
}
}
/* No link level ACK -> do retry if retry counter greater zero
* Note : Event broadcast packets can be sent repeatedly if in
* constant RX mode . In WOR mode it is not necessary , so
* set retry count to zero . */
2013-06-24 22:37:35 +02:00
if ( ! rflags . LL_ACK & & retries > 0 ) {
2013-06-21 22:36:48 +02:00
return send_burst ( packet , retries - 1 , rtc + 1 ) ;
}
/* Store number of transmission retries */
rflags . RETC = rtc ;
rflags . RPS = rtc * cc1100_burst_count + i ;
2013-06-24 22:37:35 +02:00
if ( i > cc1100_burst_count ) {
2013-06-21 22:36:48 +02:00
rflags . RPS - - ;
}
rflags . TX = false ;
/* Go to mode after TX (CONST_RX -> RX, WOR -> WOR) */
cc1100_go_after_tx ( ) ;
/* Burst from any other node is definitely over */
last_seq_num = 0 ;
2013-06-24 22:37:35 +02:00
if ( packet - > address ! = CC1100_BROADCAST_ADDRESS & & ! rflags . LL_ACK ) {
2013-06-21 22:36:48 +02:00
return false ;
}
return true ;
2010-09-22 15:10:42 +02:00
}
2014-07-07 00:35:29 +02:00
int cc1100_send ( radio_address_t addr , protocol_t protocol , int priority , char * payload , radio_packet_length_t payload_len )
2010-09-22 15:10:42 +02:00
{
2013-11-21 14:18:54 +01:00
( void ) priority ;
2013-06-21 22:36:48 +02:00
bool result ;
int return_code ;
uint8_t address ;
uint8_t retries ;
/* Lock mutex, nobody else should send now */
cc1100_phy_mutex_lock ( ) ;
/* TX state machine lock -> no timers (WOR), no packets (only ACKs) */
rflags . TX = true ;
/* Set chip to idle state */
cc1100_set_idle ( ) ;
/* CC1100 radio layer only supports 8-bit addresses */
address = addr ;
/* Loopback not supported */
2013-06-24 22:37:35 +02:00
if ( address = = cc1100_get_address ( ) ) {
2013-06-21 22:36:48 +02:00
return_code = RADIO_ADDR_OUT_OF_RANGE ;
goto mode_before_final ;
}
/* Check address */
2013-06-24 22:37:35 +02:00
if ( address > MAX_UID ) {
2013-06-21 22:36:48 +02:00
return_code = RADIO_ADDR_OUT_OF_RANGE ;
goto mode_before_final ;
}
/* Packet too long */
2013-06-24 22:37:35 +02:00
if ( payload_len > MAX_DATA_LENGTH ) {
2013-06-21 22:36:48 +02:00
return_code = RADIO_PAYLOAD_TOO_LONG ;
goto mode_before_final ;
}
2013-06-24 22:37:35 +02:00
if ( radio_state = = RADIO_PWD ) {
2013-06-21 22:36:48 +02:00
return_code = RADIO_WRONG_MODE ;
goto mode_before_final ;
}
/* Set number of transmission retries */
retries = ( address = = CC1100_BROADCAST_ADDRESS ) ?
cc1100_retransmission_count_bc : cc1100_retransmission_count_uc ;
2014-07-31 20:49:35 +02:00
memset ( tx_buffer . data , 0 , MAX_DATA_LENGTH ) ; /* Clean data */
2013-06-21 22:36:48 +02:00
/* TODO: If packets are shorter than max packet size, WOR interval is too long.
2012-11-05 20:17:00 +01:00
* This must be solved in some way . */
2014-07-31 20:49:35 +02:00
tx_buffer . length = 3 + payload_len ; /* 3 bytes (A&PS&F) + data length */
tx_buffer . address = address ; /* Copy destination address */
tx_buffer . flags = 0x00 ; /* Set clean state */
tx_buffer . flags = W_FLAGS_PROTOCOL ( protocol ) ; /* Copy protocol identifier */
tx_buffer . phy_src = ( uint8_t ) cc1100_get_address ( ) ; /* Copy sender address */
2013-06-21 22:36:48 +02:00
/* Set identification number of packet */
2014-07-31 20:49:35 +02:00
tx_buffer . flags | = rflags . SEQ ; /* Set flags.identification (bit 0) */
rflags . SEQ = ! rflags . SEQ ; /* Toggle value of layer 0 sequence number bit */
2013-06-21 22:36:48 +02:00
2014-07-31 20:49:35 +02:00
memcpy ( tx_buffer . data , payload , payload_len ) ; /* Copy data */
2013-06-21 22:36:48 +02:00
/* Send the packet */
2014-07-31 20:49:35 +02:00
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x08 ) ; /* Turn off FS-Autocal */
result = send_burst ( & tx_buffer , retries , 0 ) ; /* Send raw burst */
2013-06-21 22:36:48 +02:00
return_code = result ? payload_len : RADIO_OP_FAILED ;
/* Collect statistics */
2013-06-24 22:37:35 +02:00
if ( address ! = CC1100_BROADCAST_ADDRESS ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . packets_out + + ;
2013-06-24 22:37:35 +02:00
if ( result ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . packets_out_acked + + ;
}
}
else {
cc1100_statistic . packets_out_broadcast + + ;
}
goto final ;
mode_before_final :
rflags . TX = false ;
/* Definitely set secure mode (CONST_RX -> RX, WOR -> WOR) */
cc1100_go_after_tx ( ) ;
final :
/* Release mutex and return */
cc1100_phy_mutex_unlock ( ) ;
return return_code ;
2010-09-22 15:10:42 +02:00
}
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
/* RX Event Handler */
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
bool cc1100_set_packet_monitor ( packet_monitor_t monitor )
{
2013-06-21 22:36:48 +02:00
packet_monitor = monitor ;
return true ;
2010-09-22 15:10:42 +02:00
}
int cc1100_set_packet_handler ( protocol_t protocol , packet_handler_t handler )
{
2013-06-24 22:37:35 +02:00
if ( protocol > 7 ) {
2013-06-21 22:36:48 +02:00
return - 1 ; /* Only 3-bit value allowed */
}
return pm_set_handler ( & handler_table , protocol , handler ) ;
2010-09-22 15:10:42 +02:00
}
2014-03-04 20:20:01 +01:00
static void * cc1100_event_handler_function ( void * arg )
2010-09-22 15:10:42 +02:00
{
2014-03-04 20:20:01 +01:00
( void ) arg ;
2011-03-08 10:54:40 +01:00
msg_t m ;
2011-04-06 11:09:29 +02:00
2013-06-24 22:37:35 +02:00
while ( 1 ) {
2013-12-19 19:44:27 +01:00
if ( timex_uint64 ( cc1100_watch_dog_period ) ! = 0 ) {
2013-06-21 22:36:48 +02:00
vtimer_remove ( & cc1100_watch_dog ) ;
}
/* Test if any resource error has occurred */
2013-06-24 22:37:35 +02:00
if ( rflags . KT_RES_ERR ) {
2013-06-21 22:36:48 +02:00
rflags . KT_RES_ERR = false ;
/* possibly do something, e.g. log error condition */
}
2013-06-24 22:37:35 +02:00
if ( m . type = = MSG_TIMER ) {
2013-06-21 22:36:48 +02:00
uint8_t state ;
2013-06-24 22:37:35 +02:00
if ( radio_mode = = CC1100_MODE_CONSTANT_RX ) {
2013-06-21 22:36:48 +02:00
state = cc1100_spi_read_status ( CC1100_MARCSTATE ) & MARC_STATE ;
2013-06-24 22:37:35 +02:00
if ( ( state < 13 | | state > 15 ) & & radio_state = = RADIO_RX & & ! rflags . TX ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . watch_dog_resets + + ;
2013-06-24 22:37:35 +02:00
if ( state ! = 1 ) {
2013-06-21 22:36:48 +02:00
cc1100_spi_strobe ( CC1100_SIDLE ) ;
}
cc1100_spi_strobe ( CC1100_SFRX ) ;
cc1100_go_receive ( ) ;
}
}
else {
/* Radio mode is WOR, cannot read current MARC state, will */
/* always be IDLE. So do nothing here, e.g. disable watchdog. */
}
}
2013-06-24 22:37:35 +02:00
while ( rx_buffer_size > 0 ) {
2013-06-21 22:36:48 +02:00
rx_buffer_t * packet = & rx_buffer [ rx_buffer_head ] ;
protocol_t p = R_FLAGS_PROTOCOL ( packet - > packet . flags ) ;
2013-06-24 22:37:35 +02:00
if ( packet_monitor ! = NULL ) {
2013-06-21 22:36:48 +02:00
packet_monitor ( ( void * ) & packet - > packet . data , packet - > packet . length , p , & packet - > info ) ;
}
pm_invoke ( & handler_table , p , ( void * ) & packet - > packet . data , MAX_DATA_LENGTH , & packet - > info ) ;
dINT ( ) ;
rx_buffer_size - - ;
rx_buffer_head + + ;
2013-06-24 22:37:35 +02:00
if ( rx_buffer_head = = RX_BUFF_SIZE ) {
2013-06-21 22:36:48 +02:00
rx_buffer_head = 0 ;
}
eINT ( ) ;
}
dINT ( ) ;
2013-06-24 22:37:35 +02:00
if ( rx_buffer_size = = 0 ) {
2013-12-19 19:44:27 +01:00
if ( timex_uint64 ( cc1100_watch_dog_period ) ! = 0 ) {
vtimer_set_msg ( & cc1100_watch_dog , cc1100_watch_dog_period ,
2013-06-21 22:36:48 +02:00
cc1100_event_handler_pid , NULL ) ;
}
msg_receive ( & m ) ;
}
eINT ( ) ;
2010-09-22 15:10:42 +02:00
}
2014-03-04 20:20:01 +01:00
return NULL ;
2010-09-22 15:10:42 +02:00
}
/*---------------------------------------------------------------------------*/
2014-07-31 20:49:35 +02:00
/* CC1100 packet (RX) ISR */
2010-09-22 15:10:42 +02:00
/*---------------------------------------------------------------------------*/
void cc1100_phy_rx_handler ( void )
{
2013-06-21 22:36:48 +02:00
msg_t m ;
m . type = MSG_POLL ;
bool dup = false ;
bool res = false ;
/* Possible packet received, RX -> IDLE (0.1 us) */
rflags . CAA = false ;
rflags . MAN_WOR = false ;
cc1100_statistic . packets_in + + ;
/* If WOR timer set, delete it now (new one will be set at end of ISR) */
2013-06-24 22:37:35 +02:00
if ( wor_hwtimer_id ! = - 1 ) {
2013-06-21 22:36:48 +02:00
hwtimer_remove ( wor_hwtimer_id ) ;
wor_hwtimer_id = - 1 ;
}
/* Transfer packet into temporary buffer position */
res = cc1100_spi_receive_packet ( ( uint8_t * ) & ( rx_buffer [ rx_buffer_tail ] . packet ) , sizeof ( cc1100_packet_layer0_t ) ) ;
2013-06-24 22:37:35 +02:00
if ( res ) {
2013-06-21 22:36:48 +02:00
/* Get packet pointer and store additional data in packet info structure */
cc1100_packet_layer0_t * p = & ( rx_buffer [ rx_buffer_tail ] . packet ) ;
rx_buffer [ rx_buffer_tail ] . info . phy_src = p - > phy_src ;
rx_buffer [ rx_buffer_tail ] . info . source = p - > phy_src ;
rx_buffer [ rx_buffer_tail ] . info . destination = p - > address ;
rx_buffer [ rx_buffer_tail ] . info . rssi = rflags . RSSI ;
rx_buffer [ rx_buffer_tail ] . info . lqi = rflags . LQI ;
rx_buffer [ rx_buffer_tail ] . info . promiscuous = false ;
/* Get protocol and id field out of flags field */
uint8_t protocol = R_FLAGS_PROTOCOL ( p - > flags ) ;
uint8_t identification = ( p - > flags & FLAGS_IDENTIFICATION ) ;
/* If received packet was an ACK (here we must be in
* TX lock state , otherwise we don ' t expect an ACK ) */
2013-06-24 22:37:35 +02:00
if ( protocol = = LAYER_1_PROTOCOL_LL_ACK & & rflags . TX ) {
2013-06-21 22:36:48 +02:00
/* And packet was for us */
2013-06-24 22:37:35 +02:00
if ( p - > address = = cc1100_get_address ( ) ) {
2013-06-21 22:36:48 +02:00
/* Stop the burst */
rflags . LL_ACK = true ;
rflags . RSSI_SEND = p - > phy_src ;
2013-09-17 19:50:35 +02:00
rflags . TCP = ( uint32_t ) ( ( uint16_t * ) p - > data ) ;
2013-06-21 22:36:48 +02:00
}
return ;
}
else {
/* No ACK received so TOF is unpredictable */
rflags . TOF = 0 ;
}
/* If we are sending a burst, don't accept packets.
* Only ACKs are processed ( for stopping the burst ) .
* Same if state machine is in TX lock . */
2013-06-24 22:37:35 +02:00
if ( radio_state = = RADIO_SEND_BURST | | rflags . TX ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . packets_in_while_tx + + ;
return ;
}
/* If buffer is currently full -> don't check sequence numbers, send
* ACK and restore state ( keep always one position free for temporary packets ) */
2013-06-24 22:37:35 +02:00
if ( rx_buffer_size > = RX_BUFF_SIZE - 1 ) {
2013-06-21 22:36:48 +02:00
goto send_ack ;
}
/* Build 16 bit sequence number of layer 0 for fast check */
uint16_t new_seq_num = p - > phy_src ;
new_seq_num < < = 8 ;
new_seq_num + = identification ;
/* Duplicate packet detection */
dup = true ;
/* If new and last sequence number are the same, then discard packet */
2013-06-24 22:37:35 +02:00
if ( last_seq_num ! = new_seq_num ) {
2013-06-21 22:36:48 +02:00
/* Do a more precise check (takes more time) with larger buffer */
2013-06-24 22:37:35 +02:00
if ( ! contains_seq_entry ( p - > phy_src , identification ) ) {
2013-06-21 22:36:48 +02:00
/* Sequence number is new, no duplicate packet */
dup = false ;
/* Store sequence number */
add_seq_entry ( p - > phy_src , identification ) ;
/* Make temporary packet in RX buffer to a "real" packet which is processed */
rx_buffer_size + + ;
2013-06-24 22:37:35 +02:00
if ( rx_buffer_size > cc1100_statistic . rx_buffer_max ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . rx_buffer_max = rx_buffer_size ;
}
rx_buffer_tail + + ;
2013-06-24 22:37:35 +02:00
if ( rx_buffer_tail = = RX_BUFF_SIZE ) {
2013-06-21 22:36:48 +02:00
rx_buffer_tail = 0 ;
}
/* Send empty message to wake up receiver process.
* Receiver process could already be running ( triggered by previous message ) ,
* so function would return 0 and assume the receiver is not waiting but indeed
* all is working fine . */
msg_send_int ( & m , cc1100_event_handler_pid ) ;
cc1100_statistic . packets_in_up + + ;
}
}
send_ack :
/* If packet was send directly to us, send an ACK packet back to sender.
2013-06-24 22:37:35 +02:00
* But only not if the packet itself was a LL - ACK ! */
if ( p - > address = = cc1100_get_address ( ) & & protocol ! = LAYER_1_PROTOCOL_LL_ACK ) {
2013-06-21 22:36:48 +02:00
send_link_level_ack ( p - > phy_src ) ;
/* After LL-ACK burst is over, reset number */
last_seq_num = 0 ;
}
/* If duplicate packet detected, clear rxBuffer position */
2013-06-24 22:37:35 +02:00
if ( dup ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . packets_in_dups + + ;
}
/* If packet interrupted this nodes send call,
* don ' t change anything after this point . */
2013-06-24 22:37:35 +02:00
if ( radio_state = = RADIO_AIR_FREE_WAITING ) {
2013-06-21 22:36:48 +02:00
cc1100_spi_strobe ( CC1100_SRX ) ;
hwtimer_wait ( IDLE_TO_RX_TIME ) ;
return ;
}
/* Valid packet. After a wake-up, the radio should be in IDLE.
* So put CC1100 to RX for WOR_TIMEOUT ( have to manually put
* the radio back to sleep / WOR ) . */
2014-07-31 20:49:35 +02:00
cc1100_spi_write_reg ( CC1100_MCSM0 , 0x08 ) ; /* Turn off FS-Autocal */
cc1100_spi_write_reg ( CC1100_MCSM2 , 0x07 ) ; /* Configure RX_TIME (until end of packet) */
2013-06-21 22:36:48 +02:00
2013-06-24 22:37:35 +02:00
if ( radio_mode = = CC1100_MODE_CONSTANT_RX ) {
2013-06-21 22:36:48 +02:00
cc1100_spi_strobe ( CC1100_SRX ) ;
hwtimer_wait ( IDLE_TO_RX_TIME ) ;
radio_state = RADIO_RX ;
/* Return here if mode is CONSTANT_RX_MODE */
return ;
}
else {
cc1100_spi_strobe ( CC1100_SPWD ) ;
radio_state = RADIO_PWD ;
}
/* Set hwtimer to put CC1100 back to RX after WOR_TIMEOUT_1 */
wor_hwtimer_id = hwtimer_set ( WOR_TIMEOUT_1 , cc1100_hwtimer_go_receive_wrapper , NULL ) ;
2013-06-24 22:37:35 +02:00
if ( wor_hwtimer_id = = - 1 ) {
2013-06-21 22:36:48 +02:00
/* Signal hwtimer resource error, radio stays in RX,
* so no big problem , only energy is wasted . */
rflags . KT_RES_ERR = true ;
}
}
else {
/* No ACK received so TOF is unpredictable */
rflags . TOF = 0 ;
/* CRC false or RX buffer full -> clear RX FIFO in both cases */
2014-07-31 20:49:35 +02:00
last_seq_num = 0 ; /* Reset for correct burst detection */
cc1100_spi_strobe ( CC1100_SIDLE ) ; /* Switch to IDLE (should already be)... */
cc1100_spi_strobe ( CC1100_SFRX ) ; /* ...for flushing the RX FIFO */
2013-06-21 22:36:48 +02:00
/* If packet interrupted this nodes send call,
* don ' t change anything after this point . */
2013-06-24 22:37:35 +02:00
if ( radio_state = = RADIO_AIR_FREE_WAITING ) {
2013-06-21 22:36:48 +02:00
cc1100_spi_strobe ( CC1100_SRX ) ;
hwtimer_wait ( IDLE_TO_RX_TIME ) ;
return ;
}
/* If currently sending, exit here (don't go to RX/WOR) */
2013-06-24 22:37:35 +02:00
if ( radio_state = = RADIO_SEND_BURST ) {
2013-06-21 22:36:48 +02:00
cc1100_statistic . packets_in_while_tx + + ;
return ;
}
/* No valid packet, so go back to RX/WOR as soon as possible */
cc1100_go_receive ( ) ;
}
2010-09-22 15:10:42 +02:00
}