mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
296 lines
8.8 KiB
C
296 lines
8.8 KiB
C
/*
|
|
* Copyright (C) 2014 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_at86rf231
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief TX related functionality for the AT86RF231 device driver
|
|
*
|
|
* @author Alaeddine Weslati <alaeddine.weslati@inria.fr>
|
|
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include "at86rf231.h"
|
|
#include "at86rf231_spi.h"
|
|
#include "hwtimer.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
#define _MAX_RETRIES (100)
|
|
|
|
static int16_t at86rf231_load(at86rf231_packet_t *packet);
|
|
static void at86rf231_gen_pkt(uint8_t *buf, at86rf231_packet_t *packet);
|
|
|
|
static uint8_t sequence_nr;
|
|
static uint8_t wait_for_ack;
|
|
|
|
int16_t at86rf231_send(at86rf231_packet_t *packet)
|
|
{
|
|
int16_t result;
|
|
result = at86rf231_load(packet);
|
|
if (result < 0) {
|
|
return result;
|
|
}
|
|
at86rf231_transmit_tx_buf(NULL);
|
|
return result;
|
|
}
|
|
|
|
netdev_802154_tx_status_t at86rf231_load_tx_buf(netdev_t *dev,
|
|
netdev_802154_pkt_kind_t kind,
|
|
netdev_802154_node_addr_t *dest,
|
|
int use_long_addr,
|
|
int wants_ack,
|
|
netdev_hlist_t *upper_layer_hdrs,
|
|
void *buf,
|
|
unsigned int len)
|
|
{
|
|
(void)dev;
|
|
|
|
uint8_t mhr[24];
|
|
uint8_t index = 3;
|
|
|
|
/* frame type */
|
|
switch (kind) {
|
|
case NETDEV_802154_PKT_KIND_BEACON:
|
|
mhr[0] = 0x00;
|
|
break;
|
|
case NETDEV_802154_PKT_KIND_DATA:
|
|
mhr[0] = 0x01;
|
|
break;
|
|
case NETDEV_802154_PKT_KIND_ACK:
|
|
mhr[0] = 0x02;
|
|
break;
|
|
default:
|
|
return NETDEV_802154_TX_STATUS_INVALID_PARAM;
|
|
}
|
|
|
|
if (wants_ack) {
|
|
mhr[0] |= 0x20;
|
|
}
|
|
|
|
wait_for_ack = wants_ack;
|
|
|
|
uint16_t src_pan = at86rf231_get_pan();
|
|
uint8_t compress_pan = 0;
|
|
|
|
if (use_long_addr) {
|
|
mhr[1] = 0xcc;
|
|
}
|
|
else {
|
|
mhr[1] = 0x88;
|
|
if (dest->pan.id == src_pan) {
|
|
compress_pan = 1;
|
|
mhr[0] |= 0x40;
|
|
}
|
|
}
|
|
|
|
mhr[2] = sequence_nr++;
|
|
|
|
/* First 3 bytes are fixed with FCS and SEQ, resume with index=3 */
|
|
if (use_long_addr) {
|
|
mhr[index++] = (uint8_t)(dest->long_addr & 0xFF);
|
|
mhr[index++] = (uint8_t)(dest->long_addr >> 8);
|
|
mhr[index++] = (uint8_t)(dest->long_addr >> 16);
|
|
mhr[index++] = (uint8_t)(dest->long_addr >> 24);
|
|
mhr[index++] = (uint8_t)(dest->long_addr >> 32);
|
|
mhr[index++] = (uint8_t)(dest->long_addr >> 40);
|
|
mhr[index++] = (uint8_t)(dest->long_addr >> 48);
|
|
mhr[index++] = (uint8_t)(dest->long_addr >> 56);
|
|
|
|
uint64_t src_long_addr = at86rf231_get_address_long();
|
|
mhr[index++] = (uint8_t)(src_long_addr & 0xFF);
|
|
mhr[index++] = (uint8_t)(src_long_addr >> 8);
|
|
mhr[index++] = (uint8_t)(src_long_addr >> 16);
|
|
mhr[index++] = (uint8_t)(src_long_addr >> 24);
|
|
mhr[index++] = (uint8_t)(src_long_addr >> 32);
|
|
mhr[index++] = (uint8_t)(src_long_addr >> 40);
|
|
mhr[index++] = (uint8_t)(src_long_addr >> 48);
|
|
mhr[index++] = (uint8_t)(src_long_addr >> 56);
|
|
}
|
|
else {
|
|
mhr[index++] = (uint8_t)(dest->pan.id & 0xFF);
|
|
mhr[index++] = (uint8_t)(dest->pan.id >> 8);
|
|
|
|
mhr[index++] = (uint8_t)(dest->pan.addr & 0xFF);
|
|
mhr[index++] = (uint8_t)(dest->pan.addr >> 8);
|
|
|
|
if (!compress_pan) {
|
|
mhr[index++] = (uint8_t)(src_pan & 0xFF);
|
|
mhr[index++] = (uint8_t)(src_pan >> 8);
|
|
}
|
|
|
|
uint16_t src_addr = at86rf231_get_address();
|
|
mhr[index++] = (uint8_t)(src_addr & 0xFF);
|
|
mhr[index++] = (uint8_t)(src_addr >> 8);
|
|
}
|
|
|
|
/* total frame size:
|
|
* index -> MAC header
|
|
* len -> payload length
|
|
* 2 -> CRC bytes
|
|
* + lengths of upper layers' headers */
|
|
size_t size = index + len + 2 + netdev_get_hlist_len(upper_layer_hdrs);
|
|
|
|
if (size > AT86RF231_MAX_PKT_LENGTH) {
|
|
DEBUG("at86rf231: packet too long, dropped it.\n");
|
|
return NETDEV_802154_TX_STATUS_PACKET_TOO_LONG;
|
|
}
|
|
|
|
uint8_t size_byte = (uint8_t)size;
|
|
netdev_hlist_t *ptr = upper_layer_hdrs;
|
|
|
|
at86rf231_write_fifo(&size_byte, 1);
|
|
at86rf231_write_fifo(mhr, (radio_packet_length_t)index);
|
|
if (upper_layer_hdrs) {
|
|
do {
|
|
at86rf231_write_fifo(ptr->header,
|
|
(radio_packet_length_t)(ptr->header_len));
|
|
netdev_hlist_advance(&ptr);
|
|
} while (ptr != upper_layer_hdrs);
|
|
}
|
|
at86rf231_write_fifo((uint8_t*)buf, len);
|
|
return NETDEV_802154_TX_STATUS_OK;
|
|
}
|
|
|
|
netdev_802154_tx_status_t at86rf231_transmit_tx_buf(netdev_t *dev)
|
|
{
|
|
(void)dev;
|
|
/* radio driver state: sending */
|
|
/* will be freed in at86rf231_rx_irq when TRX_END interrupt occurs */
|
|
driver_state = AT_DRIVER_STATE_SENDING;
|
|
|
|
/* Start TX */
|
|
at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__TX_START);
|
|
DEBUG("at86rf231: Started TX\n");
|
|
|
|
if (!wait_for_ack) {
|
|
DEBUG("at86rf231: Don't wait for ACK, TX done.\n");
|
|
return NETDEV_802154_TX_STATUS_OK;
|
|
}
|
|
|
|
uint8_t trac_status;
|
|
do {
|
|
trac_status = at86rf231_reg_read(AT86RF231_REG__TRX_STATE);
|
|
trac_status &= AT86RF231_TRX_STATE_MASK__TRAC;
|
|
}
|
|
while (trac_status == AT86RF231_TRX_STATE__TRAC_INVALID);
|
|
|
|
switch (trac_status) {
|
|
case AT86RF231_TRX_STATE__TRAC_CHANNEL_ACCESS_FAILURE:
|
|
return NETDEV_802154_TX_STATUS_MEDIUM_BUSY;
|
|
|
|
case AT86RF231_TRX_STATE__TRAC_NO_ACK:
|
|
return NETDEV_802154_TX_STATUS_NOACK;
|
|
|
|
default:
|
|
return NETDEV_802154_TX_STATUS_OK;
|
|
}
|
|
}
|
|
|
|
int16_t at86rf231_load(at86rf231_packet_t *packet)
|
|
{
|
|
// Set missing frame information
|
|
packet->frame.fcf.frame_ver = 0;
|
|
|
|
packet->frame.src_pan_id = at86rf231_get_pan();
|
|
|
|
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)(at86rf231_get_address() >> 8);
|
|
packet->frame.src_addr[1] = (uint8_t)(at86rf231_get_address() & 0xFF);
|
|
}
|
|
else if (packet->frame.fcf.src_addr_m == 3) {
|
|
packet->frame.src_addr[0] = (uint8_t)(at86rf231_get_address_long() >> 56);
|
|
packet->frame.src_addr[1] = (uint8_t)(at86rf231_get_address_long() >> 48);
|
|
packet->frame.src_addr[2] = (uint8_t)(at86rf231_get_address_long() >> 40);
|
|
packet->frame.src_addr[3] = (uint8_t)(at86rf231_get_address_long() >> 32);
|
|
packet->frame.src_addr[4] = (uint8_t)(at86rf231_get_address_long() >> 24);
|
|
packet->frame.src_addr[5] = (uint8_t)(at86rf231_get_address_long() >> 16);
|
|
packet->frame.src_addr[6] = (uint8_t)(at86rf231_get_address_long() >> 8);
|
|
packet->frame.src_addr[7] = (uint8_t)(at86rf231_get_address_long() & 0xFF);
|
|
}
|
|
|
|
packet->frame.seq_nr = sequence_nr++;
|
|
|
|
/* calculate size of the frame (payload + FCS) */
|
|
packet->length = ieee802154_frame_get_hdr_len(&packet->frame) +
|
|
packet->frame.payload_len + 1;
|
|
|
|
if (packet->length > AT86RF231_MAX_PKT_LENGTH) {
|
|
DEBUG("at86rf231: ERROR: packet too long, dropped it.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* FCS is added in hardware */
|
|
uint8_t pkt[packet->length];
|
|
|
|
/* generate pkt */
|
|
at86rf231_gen_pkt(pkt, packet);
|
|
|
|
/* Go to state PLL_ON */
|
|
at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__PLL_ON);
|
|
|
|
/* wait until it is on PLL_ON state */
|
|
do {
|
|
int max_wait = _MAX_RETRIES;
|
|
if (!--max_wait) {
|
|
DEBUG("at86rf231 : ERROR : could not enter PLL_ON mode\n");
|
|
break;
|
|
}
|
|
} while ((at86rf231_get_status() & AT86RF231_TRX_STATUS_MASK__TRX_STATUS)
|
|
!= AT86RF231_TRX_STATUS__PLL_ON);
|
|
|
|
/* change into TX_ARET_ON state */
|
|
at86rf231_reg_write(AT86RF231_REG__TRX_STATE, AT86RF231_TRX_STATE__TX_ARET_ON);
|
|
|
|
do {
|
|
int max_wait = _MAX_RETRIES;
|
|
if (!--max_wait) {
|
|
DEBUG("at86rf231 : ERROR : could not enter TX_ARET_ON mode\n");
|
|
break;
|
|
}
|
|
} while (at86rf231_get_status() != AT86RF231_TRX_STATUS__TX_ARET_ON);
|
|
|
|
/* load packet into fifo */
|
|
at86rf231_write_fifo(pkt, packet->length);
|
|
DEBUG("at86rf231: Wrote to FIFO\n");
|
|
|
|
return packet->length;
|
|
}
|
|
|
|
/**
|
|
* @brief Static function to generate byte array from at86rf231 packet.
|
|
*/
|
|
static void at86rf231_gen_pkt(uint8_t *buf, at86rf231_packet_t *packet)
|
|
{
|
|
uint8_t index, offset;
|
|
index = ieee802154_frame_init(&packet->frame, &buf[1]);
|
|
|
|
// add length for at86rf231
|
|
buf[0] = packet->length + 1;
|
|
index++;
|
|
|
|
offset = index;
|
|
|
|
while (index < packet->length) {
|
|
buf[index] = packet->frame.payload[index - offset];
|
|
index += 1;
|
|
}
|
|
}
|