mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
4183e16820
Introduced CC110X_L2_HDR_SIZE to get rid of magic numbers.
328 lines
8.6 KiB
C
328 lines
8.6 KiB
C
/*
|
|
* 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"
|
|
|
|
#ifdef MODULE_OD
|
|
#include "od.h"
|
|
#endif
|
|
|
|
#include "log.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
const char *cc110x_state_to_text(uint8_t state)
|
|
{
|
|
switch (state){
|
|
case RADIO_IDLE:
|
|
return "idle";
|
|
case RADIO_TX_BUSY:
|
|
return "tx busy";
|
|
case RADIO_RX:
|
|
return "rx";
|
|
case RADIO_RX_BUSY:
|
|
return "rx busy";
|
|
case RADIO_PWD:
|
|
return "pwd";
|
|
case RADIO_UNKNOWN:
|
|
return "unknown";
|
|
}
|
|
return "invalid";
|
|
}
|
|
|
|
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,
|
|
CC110X_GDO_HIGH_ON_RX_FIFO_FILLED_OR_PKT_END);
|
|
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 & RXFIFO_OVERFLOW) {
|
|
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 - CC110X_L2_HDR_SIZE);
|
|
/* 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++;
|
|
#if defined(MODULE_OD) && ENABLE_DEBUG
|
|
od_hex_dump(pkt_buf->packet.data,
|
|
pkt_buf->packet.length - CC110X_L2_HDR_SIZE,
|
|
OD_WIDTH_DEFAULT);
|
|
#endif
|
|
_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 = CC110X_FIFO_LENGTH - cc110x_get_reg_robust(dev, 0xfa);
|
|
|
|
if (fifo & TXFIFO_UNDERFLOW) {
|
|
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,
|
|
CC110X_GDO_LOW_ON_TX_FIFO_BELOW_THRESHOLD);
|
|
}
|
|
else {
|
|
/* set GDO2 to 0x6 -> will deassert at packet end */
|
|
cc110x_write_reg(dev, CC110X_IOCFG2, CC110X_GDO_HIGH_ON_SYNC_WORD);
|
|
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 - CC110X_L2_HDR_SIZE);
|
|
unsigned 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,
|
|
CC110X_GDO_LOW_ON_TX_FIFO_BELOW_THRESHOLD);
|
|
|
|
/* 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 (int)size;
|
|
}
|