mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
315 lines
9.4 KiB
C
315 lines
9.4 KiB
C
/**
|
|
* cc2420_rx.c - Implementation of transmitting cc2420 functions.
|
|
* Copyright (C) 2013 Milan Babel <babel@inf.fu-berlin.de>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "cc2420.h"
|
|
#include "cc2420_spi.h"
|
|
#include "cc2420_settings.h"
|
|
#include "cc2420_arch.h"
|
|
#include "ieee802154_frame.h"
|
|
|
|
#include "irq.h"
|
|
#include "hwtimer.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
static void cc2420_gen_pkt(uint8_t *buf, cc2420_packet_t *packet);
|
|
static uint8_t sequenz_nr;
|
|
static bool wait_for_ack;
|
|
|
|
radio_tx_status_t cc2420_load_tx_buf(ieee802154_packet_kind_t kind,
|
|
ieee802154_node_addr_t dest,
|
|
bool use_long_addr,
|
|
bool wants_ack,
|
|
void *buf,
|
|
unsigned int len)
|
|
{
|
|
uint8_t hdr[24];
|
|
|
|
/* FCS : frame version 0, we don't manage security,
|
|
nor batchs of packets */
|
|
switch (kind) {
|
|
case PACKET_KIND_BEACON:
|
|
hdr[0] = 0x00;
|
|
break;
|
|
case PACKET_KIND_DATA:
|
|
hdr[0] = 0x01;
|
|
break;
|
|
case PACKET_KIND_ACK:
|
|
hdr[0] = 0x02;
|
|
break;
|
|
default:
|
|
return RADIO_TX_INVALID_PARAM;
|
|
}
|
|
|
|
if (wants_ack) {
|
|
hdr[0] |= 0x20;
|
|
}
|
|
wait_for_ack = wants_ack;
|
|
|
|
uint16_t src_pan = cc2420_get_pan();
|
|
bool compress_pan = false;
|
|
|
|
if (use_long_addr) {
|
|
hdr[1] = 0xcc;
|
|
} else {
|
|
hdr[1] = 0x88;
|
|
/* short address mode, use PAN ID compression
|
|
for intra-PAN communication */
|
|
if (dest.pan.id == src_pan) {
|
|
compress_pan = true;
|
|
hdr[0] |= 0x40;
|
|
}
|
|
}
|
|
|
|
/* sequence number */
|
|
hdr[2] = sequenz_nr++;
|
|
|
|
/* variable-length fields */
|
|
int idx = 3;
|
|
|
|
if (use_long_addr) {
|
|
/* dest long addr */
|
|
hdr[idx++] = (uint8_t)(dest.long_addr & 0xFF);
|
|
hdr[idx++] = (uint8_t)(dest.long_addr >> 8);
|
|
hdr[idx++] = (uint8_t)(dest.long_addr >> 16);
|
|
hdr[idx++] = (uint8_t)(dest.long_addr >> 24);
|
|
hdr[idx++] = (uint8_t)(dest.long_addr >> 32);
|
|
hdr[idx++] = (uint8_t)(dest.long_addr >> 40);
|
|
hdr[idx++] = (uint8_t)(dest.long_addr >> 48);
|
|
hdr[idx++] = (uint8_t)(dest.long_addr >> 56);
|
|
/* src long addr */
|
|
uint64_t src_long_addr = cc2420_get_address_long();
|
|
hdr[idx++] = (uint8_t)(src_long_addr & 0xFF);
|
|
hdr[idx++] = (uint8_t)(src_long_addr >> 8);
|
|
hdr[idx++] = (uint8_t)(src_long_addr >> 16);
|
|
hdr[idx++] = (uint8_t)(src_long_addr >> 24);
|
|
hdr[idx++] = (uint8_t)(src_long_addr >> 32);
|
|
hdr[idx++] = (uint8_t)(src_long_addr >> 40);
|
|
hdr[idx++] = (uint8_t)(src_long_addr >> 48);
|
|
hdr[idx++] = (uint8_t)(src_long_addr >> 56);
|
|
} else {
|
|
/* dest PAN ID */
|
|
hdr[idx++] = (uint8_t)(dest.pan.id & 0xFF);
|
|
hdr[idx++] = (uint8_t)(dest.pan.id >> 8);
|
|
/* dest short addr */
|
|
hdr[idx++] = (uint8_t)(dest.pan.addr & 0xFF);
|
|
hdr[idx++] = (uint8_t)(dest.pan.addr >> 8);
|
|
/* src PAN ID */
|
|
if (!compress_pan) {
|
|
uint16_t src_pan = cc2420_get_pan();
|
|
hdr[idx++] = (uint8_t)(src_pan & 0xFF);
|
|
hdr[idx++] = (uint8_t)(src_pan >> 8);
|
|
}
|
|
/* src short addr */
|
|
uint16_t src_addr = cc2420_get_address();
|
|
hdr[idx++] = (uint8_t)(src_addr & 0xFF);
|
|
hdr[idx++] = (uint8_t)(src_addr >> 8);
|
|
}
|
|
|
|
/* total frame size */
|
|
uint8_t size = idx + len + 2;
|
|
if (size > CC2420_MAX_PKT_LENGTH) {
|
|
return RADIO_TX_PACKET_TOO_LONG;
|
|
}
|
|
|
|
/* flush TX buffer */
|
|
cc2420_strobe(CC2420_STROBE_FLUSHTX);
|
|
|
|
/* write length, header and payload to TX FIFO */
|
|
cc2420_write_fifo(&size, 1);
|
|
cc2420_write_fifo(hdr, idx);
|
|
cc2420_write_fifo(buf, len);
|
|
|
|
return RADIO_TX_OK;
|
|
}
|
|
|
|
#define CC2420_ACK_WAIT_DELAY_uS 1000
|
|
#define ACK_LENGTH 5
|
|
|
|
radio_tx_status_t cc2420_transmit_tx_buf(void)
|
|
{
|
|
/* check if channel clear */
|
|
if (!cc2420_channel_clear()) {
|
|
return RADIO_TX_MEDIUM_BUSY;
|
|
}
|
|
|
|
/* put tranceiver in idle mode */
|
|
cc2420_strobe(CC2420_STROBE_RFOFF);
|
|
|
|
/* begin transmission: wait for preamble to be sent */
|
|
unsigned int cpsr = disableIRQ();
|
|
cc2420_strobe(CC2420_STROBE_TXON);
|
|
|
|
int abort_count = 0;
|
|
while (cc2420_get_sfd() == 0) {
|
|
/* Wait for SFD signal to be set -> sync word transmitted */
|
|
abort_count++;
|
|
|
|
if (abort_count > CC2420_SYNC_WORD_TX_TIME) {
|
|
/* Abort waiting. CC2420 maybe in wrong mode
|
|
e.g. sending preambles for always */
|
|
puts("[CC2420 TX] fatal error: could not send packet\n");
|
|
return RADIO_TX_ERROR;
|
|
}
|
|
}
|
|
|
|
restoreIRQ(cpsr);
|
|
|
|
/* wait for packet to be sent, i.e.: SFD to go down */
|
|
uint8_t st;
|
|
do {
|
|
st = cc2420_status_byte();
|
|
} while (cc2420_get_sfd() != 0);
|
|
cc2420_switch_to_rx();
|
|
|
|
/* check for underflow error flag */
|
|
if (st & CC2420_STATUS_TX_UNDERFLOW) {
|
|
return RADIO_TX_UNDERFLOW;
|
|
}
|
|
|
|
/* wait for ACK only if needed */
|
|
if (!wait_for_ack) {
|
|
return RADIO_TX_OK;
|
|
}
|
|
|
|
/* delay for the peer to answer our packet */
|
|
//TODO design a more robust method?
|
|
hwtimer_wait(HWTIMER_TICKS(CC2420_ACK_WAIT_DELAY_uS));
|
|
/* try to read a returning ACK packet */
|
|
if (cc2420_get_fifop()) {
|
|
uint8_t ackbuf[ACK_LENGTH];
|
|
/* a packet has arrived, read the arrived data */
|
|
cc2420_read_fifo(ackbuf, ACK_LENGTH);
|
|
if (ackbuf[0] == 0x02 /* ack packet in buffer */
|
|
&& (ackbuf[2] == sequenz_nr - 1)) /* correct sequence number */
|
|
return RADIO_TX_OK;
|
|
}
|
|
return RADIO_TX_NOACK;
|
|
}
|
|
|
|
radio_tx_status_t cc2420_do_send(ieee802154_packet_kind_t kind,
|
|
ieee802154_node_addr_t dest,
|
|
bool use_long_addr,
|
|
bool wants_ack,
|
|
void *buf,
|
|
unsigned int len)
|
|
{
|
|
radio_tx_status_t st = cc2420_load_tx_buf(kind, dest,
|
|
use_long_addr,
|
|
wants_ack,
|
|
buf, len);
|
|
if (st != RADIO_TX_OK) {
|
|
return st;
|
|
}
|
|
return cc2420_transmit_tx_buf();
|
|
}
|
|
|
|
int16_t cc2420_send(cc2420_packet_t *packet)
|
|
{
|
|
volatile uint32_t abort_count = 0;
|
|
|
|
/* Set missing frame information */
|
|
packet->frame.fcf.frame_ver = 0;
|
|
|
|
if (packet->frame.src_pan_id == packet->frame.dest_pan_id) {
|
|
packet->frame.fcf.panid_comp = 1;
|
|
}
|
|
else {
|
|
packet->frame.fcf.panid_comp = 0;
|
|
}
|
|
|
|
if (packet->frame.fcf.src_addr_m == 2) {
|
|
packet->frame.src_addr[0] = (uint8_t)(cc2420_get_address() >> 8);
|
|
packet->frame.src_addr[1] = (uint8_t)(cc2420_get_address() & 0xFF);
|
|
}
|
|
else if (packet->frame.fcf.src_addr_m == 3) {
|
|
packet->frame.src_addr[0] = (uint8_t)(cc2420_get_address_long() >> 56);
|
|
packet->frame.src_addr[1] = (uint8_t)(cc2420_get_address_long() >> 48);
|
|
packet->frame.src_addr[2] = (uint8_t)(cc2420_get_address_long() >> 40);
|
|
packet->frame.src_addr[3] = (uint8_t)(cc2420_get_address_long() >> 32);
|
|
packet->frame.src_addr[4] = (uint8_t)(cc2420_get_address_long() >> 24);
|
|
packet->frame.src_addr[5] = (uint8_t)(cc2420_get_address_long() >> 16);
|
|
packet->frame.src_addr[6] = (uint8_t)(cc2420_get_address_long() >> 8);
|
|
packet->frame.src_addr[7] = (uint8_t)(cc2420_get_address_long() & 0xFF);
|
|
}
|
|
|
|
packet->frame.src_pan_id = cc2420_get_pan();
|
|
packet->frame.seq_nr = sequenz_nr;
|
|
|
|
sequenz_nr += 1;
|
|
|
|
/* calculate size of the package (header + payload + fcs) */
|
|
packet->length = ieee802154_frame_get_hdr_len(&packet->frame) +
|
|
packet->frame.payload_len + 2;
|
|
|
|
if (packet->length > CC2420_MAX_PKT_LENGTH) {
|
|
return -1;
|
|
}
|
|
|
|
/* FCS is added in hardware */
|
|
uint8_t pkt[packet->length - 2];
|
|
|
|
/* generate pkt */
|
|
cc2420_gen_pkt(pkt, packet);
|
|
|
|
/* idle & flush tx */
|
|
cc2420_strobe(CC2420_STROBE_RFOFF);
|
|
cc2420_strobe(CC2420_STROBE_FLUSHTX);
|
|
|
|
/* write length and packet to fifo */
|
|
cc2420_write_fifo(&packet->length, 1);
|
|
cc2420_write_fifo(pkt, packet->length - 2);
|
|
|
|
unsigned int cpsr = disableIRQ();
|
|
cc2420_strobe(CC2420_STROBE_TXON);
|
|
|
|
// Wait for SFD to be set -> sync word transmitted
|
|
while (cc2420_get_sfd() == 0) {
|
|
abort_count++;
|
|
|
|
if (abort_count > CC2420_SYNC_WORD_TX_TIME) {
|
|
// Abort waiting. CC2420 maybe in wrong mode
|
|
// e.g. sending preambles for always
|
|
puts("[CC2420 TX] fatal error\n");
|
|
/* TODO: error handling */
|
|
packet->length = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DEBUG("SEQ: %u\n", packet->frame.seq_nr);
|
|
restoreIRQ(cpsr);
|
|
|
|
/* wait for packet to be send */
|
|
while (cc2420_get_sfd() != 0);
|
|
|
|
cc2420_switch_to_rx();
|
|
return packet->length;
|
|
}
|
|
|
|
/**
|
|
* @brief Static function to generate byte array from cc2420 packet.
|
|
*
|
|
*/
|
|
|
|
static void cc2420_gen_pkt(uint8_t *buf, cc2420_packet_t *packet)
|
|
{
|
|
uint8_t index, offset;
|
|
index = ieee802154_frame_init(&packet->frame, buf);
|
|
offset = index;
|
|
|
|
while (index < packet->length - 2) {
|
|
buf[index] = packet->frame.payload[index - offset];
|
|
index += 1;
|
|
}
|
|
}
|