1146 lines
26 KiB
C
1146 lines
26 KiB
C
/* cc2520.c - IEEE 802.15.4 driver for TI CC2520 */
|
||
|
||
/*
|
||
* Copyright (c) 2015 Intel Corporation.
|
||
*
|
||
* Copyright (c) 2011, Swedish Institute of Computer Science
|
||
* All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms, with or without
|
||
* modification, are permitted provided that the following conditions are met:
|
||
*
|
||
* 1) Redistributions of source code must retain the above copyright notice,
|
||
* this list of conditions and the following disclaimer.
|
||
*
|
||
* 2) 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.
|
||
*
|
||
* 3) Neither the name of Intel Corporation nor the names of its contributors
|
||
* may be used to endorse or promote products derived from this software without
|
||
* specific prior written permission.
|
||
*
|
||
* 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 HOLDER 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.
|
||
*/
|
||
|
||
#include <stdint.h>
|
||
|
||
#include <nanokernel.h>
|
||
|
||
#include <errno.h>
|
||
#include <gpio.h>
|
||
#include <spi.h>
|
||
|
||
#include <board.h>
|
||
#include <init.h>
|
||
|
||
#include <802.15.4/cc2520.h>
|
||
|
||
#include <net/l2_buf.h>
|
||
#include "packetbuf.h"
|
||
#include "net_driver_15_4.h"
|
||
|
||
#include "cc2520.h"
|
||
#include "cc2520_arch.h"
|
||
|
||
#ifndef CC2520_CONF_AUTOACK
|
||
#define CC2520_CONF_AUTOACK 0
|
||
#endif /* CC2520_CONF_AUTOACK */
|
||
|
||
#define WITH_SEND_CCA 1
|
||
|
||
#define FOOTER_LEN 2
|
||
|
||
#define AUTOCRC (1 << 6)
|
||
#define AUTOACK (1 << 5)
|
||
#define FRAME_MAX_VERSION ((1 << 3) | (1 << 2))
|
||
#define FRAME_FILTER_ENABLE (1 << 0)
|
||
#define CORR_THR(n) (((n) & 0x1f) << 6)
|
||
#define FIFOP_THR(n) ((n) & 0x7f)
|
||
|
||
#define FOOTER1_CRC_OK 0x80
|
||
#define FOOTER1_CORRELATION 0x7f
|
||
|
||
#define WAIT_100ms 100
|
||
#define WAIT_1000ms 1000
|
||
#define WAIT_200ms 200
|
||
#define WAIT_10ms 10
|
||
#define WAIT_1ms 1
|
||
|
||
struct cc2520_gpio_config cc2520_gpio_config[CC2520_GPIO_IDX_LAST_ENTRY];
|
||
struct cc2520_config cc2520_config;
|
||
|
||
/* CC2520 is currently a singleton instance
|
||
* This would be needed no get fixed. The main
|
||
* issue is the gpio cb handler: it would need to
|
||
* get access to the relevant instance of cc2520 driver
|
||
*/
|
||
struct device *cc2520_sgl_dev;
|
||
|
||
/* static int cc2520_authority_level_of_sender; */
|
||
static int cc2520_packets_seen, cc2520_packets_read;
|
||
|
||
static bool init_ok;
|
||
|
||
/* max time is in millisecs */
|
||
#define BUSYWAIT_UNTIL(cond, max_time) \
|
||
do { \
|
||
uint32_t t0 = clock_get_cycle(); \
|
||
uint32_t limit = t0 + CLOCK_MSEC_TO_CYCLES(max_time); \
|
||
while (!(cond) && CLOCK_CYCLE_LT(clock_get_cycle(), \
|
||
limit)); \
|
||
} while (0)
|
||
|
||
#define CC2520_STROBE_PLUS_NOP(strobe) cc2520_strobe_plus_nop(strobe)
|
||
#define CC2520_ENABLE_FIFOP_INT() cc2520_enable_fifop_int(1)
|
||
#define CC2520_DISABLE_FIFOP_INT() cc2520_enable_fifop_int(0)
|
||
#define CC2520_FIFOP_INT_INIT() cc2520_init_fifop_int(cc2520_gpio_int_handler)
|
||
#define CC2520_CLEAR_FIFOP_INT() cc2520_clear_fifop_int()
|
||
#define SET_VREG_ACTIVE() cc2520_set_vreg(1)
|
||
#define SET_VREG_INACTIVE() cc2520_set_vreg(0)
|
||
#define SET_RESET_ACTIVE() cc2520_set_reset(0)
|
||
#define SET_RESET_INACTIVE() cc2520_set_reset(1)
|
||
|
||
#define CC2520_FIFOP_IS_1 (cc2520_get_fifop() != 0)
|
||
#define CC2520_FIFO_IS_1 (cc2520_get_fifo() != 0)
|
||
#define CC2520_SFD_IS_1 (cc2520_get_sfd() != 0)
|
||
#define CC2520_CCA_IS_1 (cc2520_get_cca() != 0)
|
||
|
||
static volatile uint8_t cc2520_sfd_counter;
|
||
static volatile uint16_t cc2520_sfd_start_time;
|
||
static volatile uint16_t cc2520_sfd_end_time;
|
||
|
||
static volatile uint16_t last_packet_timestamp;
|
||
|
||
static signed char cc2520_last_rssi;
|
||
static uint8_t cc2520_last_correlation;
|
||
|
||
static uint8_t receive_on;
|
||
static int channel;
|
||
static bool cc2520_read_fifo_byte(uint8_t *byte)
|
||
{
|
||
return cc2520_read_fifo_buf(byte, 1);
|
||
}
|
||
|
||
static uint8_t getreg(uint16_t regname)
|
||
{
|
||
uint16_t reg = 0;
|
||
|
||
if (!cc2520_read_reg(regname, ®)) {
|
||
DBG("%s: cannot read reg %d value\n", __func__,
|
||
regname);
|
||
}
|
||
return reg;
|
||
}
|
||
|
||
static void setreg(uint16_t regname, uint8_t value)
|
||
{
|
||
if (!cc2520_write_reg(regname, value)) {
|
||
DBG("%s: cannot set reg %d to %d\n", __func__,
|
||
regname, value);
|
||
}
|
||
}
|
||
|
||
#ifdef CONFIG_TI_CC2520_DEBUG
|
||
static void print_radio_status(void)
|
||
{
|
||
uint8_t value = getreg(CC2520_FSMSTAT1);
|
||
|
||
DBG("Radio status FSMSTAT1: ");
|
||
if (value & BIT(CC2520_STATUS_FIFO)) {
|
||
DBG("FIFO ");
|
||
}
|
||
if (value & BIT(CC2520_STATUS_FIFOP)) {
|
||
DBG("FIFOP ");
|
||
}
|
||
if (value & BIT(CC2520_STATUS_SFD)) {
|
||
DBG("SFD ");
|
||
}
|
||
if (value & BIT(CC2520_STATUS_CCA)) {
|
||
DBG("CCA ");
|
||
}
|
||
if (value & BIT(CC2520_STATUS_SAMPLED_CCA)) {
|
||
DBG("SAMPLED_CCA ");
|
||
}
|
||
if (value & BIT(CC2520_STATUS_LOCK_STATUS)) {
|
||
DBG("LOCK_STATUS ");
|
||
}
|
||
if (value & BIT(CC2520_STATUS_TX_ACTIVE)) {
|
||
DBG("TX_ACTIVE ");
|
||
}
|
||
if (value & BIT(CC2520_STATUS_RX_ACTIVE)) {
|
||
DBG("RX_ACTIVE ");
|
||
}
|
||
DBG("\n");
|
||
}
|
||
|
||
static inline void print_exceptions_0(void)
|
||
{
|
||
uint8_t flag = getreg(CC2520_EXCFLAG0);
|
||
|
||
DBG("EXCFLAG0: ");
|
||
if (flag & BIT(CC2520_EXCFLAGS0_RF_IDLE)) {
|
||
DBG("RF_IDLE ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS0_TX_FRM_DONE)) {
|
||
DBG("TX_FRM_DONE ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS0_TX_ACK_DONE)) {
|
||
DBG("TX_ACK_DONE ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS0_TX_UNDERFLOW)) {
|
||
DBG("TX_UNDERFLOW ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS0_TX_OVERFLOW)) {
|
||
DBG("TX_OVERFLOW ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS0_RX_UNDERFLOW)) {
|
||
DBG("RX_UNDERFLOW ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS0_RX_OVERFLOW)) {
|
||
DBG("RX_OVERFLOW ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS0_RXENABLE_ZERO)) {
|
||
DBG("RXENABLE_ZERO");
|
||
}
|
||
DBG("\n");
|
||
}
|
||
|
||
static inline void print_exceptions_1(void)
|
||
{
|
||
uint8_t flag = getreg(CC2520_EXCFLAG1);
|
||
|
||
DBG("EXCFLAG1: ");
|
||
if (flag & BIT(CC2520_EXCFLAGS1_RX_FRM_DONE)) {
|
||
DBG("RX_FRM_DONE ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS1_RX_FRM_ACCEPTED)) {
|
||
DBG("RX_FRM_ACCEPTED ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS1_SRC_MATCH_DONE)) {
|
||
DBG("SRC_MATCH_DONE ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS1_SRC_MATCH_FOUND)) {
|
||
DBG("SRC_MATCH_FOUND ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS1_FIFOP)) {
|
||
DBG("FIFOP ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS1_SFD)) {
|
||
DBG("SFD ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS1_DPU_DONE_L)) {
|
||
DBG("DPU_DONE_L ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS1_DPU_DONE_H)) {
|
||
DBG("DPU_DONE_H");
|
||
}
|
||
DBG("\n");
|
||
}
|
||
|
||
static inline void print_errors(void)
|
||
{
|
||
uint8_t flag = getreg(CC2520_EXCFLAG2);
|
||
|
||
DBG("EXCFLAG2: ");
|
||
if (flag & BIT(CC2520_EXCFLAGS2_MEMADDR_ERROR)) {
|
||
DBG("MEMADDR_ERROR ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS2_USAGE_ERROR)) {
|
||
DBG("USAGE_ERROR ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS2_OPERAND_ERROR)) {
|
||
DBG("OPERAND_ERROR ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS2_SPI_ERROR)) {
|
||
DBG("SPI_ERROR ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS2_RF_NO_LOCK)) {
|
||
DBG("RF_NO_LOCK ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS2_RX_FRM_ABORTED)) {
|
||
DBG("RX_FRM_ABORTED ");
|
||
}
|
||
if (flag & BIT(CC2520_EXCFLAGS2_RFBUFMOV_TIMEOUT)) {
|
||
DBG("RFBUFMOV_TIMEOUT");
|
||
}
|
||
DBG("\n");
|
||
}
|
||
|
||
static void clear_exceptions(void)
|
||
{
|
||
DBG("Clearing up exceptions & errors\n");
|
||
|
||
setreg(CC2520_EXCFLAG0, 0);
|
||
setreg(CC2520_EXCFLAG1, 0);
|
||
setreg(CC2520_EXCFLAG2, 0);
|
||
}
|
||
|
||
static void cc2520_print_gpio_config(void)
|
||
{
|
||
DBG("GPIOCTRL0: 0x%x\n", getreg(CC2520_GPIOCTRL0));
|
||
DBG("GPIOCTRL1: 0x%x\n", getreg(CC2520_GPIOCTRL1));
|
||
DBG("GPIOCTRL2: 0x%x\n", getreg(CC2520_GPIOCTRL2));
|
||
DBG("GPIOCTRL3: 0x%x\n", getreg(CC2520_GPIOCTRL3));
|
||
DBG("GPIOCTRL4: 0x%x\n", getreg(CC2520_GPIOCTRL4));
|
||
DBG("GPIOCTRL5: 0x%x\n", getreg(CC2520_GPIOCTRL5));
|
||
DBG("GPIOPOLARITY: 0x%x\n", getreg(CC2520_GPIOPOLARITY));
|
||
DBG("GPIOCTRL: 0x%x\n", getreg(CC2520_GPIOCTRL));
|
||
}
|
||
|
||
#else
|
||
#define print_radio_status()
|
||
#define print_exceptions_0()
|
||
#define print_exceptions_1()
|
||
#define print_errors()
|
||
#define clear_exceptions()
|
||
#define cc2520_print_gpio_config()
|
||
#endif
|
||
|
||
static inline unsigned int status(void)
|
||
{
|
||
uint8_t status = 0x00;
|
||
|
||
if (!cc2520_get_status(&status)) {
|
||
DBG("Reading status 0x%x failed\n", status);
|
||
return 0x00;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
static inline int cc2520_pending_packet(void)
|
||
{
|
||
return CC2520_FIFOP_IS_1;
|
||
}
|
||
|
||
static void flushrx(void)
|
||
{
|
||
uint8_t dummy;
|
||
|
||
cc2520_read_fifo_byte(&dummy);
|
||
dummy++;
|
||
|
||
cc2520_strobe(CC2520_INS_SFLUSHRX);
|
||
cc2520_strobe(CC2520_INS_SFLUSHRX);
|
||
|
||
#if 0
|
||
/* SFLUSHRX causes recalibration so wait small delay after
|
||
* it before continuing. Errata chapter 1.3
|
||
*/
|
||
clock_delay_usec_busywait(500);
|
||
#endif
|
||
}
|
||
|
||
static void on(void)
|
||
{
|
||
DBG("cc2520 radio on\n");
|
||
|
||
CC2520_ENABLE_FIFOP_INT();
|
||
cc2520_strobe(CC2520_INS_SRXON);
|
||
|
||
BUSYWAIT_UNTIL(status() & (BIT(CC2520_XOSC16M_STABLE)), WAIT_10ms);
|
||
if (!(status() & BIT(CC2520_XOSC16M_STABLE))) {
|
||
DBG("Clock is not stabilized, radio is not on\n");
|
||
return;
|
||
}
|
||
|
||
print_radio_status();
|
||
|
||
receive_on = 1;
|
||
}
|
||
|
||
static void off(void)
|
||
{
|
||
DBG("cc2520 radio off\n");
|
||
receive_on = 0;
|
||
|
||
/* Wait for transmission to end before turning radio off. */
|
||
BUSYWAIT_UNTIL(!(status() & BIT(CC2520_TX_ACTIVE)), WAIT_100ms);
|
||
|
||
cc2520_strobe(CC2520_INS_SRFOFF);
|
||
CC2520_DISABLE_FIFOP_INT();
|
||
|
||
if (!cc2520_pending_packet()) {
|
||
flushrx();
|
||
}
|
||
}
|
||
|
||
static int cc2520_off(void)
|
||
{
|
||
/* Don't do anything if we are already turned off. */
|
||
if (receive_on == 1) {
|
||
off();
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
int cc2520_on(void)
|
||
{
|
||
if (!init_ok) {
|
||
DBG("cc2520 not initialized, radio will stay off\n");
|
||
return 0;
|
||
}
|
||
|
||
if (!receive_on) {
|
||
on();
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
static radio_result_t cc2520_set_rx_mode(radio_value_t value)
|
||
{
|
||
static radio_value_t old_value = -1;
|
||
|
||
if (value == old_value) {
|
||
return RADIO_RESULT_OK;
|
||
}
|
||
|
||
#if CC2520_CONF_AUTOACK
|
||
value |= RADIO_RX_MODE_AUTOACK;
|
||
#endif /* CC2520_CONF_AUTOACK */
|
||
|
||
/*
|
||
* Writing RAM requires crystal oscillator to be stable.
|
||
*/
|
||
BUSYWAIT_UNTIL(status() & (BIT(CC2520_XOSC16M_STABLE)), WAIT_100ms);
|
||
if (!(status() & (BIT(CC2520_XOSC16M_STABLE)))) {
|
||
DBG("cc2520_set_rx_mode: CC2520_XOSC16M_STABLE not set\n");
|
||
}
|
||
|
||
/* Wait for any transmission to end. */
|
||
BUSYWAIT_UNTIL(!(status() & BIT(CC2520_TX_ACTIVE)), WAIT_100ms);
|
||
|
||
if ((value & RADIO_RX_MODE_AUTOACK) !=
|
||
(old_value & RADIO_RX_MODE_AUTOACK)) {
|
||
if (value & RADIO_RX_MODE_AUTOACK) {
|
||
setreg(CC2520_FRMCTRL0, AUTOCRC | AUTOACK);
|
||
} else {
|
||
setreg(CC2520_FRMCTRL0, AUTOCRC);
|
||
}
|
||
}
|
||
|
||
if ((value & RADIO_RX_MODE_ADDRESS_FILTER) !=
|
||
(old_value & RADIO_RX_MODE_ADDRESS_FILTER)) {
|
||
if (value & RADIO_RX_MODE_ADDRESS_FILTER) {
|
||
setreg(CC2520_FRMFILT0,
|
||
FRAME_MAX_VERSION | FRAME_FILTER_ENABLE);
|
||
} else {
|
||
setreg(CC2520_FRMFILT0, FRAME_MAX_VERSION);
|
||
}
|
||
}
|
||
old_value = value;
|
||
|
||
#if 1
|
||
if (receive_on) {
|
||
cc2520_strobe(CC2520_INS_SRXON);
|
||
|
||
BUSYWAIT_UNTIL((status() & BIT(CC2520_RSSI_VALID)), WAIT_100ms);
|
||
if (!(status() & BIT(CC2520_RSSI_VALID))) {
|
||
return RADIO_RESULT_ERROR;
|
||
}
|
||
}
|
||
#endif /* 1/0 */
|
||
|
||
return RADIO_RESULT_OK;
|
||
}
|
||
|
||
radio_result_t cc2520_get_value(radio_param_t param, radio_value_t *value)
|
||
{
|
||
if (!value) {
|
||
return RADIO_RESULT_INVALID_VALUE;
|
||
}
|
||
|
||
switch (param) {
|
||
case RADIO_PARAM_POWER_MODE:
|
||
*value = receive_on ? RADIO_POWER_MODE_ON :
|
||
RADIO_POWER_MODE_OFF;
|
||
return RADIO_RESULT_OK;
|
||
case RADIO_PARAM_CHANNEL:
|
||
*value = cc2520_get_channel();
|
||
return RADIO_RESULT_OK;
|
||
case RADIO_CONST_CHANNEL_MIN:
|
||
*value = 11;
|
||
return RADIO_RESULT_OK;
|
||
case RADIO_CONST_CHANNEL_MAX:
|
||
*value = 26;
|
||
return RADIO_RESULT_OK;
|
||
default:
|
||
return RADIO_RESULT_NOT_SUPPORTED;
|
||
}
|
||
}
|
||
|
||
radio_result_t cc2520_set_value(radio_param_t param, radio_value_t value)
|
||
{
|
||
switch (param) {
|
||
case RADIO_PARAM_POWER_MODE:
|
||
if (value == RADIO_POWER_MODE_ON) {
|
||
cc2520_on();
|
||
return RADIO_RESULT_OK;
|
||
}
|
||
if (value == RADIO_POWER_MODE_OFF) {
|
||
cc2520_off();
|
||
return RADIO_RESULT_OK;
|
||
}
|
||
return RADIO_RESULT_INVALID_VALUE;
|
||
case RADIO_PARAM_CHANNEL:
|
||
if (value < 11 || value > 26) {
|
||
return RADIO_RESULT_INVALID_VALUE;
|
||
}
|
||
cc2520_set_channel(value);
|
||
return RADIO_RESULT_OK;
|
||
case RADIO_PARAM_PAN_ID:
|
||
cc2520_set_pan_addr(value, 0x0000, NULL);
|
||
return RADIO_RESULT_OK;
|
||
case RADIO_PARAM_RX_MODE:
|
||
return cc2520_set_rx_mode(value);
|
||
default:
|
||
return RADIO_RESULT_NOT_SUPPORTED;
|
||
}
|
||
}
|
||
|
||
static void getrxdata(void *buf, int len)
|
||
{
|
||
cc2520_read_fifo_buf(buf, len);
|
||
}
|
||
|
||
static void getrxbyte(uint8_t *byte)
|
||
{
|
||
cc2520_read_fifo_byte(byte);
|
||
}
|
||
|
||
static inline bool strobe(uint8_t regname)
|
||
{
|
||
return cc2520_strobe(regname);
|
||
}
|
||
|
||
static void set_txpower(uint8_t power)
|
||
{
|
||
setreg(CC2520_TXPOWER, power);
|
||
}
|
||
|
||
static inline int cc2520_receiving_packet(void)
|
||
{
|
||
return CC2520_SFD_IS_1;
|
||
}
|
||
|
||
static int cc2520_transmit(struct net_buf *buf, unsigned short payload_len)
|
||
{
|
||
int txpower;
|
||
uint32_t tx_start_wait;
|
||
uint8_t sampled_cca;
|
||
|
||
if (!init_ok) {
|
||
return -EIO;
|
||
}
|
||
|
||
txpower = 0;
|
||
|
||
if (packetbuf_attr(buf, PACKETBUF_ATTR_RADIO_TXPOWER) > 0) {
|
||
/* Remember the current transmission power */
|
||
txpower = cc2520_get_txpower();
|
||
/* Set the specified transmission power */
|
||
set_txpower(packetbuf_attr(buf, PACKETBUF_ATTR_RADIO_TXPOWER) - 1);
|
||
}
|
||
|
||
/* The TX FIFO can only hold one packet. Make sure to not overrun
|
||
* FIFO by waiting for transmission to start here and synchronizing
|
||
* with the CC2520_TX_ACTIVE check in cc2520_send.
|
||
*
|
||
* Note that we may have to wait up to 320 us (20 symbols) before
|
||
* transmission starts.
|
||
*/
|
||
|
||
#if WITH_SEND_CCA
|
||
strobe(CC2520_INS_SRXON);
|
||
BUSYWAIT_UNTIL(status() & BIT(CC2520_RSSI_VALID), WAIT_100ms);
|
||
strobe(CC2520_INS_STXONCCA);
|
||
BUSYWAIT_UNTIL((sampled_cca = (getreg(CC2520_FSMSTAT1) &
|
||
CC2520_FSMSTAT1_SAMPLED_CCA)),
|
||
WAIT_10ms);
|
||
if (sampled_cca == 0) {
|
||
DBG("cc2520: sample_cca is 0, TX ERROR\n");
|
||
return RADIO_TX_ERR;
|
||
}
|
||
#else /* WITH_SEND_CCA */
|
||
strobe(CC2520_INS_STXON);
|
||
#endif /* WITH_SEND_CCA */
|
||
|
||
tx_start_wait = clock_get_cycle() + CLOCK_MSEC_TO_CYCLES(3000) + 1;
|
||
while (CLOCK_CYCLE_LT(clock_get_cycle(), tx_start_wait)) {
|
||
if (!CC2520_SFD_IS_1) {
|
||
continue;
|
||
}
|
||
|
||
#if PACKETBUF_WITH_PACKET_TYPE
|
||
{
|
||
uint32_t sfd_timestamp;
|
||
|
||
sfd_timestamp = cc2520_sfd_start_time;
|
||
if (packetbuf_attr(buf, PACKETBUF_ATTR_PACKET_TYPE) ==
|
||
PACKETBUF_ATTR_PACKET_TYPE_TIMESTAMP) {
|
||
/* Write timestamp to last two bytes of packet
|
||
* in TXFIFO.
|
||
*/
|
||
cc2520_write_ram(&sfd_timestamp,
|
||
CC2520RAM_TXFIFO + payload_len - 1, 2);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (!(status() & BIT(CC2520_TX_ACTIVE))) {
|
||
/* SFD went high but we are not transmitting.
|
||
* This means that we just started receiving a packet,
|
||
* so we drop the transmission.
|
||
*/
|
||
DBG("TX collision 0x%x\n", status());
|
||
return RADIO_TX_COLLISION;
|
||
}
|
||
|
||
/* We wait until transmission has ended so that we get an
|
||
* accurate measurement of the transmission time.
|
||
*/
|
||
BUSYWAIT_UNTIL(!(status() & BIT(CC2520_TX_ACTIVE)), WAIT_100ms);
|
||
|
||
DBG("status 0x%x\n", status());
|
||
|
||
if (!receive_on) {
|
||
/* We need to explicitly turn off the radio,
|
||
* since STXON[CCA] -> TX_ACTIVE -> RX_ACTIVE
|
||
*/
|
||
off();
|
||
}
|
||
|
||
if (packetbuf_attr(buf, PACKETBUF_ATTR_RADIO_TXPOWER) > 0) {
|
||
/* Restore the transmission power */
|
||
set_txpower(txpower & 0xff);
|
||
}
|
||
|
||
return RADIO_TX_OK;
|
||
}
|
||
|
||
/* If we are using WITH_SEND_CCA, we get here if the packet wasn't
|
||
* transmitted because of other channel activity.
|
||
*/
|
||
DBG("cc2520: transmission never started\n");
|
||
|
||
print_exceptions_0();
|
||
print_exceptions_1();
|
||
|
||
if (packetbuf_attr(buf, PACKETBUF_ATTR_RADIO_TXPOWER) > 0) {
|
||
/* Restore the transmission power */
|
||
set_txpower(txpower & 0xff);
|
||
}
|
||
|
||
return RADIO_TX_COLLISION;
|
||
}
|
||
|
||
static int cc2520_prepare(const void *payload, unsigned short payload_len)
|
||
{
|
||
uint8_t *buf = (uint8_t *)payload;
|
||
uint8_t total_len;
|
||
|
||
if (!init_ok) {
|
||
return -EIO;
|
||
}
|
||
|
||
DBG("cc2520: sending %d bytes\n", payload_len);
|
||
|
||
clear_exceptions();
|
||
|
||
/* Write packet to TX FIFO. */
|
||
strobe(CC2520_INS_SFLUSHTX);
|
||
|
||
total_len = payload_len + FOOTER_LEN;
|
||
DBG("TX FIFO has %u bytes\n", getreg(CC2520_TXFIFOCNT));
|
||
cc2520_write_fifo_buf(&total_len, 1);
|
||
cc2520_write_fifo_buf(buf, payload_len);
|
||
DBG("TX FIFO has %u bytes\n", getreg(CC2520_TXFIFOCNT));
|
||
|
||
print_errors();
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int cc2520_send(struct net_buf *buf, const void *payload,
|
||
unsigned short payload_len)
|
||
{
|
||
cc2520_prepare(payload, payload_len);
|
||
|
||
return cc2520_transmit(buf, payload_len);
|
||
}
|
||
|
||
int cc2520_get_channel(void)
|
||
{
|
||
return channel;
|
||
}
|
||
|
||
int cc2520_set_channel(int c)
|
||
{
|
||
uint16_t f;
|
||
|
||
/*
|
||
* Subtract the base channel (11), multiply by 5, which is the
|
||
* channel spacing. 357 is 2405-2048 and 0x4000 is LOCK_THR = 1.
|
||
*/
|
||
channel = c;
|
||
|
||
f = MIN_CHANNEL + ((channel - MIN_CHANNEL) * CHANNEL_SPACING);
|
||
/*
|
||
* Writing RAM requires crystal oscillator to be stable.
|
||
*/
|
||
BUSYWAIT_UNTIL((status() & (BIT(CC2520_XOSC16M_STABLE))), WAIT_100ms);
|
||
|
||
/* Wait for any transmission to end. */
|
||
BUSYWAIT_UNTIL(!(status() & BIT(CC2520_TX_ACTIVE)), WAIT_100ms);
|
||
|
||
/* Define radio channel (between 11 and 25) */
|
||
setreg(CC2520_FREQCTRL, f);
|
||
|
||
/* If we are in receive mode, we issue an SRXON command to ensure
|
||
* that the VCO is calibrated.
|
||
*/
|
||
if (receive_on) {
|
||
strobe(CC2520_INS_SRXON);
|
||
BUSYWAIT_UNTIL((status() & BIT(CC2520_RSSI_VALID)), \
|
||
WAIT_100ms);
|
||
if (!(status() & BIT(CC2520_RSSI_VALID))) {
|
||
return RADIO_RESULT_ERROR;
|
||
}
|
||
}
|
||
|
||
return RADIO_RESULT_OK;
|
||
}
|
||
|
||
bool cc2520_set_pan_addr(unsigned pan, unsigned addr,
|
||
const uint8_t *ieee_addr)
|
||
{
|
||
uint8_t tmp[2];
|
||
|
||
/*
|
||
* Writing RAM requires crystal oscillator to be stable.
|
||
*/
|
||
BUSYWAIT_UNTIL((status()) & (BIT(CC2520_XOSC16M_STABLE)), WAIT_1000ms);
|
||
|
||
tmp[0] = pan & 0xff;
|
||
tmp[1] = pan >> 8;
|
||
cc2520_write_ram(tmp, CC2520RAM_PANID, 2);
|
||
|
||
tmp[0] = addr & 0xff;
|
||
tmp[1] = addr >> 8;
|
||
cc2520_write_ram(tmp, CC2520RAM_SHORTADDR, 2);
|
||
|
||
if (ieee_addr) {
|
||
uint8_t tmp_addr[8];
|
||
int f;
|
||
|
||
/* LSB first, MSB last for 802.15.4 addresses in CC2520 */
|
||
for (f = 0; f < 8; f++) {
|
||
tmp_addr[7 - f] = ieee_addr[f];
|
||
}
|
||
|
||
cc2520_write_ram(tmp_addr, CC2520RAM_IEEEADDR, 8);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
#if CONFIG_TI_CC2520_DEBUG
|
||
#define read_packet() read_packet_debug(__func__)
|
||
static void read_packet_debug(const char *caller);
|
||
#else
|
||
static void read_packet(void);
|
||
#endif
|
||
|
||
static int cc2520_read(void *buf, unsigned short bufsize)
|
||
{
|
||
uint8_t footer[2];
|
||
uint8_t len;
|
||
|
||
if (!init_ok) {
|
||
return -EIO;
|
||
}
|
||
|
||
if (!cc2520_pending_packet()) {
|
||
return 0;
|
||
}
|
||
|
||
cc2520_packets_read++;
|
||
|
||
getrxbyte(&len);
|
||
|
||
if (len > CC2520_MAX_PACKET_LEN) {
|
||
/* Oops, we must be out of sync. */
|
||
flushrx();
|
||
return 0;
|
||
}
|
||
|
||
if (len <= FOOTER_LEN) {
|
||
flushrx();
|
||
return 0;
|
||
}
|
||
|
||
if (len - FOOTER_LEN > bufsize) {
|
||
flushrx();
|
||
return 0;
|
||
}
|
||
|
||
getrxdata(buf, len - FOOTER_LEN);
|
||
getrxdata(footer, FOOTER_LEN);
|
||
|
||
if (footer[1] & FOOTER1_CRC_OK) {
|
||
cc2520_last_rssi = footer[0];
|
||
cc2520_last_correlation = footer[1] & FOOTER1_CORRELATION;
|
||
|
||
packetbuf_set_attr(buf, PACKETBUF_ATTR_RSSI, cc2520_last_rssi);
|
||
packetbuf_set_attr(buf, PACKETBUF_ATTR_LINK_QUALITY,
|
||
cc2520_last_correlation);
|
||
|
||
} else {
|
||
flushrx();
|
||
len = FOOTER_LEN;
|
||
}
|
||
|
||
if (cc2520_pending_packet()) {
|
||
if (!CC2520_FIFO_IS_1) {
|
||
/* Clean up in case of FIFO overflow! This happens
|
||
* for every full length frame and is signaled by
|
||
* FIFOP = 1 and FIFO = 0
|
||
*/
|
||
flushrx();
|
||
} else {
|
||
/* Another packet has been received and needs attention. */
|
||
read_packet();
|
||
}
|
||
}
|
||
|
||
if (len < FOOTER_LEN) {
|
||
return 0;
|
||
}
|
||
|
||
return len - FOOTER_LEN;
|
||
}
|
||
|
||
#if CONFIG_TI_CC2520_DEBUG
|
||
static void read_packet_debug(const char *caller)
|
||
#else
|
||
static void read_packet(void)
|
||
#endif
|
||
{
|
||
struct net_buf *buf;
|
||
|
||
buf = l2_buf_get_reserve(0);
|
||
if (buf) {
|
||
int len;
|
||
|
||
packetbuf_set_attr(buf, PACKETBUF_ATTR_TIMESTAMP,
|
||
last_packet_timestamp);
|
||
|
||
len = cc2520_read(packetbuf_dataptr(buf), PACKETBUF_SIZE);
|
||
if (len < 0) {
|
||
l2_buf_unref(buf);
|
||
return;
|
||
}
|
||
|
||
packetbuf_set_datalen(buf, len);
|
||
|
||
#if CONFIG_TI_CC2520_DEBUG
|
||
DBG("%s: %s: received %d bytes\n", caller, __func__, len);
|
||
#endif
|
||
|
||
if (net_driver_15_4_recv_from_hw(buf) < 0) {
|
||
#if CONFIG_TI_CC2520_DEBUG
|
||
DBG("%s: %s: rdc input failed, packet discarded\n",
|
||
caller, __func__);
|
||
#endif
|
||
l2_buf_unref(buf);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void cc2520_gpio_int_handler(struct device *port, uint32_t pin)
|
||
{
|
||
DBG("%s: RX interrupt in pin %d\n", __func__, pin);
|
||
|
||
/* In order to make this driver available for 2+ instances
|
||
* it would require this handler to get access to the concerned
|
||
* instance
|
||
*/
|
||
|
||
CC2520_CLEAR_FIFOP_INT();
|
||
|
||
read_packet();
|
||
|
||
last_packet_timestamp = cc2520_sfd_start_time;
|
||
|
||
cc2520_packets_seen++;
|
||
}
|
||
|
||
void cc2520_set_txpower(uint8_t power)
|
||
{
|
||
set_txpower(power);
|
||
}
|
||
|
||
int cc2520_get_txpower(void)
|
||
{
|
||
uint8_t power;
|
||
|
||
power = getreg(CC2520_TXPOWER);
|
||
|
||
return power;
|
||
}
|
||
|
||
int cc2520_rssi(void)
|
||
{
|
||
int radio_was_off = 0;
|
||
int rssi;
|
||
|
||
if (!receive_on) {
|
||
radio_was_off = 1;
|
||
cc2520_on();
|
||
}
|
||
BUSYWAIT_UNTIL(status() & BIT(CC2520_RSSI_VALID), WAIT_10ms);
|
||
|
||
rssi = (int)((signed char)getreg(CC2520_RSSI));
|
||
|
||
if (radio_was_off) {
|
||
cc2520_off();
|
||
}
|
||
|
||
return rssi;
|
||
}
|
||
|
||
int cc2520_cca_valid(void)
|
||
{
|
||
int valid;
|
||
|
||
valid = !!(status() & BIT(CC2520_RSSI_VALID));
|
||
|
||
return valid;
|
||
}
|
||
|
||
static int cc2520_cca(void)
|
||
{
|
||
int radio_was_off = 0;
|
||
int cca;
|
||
|
||
if (!receive_on) {
|
||
radio_was_off = 1;
|
||
cc2520_on();
|
||
}
|
||
|
||
/* Make sure that the radio really got turned on. */
|
||
if (!receive_on) {
|
||
if (radio_was_off) {
|
||
cc2520_off();
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
BUSYWAIT_UNTIL(status() & BIT(CC2520_RSSI_VALID), WAIT_10ms);
|
||
|
||
cca = CC2520_CCA_IS_1;
|
||
|
||
if (radio_was_off) {
|
||
cc2520_off();
|
||
}
|
||
|
||
return cca;
|
||
}
|
||
|
||
void cc2520_set_cca_threshold(int value)
|
||
{
|
||
setreg(CC2520_CCACTRL0, value & 0xff);
|
||
}
|
||
|
||
static struct device *cc2520_spi_configure(void)
|
||
{
|
||
struct device *spi;
|
||
struct spi_config spi_conf = {
|
||
.config = (8 << 4),
|
||
.max_sys_freq = CONFIG_TI_CC2520_SPI_FREQ,
|
||
};
|
||
|
||
spi = device_get_binding(CONFIG_TI_CC2520_SPI_DRV_NAME);
|
||
spi_configure(spi, &spi_conf);
|
||
|
||
return spi;
|
||
}
|
||
|
||
static void cc2520_configure(struct device *dev)
|
||
{
|
||
CC2520_DISABLE_FIFOP_INT();
|
||
CC2520_FIFOP_INT_INIT();
|
||
|
||
/* Initially reset must be set */
|
||
SET_RESET_ACTIVE();
|
||
SET_VREG_INACTIVE();
|
||
clock_delay_usec_busywait(250);
|
||
|
||
/* Turn on voltage regulator. */
|
||
SET_VREG_ACTIVE();
|
||
clock_delay_usec_busywait(400);
|
||
|
||
/* Release reset */
|
||
SET_RESET_INACTIVE();
|
||
clock_delay_usec_busywait(800);
|
||
|
||
/* Turn on the crystal oscillator. */
|
||
if (!CC2520_STROBE_PLUS_NOP(CC2520_INS_SXOSCON)) {
|
||
DBG("Strobe SXOSCON sending failed\n");
|
||
return;
|
||
}
|
||
|
||
clock_delay_usec_busywait(800);
|
||
|
||
BUSYWAIT_UNTIL((status() & BIT(CC2520_XOSC16M_STABLE)), WAIT_10ms);
|
||
if (!(status() & BIT(CC2520_XOSC16M_STABLE))) {
|
||
DBG("Clock is not stabilized.\n");
|
||
return;
|
||
}
|
||
|
||
/* Change default values as recommended in the data sheet, */
|
||
/* correlation threshold = 20, RX bandpass filter = 1.3uA.*/
|
||
|
||
setreg(CC2520_TXCTRL, 0x94);
|
||
setreg(CC2520_TXPOWER, 0x13); /* Output power 1 dBm */
|
||
|
||
/* TXPOWER values
|
||
* 0x03 -> -18 dBm
|
||
* 0x2C -> -7 dBm
|
||
* 0x88 -> -4 dBm
|
||
* 0x81 -> -2 dBm
|
||
* 0x32 -> 0 dBm
|
||
* 0x13 -> 1 dBm
|
||
* 0x32 -> 0 dBm
|
||
* 0x13 -> 1 dBm
|
||
* 0xAB -> 2 dBm
|
||
* 0xF2 -> 3 dBm
|
||
* 0xF7 -> 5 dBm
|
||
*/
|
||
setreg(CC2520_CCACTRL0, 0xF8); /* CCA threshold -80dBm */
|
||
|
||
/* Recommended RX settings */
|
||
setreg(CC2520_MDMCTRL0, 0x84); /* Controls modem */
|
||
setreg(CC2520_MDMCTRL1, 0x14); /* Controls modem */
|
||
setreg(CC2520_RXCTRL, 0x3F); /* Adjust currents in RX analog */
|
||
setreg(CC2520_FSCTRL, 0x5A); /* Adjust currents in synt. */
|
||
setreg(CC2520_FSCAL1, 0x2B); /* Adjust currents in VCO */
|
||
setreg(CC2520_AGCCTRL1, 0x11); /* Adjust target for AGC ctrl loop */
|
||
setreg(CC2520_AGCCTRL2, 0xEB);
|
||
|
||
/* Disable external clock */
|
||
setreg(CC2520_EXTCLOCK, 0x00);
|
||
|
||
/* Tune ADC performance */
|
||
setreg(CC2520_ADCTEST0, 0x10);
|
||
setreg(CC2520_ADCTEST1, 0x0E);
|
||
setreg(CC2520_ADCTEST2, 0x03);
|
||
|
||
/* Set auto CRC on frame. */
|
||
#if CC2520_CONF_AUTOACK
|
||
setreg(CC2520_FRMCTRL0, AUTOCRC | AUTOACK);
|
||
setreg(CC2520_FRMFILT0, FRAME_MAX_VERSION|FRAME_FILTER_ENABLE);
|
||
#else
|
||
/* setreg(CC2520_FRMCTRL0, 0x60); */
|
||
setreg(CC2520_FRMCTRL0, AUTOCRC);
|
||
/* Disable filter on @ (remove if you want to address specific wismote) */
|
||
setreg(CC2520_FRMFILT0, 0x00);
|
||
#endif /* CC2520_CONF_AUTOACK */
|
||
/* SET_RXENMASK_ON_TX */
|
||
setreg(CC2520_FRMCTRL1, 1);
|
||
/* Set FIFOP threshold to maximum .*/
|
||
setreg(CC2520_FIFOPCTRL, FIFOP_THR(0x7F));
|
||
|
||
if (!cc2520_set_pan_addr(0xffff, 0x0000, NULL)) {
|
||
return;
|
||
}
|
||
cc2520_set_channel(26);
|
||
|
||
flushrx();
|
||
|
||
cc2520_print_gpio_config();
|
||
|
||
init_ok = true;
|
||
}
|
||
|
||
static radio_result_t get_object(radio_param_t param,
|
||
void *dest, size_t size)
|
||
{
|
||
return RADIO_RESULT_NOT_SUPPORTED;
|
||
}
|
||
|
||
static radio_result_t set_object(radio_param_t param,
|
||
const void *src, size_t size)
|
||
{
|
||
return RADIO_RESULT_NOT_SUPPORTED;
|
||
}
|
||
|
||
static int cc2520_contiki_init(void)
|
||
{
|
||
return init_ok;
|
||
}
|
||
|
||
/* Contiki IP stack needs radio driver that it uses to deal with the
|
||
* hardware. This driver API acts as a middle man between Contiki and
|
||
* the Zephyr CC2520 driver. This api needs to external so that
|
||
* Contiki stack can call the API functions.
|
||
*/
|
||
cc2520_driver_api_t cc2520_15_4_radio_driver = {
|
||
.init = cc2520_contiki_init,
|
||
.prepare = cc2520_prepare,
|
||
.transmit = cc2520_transmit,
|
||
.send = cc2520_send,
|
||
.read = cc2520_read,
|
||
.channel_clear = cc2520_cca,
|
||
.receiving_packet = cc2520_receiving_packet,
|
||
.pending_packet = cc2520_pending_packet,
|
||
.on = cc2520_on,
|
||
.off = cc2520_off,
|
||
.get_value = cc2520_get_value,
|
||
.set_value = cc2520_set_value,
|
||
.get_object = get_object,
|
||
.set_object = set_object,
|
||
};
|
||
|
||
static int cc2520_init(struct device *dev)
|
||
{
|
||
struct cc2520_config *info = dev->config->config_info;
|
||
|
||
DBG("%s setup\n", DRIVER_STR);
|
||
|
||
dev->driver_api = &cc2520_15_4_radio_driver;
|
||
cc2520_sgl_dev = dev;
|
||
|
||
info->gpios = cc2520_gpio_configure();
|
||
info->spi = cc2520_spi_configure();
|
||
info->spi_slave = CONFIG_TI_CC2520_SPI_SLAVE;
|
||
|
||
cc2520_configure(dev);
|
||
|
||
if (init_ok) {
|
||
DBG("%s initialized on device: %p\n", DRIVER_STR, dev);
|
||
} else {
|
||
cc2520_sgl_dev = NULL;
|
||
DBG("%s initialization failed\n", DRIVER_STR);
|
||
}
|
||
|
||
return DEV_OK;
|
||
}
|
||
|
||
DEVICE_INIT(cc2520, CONFIG_CC2520_DRV_NAME,
|
||
cc2520_init, &cc2520_config, NULL,
|
||
NANOKERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|