osd-contiki/cpu/arm/aducrf101/dev/radio.c
Jim Paris 6db05caed9 Fix radio hangups when trying to transmit with radio off.
The border-router tries to transmit and do other stuff after turning
the radio off, and the radio driver didn't handle that very well.
With this fix, it's no longer necessary to reset the border router
after starting tunslip6.
2014-07-23 16:21:53 -04:00

425 lines
11 KiB
C

/**
* Copyright (c) 2014, Analog Devices, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the
* disclaimer below) provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - 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.
*
* - Neither the name of Analog Devices, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS 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 COPYRIGHT OWNER 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.
*/
/**
* \author Jim Paris <jim.paris@rigado.com>
*/
#include <string.h>
#include <stdint.h>
#include <aducrf101-contiki.h>
#include "contiki.h"
#include "contiki-net.h"
#include "net/netstack.h"
#include "radio.h"
#define MAX_PACKET_LEN 240
static uint8_t tx_buf[MAX_PACKET_LEN];
#ifndef ADUCRF101_RADIO_BASE_CONFIG
#define ADUCRF101_RADIO_BASE_CONFIG DR_38_4kbps_Dev20kHz
#endif
static RIE_BaseConfigs base_config = ADUCRF101_RADIO_BASE_CONFIG;
static int current_channel = 915000000;
static int current_power = 31;
static int radio_is_on = 0;
/*---------------------------------------------------------------------------*/
/* Sniffer configuration. We can re-use the CC2538 sniffer application
if we also accept CC2538_RF_CONF_SNIFFER. */
#ifndef ADUCRF101_RF_CONF_SNIFFER
#if CC2538_RF_CONF_SNIFFER
#define ADUCRF101_RF_CONF_SNIFFER 1
#endif
#endif
#if ADUCRF101_RF_CONF_SNIFFER
#include "dev/uart.h"
static const uint8_t magic[] = { 0x53, 0x6E, 0x69, 0x66 }; /* Snif */
#endif
/*---------------------------------------------------------------------------*/
/* "Channel" is really frequency, and can be within the bands:
431000000 Hz to 464000000 Hz
862000000 Hz to 928000000 Hz
*/
#define MIN_CHANNEL 431000000
#define MAX_CHANNEL 928000000
static int
_set_channel(int freq)
{
if(freq < 431000000) {
freq = 431000000;
} else if(freq > 464000000 && freq < 663000000) {
freq = 464000000;
} else if(freq >= 663000000 && freq < 862000000) {
freq = 862000000;
} else if(freq > 928000000) {
freq = 928000000;
}
current_channel = freq;
if(RadioSetFrequency(freq) != RIE_Success) {
return RADIO_RESULT_ERROR;
}
return RADIO_RESULT_OK;
}
/*---------------------------------------------------------------------------*/
/* "Power" covers both PA type and power level:
0 through 15 means single-ended, power level 0 through 15
16 through 31 means differential, power level 0 through 15 */
#define MIN_POWER 0
#define MAX_POWER 31
static int
_set_power(int power)
{
RIE_Responses ret;
if(power < 0) {
power = 0;
}
if(power > 31) {
power = 31;
}
if(power <= 15) {
ret = RadioTxSetPA(SingleEndedPA, power);
} else {
ret = RadioTxSetPA(DifferentialPA, power - 16);
}
current_power = power;
if(ret != RIE_Success) {
return RADIO_RESULT_ERROR;
}
return RADIO_RESULT_OK;
}
/*---------------------------------------------------------------------------*/
PROCESS(aducrf101_rf_process, "ADuCRF101 RF driver");
/*---------------------------------------------------------------------------*/
/** Turn the radio on. */
static int
on(void)
{
if(radio_is_on) {
return 1;
}
/* Power radio on */
if(RadioInit(base_config) != RIE_Success) {
return 0;
}
/* Ensure channel and power are set */
if(_set_channel(current_channel) != RADIO_RESULT_OK) {
return 0;
}
if(_set_power(current_power) != RADIO_RESULT_OK) {
return 0;
}
/* Enter receive mode */
RadioRxPacketVariableLen();
radio_is_on = 1;
return 1;
}
/*---------------------------------------------------------------------------*/
/** Turn the radio off. */
static int
off(void)
{
if(!radio_is_on) {
return 1;
}
if(RadioPowerOff() != RIE_Success) {
return 0;
}
radio_is_on = 0;
return 1;
}
/*---------------------------------------------------------------------------*/
static int
init(void)
{
off();
on();
process_start(&aducrf101_rf_process, NULL);
return 1;
}
/*---------------------------------------------------------------------------*/
/** Prepare the radio with a packet to be sent. */
static int
prepare(const void *payload, unsigned short payload_len)
{
/* Truncate long packets */
if(payload_len > MAX_PACKET_LEN) {
payload_len = MAX_PACKET_LEN;
}
memcpy(tx_buf, payload, payload_len);
return 0;
}
/*---------------------------------------------------------------------------*/
/** Send the packet that has previously been prepared. */
static int
transmit(unsigned short transmit_len)
{
if(!radio_is_on)
return RADIO_TX_ERR;
/* Transmit the packet */
if(transmit_len > MAX_PACKET_LEN) {
transmit_len = MAX_PACKET_LEN;
}
if(RadioTxPacketVariableLen(transmit_len, tx_buf) != RIE_Success) {
return RADIO_TX_ERR;
}
while(!RadioTxPacketComplete())
continue;
/* Enter receive mode immediately after transmitting a packet */
RadioRxPacketVariableLen();
return RADIO_TX_OK;
}
/*---------------------------------------------------------------------------*/
/** Prepare & transmit a packet. */
static int
send(const void *payload, unsigned short payload_len)
{
prepare(payload, payload_len);
return transmit(payload_len);
}
/*---------------------------------------------------------------------------*/
/** Read a received packet into a buffer. */
static int
read(void *buf, unsigned short buf_len)
{
uint8_t packet_len;
int8_t rssi;
if(!radio_is_on)
return 0;
if(buf_len > MAX_PACKET_LEN) {
buf_len = MAX_PACKET_LEN;
}
/* Read already-received packet */
if(RadioRxPacketRead(buf_len, &packet_len, buf, &rssi) != RIE_Success) {
return 0;
}
if(packet_len > buf_len) {
packet_len = buf_len;
}
/* Re-enter receive mode immediately after receiving a packet */
RadioRxPacketVariableLen();
#if ADUCRF101_RF_CONF_SNIFFER
uart_put(magic[0]);
uart_put(magic[1]);
uart_put(magic[2]);
uart_put(magic[3]);
uart_put(packet_len + 2);
for(int i = 0; i < packet_len; i++) {
uart_put(((uint8_t *)buf)[i]);
}
/* FCS value is Wireshark's "TI CC24xx format" option: */
uart_put(rssi); /* RSSI */
uart_put(0x80); /* CRC is OK, LQI correlation is 0 */
#endif
return packet_len;
}
/*---------------------------------------------------------------------------*/
/** Perform a Clear-Channel Assessment (CCA) to find out if there is
a packet in the air or not. */
static int
channel_clear(void)
{
/* Not implemented; assume clear */
return 1;
}
/*---------------------------------------------------------------------------*/
/** Check if the radio driver is currently receiving a packet */
static int
receiving_packet(void)
{
/* Not implemented; assume no. */
return 0;
}
/*---------------------------------------------------------------------------*/
/** Check if the radio driver has just received a packet */
static int
pending_packet(void)
{
if(RadioRxPacketAvailable()) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
/** Get a radio parameter value. */
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_RSSI:
{
int8_t dbm;
if(!radio_is_on || RadioRadioGetRSSI(&dbm) != RIE_Success) {
return RADIO_RESULT_ERROR;
}
*value = dbm;
return RADIO_RESULT_OK;
}
case RADIO_PARAM_CHANNEL:
*value = current_channel;
return RADIO_RESULT_OK;
case RADIO_CONST_CHANNEL_MIN:
*value = MIN_CHANNEL;
return RADIO_RESULT_OK;
case RADIO_CONST_CHANNEL_MAX:
*value = MAX_CHANNEL;
return RADIO_RESULT_OK;
case RADIO_PARAM_TXPOWER:
*value = current_power;
return RADIO_RESULT_OK;
case RADIO_CONST_TXPOWER_MIN:
*value = MIN_POWER;
return RADIO_RESULT_OK;
case RADIO_CONST_TXPOWER_MAX:
*value = MAX_POWER;
return RADIO_RESULT_OK;
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
}
/*---------------------------------------------------------------------------*/
/** Set a radio parameter value. */
static radio_result_t
set_value(radio_param_t param, radio_value_t value)
{
switch(param) {
case RADIO_PARAM_CHANNEL:
return _set_channel(value);
case RADIO_PARAM_TXPOWER:
return _set_power(value);
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
}
/*---------------------------------------------------------------------------*/
/**
* Get a radio parameter object. The argument 'dest' must point to a
* memory area of at least 'size' bytes, and this memory area will
* contain the parameter object if the function succeeds.
*/
static radio_result_t
get_object(radio_param_t param, void *dest, size_t size)
{
return RADIO_RESULT_NOT_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
/**
* Set a radio parameter object. The memory area referred to by the
* argument 'src' will not be accessed after the function returns.
*/
static radio_result_t
set_object(radio_param_t param, const void *src, size_t size)
{
return RADIO_RESULT_NOT_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Implementation of the ADuCRF101 RF driver process
*
* This process is started by init(). It waits for events triggered
* by packet reception.
*/
PROCESS_THREAD(aducrf101_rf_process, ev, data)
{
int len;
PROCESS_BEGIN();
while(1) {
PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL);
packetbuf_clear();
len = read(packetbuf_dataptr(), PACKETBUF_SIZE);
if(len > 0) {
packetbuf_set_datalen(len);
NETSTACK_RDC.input();
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
/**
* \brief Trigger function called by ADI radio engine upon packet RX.
*/
void
aducrf101_rx_packet_hook(void)
{
process_poll(&aducrf101_rf_process);
}
/*---------------------------------------------------------------------------*/
const struct radio_driver aducrf101_radio_driver = {
.init = init,
.prepare = prepare,
.transmit = transmit,
.send = send,
.read = read,
.channel_clear = channel_clear,
.receiving_packet = receiving_packet,
.pending_packet = pending_packet,
.on = on,
.off = off,
.get_value = get_value,
.set_value = set_value,
.get_object = get_object,
.set_object = set_object,
};