contiki/cpu/avr/radio/rf230bb/rf230bb.c

2284 lines
68 KiB
C

/*
* Copyright (c) 2007, Swedish Institute of Computer Science
* All rights reserved.
*
* Additional fixes for AVR contributed by:
*
* David Kopf dak664@embarqmail.com
* Ivan Delamer delamer@ieee.com
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*
*/
/*
* This code is almost device independent and should be easy to port.
* Ported to Atmel RF230 21Feb2010 by dak
*/
#include <stdio.h>
#include <string.h>
#include "contiki.h"
#if defined(__AVR__)
#include <avr/io.h>
#include <dev/watchdog.h>
//_delay_us has the potential to use floating point which brings the 256 byte clz table into RAM
//#include <util/delay.h>
//#define delay_us( us ) ( _delay_us( ( us ) ) )
//_delay_loop_2(uint16_t count) is 4 CPU cycles per iteration, up to 32 milliseconds at 8MHz
#include <util/delay_basic.h>
#define delay_us( us ) ( _delay_loop_2(1+((unsigned long long)us*F_CPU)/4000000UL) )
#include <avr/pgmspace.h>
#elif defined(__MSP430__)
#include <io.h>
#endif
#include "dev/leds.h"
#include "dev/spi.h"
#include "rf230bb.h"
#include "net/packetbuf.h"
#include "net/rime/rimestats.h"
#include "net/netstack.h"
#define WITH_SEND_CCA 0
/* Timestamps have not been tested */
#if RF230_CONF_TIMESTAMPS
#include "net/rime/timesynch.h"
#define TIMESTAMP_LEN 3
#else /* RF230_CONF_TIMESTAMPS */
#define TIMESTAMP_LEN 0
#endif /* RF230_CONF_TIMESTAMPS */
/* Nonzero FOOTER_LEN has not been tested */
#define FOOTER_LEN 0
/* RF230_CONF_CHECKSUM=0 for automatic hardware checksum */
#ifndef RF230_CONF_CHECKSUM
#define RF230_CONF_CHECKSUM 0
#endif
/* Autoack setting ignored in non-extended mode */
#ifndef RF230_CONF_AUTOACK
#define RF230_CONF_AUTOACK 1
#endif
/* We need to turn off autoack in promiscuous mode */
#if RF230_CONF_AUTOACK
static bool is_promiscuous;
#endif
/* Poll mode disabled by default */
uint8_t poll_mode = 0;
/* RF230_CONF_FRAME_RETRIES is 1 plus the number written to the hardware. */
/* Valid range 1-16, zero disables extended mode. */
#ifndef RF230_CONF_FRAME_RETRIES
#ifdef RF230_CONF_AUTORETRIES /* Support legacy definition. */
#define RF230_CONF_FRAME_RETRIES RF230_CONF_AUTORETRIES
#else
#define RF230_CONF_FRAME_RETRIES 0 /* Extended mode disabled by default. */
#endif
#endif
/* In extended mode (FRAME_RETRIES>0) the tx routine waits for hardware
* processing of an expected ACK and returns RADIO_TX_OK/NOACK result.
* In non-extended mode the ACK is treated as a normal rx packet.
* If the caller needs the ACK to be returned as an rx packet,
* RF230_INSERTACK will generate one based on the hardware result.
* This is triggered when the read routine is called with a buffer
* length of three (the ack length).
* In extended nmode it can be enabled by default to support either
* method. In nonextended mode it would pass an extra ACK to RDCs
* that use the TX_OK result to signal a successful ACK.
* Adds 100 bytes of program flash and two bytes of RAM.
*/
#if RF320_CONF_INSERTACK && RF230_CONF_FRAME_RETRIES
#define RF230_INSERTACK 1
uint8_t ack_pending,ack_seqnum;
#endif
/* RF230_CONF_CSMA_RETRIES is number of random-backoff/CCA retries. */
/* The hardware will accept 0-7, but 802.15.4-2003 only allows 5 maximum */
/* In RF231/128RFA1, a value of 7 means no CSMA bebofe the Tx. */
/* CSMA backoffs are long and can block radio duty cycling
* over several channel check periods! */
/* Used only if RF230_CONF_FRAME_RETRIES > 0. */
#ifndef RF230_CONF_CSMA_RETRIES
#define RF230_CONF_CSMA_RETRIES 5
#endif
//Automatic and manual CRC both append 2 bytes to packets
#if RF230_CONF_CHECKSUM || defined(RF230BB_HOOK_TX_PACKET)
#include "lib/crc16.h"
#endif
#define CHECKSUM_LEN 2
/* Note the AUX_LEN is equal to the CHECKSUM_LEN in any tested configurations to date! */
#define AUX_LEN (CHECKSUM_LEN + TIMESTAMP_LEN + FOOTER_LEN)
#if AUX_LEN != CHECKSUM_LEN
#warning RF230 Untested Configuration!
#endif
static rtimer_clock_t rf230_last_rx_packet_timestamp;
struct timestamp {
uint16_t time;
uint8_t authority_level;
};
#define FOOTER1_CRC_OK 0x80
#define FOOTER1_CORRELATION 0x7f
/* Leave radio on when USB powered or for testing low power protocols */
/* This allows DEBUGFLOW indication of packets received when the radio is "off" */
#if JACKDAW
#define RADIOALWAYSON 1
#else
#define RADIOALWAYSON 0
#define RADIOSLEEPSWHENOFF 1
#endif
/* RS232 delays will cause 6lowpan fragment overruns! Use DEBUGFLOW instead. */
#define DEBUG 0
#if DEBUG
#define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#define PRINTSHORT(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
#else
#define PRINTF(...)
#define PRINTSHORT(...)
#endif
#if DEBUG>1
/* Output format is suitable for text2pcap to convert to wireshark pcap file.
* Use $text2pcap -e 0x809a (these_outputs) capture.pcap
* Since the hardware calculates and appends the two byte checksum to Tx packets,
* we just add two zero bytes to the packet dump. Don't forget to enable wireshark
* 802.15.4 dissection even when the checksum is wrong!
*/
#endif
/* See clock.c and httpd-cgi.c for RADIOSTATS code */
#if AVR_WEBSERVER
#define RADIOSTATS 1
#endif
#if RADIOSTATS
uint16_t RF230_sendpackets,RF230_receivepackets,RF230_sendfail,RF230_receivefail;
#endif
#if RADIO_CONF_CALIBRATE_INTERVAL
/* Set in clock.c every 256 seconds */
/* The calibration is automatic when the radio turns on, so not needed when duty cycling */
uint8_t rf230_calibrate;
uint8_t rf230_calibrated; //for debugging, prints from main loop when calibration occurs
#endif
/* Track flow through mac, rdc and radio drivers, see contiki-raven-main.c for example of use */
#if DEBUGFLOWSIZE
extern uint8_t debugflowsize,debugflow[DEBUGFLOWSIZE];
#define DEBUGFLOW(c) if (debugflowsize<(DEBUGFLOWSIZE-1)) debugflow[debugflowsize++]=c
#else
#define DEBUGFLOW(c)
#endif
static radio_status_t radio_set_trx_state(uint8_t new_state);
/* XXX hack: these will be made as Chameleon packet attributes */
#if RF230_CONF_TIMESTAMPS
rtimer_clock_t rf230_time_of_arrival, rf230_time_of_departure;
int rf230_authority_level_of_sender;
static rtimer_clock_t setup_time_for_transmission;
static unsigned long total_time_for_transmission, total_transmission_len;
static int num_transmissions;
#endif
#if defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega128RFR2__) || defined(__AVR_ATmega256RFR2__)
volatile uint8_t rf230_wakewait, rf230_txendwait, rf230_ccawait;
#endif
uint8_t volatile rf230_pending;
/* RF230 hardware delay times, from datasheet */
typedef enum{
TIME_TO_ENTER_P_ON = 510, /**< Transition time from VCC is applied to P_ON - most favorable case! */
TIME_P_ON_TO_TRX_OFF = 510, /**< Transition time from P_ON to TRX_OFF. */
TIME_SLEEP_TO_TRX_OFF = 880, /**< Transition time from SLEEP to TRX_OFF. */
TIME_RESET = 6, /**< Time to hold the RST pin low during reset */
TIME_ED_MEASUREMENT = 140, /**< Time it takes to do a ED measurement. */
TIME_CCA = 140, /**< Time it takes to do a CCA. */
TIME_PLL_LOCK = 150, /**< Maximum time it should take for the PLL to lock. */
TIME_FTN_TUNING = 25, /**< Maximum time it should take to do the filter tuning. */
TIME_NOCLK_TO_WAKE = 6, /**< Transition time from *_NOCLK to being awake. */
TIME_CMD_FORCE_TRX_OFF = 1, /**< Time it takes to execute the FORCE_TRX_OFF command. */
TIME_TRX_OFF_TO_PLL_ACTIVE = 180, /**< Transition time from TRX_OFF to: RX_ON, PLL_ON, TX_ARET_ON and RX_AACK_ON. */
TIME_STATE_TRANSITION_PLL_ACTIVE = 1, /**< Transition time from PLL active state to another. */
}radio_trx_timing_t;
/*---------------------------------------------------------------------------*/
PROCESS(rf230_process, "RF230 driver");
/*---------------------------------------------------------------------------*/
int rf230_interrupt(void);
static int rf230_on(void);
static int rf230_off(void);
static int rf230_read(void *buf, unsigned short bufsize);
static int rf230_prepare(const void *data, unsigned short len);
static int rf230_transmit(unsigned short len);
static int rf230_send(const void *data, unsigned short len);
static int rf230_receiving_packet(void);
static int rf230_pending_packet(void);
static int rf230_cca(void);
uint8_t rf230_last_correlation,rf230_last_rssi,rf230_smallest_rssi;
static void
set_poll_mode(bool enable)
{
poll_mode = enable;
if(poll_mode) {
rf230_set_rpc(0x0); /* Disbable all RPC features */
radio_set_trx_state(RX_ON);
hal_subregister_write(SR_IRQ_MASK, RF230_SUPPORTED_INTERRUPT_MASK);
/* hal_register_write(RG_IRQ_MASK, 0xFF); */
} else {
/* Initialize and enable interrupts */
rf230_set_rpc(0xFF); /* Enable all RPC features. Only XRFR2 radios */
radio_set_trx_state(RX_AACK_ON);
}
}
static bool
get_poll_mode(void)
{
return poll_mode;
}
static void
set_frame_filtering(bool i)
{
if(i)
hal_subregister_write(SR_AACK_PROM_MODE, 0);
else {
hal_subregister_write(SR_AACK_PROM_MODE, 1);
}
}
static bool
get_frame_filtering(void)
{
int i = hal_subregister_read(SR_AACK_PROM_MODE);
if(i)
return 0;
return 1;
}
static void
set_auto_ack(bool i)
{
if(i)
hal_subregister_write(SR_AACK_DIS_ACK, 0);
else
hal_subregister_write(SR_AACK_DIS_ACK, 1);
}
static bool
get_auto_ack(void)
{
int i = hal_subregister_read(SR_AACK_DIS_ACK);
if(i)
return 0;
return 1;
}
uint16_t
rf230_get_panid(void)
{
unsigned pan;
uint8_t byte;
byte = hal_register_read(RG_PAN_ID_1);
pan = byte;
byte = hal_register_read(RG_PAN_ID_0);
pan = (pan << 8) + byte;
return pan;
}
static void
rf230_set_panid(uint16_t pan)
{
hal_register_write(RG_PAN_ID_1, (pan >> 8));
hal_register_write(RG_PAN_ID_0, (pan & 0xFF));
}
static uint16_t
rf230_get_short_addr(void)
{
unsigned char a0, a1;
a0 = hal_register_read(RG_SHORT_ADDR_0);
a1 = hal_register_read(RG_SHORT_ADDR_1);
return (a1 << 8) | a0;
}
static void
rf230_set_short_addr(uint16_t addr)
{
hal_register_write(RG_SHORT_ADDR_0, (addr & 0xFF));
hal_register_write(RG_SHORT_ADDR_1, (addr >> 8));
}
#define RSSI_BASE_VAL (-90)
/* Returns the current CCA threshold in dBm */
static radio_value_t
rf230_get_cca_threshold()
{
radio_value_t cca_thresh = 0;
cca_thresh = hal_subregister_read(SR_CCA_ED_THRES);
cca_thresh = RSSI_BASE_VAL + 2 * cca_thresh;
return cca_thresh;
}
/* Sets the CCA threshold in dBm */
static radio_value_t
rf230_set_cca_threshold(radio_value_t cca_thresh)
{
if(cca_thresh > -60) /* RSSI_BASE_VAL - 2 * 0xF */
cca_thresh = -60;
cca_thresh = (RSSI_BASE_VAL - cca_thresh)/2;
if(cca_thresh < 0)
cca_thresh = - cca_thresh;
hal_subregister_write(SR_CCA_ED_THRES, cca_thresh);
return cca_thresh;
}
/*---------------------------------------------------------------------------*/
static radio_result_t
get_value(radio_param_t param, radio_value_t *value)
{
if(!value) {
return RADIO_RESULT_INVALID_VALUE;
}
switch(param) {
case RADIO_PARAM_POWER_MODE:
*value = rf230_is_sleeping() ? RADIO_POWER_MODE_OFF : RADIO_POWER_MODE_ON;
return RADIO_RESULT_OK;
case RADIO_PARAM_TX_MODE:
return RADIO_RESULT_OK;
case RADIO_PARAM_CHANNEL:
*value = (radio_value_t)rf230_get_channel();
return RADIO_RESULT_OK;
case RADIO_PARAM_PAN_ID:
*value = rf230_get_panid();
return RADIO_RESULT_OK;
case RADIO_PARAM_16BIT_ADDR:
*value = rf230_get_short_addr();
return RADIO_RESULT_OK;
case RADIO_PARAM_RX_MODE:
*value = 0;
if(get_frame_filtering()) {
*value |= RADIO_RX_MODE_ADDRESS_FILTER;
}
if(get_auto_ack()) {
*value |= RADIO_RX_MODE_AUTOACK;
}
if(get_poll_mode()) {
*value |= RADIO_RX_MODE_POLL_MODE;
}
return RADIO_RESULT_OK;
case RADIO_PARAM_TXPOWER:
*value = rf230_get_txpower();
return RADIO_RESULT_OK;
case RADIO_PARAM_CCA_THRESHOLD:
*value = rf230_get_cca_threshold();
return RADIO_RESULT_OK;
case RADIO_PARAM_RSSI:
*value = rf230_get_raw_rssi();
return RADIO_RESULT_OK;
case RADIO_CONST_CHANNEL_MIN:
*value = RF230_MIN_CHANNEL;
return RADIO_RESULT_OK;
case RADIO_CONST_CHANNEL_MAX:
*value = RF230_MAX_CHANNEL;;
return RADIO_RESULT_OK;
case RADIO_CONST_TXPOWER_MIN:
*value = TX_PWR_MIN;
return RADIO_RESULT_OK;
case RADIO_CONST_TXPOWER_MAX:
*value = TX_PWR_MAX;
return RADIO_RESULT_OK;
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
}
/*---------------------------------------------------------------------------*/
static radio_result_t
set_value(radio_param_t param, radio_value_t value)
{
switch(param) {
case RADIO_PARAM_POWER_MODE:
if(value == RADIO_POWER_MODE_ON) {
rf230_on();
return RADIO_RESULT_OK;
}
if(value == RADIO_POWER_MODE_OFF) {
rf230_off();
return RADIO_RESULT_OK;
}
return RADIO_RESULT_INVALID_VALUE;
case RADIO_PARAM_CHANNEL:
if(value < RF230_MIN_CHANNEL ||
value > RF230_MAX_CHANNEL) {
return RADIO_RESULT_INVALID_VALUE;
}
rf230_set_channel(value);
return RADIO_RESULT_OK;
case RADIO_PARAM_TX_MODE:
return RADIO_RESULT_OK;
case RADIO_PARAM_PAN_ID:
rf230_set_panid(value & 0xffff);
return RADIO_RESULT_OK;
case RADIO_PARAM_16BIT_ADDR:
rf230_set_short_addr(value & 0xffff);
return RADIO_RESULT_OK;
case RADIO_PARAM_RX_MODE:
if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER | RADIO_RX_MODE_POLL_MODE |
RADIO_RX_MODE_AUTOACK)) {
return RADIO_RESULT_INVALID_VALUE;
}
set_frame_filtering((value & RADIO_RX_MODE_ADDRESS_FILTER) != 0);
set_auto_ack((value & RADIO_RX_MODE_AUTOACK) != 0);
set_poll_mode((value & RADIO_RX_MODE_POLL_MODE) != 0);
return RADIO_RESULT_OK;
case RADIO_PARAM_TXPOWER:
/* MIN = 15, MAX = 0 */
if(value > TX_PWR_MIN || value < TX_PWR_MAX) {
return RADIO_RESULT_INVALID_VALUE;
}
rf230_set_txpower(value);
return RADIO_RESULT_OK;
case RADIO_PARAM_CCA_THRESHOLD:
rf230_set_cca_threshold(value);
return RADIO_RESULT_OK;
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
}
/*---------------------------------------------------------------------------*/
static radio_result_t
get_object(radio_param_t param, void *dest, size_t size)
{
if(param == RADIO_PARAM_LAST_PACKET_TIMESTAMP) {
if(size != sizeof(rtimer_clock_t) || !dest) {
return RADIO_RESULT_INVALID_VALUE;
}
*(rtimer_clock_t *)dest = rf230_last_rx_packet_timestamp;
return RADIO_RESULT_OK;
}
return RADIO_RESULT_NOT_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
static radio_result_t
set_object(radio_param_t param, const void *src, size_t size)
{
return RADIO_RESULT_NOT_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
const struct radio_driver rf230_driver =
{
rf230_init,
rf230_prepare,
rf230_transmit,
rf230_send,
rf230_read,
rf230_cca,
rf230_receiving_packet,
rf230_pending_packet,
rf230_on,
rf230_off,
get_value,
set_value,
get_object,
set_object
};
uint8_t RF230_receive_on;
static uint8_t channel;
/* Received frames are buffered to rxframe in the interrupt routine in hal.c */
uint8_t rxframe_head,rxframe_tail;
hal_rx_frame_t rxframe[RF230_CONF_RX_BUFFERS];
/*----------------------------------------------------------------------------*/
/** \brief This function return the Radio Transceivers current state.
*
* \retval P_ON When the external supply voltage (VDD) is
* first supplied to the transceiver IC, the
* system is in the P_ON (Poweron) mode.
* \retval BUSY_RX The radio transceiver is busy receiving a
* frame.
* \retval BUSY_TX The radio transceiver is busy transmitting a
* frame.
* \retval RX_ON The RX_ON mode enables the analog and digital
* receiver blocks and the PLL frequency
* synthesizer.
* \retval TRX_OFF In this mode, the SPI module and crystal
* oscillator are active.
* \retval PLL_ON Entering the PLL_ON mode from TRX_OFF will
* first enable the analog voltage regulator. The
* transceiver is ready to transmit a frame.
* \retval BUSY_RX_AACK The radio was in RX_AACK_ON mode and received
* the Start of Frame Delimiter (SFD). State
* transition to BUSY_RX_AACK is done if the SFD
* is valid.
* \retval BUSY_TX_ARET The radio transceiver is busy handling the
* auto retry mechanism.
* \retval RX_AACK_ON The auto acknowledge mode of the radio is
* enabled and it is waiting for an incomming
* frame.
* \retval TX_ARET_ON The auto retry mechanism is enabled and the
* radio transceiver is waiting for the user to
* send the TX_START command.
* \retval RX_ON_NOCLK The radio transceiver is listening for
* incomming frames, but the CLKM is disabled so
* that the controller could be sleeping.
* However, this is only true if the controller
* is run from the clock output of the radio.
* \retval RX_AACK_ON_NOCLK Same as the RX_ON_NOCLK state, but with the
* auto acknowledge module turned on.
* \retval BUSY_RX_AACK_NOCLK Same as BUSY_RX_AACK, but the controller
* could be sleeping since the CLKM pin is
* disabled.
* \retval STATE_TRANSITION The radio transceiver's state machine is in
* transition between two states.
*/
uint8_t
radio_get_trx_state(void)
{
return hal_subregister_read(SR_TRX_STATUS);
}
/*----------------------------------------------------------------------------*/
/** \brief This function checks if the radio transceiver is sleeping.
*
* \retval true The radio transceiver is in SLEEP or one of the *_NOCLK
* states.
* \retval false The radio transceiver is not sleeping.
*/
static bool rf230_is_sleeping(void)
{
bool sleeping = false;
/* The radio transceiver will be at SLEEP or one of the *_NOCLK states only if */
/* the SLP_TR pin is high. */
if (hal_get_slptr() != 0){
sleeping = true;
}
return sleeping;
}
/*----------------------------------------------------------------------------*/
/** \brief This function will reset the state machine (to TRX_OFF) from any of
* its states, except for the SLEEP state.
*/
static void
radio_reset_state_machine(void)
{
/* The data sheet is not clear on what happens when slptr is raised in RX on
* states, it "remains in the new state and returns to the preceding state
* when slptr is lowered". Possibly that is why there is an undocumented
* TIME_NOCLK_TO_WAKE delay here?
*/
if (hal_get_slptr()) {
DEBUGFLOW('V');
hal_set_slptr_low();
delay_us(TIME_NOCLK_TO_WAKE);
}
hal_subregister_write(SR_TRX_CMD, CMD_FORCE_TRX_OFF);
delay_us(TIME_CMD_FORCE_TRX_OFF);
}
/*---------------------------------------------------------------------------*/
static char
rf230_isidle(void)
{
uint8_t radio_state;
/* Contikimac can turn the radio off during an interrupt, so we always check
* slptr before doing the SPI transfer. The caller must also make this test
* if it could otherwise hang waiting for idle! */
if (hal_get_slptr()) {
if (RF230_receive_on) DEBUGFLOW('-');
return 1;
}
else {
radio_state = hal_subregister_read(SR_TRX_STATUS);
if (radio_state != BUSY_TX_ARET &&
radio_state != BUSY_RX_AACK &&
radio_state != STATE_TRANSITION &&
radio_state != BUSY_RX &&
radio_state != BUSY_TX) {
return(1);
}
else {
return(0);
}
}
}
static void
rf230_waitidle(void)
{
/* TX_ARET with multiple csma retries can take a very long time to finish */
while (1) {
if (hal_get_slptr()) return;
if (rf230_isidle()) break;
}
}
/* Set reduced power consumption for AtMegaXXXRFR2 MCU's. See AT02594 */
static uint8_t rpc = 0xFF; /* Default max power save */
void
rf230_set_rpc(uint8_t data)
{
rpc = data;
}
uint8_t
rf230_get_rpc(void)
{
return rpc;
}
/** \brief This function will change the current state of the radio
* transceiver's internal state machine.
*
* \param new_state Here is a list of possible states:
* - RX_ON Requested transition to RX_ON state.
* - TRX_OFF Requested transition to TRX_OFF state.
* - PLL_ON Requested transition to PLL_ON state.
* - RX_AACK_ON Requested transition to RX_AACK_ON state.
* - TX_ARET_ON Requested transition to TX_ARET_ON state.
*
* \retval RADIO_SUCCESS Requested state transition completed
* successfully.
* \retval RADIO_INVALID_ARGUMENT Supplied function parameter out of bounds.
* \retval RADIO_WRONG_STATE Illegal state to do transition from.
* \retval RADIO_BUSY_STATE The radio transceiver is busy.
* \retval RADIO_TIMED_OUT The state transition could not be completed
* within resonable time.
*/
static radio_status_t
radio_set_trx_state(uint8_t new_state)
{
uint8_t current_state;
/*Check function parameter and current state of the radio transceiver.*/
if (!((new_state == TRX_OFF) ||
(new_state == RX_ON) ||
(new_state == PLL_ON) ||
(new_state == RX_AACK_ON) ||
(new_state == TX_ARET_ON))){
return RADIO_INVALID_ARGUMENT;
}
if (hal_get_slptr()) {
DEBUGFLOW('W');
return RADIO_WRONG_STATE;
}
/* Wait for radio to finish previous operation */
rf230_waitidle();
current_state = radio_get_trx_state();
if (new_state == current_state){
return RADIO_SUCCESS;
}
/* At this point it is clear that the requested new_state is: */
/* TRX_OFF, RX_ON, PLL_ON, RX_AACK_ON or TX_ARET_ON. */
/* The radio transceiver can be in one of the following states: */
/* TRX_OFF, RX_ON, PLL_ON, RX_AACK_ON, TX_ARET_ON. */
if(new_state == TRX_OFF){
if (hal_get_slptr()) DEBUGFLOW('K');DEBUGFLOW('K');DEBUGFLOW('A'+hal_subregister_read(SR_TRX_STATUS));
radio_reset_state_machine(); /* Go to TRX_OFF from any state. */
} else {
/* It is not allowed to go from RX_AACK_ON or TX_AACK_ON and directly to */
/* TX_AACK_ON or RX_AACK_ON respectively. Need to go via PLL_ON. */
/* (Old datasheets allowed other transitions, but this code complies with */
/* the current specification for RF230, RF231 and 128RFA1.) */
if (((new_state == TX_ARET_ON) && (current_state == RX_AACK_ON)) ||
((new_state == RX_AACK_ON) && (current_state == TX_ARET_ON))){
/* First do intermediate state transition to PLL_ON. */
/* The final state transition is handled after the if-else if. */
hal_subregister_write(SR_TRX_CMD, PLL_ON);
delay_us(TIME_STATE_TRANSITION_PLL_ACTIVE);
}
/* Any other state transition can be done directly. */
hal_subregister_write(SR_TRX_CMD, new_state);
/* When the PLL is active most states can be reached in 1us. However, from */
/* TRX_OFF the PLL needs time to activate. */
if (current_state == TRX_OFF){
#if defined(__AVR_ATmega128RFR2__) || defined(__AVR_ATmega256RFR2__)
hal_subregister_write(SR_TRX_RPC, rpc); /* Enable RPC features */
#endif
delay_us(TIME_TRX_OFF_TO_PLL_ACTIVE);
} else {
delay_us(TIME_STATE_TRANSITION_PLL_ACTIVE);
}
} /* end: if(new_state == TRX_OFF) ... */
/* Verify state transition.
* Radio could have already switched to an RX_BUSY state, at least in cooja.
* Don't know what the hardware does but this would not be an error.*/
current_state = radio_get_trx_state();
if (current_state != new_state) {
if (((new_state == RX_ON) && (current_state == BUSY_RX)) ||
((new_state == RX_AACK_ON) && (current_state == BUSY_RX_AACK))) {
/* This is OK. */
} else {
DEBUGFLOW('N');DEBUGFLOW('A'+new_state);DEBUGFLOW('A'+radio_get_trx_state());DEBUGFLOW('N');
return RADIO_TIMED_OUT;
}
}
return RADIO_SUCCESS;
}
void
rf230_set_promiscuous_mode(bool isPromiscuous) {
#if RF230_CONF_AUTOACK
is_promiscuous = isPromiscuous;
/* TODO: Figure out when to pass promisc state to 802.15.4 */
radio_set_trx_state(is_promiscuous?RX_ON:RX_AACK_ON);
#endif
}
bool
rf230_is_ready_to_send() {
switch(radio_get_trx_state()) {
case BUSY_TX:
case BUSY_TX_ARET:
return false;
}
return true;
}
static void
flushrx(void)
{
/* Clear the length field to allow buffering of the next packet */
rxframe[rxframe_head].length=0;
rxframe_head++;
if (rxframe_head >= RF230_CONF_RX_BUFFERS) {
rxframe_head=0;
}
/* If another packet has been buffered, schedule another receive poll */
if (rxframe[rxframe_head].length) {
rf230_interrupt();
}
else {
rf230_pending = 0;
}
}
/*---------------------------------------------------------------------------*/
static void
radio_on(void)
{
// ENERGEST_OFF(ENERGEST_TYPE_LISTEN);//testing
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
RF230_receive_on = 1;
#ifdef RF230BB_HOOK_RADIO_ON
RF230BB_HOOK_RADIO_ON();
#endif
/* If radio is off (slptr high), turn it on */
if (hal_get_slptr()) {
ENERGEST_ON(ENERGEST_TYPE_LED_RED);
#if RF230BB_CONF_LEDONPORTE1
PORTE|=(1<<PE1); //ledon
#endif
#if defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega128RFR2__) || defined(__AVR_ATmega256RFR2__)
/* Use the poweron interrupt for delay */
rf230_wakewait=1;
{
uint8_t sreg = SREG;
sei();
if (hal_get_slptr() == 0) DEBUGFLOW('$');
hal_set_slptr_low();
{
int i;
for (i=0;i<10000;i++) {
if (!rf230_wakewait) break;
}
if (i>=10000) {DEBUGFLOW('G');DEBUGFLOW('g');DEBUGFLOW('A'+hal_subregister_read(SR_TRX_STATUS));}
}
SREG = sreg;
}
#else
/* SPI based radios. The wake time depends on board capacitance.
* Make sure the delay is long enough, as using SPI too soon will reset the MCU!
* Use 2x the nominal value for safety. 1.5x is not long enough for Raven!
*/
// uint8_t sreg = SREG;cli();
hal_set_slptr_low();
delay_us(2*TIME_SLEEP_TO_TRX_OFF);
// SREG=sreg;
#endif
}
#if RF230_CONF_AUTOACK
// radio_set_trx_state(is_promiscuous?RX_ON:RX_AACK_ON);
radio_set_trx_state(RX_AACK_ON);
#else
radio_set_trx_state(RX_ON);
#endif
rf230_waitidle();
}
static void
radio_off(void)
{
RF230_receive_on = 0;
if (hal_get_slptr()) {
DEBUGFLOW('F');
return;
}
#if RF230BB_CONF_LEDONPORTE1
PORTE&=~(1<<PE1); //ledoff
#endif
#ifdef RF230BB_HOOK_RADIO_OFF
RF230BB_HOOK_RADIO_OFF();
#endif
/* Wait for any transmission to end */
rf230_waitidle();
#if RADIOALWAYSON
/* Do not transmit autoacks when stack thinks radio is off */
radio_set_trx_state(RX_ON);
#else
/* Force the device into TRX_OFF.
* First make sure an interrupt did not initiate a sleep. */
if (hal_get_slptr()) {
DEBUGFLOW('?');
return;
}
radio_reset_state_machine();
#if RADIOSLEEPSWHENOFF
/* Sleep Radio */
hal_set_slptr_high();
ENERGEST_OFF(ENERGEST_TYPE_LED_RED);
#endif
#endif /* RADIOALWAYSON */
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
}
/*---------------------------------------------------------------------------*/
static void
set_txpower(uint8_t power)
{
if (power > TX_PWR_17_2DBM){
power=TX_PWR_17_2DBM;
}
if (hal_get_slptr()) {
DEBUGFLOW('f');
PRINTF("rf230_set_txpower:Sleeping"); //happens with cxmac
} else {
//DEBUGFLOW('g');
hal_subregister_write(SR_TX_PWR, power);
}
}
void rf230_setpendingbit(uint8_t value)
{
hal_subregister_write(SR_AACK_SET_PD, value);
}
#if 0
/*----------------------------------------------------------------------------*/
/**
\brief Calibrate the internal RC oscillator
This function calibrates the internal RC oscillator, based
on an external 32KHz crystal connected to TIMER2. In order to
verify the calibration result you can program the CKOUT fuse
and monitor the CPU clock on an I/O pin.
*/
#define AVR_ENTER_CRITICAL_REGION( ) {uint8_t volatile saved_sreg = SREG; cli( )
#define AVR_LEAVE_CRITICAL_REGION( ) SREG = saved_sreg;}
uint8_t osccal_original,osccal_calibrated;
void
calibrate_rc_osc_32k(void)
{
/* Calibrate RC Oscillator: The calibration routine is done by clocking TIMER2
* from the external 32kHz crystal while running an internal timer simultaneously.
* The internal timer will be clocked at the same speed as the internal RC
* oscillator, while TIMER2 is running at 32768 Hz. This way it is not necessary
* to use a timed loop, and keep track cycles in timed loop vs. optimization
* and compiler.
*/
uint8_t osccal_original = OSCCAL;
volatile uint16_t temp;
/* Start with current value, which for some MCUs could be in upper or lower range */
// PRR0 &= ~((1 << PRTIM2)|(1 << PRTIM1)); /* Enable Timer 1 and 2 */
TIMSK2 = 0x00; /* Disable Timer/Counter 2 interrupts. */
TIMSK1 = 0x00; /* Disable Timer/Counter 1 interrupts. */
/* Enable TIMER/COUNTER 2 to be clocked from the external 32kHz clock crystal.
* Then wait for the timer to become stable before doing any calibration.
*/
ASSR |= (1 << AS2);
// while (ASSR & ((1 << TCN2UB)|(1 << OCR2AUB)|(1 << TCR2AUB)|(1 << TCR2BUB))) { ; }
TCCR2B = 1 << CS20; /* run timer 2 at divide by 1 (32KHz) */
delay_us(50000UL); //crystal takes significant time to stabilize
AVR_ENTER_CRITICAL_REGION();
uint8_t counter = 128;
bool cal_ok = false;
do{
/* wait for timer to be ready for updated config */
TCCR1B = 1 << CS10;
while (ASSR & ((1 << TCN2UB)|(1 << OCR2AUB)|(1 << TCR2AUB)|(1 << TCR2BUB))) { ; }
TCNT2 = 0x80;
TCNT1 = 0;
TIFR2 = 0xFF; /* Clear TIFR2 flags (Yes, really) */
/* Wait for TIMER/COUNTER 2 to overflow. Stop TIMER/COUNTER 1 and 2, and
* read the counter value of TIMER/COUNTER 1. It will now contain the
* number of cpu cycles elapsed within the 3906.25 microsecond period.
*/
while (!(TIFR2 & (1 << TOV2))){
;
}
temp = TCNT1;
TCCR1B = 0;
/* Defining these as floating point introduces a lot of code and the 256 byte .clz table to RAM */
/* At 8 MHz we would expect 8*3906.25 = 31250 CPU clocks */
#define cal_upper 32812 //(31250*1.05) // 32812 = 0x802c
#define cal_lower 29687 //(31250*0.95) // 29687 = 0x73f7
/* Iteratively reduce the error to be within limits */
if (temp < cal_lower) {
/* Too slow. Put the hammer down. */
if (OSCCAL==0x7e) break; //stay in lowest range
if (OSCCAL==0xff) break;
OSCCAL++;
} else if (temp > cal_upper) {
/* Too fast, retard. */
if (OSCCAL==0x81) break; //stay in highest range
if (OSCCAL==0x00) break;
OSCCAL--;
} else {
/* The CPU clock frequency is now within +/- 0.5% of the target value. */
cal_ok = true;
}
counter--;
} while ((counter != 0) && (false == cal_ok));
osccal_calibrated=OSCCAL;
if (true != cal_ok) {
/* We failed, therefore restore previous OSCCAL value. */
OSCCAL = osccal_original;
}
OSCCAL = osccal_original;
TCCR2B = 0;
ASSR &= ~(1 << AS2);
/* Disable both timers again to save power. */
// PRR0 |= (1 << PRTIM2);/* |(1 << PRTIM1); */
AVR_LEAVE_CRITICAL_REGION();
}
#endif
/*---------------------------------------------------------------------------*/
int
rf230_init(void)
{
uint8_t i;
DEBUGFLOW('i');
/* Wait in case VCC just applied */
delay_us(TIME_TO_ENTER_P_ON);
/* Initialize Hardware Abstraction Layer */
hal_init();
/* Calibrate oscillator */
// printf_P(PSTR("\nBefore calibration OSCCAL=%x\n"),OSCCAL);
// calibrate_rc_osc_32k();
// printf_P(PSTR("After calibration OSCCAL=%x\n"),OSCCAL);
/* Set receive buffers empty and point to the first */
for (i=0;i<RF230_CONF_RX_BUFFERS;i++) {
rxframe[i].length=0;
}
rxframe_head=0;rxframe_tail=0;
/* Do full rf230 Reset */
hal_set_rst_low();
hal_set_slptr_low();
/* On powerup a TIME_RESET delay is needed here, however on some other MCU reset
* (JTAG, WDT, Brownout) the radio may be sleeping. It can enter an uncertain
* state (sending wrong hardware FCS for example) unless the full wakeup delay
* is done.
* Wake time depends on board capacitance; use 2x the nominal delay for safety.
* See www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=78725
*/
delay_us(2*TIME_SLEEP_TO_TRX_OFF);
//delay_us(TIME_RESET); /* Old impl. */
hal_set_rst_high();
/* Force transition to TRX_OFF */
hal_subregister_write(SR_TRX_CMD, CMD_FORCE_TRX_OFF);
delay_us(TIME_P_ON_TO_TRX_OFF);
/* Verify that it is a supported version */
/* Note gcc optimizes this away if DEBUG is not set! */
//ATMEGA128RFA1 - version 4, ID 31
uint8_t tvers = hal_register_read(RG_VERSION_NUM);
uint8_t tmanu = hal_register_read(RG_MAN_ID_0);
if ((tvers != RF230_REVA) && (tvers != RF230_REVB))
PRINTF("rf230: Unsupported version %u\n",tvers);
if (tmanu != SUPPORTED_MANUFACTURER_ID)
PRINTF("rf230: Unsupported manufacturer ID %u\n",tmanu);
PRINTF("rf230: Version %u, ID %u\n",tvers,tmanu);
rf230_warm_reset();
/* Start the packet receive process */
process_start(&rf230_process, NULL);
/* Leave radio in on state (?)*/
radio_on();
return 1;
}
/*---------------------------------------------------------------------------*/
/* Used to reinitialize radio parameters without losing pan and mac address, channel, power, etc. */
void rf230_warm_reset(void) {
#if RF230_CONF_SNEEZER && JACKDAW
/* Take jackdaw radio out of test mode */
#warning Manipulating PORTB pins for RF230 Sneezer mode!
PORTB &= ~(1<<7);
DDRB &= ~(1<<7);
#endif
hal_subregister_write(SR_IRQ_MASK, RF230_SUPPORTED_INTERRUPT_MASK);
/* Set up number of automatic retries 0-15
* (0 implies PLL_ON sends instead of the extended TX_ARET mode */
hal_subregister_write(SR_MAX_FRAME_RETRIES,
(RF230_CONF_FRAME_RETRIES > 0) ? (RF230_CONF_FRAME_RETRIES - 1) : 0 );
/* Set up carrier sense/clear channel assesment parameters for extended operating mode */
hal_subregister_write(SR_MAX_CSMA_RETRIES, RF230_CONF_CSMA_RETRIES );//highest allowed retries
hal_register_write(RG_CSMA_BE, 0x80); //min backoff exponent 0, max 8 (highest allowed)
hal_register_write(RG_CSMA_SEED_0,hal_register_read(RG_PHY_RSSI) );//upper two RSSI reg bits RND_VALUE are random in rf231
// hal_register_write(CSMA_SEED_1,42 );
/* CCA Mode Mode 1=Energy above threshold 2=Carrier sense only 3=Both 0=Either (RF231 only) */
//hal_subregister_write(SR_CCA_MODE,1); //1 is the power-on default
/* Carrier sense threshold (not implemented in RF230 or RF231) */
// hal_subregister_write(SR_CCA_CS_THRES,1);
/* Receiver sensitivity. If nonzero rf231/128rfa1 saves 0.5ma in rx mode */
/* Not implemented on rf230 but does not hurt to write to it */
#ifdef RF230_MIN_RX_POWER
#if RF230_MIN_RX_POWER > 84
#warning rf231 power threshold clipped to -48dBm by hardware register
hal_register_write(RG_RX_SYN, 0xf);
#elif RF230_MIN_RX_POWER < 0
#error RF230_MIN_RX_POWER can not be negative!
#endif
hal_register_write(RG_RX_SYN, RF230_MIN_RX_POWER/6 + 1); //1-15 -> -90 to -48dBm
#endif
/* CCA energy threshold = -91dB + 2*SR_CCA_ED_THRESH. Reset defaults to -77dB */
/* Use RF230 base of -91; RF231 base is -90 according to datasheet */
#ifdef RF230_CONF_CCA_THRES
#if RF230_CONF_CCA_THRES < -91
#warning
#warning RF230_CONF_CCA_THRES below hardware limit, setting to -91dBm
#warning
hal_subregister_write(SR_CCA_ED_THRES,0);
#elif RF230_CONF_CCA_THRES > -61
#warning
#warning RF230_CONF_CCA_THRES above hardware limit, setting to -61dBm
#warning
hal_subregister_write(SR_CCA_ED_THRES,15);
#else
hal_subregister_write(SR_CCA_ED_THRES,(RF230_CONF_CCA_THRES+91)/2);
#endif
#endif
/* Use automatic CRC unless manual is specified */
#if RF230_CONF_CHECKSUM
hal_subregister_write(SR_TX_AUTO_CRC_ON, 0);
#else
hal_subregister_write(SR_TX_AUTO_CRC_ON, 1);
#endif
/* Limit tx power for testing miniature Raven mesh */
#ifdef RF230_MAX_TX_POWER
set_txpower(RF230_MAX_TX_POWER); //0=3dbm 15=-17.2dbm
#endif
}
/*---------------------------------------------------------------------------*/
static uint8_t buffer[RF230_MAX_TX_FRAME_LENGTH+AUX_LEN];
static int
rf230_transmit(unsigned short payload_len)
{
int txpower;
uint8_t total_len;
uint8_t tx_result;
#if RF230_CONF_TIMESTAMPS
struct timestamp timestamp;
#endif /* RF230_CONF_TIMESTAMPS */
/* If radio is sleeping we have to turn it on first */
/* This automatically does the PLL calibrations */
if (hal_get_slptr()) {
#if defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega128RFR2__) || defined(__AVR_ATmega256RFR2__)
ENERGEST_ON(ENERGEST_TYPE_LED_RED);
#if RF230BB_CONF_LEDONPORTE1
PORTE|=(1<<PE1); //ledon
#endif
rf230_wakewait=1;
hal_set_slptr_low();
{
int i;
for (i=0;i<10000;i++) {
if (!rf230_wakewait) break;
}
if (i>=10000) {DEBUGFLOW('G');DEBUGFLOW('G');DEBUGFLOW('A'+hal_subregister_read(SR_TRX_STATUS));}
}
#else
hal_set_slptr_low();
DEBUGFLOW('j');
delay_us(2*TIME_SLEEP_TO_TRX_OFF); //extra delay (2x) depends on board capacitance
#endif
} else {
#if RADIO_CONF_CALIBRATE_INTERVAL
/* If nonzero, do periodic calibration. See clock.c */
if (rf230_calibrate) {
DEBUGFLOW('k');
hal_subregister_write(SR_PLL_CF_START,1); //takes 80us max
hal_subregister_write(SR_PLL_DCU_START,1); //takes 6us, concurrently
rf230_calibrate=0;
rf230_calibrated=1;
delay_us(80); //?
}
#endif
}
/* Wait for any previous operation or state transition to finish */
rf230_waitidle();
if(RF230_receive_on) {
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
}
/* Prepare to transmit */
#if RF230_CONF_FRAME_RETRIES
radio_set_trx_state(TX_ARET_ON);
DEBUGFLOW('t');
#else
radio_set_trx_state(PLL_ON);
DEBUGFLOW('T');
#endif
txpower = 0;
if(packetbuf_attr(PACKETBUF_ATTR_RADIO_TXPOWER) > 0) {
/* Remember the current transmission power */
txpower = rf230_get_txpower();
/* Set the specified transmission power */
set_txpower(packetbuf_attr(PACKETBUF_ATTR_RADIO_TXPOWER) - 1);
}
total_len = payload_len + AUX_LEN;
#if RF230_CONF_TIMESTAMPS
rtimer_clock_t txtime = timesynch_time();
#endif /* RF230_CONF_TIMESTAMPS */
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
/* No interrupts across frame download! */
HAL_ENTER_CRITICAL_REGION();
/* Toggle the SLP_TR pin to initiate the frame transmission, then transfer
* the frame. We have about 16 us + the on-air transmission time of 40 bits
* (for the synchronization header) before the transceiver sends the PHR. */
hal_set_slptr_high();
hal_set_slptr_low();
hal_frame_write(buffer, total_len);
HAL_LEAVE_CRITICAL_REGION();
PRINTF("rf230_transmit: %d\n", (int)total_len);
#if DEBUG>1
/* Note the dumped packet will have a zero checksum unless compiled with RF230_CONF_CHECKSUM
* since we don't know what it will be if calculated by the hardware.
*/
{
uint8_t i;
PRINTF("0000"); //Start a new wireshark packet
for (i=0;i<total_len;i++) PRINTF(" %02x",buffer[i]);
PRINTF("\n");
}
#endif
#if RADIOSTATS
RF230_sendpackets++;
#endif
/* We wait until transmission has ended so that we get an
accurate measurement of the transmission time.*/
rf230_waitidle();
/* Get the transmission result */
#if RF230_CONF_FRAME_RETRIES
tx_result = hal_subregister_read(SR_TRAC_STATUS);
#else
tx_result=RADIO_TX_OK;
#endif
#ifdef ENERGEST_CONF_LEVELDEVICE_LEVELS
ENERGEST_OFF_LEVEL(ENERGEST_TYPE_TRANSMIT,rf230_get_txpower());
#endif
/* Restore the transmission power */
if(packetbuf_attr(PACKETBUF_ATTR_RADIO_TXPOWER) > 0) {
set_txpower(txpower & 0xff);
}
#if RF230_CONF_TIMESTAMPS
setup_time_for_transmission = txtime - timestamp.time;
if(num_transmissions < 10000) {
total_time_for_transmission += timesynch_time() - txtime;
total_transmission_len += total_len;
num_transmissions++;
}
#endif /* RF230_CONF_TIMESTAMPS */
ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
if(RF230_receive_on) {
DEBUGFLOW('l');
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
radio_on();
} else {
#if RADIOALWAYSON
/* Enable reception */
radio_on();
#else
radio_off();
PRINTF("rf230_transmit: turning radio off\n");
#endif
}
#if RF230_INSERTACK
ack_pending = 0;
#endif
if (tx_result==1) { //success, data pending from addressee
tx_result=RADIO_TX_OK; //handle as ordinary success
}
if (tx_result==RADIO_TX_OK) {
RIMESTATS_ADD(lltx);
#if NETSTACK_CONF_WITH_RIME
if(packetbuf_attr(PACKETBUF_ATTR_RELIABLE))
RIMESTATS_ADD(ackrx); //ack was requested and received
#endif
#if RF230_INSERTACK
/* Not PAN broadcast to FFFF, and ACK was requested and received */
if (!((buffer[5]==0xff) && (buffer[6]==0xff)) && (buffer[0]&(1<<6)))
ack_pending=1;
#endif
} else if (tx_result==3) { //CSMA channel access failure
DEBUGFLOW('m');
RIMESTATS_ADD(contentiondrop);
PRINTF("rf230_transmit: Transmission never started\n");
tx_result = RADIO_TX_COLLISION;
} else if (tx_result==5) { //Expected ACK, none received
DEBUGFLOW('n');
tx_result = RADIO_TX_NOACK;
PRINTF("rf230_transmit: ACK not received\n");
RIMESTATS_ADD(badackrx); //ack was requested but not received
} else if (tx_result==7) { //Invalid (Can't happen since waited for idle above?)
DEBUGFLOW('o');
tx_result = RADIO_TX_ERR;
}
return tx_result;
}
/*---------------------------------------------------------------------------*/
static int
rf230_prepare(const void *payload, unsigned short payload_len)
{
int ret = 0;
uint8_t total_len,*pbuf;
#if RF230_CONF_TIMESTAMPS
struct timestamp timestamp;
#endif
#if RF230_CONF_CHECKSUM
uint16_t checksum;
#endif
#if RF230_INSERTACK
/* The sequence number is needed to construct the ack packet */
ack_seqnum=*(((uint8_t *)payload)+2);
#endif
DEBUGFLOW('p');
// PRINTF("rf230: sending %d bytes\n", payload_len);
// PRINTSHORT("s%d ",payload_len);
RIMESTATS_ADD(tx);
#if RF230_CONF_CHECKSUM
checksum = crc16_data(payload, payload_len, 0);
#endif
/* Copy payload to RAM buffer */
total_len = payload_len + AUX_LEN;
if (total_len > RF230_MAX_TX_FRAME_LENGTH){
#if RADIOSTATS
RF230_sendfail++;
#endif
PRINTF("rf230_prepare: packet too large (%d, max: %d)\n",total_len,RF230_MAX_TX_FRAME_LENGTH);
ret = -1;
goto bail;
}
pbuf=&buffer[0];
memcpy(pbuf,payload,payload_len);
pbuf+=payload_len;
#if RF230_CONF_CHECKSUM
memcpy(pbuf,&checksum,CHECKSUM_LEN);
pbuf+=CHECKSUM_LEN;
#endif
#if RF230_CONF_TIMESTAMPS
timestamp.authority_level = timesynch_authority_level();
timestamp.time = timesynch_time();
memcpy(pbuf,&timestamp,TIMESTAMP_LEN);
pbuf+=TIMESTAMP_LEN;
#endif
/*------------------------------------------------------------*/
#ifdef RF230BB_HOOK_TX_PACKET
#if !RF230_CONF_CHECKSUM
{ // Add a checksum before we log the packet out
uint16_t checksum;
checksum = crc16_data(payload, payload_len, 0);
memcpy(buffer+total_len-CHECKSUM_LEN,&checksum,CHECKSUM_LEN);
}
#endif /* RF230_CONF_CHECKSUM */
RF230BB_HOOK_TX_PACKET(buffer,total_len);
#endif
bail:
return ret;
}
/*---------------------------------------------------------------------------*/
static int
rf230_send(const void *payload, unsigned short payload_len)
{
int ret = 0;
#ifdef RF230BB_HOOK_IS_SEND_ENABLED
if(!RF230BB_HOOK_IS_SEND_ENABLED()) {
goto bail;
}
#endif
if((ret=rf230_prepare(payload, payload_len))) {
PRINTF("rf230_send: Unable to send, prep failed (%d)\n",ret);
goto bail;
}
ret = rf230_transmit(payload_len);
bail:
#if RADIOSTATS
if (ret) RF230_sendfail++;
#endif
return ret;
}
/*---------------------------------------------------------------------------*/
static int
rf230_off(void)
{
/* Don't do anything if we are already turned off. */
if(RF230_receive_on == 0) {
//if (!hal_get_slptr()) DEBUGFLOW('5');
return 0;
}
//if (hal_get_slptr()) DEBUGFLOW('6');
/* If we are currently receiving a packet, we still call radio_off(),
as that routine waits until Rx is complete (packet uploaded in ISR
so no worries about losing it). The transmit routine may also turn
+ the radio off on a return to sleep. rf230_isidle checks for that. */
if (!rf230_isidle()) {
//DEBUGFLOW('X');DEBUGFLOW('X');DEBUGFLOW('A'+hal_subregister_read(SR_TRX_STATUS));
PRINTF("rf230_off: busy receiving\r\n");
//return 1;
}
radio_off();
return 0;
}
/*---------------------------------------------------------------------------*/
static int
rf230_on(void)
{
if(RF230_receive_on) {
//if (hal_get_slptr()) DEBUGFLOW('Q');//Cooja TODO: shows sleeping occasionally
return 1;
}
radio_on();
return 1;
}
/*---------------------------------------------------------------------------*/
uint8_t
rf230_get_channel(void)
{
//jackdaw reads zero channel, raven reads correct channel?
//return hal_subregister_read(SR_CHANNEL);
return channel;
}
/*---------------------------------------------------------------------------*/
void
rf230_set_channel(uint8_t c)
{
/* Wait for any transmission to end. */
PRINTF("rf230: Set Channel %u\n",c);
rf230_waitidle();
channel=c;
hal_subregister_write(SR_CHANNEL, c);
}
/*---------------------------------------------------------------------------*/
void
rf230_listen_channel(uint8_t c)
{
/* Same as set channel but forces RX_ON state for sniffer or energy scan */
// PRINTF("rf230: Listen Channel %u\n",c);
rf230_set_channel(c);
radio_set_trx_state(RX_ON);
}
/*---------------------------------------------------------------------------*/
void
rf230_set_pan_addr(unsigned pan,
unsigned addr,
const uint8_t ieee_addr[8])
{
PRINTF("rf230: PAN=%x Short Addr=%x\n",pan,addr);
rf230_set_panid(pan);
rf230_set_short_addr(addr);
if (ieee_addr != NULL) {
PRINTF("MAC=%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_7, *ieee_addr++);
PRINTF(":%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_6, *ieee_addr++);
PRINTF(":%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_5, *ieee_addr++);
PRINTF(":%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_4, *ieee_addr++);
PRINTF(":%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_3, *ieee_addr++);
PRINTF(":%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_2, *ieee_addr++);
PRINTF(":%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_1, *ieee_addr++);
PRINTF(":%x",*ieee_addr);
hal_register_write(RG_IEEE_ADDR_0, *ieee_addr);
PRINTF("\n");
}
}
/* From ISR context */
void
rf230_get_last_rx_packet_timestamp(void)
{
rf230_last_rx_packet_timestamp = RTIMER_NOW();
}
/*---------------------------------------------------------------------------*/
/*
* Interrupt leaves frame intact in FIFO.
*/
#if RF230_CONF_TIMESTAMPS
static volatile rtimer_clock_t interrupt_time;
static volatile int interrupt_time_set;
#endif /* RF230_CONF_TIMESTAMPS */
int
rf230_interrupt(void)
{
/* Poll the receive process, unless the stack thinks the radio is off */
#if RADIOALWAYSON
if (RF230_receive_on) {
DEBUGFLOW('+');
#endif
#if RF230_CONF_TIMESTAMPS
interrupt_time = timesynch_time();
interrupt_time_set = 1;
#endif /* RF230_CONF_TIMESTAMPS */
process_poll(&rf230_process);
rf230_pending = 1;
#if RADIOSTATS //TODO:This will double count buffered packets
RF230_receivepackets++;
#endif
RIMESTATS_ADD(llrx);
#if RADIOALWAYSON
} else {
DEBUGFLOW('-');
rxframe[rxframe_head].length=0;
}
#endif
return 1;
}
/*---------------------------------------------------------------------------*/
/* Process to handle input packets
* Receive interrupts cause this process to be polled
* It calls the core MAC layer which calls rf230_read to get the packet
* rf230processflag can be printed in the main idle loop for debugging
*/
#if 0
uint8_t rf230processflag;
#define RF230PROCESSFLAG(arg) rf230processflag=arg
#else
#define RF230PROCESSFLAG(arg)
#endif
PROCESS_THREAD(rf230_process, ev, data)
{
int len;
PROCESS_BEGIN();
RF230PROCESSFLAG(99);
while(1) {
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
RF230PROCESSFLAG(42);
packetbuf_clear();
/* Turn off interrupts to avoid ISR writing to the same buffers we are reading. */
HAL_ENTER_CRITICAL_REGION();
len = rf230_read(packetbuf_dataptr(), PACKETBUF_SIZE);
/* Restore interrupts. */
HAL_LEAVE_CRITICAL_REGION();
PRINTF("rf230_read: %u bytes lqi %u\n",len,rf230_last_correlation);
if(is_promiscuous) {
uint8_t i;
unsigned const char * rxdata = packetbuf_dataptr();
/* Print magic */
putchar(0xC1);
putchar(0x1F);
putchar(0xFE);
putchar(0x72);
/* Print version */
putchar(0x01);
/* Print CMD == frame */
putchar(0x00);
putchar(len+3);
for (i=0;i<len;i++) putchar(rxdata[i]);
printf("\n");
}
#if DEBUG>1
{
uint8_t i;
unsigned const char * rxdata = packetbuf_dataptr();
PRINTF("0000");
for (i=0;i<len+AUX_LEN;i++) PRINTF(" %02x",rxdata[i]);
PRINTF("\n");
}
#endif
RF230PROCESSFLAG(1);
if(len > 0) {
packetbuf_set_datalen(len);
RF230PROCESSFLAG(2);
NETSTACK_RDC.input();
} else {
#if RADIOSTATS
RF230_receivefail++;
#endif
}
}
PROCESS_END();
}
/* Read packet that was uploaded from Radio in ISR, else return zero.
* The two-byte checksum is appended but the returned length does not include it.
* Frames are buffered in the interrupt routine so this routine
* does not access the hardware or change its status.
* However, this routine must be called with interrupts disabled to avoid ISR
* writing to the same buffer we are reading.
* As a result, PRINTF cannot be used in here.
*/
/*---------------------------------------------------------------------------*/
static int
rf230_read(void *buf, unsigned short bufsize)
{
uint8_t len,*framep;
#if FOOTER_LEN
uint8_t footer[FOOTER_LEN];
#endif
#if RF230_CONF_CHECKSUM
uint16_t checksum;
#endif
#if RF230_CONF_TIMESTAMPS
struct timestamp t;
#endif
#if RF230_INSERTACK
/* Return an ACK to the mac layer */
if(ack_pending && bufsize == 3){
ack_pending=0;
uint8_t *buff=(uint8_t *)buf;
buff[0]=2;
buff[1]=0;
buff[2]=ack_seqnum;
return bufsize;
}
#endif
/* The length includes the twp-byte checksum but not the LQI byte */
len=rxframe[rxframe_head].length;
if (len==0) {
#if RADIOALWAYSON && DEBUGFLOWSIZE
if (RF230_receive_on==0) {if (debugflow[debugflowsize-1]!='z') DEBUGFLOW('z');} //cxmac calls with radio off?
#endif
flushrx();
return 0;
}
#if RF230_CONF_TIMESTAMPS
if(interrupt_time_set) {
rf230_time_of_arrival = interrupt_time;
interrupt_time_set = 0;
} else {
rf230_time_of_arrival = 0;
}
rf230_time_of_departure = 0;
#endif /* RF230_CONF_TIMESTAMPS */
if(len > RF230_MAX_TX_FRAME_LENGTH) {
/* Oops, we must be out of sync. */
DEBUGFLOW('u');
flushrx();
RIMESTATS_ADD(badsynch);
return 0;
}
if(len <= AUX_LEN) {
DEBUGFLOW('s');
//PRINTF("len <= AUX_LEN\n");
flushrx();
RIMESTATS_ADD(tooshort);
return 0;
}
if(len - AUX_LEN > bufsize) {
DEBUGFLOW('v');
//PRINTF("len - AUX_LEN > bufsize\n");
flushrx();
RIMESTATS_ADD(toolong);
return 0;
}
/* Transfer the frame, stripping the footer, but copying the checksum */
framep=&(rxframe[rxframe_head].data[0]);
memcpy(buf,framep,len-AUX_LEN+CHECKSUM_LEN);
rf230_last_correlation = rxframe[rxframe_head].lqi;
/* Prepare to receive another packet */
flushrx();
/* Point to the checksum */
framep+=len-AUX_LEN;
#if RF230_CONF_CHECKSUM
memcpy(&checksum,framep,CHECKSUM_LEN);
#endif /* RF230_CONF_CHECKSUM */
framep+=CHECKSUM_LEN;
#if RF230_CONF_TIMESTAMPS
memcpy(&t,framep,TIMESTAMP_LEN);
#endif /* RF230_CONF_TIMESTAMPS */
framep+=TIMESTAMP_LEN;
#if FOOTER_LEN
memcpy(footer,framep,FOOTER_LEN);
#endif
#if RF230_CONF_CHECKSUM
if(checksum != crc16_data(buf, len - AUX_LEN, 0)) {
DEBUGFLOW('w');
//PRINTF("checksum failed 0x%04x != 0x%04x\n",
// checksum, crc16_data(buf, len - AUX_LEN, 0));
}
#if FOOTER_LEN
if(footer[1] & FOOTER1_CRC_OK &&
checksum == crc16_data(buf, len - AUX_LEN, 0)) {
#endif
#endif /* RF230_CONF_CHECKSUM */
/* Get the received signal strength for the packet, 0-84 dB above rx threshold */
#if 0 //more general
rf230_last_rssi = rf230_get_raw_rssi();
#else //faster
#if RF230_CONF_AUTOACK
// rf230_last_rssi = hal_subregister_read(SR_ED_LEVEL); //0-84 resolution 1 dB
rf230_last_rssi = hal_register_read(RG_PHY_ED_LEVEL); //0-84, resolution 1 dB
#else
/* last_rssi will have been set at RX_START interrupt */
// rf230_last_rssi = 3*hal_subregister_read(SR_RSSI); //0-28 resolution 3 dB
#endif
#endif /* speed vs. generality */
/* Save the smallest rssi. The display routine can reset by setting it to zero */
if ((rf230_smallest_rssi==0) || (rf230_last_rssi<rf230_smallest_rssi))
rf230_smallest_rssi=rf230_last_rssi;
// rf230_last_correlation = rxframe[rxframe_head].lqi;
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, rf230_last_rssi);
packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, rf230_last_correlation);
RIMESTATS_ADD(rx);
#if RF230_CONF_TIMESTAMPS
rf230_time_of_departure =
t.time +
setup_time_for_transmission +
(total_time_for_transmission * (len - 2)) / total_transmission_len;
rf230_authority_level_of_sender = t.authority_level;
packetbuf_set_attr(PACKETBUF_ATTR_TIMESTAMP, t.time);
#endif /* RF230_CONF_TIMESTAMPS */
#if RF230_CONF_CHECKSUM
#if FOOTER_LEN
} else {
DEBUGFLOW('x');
//PRINTF("bad crc");
RIMESTATS_ADD(badcrc);
len = AUX_LEN;
}
#endif
#endif
#ifdef RF230BB_HOOK_RX_PACKET
RF230BB_HOOK_RX_PACKET(buf,len);
#endif
/* Here return just the data length. The checksum is however still in the buffer for packet sniffing */
return len - AUX_LEN;
}
/*---------------------------------------------------------------------------*/
void
rf230_set_txpower(uint8_t power)
{
set_txpower(power);
}
/*---------------------------------------------------------------------------*/
uint8_t
rf230_get_txpower(void)
{
uint8_t power = TX_PWR_UNDEFINED;
if (hal_get_slptr()) {
PRINTF("rf230_get_txpower:Sleeping");
} else {
power = hal_subregister_read(SR_TX_PWR);
}
return power;
}
/*---------------------------------------------------------------------------*/
uint8_t
rf230_get_raw_rssi(void)
{
uint8_t rssi,state;
bool radio_was_off = 0;
/*The RSSI measurement should only be done in RX_ON or BUSY_RX.*/
if(!RF230_receive_on) {
radio_was_off = 1;
rf230_on();
}
/* The energy detect register is used in extended mode (since RSSI will read 0) */
/* The rssi register is multiplied by 3 to a consistent value from either register */
state=radio_get_trx_state();
if ((state==RX_AACK_ON) || (state==BUSY_RX_AACK)) {
// rssi = hal_subregister_read(SR_ED_LEVEL); //0-84, resolution 1 dB
rssi = hal_register_read(RG_PHY_ED_LEVEL); //0-84, resolution 1 dB
} else {
#if 0 // 3-clock shift and add is faster on machines with no hardware multiply
/* avr-gcc may have an -Os bug that uses the general subroutine for multiplying by 3 */
rssi = hal_subregister_read(SR_RSSI); //0-28, resolution 3 dB
rssi = (rssi << 1) + rssi; //*3
#else // 1 or 2 clock multiply, or compiler with correct optimization
rssi = 3 * hal_subregister_read(SR_RSSI);
#endif
}
if(radio_was_off) {
rf230_off();
}
return rssi;
}
/*---------------------------------------------------------------------------*/
static int
rf230_cca(void)
{
uint8_t cca=0;
uint8_t radio_was_off = 0;
/* Turn radio on if necessary. If radio is currently busy return busy channel */
/* This may happen when testing radio duty cycling with RADIOALWAYSON,
* or because a packet just started. */
if(RF230_receive_on) {
if (hal_get_slptr()) { //should not be sleeping!
DEBUGFLOW('<');
goto busyexit;
} else {
if (!rf230_isidle()) {
//DEBUGFLOW('2');
goto busyexit;
}
}
} else {
radio_was_off = 1;
rf230_on();
}
ENERGEST_ON(ENERGEST_TYPE_LED_YELLOW);
/* CCA Mode Mode 1=Energy above threshold 2=Carrier sense only 3=Both 0=Either (RF231 only) */
/* Use the current mode. Note triggering a manual CCA is not recommended in extended mode */
//hal_subregister_write(SR_CCA_MODE,1);
/* Start the CCA, wait till done, return result */
/* Note reading the TRX_STATUS register clears both CCA_STATUS and CCA_DONE bits */
#if defined(__AVR_ATmega128RFA1__) || defined(__AVR_ATmega128RFR2__) || defined(__AVR_ATmega256RFR2__)
#if 1 //interrupt method
/* Disable rx transitions to busy (RX_PDT_BIT) */
/* Note: for speed this resets rx threshold to the compiled default */
#ifdef RF230_MIN_RX_POWER
hal_register_write(RG_RX_SYN, RF230_MIN_RX_POWER/6 + 0x81);
#else
hal_register_write(RG_RX_SYN, 0x80);
#endif
/* Switch to RX_ON for measurement. This will wait if a packet is being received */
radio_set_trx_state(RX_ON);
rf230_ccawait=1;
//CCA_REQUEST is supposed to trigger the interrupt but it doesn't
// hal_subregister_write(SR_CCA_REQUEST,1);
/* Write to ED_LEVEL register to start CCA */
{
uint8_t volatile saved_sreg = SREG;
sei( );
hal_register_write(PHY_ED_LEVEL,0);
while (rf230_ccawait) {}
SREG = saved_sreg;
}
/* Use ED register to determine result. 77dBm is poweron csma default.*/
#ifdef RF230_CONF_CCA_THRES
if (hal_register_read(RG_PHY_ED_LEVEL)<(91+RF230_CONF_CCA_THRES)) cca=0xff;
#else
if (hal_register_read(RG_PHY_ED_LEVEL)<(91-77)) cca=0xff;
#endif
//TODO:see if the status register works!
// cca=hal_register_read(RG_TRX_STATUS);
#if RF230_CONF_AUTOACK
radio_set_trx_state(RX_AACK_ON);
#endif
/* Enable packet reception */
#ifdef RF230_MIN_RX_POWER
hal_register_write(RG_RX_SYN, RF230_MIN_RX_POWER/6 + 0x01);
#else
hal_register_write(RG_RX_SYN, 0x00);
#endif
#else
/* If already in receive mode can read the current ED register without delay */
/* CCA energy threshold = -91dB + 2*SR_CCA_ED_THRESH. Reset defaults to -77dB */
#ifdef RF230_CONF_CCA_THRES
if (hal_register_read(RG_PHY_ED_LEVEL)<(91+RF230_CONF_CCA_THRES)) cca=0xff;
#else
if (hal_register_read(RG_PHY_ED_LEVEL)<(91-77)) cca=0xff;
#endif
#endif
#else /* RF230, RF231 */
/* Don't allow interrupts! */
/* Start the CCA, wait till done, return result */
/* Note reading the TRX_STATUS register clears both CCA_STATUS and CCA_DONE bits */
{ uint8_t volatile saved_sreg = SREG;
cli();
rf230_waitidle();
hal_subregister_write(SR_CCA_REQUEST,1);
delay_us(TIME_CCA);
while ((cca & 0x80) == 0 ) {
if (hal_get_slptr()) {
DEBUGFLOW('S');
break;
}
cca=hal_register_read(RG_TRX_STATUS);
}
SREG=saved_sreg;
}
#endif
ENERGEST_OFF(ENERGEST_TYPE_LED_YELLOW);
if(radio_was_off) {
rf230_off();
}
// if (cca & 0x40) {/*DEBUGFLOW('3')*/;} else {rf230_pending=1;DEBUGFLOW('4');}
if (cca & 0x40) {
// DEBUGFLOW('5');
return 1;
} else {
// DEBUGFLOW('6');
busyexit:
return 0;
}
}
/*---------------------------------------------------------------------------*/
int
rf230_receiving_packet(void)
{
uint8_t radio_state;
if (hal_get_slptr()) {
DEBUGFLOW('=');
} else {
radio_state = hal_subregister_read(SR_TRX_STATUS);
if ((radio_state==BUSY_RX) || (radio_state==BUSY_RX_AACK)) {
// DEBUGFLOW('8');
return 1;
}
}
return 0;
}
/*---------------------------------------------------------------------------*/
static int
rf230_pending_packet(void)
{
#if RF230_INSERTACK
if(ack_pending == 1) return 1;
#endif
return rf230_pending;
}
/*---------------------------------------------------------------------------*/
#if RF230_CONF_SNEEZER && JACKDAW
/* See A.2 in the datasheet for the sequence needed.
* This version for RF230 only, hence Jackdaw.
* A full reset seems not necessary and allows keeping the pan address, etc.
* for an easy reset back to network mode.
*/
void rf230_start_sneeze(void) {
//write buffer with random data for uniform spectral noise
//uint8_t txpower = hal_register_read(0x05); //save auto_crc bit and power
// hal_set_rst_low();
// hal_set_slptr_low();
// delay_us(TIME_RESET);
// hal_set_rst_high();
hal_register_write(0x0E, 0x01);
hal_register_write(0x02, 0x03);
hal_register_write(0x03, 0x10);
// hal_register_write(0x08, 0x20+26); //channel 26
hal_subregister_write(SR_CCA_MODE,1); //leave channel unchanged
// hal_register_write(0x05, 0x00); //output power maximum
hal_subregister_write(SR_TX_AUTO_CRC_ON, 0); //clear AUTO_CRC, leave output power unchanged
hal_register_read(0x01); //should be trx-off state=0x08
hal_frame_write(buffer, 127); //maximum length, random for spectral noise
hal_register_write(0x36,0x0F); //configure continuous TX
hal_register_write(0x3D,0x00); //Modulated frame, other options are:
// hal_register_write(RG_TX_2,0x10); //CW -2MHz
// hal_register_write(RG_TX_2,0x80); //CW -500KHz
// hal_register_write(RG_TX_2,0xC0); //CW +500KHz
DDRB |= 1<<7; //Raven USB stick has PB7 connected to the RF230 TST pin.
PORTB |= 1<<7; //Raise it to enable continuous TX Test Mode.
hal_register_write(0x02,0x09); //Set TRX_STATE to PLL_ON
delay_us(TIME_TRX_OFF_TO_PLL_ACTIVE);
delay_us(TIME_PLL_LOCK);
delay_us(TIME_PLL_LOCK);
// while (hal_register_read(0x0f)!=1) {continue;} //wait for pll lock-hangs
hal_register_write(0x02,0x02); //Set TRX_STATE to TX_START
}
#endif
#ifdef AES_128_HW_CONF
#define IEEE_VECT 0
extern unsigned char aes_key[16];
extern unsigned char aes_p[];
extern unsigned char aes_c[];
extern unsigned char aes_s[];
extern unsigned char tmp[16];
/*
After PWR_SAVE sleep key is lost. We'll lock en/decyption to avoid
not to forced in to sleep while doing crypto. Also the key is hold
be the user so AES block should be reentrant. Encode/Docode och 128bit
(16 bytes) is promised to be less than 24us.
Note! Radio must be on to use the HW crypto engine. --ro
*/
static void
rf230_aes_write_key(unsigned char *key)
{
uint8_t i;
for(i = 0; i < 16; i++) {
hal_subregister_write(SR_AES_KEY, key[i]);
}
}
static void
rf230_aes_read_key(unsigned char *key)
{
uint8_t i;
for(i = 0; i < 16; i++) {
key[i] = hal_subregister_read(SR_AES_KEY);
}
}
static void
rf230_aes_write_state(unsigned char *state)
{
uint8_t i;
for(i = 0; i < 16; i++) {
hal_subregister_write(SR_AES_STATE, state[i]);
}
}
static void
rf230_aes_read_state(unsigned char *state)
{
uint8_t i;
for(i = 0; i < 16; i++) {
state[i] = hal_subregister_read(SR_AES_STATE);
}
}
static int
crypt(void)
{
uint8_t status;
hal_subregister_write(SR_AES_CNTRL_REQUEST, 1); /* Kick */
do {
watchdog_periodic();
status = hal_subregister_read(SR_AES_STATUS);
} while(status == 0);
if (hal_subregister_read(SR_AES_STATUS_ERR)) {
PRINTF("AES ERR\n");
return 0;
}
if (hal_subregister_read(SR_AES_STATUS_DONE)) {
PRINTF("AES DONE\n");
return 1;
}
return 0; /* Unknown */
}
int
rf230_aes_encrypt_cbc(unsigned char *key, unsigned char *plain, int len, unsigned char *mic)
{
uint8_t i;
uint8_t sreg;
int res;
sreg = SREG;
cli();
rf230_aes_write_key(key);
hal_subregister_write(SR_AES_CNTRL_MODE, 0); /* AES_MODE=0 -> ECB for 1:st block*/
hal_subregister_write(SR_AES_CNTRL_DIR, 0); /* AES_DIR=0 -> encryption */
/* write string to encrypt into buffer */
for(i = 0; i < 16; i++) {
AES_STATE = plain[i] ^ IEEE_VECT;
}
res = crypt();
if(!res)
goto out;
len -= 16;
/* Swiitch Mode */
hal_subregister_write(SR_AES_CNTRL_MODE, 1); /* AES_MODE=1 -> CBC */
hal_subregister_write(SR_AES_CNTRL_DIR, 0); /* AES_DIR=0 -> encryption */
while(len > 0) {
rf230_aes_write_state(plain);
res = crypt();
if(!res)
goto out;
len -= 16;
}
/* Read and retrun cipher */
rf230_aes_read_state(mic);
out:
SREG = sreg;
return res;
}
/* Electonic Code Block */
int
rf230_aes_encrypt_ebc(unsigned char *key, unsigned char *plain, unsigned char *cipher)
{
int res;
uint8_t sreg;
sreg = SREG;
cli();
rf230_aes_write_key(key);
hal_subregister_write(SR_AES_CNTRL_MODE, 0); /* AES_MODE=0 -> ECB for 1:st block*/
hal_subregister_write(SR_AES_CNTRL_DIR, 0); /* AES_DIR=0 -> encryption */
rf230_aes_write_state(plain); /* write string to encrypt into buffer */
res = crypt();
if(!res)
goto out;
rf230_aes_read_state(cipher); /* Read and return cipher */
out:
SREG = sreg;
return res;
}
int
rf230_aes_decrypt_ebc(unsigned char *key, unsigned char *cipher, unsigned char *plain)
{
int res;
uint8_t sreg;
/*
Dummy encryption of 0 w. original key
to get last round key to be used decrytion
*/
sreg = SREG;
cli();
rf230_aes_write_key(key);
hal_subregister_write(SR_AES_CNTRL_MODE, 0); /* AES_MODE=0 -> ECB for 1:st block*/
hal_subregister_write(SR_AES_CNTRL_DIR, 0); /* AES_DIR=0 -> encryption */
memset(tmp, 0, sizeof(tmp)); /* Setup for last round */
rf230_aes_write_state(tmp);
res = crypt();
if(!res)
goto out;
rf230_aes_read_key(tmp);/* Save the last round key */
/* And use as decrytion key */
rf230_aes_write_key(tmp);
hal_subregister_write(SR_AES_CNTRL_MODE, 0); /* AES_MODE=0 -> ECB for 1:st block*/
hal_subregister_write(SR_AES_CNTRL_DIR, 1); /* AES_DIR=1 -> decryption */
/* Write string to decrypt into buffer */
rf230_aes_write_state(cipher);
res = crypt();
if(!res)
goto out;
rf230_aes_read_state(plain); /* Read plaintext into string */
out:
SREG = sreg;
return res;
}
#endif /* AES_128_HW_CONF */