You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

297 lines
7.7 KiB

/*
* Copyright (C) 2014 Freie Universität Berlin
* Copyright (C) 2013 INRIA
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_cc110x
* @{
* @file
* @brief Functions for packet reception and transmission on cc110x devices
*
* @author Oliver Hahm <oliver.hahm@inria.fr>
* @author Fabian Nack <nack@inf.fu-berlin.de>
* @author Kaspar Schleiser <kaspar@schleiser.de>
* @}
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "cc110x.h"
#include "cc110x-spi.h"
#include "cc110x-internal.h"
#include "cc110x-interface.h"
#include "cc110x-defines.h"
#include "periph/gpio.h"
#include "irq.h"
#include "kernel_types.h"
#include "msg.h"
#include "cpu_conf.h"
#include "cpu.h"
#include "log.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _rx_abort(cc110x_t *dev)
{
gpio_irq_disable(dev->params.gdo2);
cc110x_strobe(dev, CC110X_SIDLE); /* Switch to IDLE (should already be)... */
cc110x_strobe(dev, CC110X_SFRX); /* ...for flushing the RX FIFO */
cc110x_switch_to_rx(dev);
}
static void _rx_start(cc110x_t *dev)
{
dev->radio_state = RADIO_RX_BUSY;
cc110x_pkt_buf_t *pkt_buf = &dev->pkt_buf;
pkt_buf->pos = 0;
gpio_irq_disable(dev->params.gdo2);
cc110x_write_reg(dev, CC110X_IOCFG2, 0x01);
gpio_irq_enable(dev->params.gdo2);
}
static void _rx_read_data(cc110x_t *dev, void(*callback)(void*), void*arg)
{
int fifo = cc110x_get_reg_robust(dev, 0xfb);
if (fifo & 0x80) {
DEBUG("%s:%s:%u rx overflow\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_rx_abort(dev);
return;
}
if (!fifo) {
gpio_irq_enable(dev->params.gdo2);
return;
}
cc110x_pkt_buf_t *pkt_buf = &dev->pkt_buf;
if (!pkt_buf->pos) {
pkt_buf->pos = 1;
pkt_buf->packet.length = cc110x_read_reg(dev, CC110X_RXFIFO);
/* Possible packet received, RX -> IDLE (0.1 us) */
dev->cc110x_statistic.packets_in++;
}
int left = pkt_buf->packet.length+1 - pkt_buf->pos;
/* if the fifo doesn't contain the rest of the packet,
* leav at least one byte as per spec sheet. */
int to_read = (fifo < left) ? (fifo-1) : fifo;
if (to_read > left) {
to_read = left;
}
if (to_read) {
cc110x_readburst_reg(dev, CC110X_RXFIFO,
((char *)&pkt_buf->packet)+pkt_buf->pos, to_read);
pkt_buf->pos += to_read;
}
if (to_read == left) {
uint8_t status[2];
/* full packet received. */
/* Read the 2 appended status bytes (status[0] = RSSI, status[1] = LQI) */
cc110x_readburst_reg(dev, CC110X_RXFIFO, (char *)status, 2);
/* Store RSSI value of packet */
pkt_buf->rssi = status[I_RSSI];
/* Bit 0-6 of LQI indicates the link quality (LQI) */
pkt_buf->lqi = status[I_LQI] & LQI_EST;
/* MSB of LQI is the CRC_OK bit */
int crc_ok = (status[I_LQI] & CRC_OK) >> 7;
if (crc_ok) {
LOG_DEBUG("cc110x: received packet from=%u to=%u payload "
"len=%u\n",
(unsigned)pkt_buf->packet.phy_src,
(unsigned)pkt_buf->packet.address,
pkt_buf->packet.length-3);
/* let someone know that we've got a packet */
callback(arg);
cc110x_switch_to_rx(dev);
}
else {
DEBUG("%s:%s:%u crc-error\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
dev->cc110x_statistic.packets_in_crc_fail++;
_rx_abort(dev);
}
}
}
static void _rx_continue(cc110x_t *dev, void(*callback)(void*), void*arg)
{
if (dev->radio_state != RADIO_RX_BUSY) {
DEBUG("%s:%s:%u _rx_continue in invalid state\n", RIOT_FILE_RELATIVE,
__func__, __LINE__);
_rx_abort(dev);
return;
}
gpio_irq_disable(dev->params.gdo2);
do {
_rx_read_data(dev, callback, arg);
} while (gpio_read(dev->params.gdo2));
}
static void _tx_abort(cc110x_t *dev)
{
cc110x_switch_to_rx(dev);
}
static void _tx_continue(cc110x_t *dev)
{
gpio_irq_disable(dev->params.gdo2);
cc110x_pkt_t *pkt = &dev->pkt_buf.packet;
int size = pkt->length + 1;
int left = size - dev->pkt_buf.pos;
if (!left) {
dev->cc110x_statistic.raw_packets_out++;
LOG_DEBUG("cc110x: packet successfully sent.\n");
cc110x_switch_to_rx(dev);
return;
}
int fifo = 64 - cc110x_get_reg_robust(dev, 0xfa);
if (fifo & 0x80) {
DEBUG("%s:%s:%u tx underflow!\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_tx_abort(dev);
return;
}
if (!fifo) {
DEBUG("%s:%s:%u fifo full!?\n", RIOT_FILE_RELATIVE, __func__, __LINE__);
_tx_abort(dev);
return;
}
int to_send = left > fifo ? fifo : left;
/* Write packet into TX FIFO */
cc110x_writeburst_reg(dev, CC110X_TXFIFO, ((char *)pkt)+dev->pkt_buf.pos, to_send);
dev->pkt_buf.pos += to_send;
if (left == size) {
/* Switch to TX mode */
cc110x_strobe(dev, CC110X_STX);
}
if (to_send < left) {
/* set GDO2 to 0x2 -> will deassert at TX FIFO below threshold */
gpio_irq_enable(dev->params.gdo2);
cc110x_write_reg(dev, CC110X_IOCFG2, 0x02);
}
else {
/* set GDO2 to 0x6 -> will deassert at packet end */
cc110x_write_reg(dev, CC110X_IOCFG2, 0x06);
gpio_irq_enable(dev->params.gdo2);
}
}
void cc110x_isr_handler(cc110x_t *dev, void(*callback)(void*), void*arg)
{
switch (dev->radio_state) {
case RADIO_RX:
if (gpio_read(dev->params.gdo2)) {
_rx_start(dev);
}
else {
DEBUG("cc110x_isr_handler((): isr handled too slow?\n");
_rx_abort(dev);
}
break;
case RADIO_RX_BUSY:
_rx_continue(dev, callback, arg);
break;
case RADIO_TX_BUSY:
if (!gpio_read(dev->params.gdo2)) {
_tx_continue(dev);
}
else {
DEBUG("cc110x_isr_handler() RADIO_TX_BUSY + GDO2\n");
}
break;
default:
DEBUG("%s:%s:%u: unhandled mode\n", RIOT_FILE_RELATIVE,
__func__, __LINE__);
}
}
int cc110x_send(cc110x_t *dev, cc110x_pkt_t *packet)
{
DEBUG("cc110x: snd pkt to %u payload_length=%u\n",
(unsigned)packet->address, (unsigned)packet->length-3);
uint8_t size;
switch (dev->radio_state) {
case RADIO_RX_BUSY:
case RADIO_TX_BUSY:
DEBUG("cc110x: invalid state for sending: %s\n",
cc110x_state_to_text(dev->radio_state));
return -EAGAIN;
}
/*
* Number of bytes to send is:
* length of phy payload (packet->length)
* + size of length field (1 byte)
*/
size = packet->length + 1;
if (size > CC110X_PACKET_LENGTH) {
DEBUG("%s:%s:%u trying to send oversized packet\n",
RIOT_FILE_RELATIVE, __func__, __LINE__);
return -ENOSPC;
}
/* set source address */
packet->phy_src = dev->radio_address;
/* Disable RX interrupt */
gpio_irq_disable(dev->params.gdo2);
dev->radio_state = RADIO_TX_BUSY;
#ifdef MODULE_CC110X_HOOKS
cc110x_hook_tx();
#endif
cc110x_write_reg(dev, CC110X_IOCFG2, 0x02);
/* Put CC110x in IDLE mode to flush the FIFO */
cc110x_strobe(dev, CC110X_SIDLE);
/* Flush TX FIFO to be sure it is empty */
cc110x_strobe(dev, CC110X_SFTX);
memcpy((char*)&dev->pkt_buf.packet, packet, size);
dev->pkt_buf.pos = 0;
_tx_continue(dev);
return size;
}