nes-proj/cpu/cc26xx/dev/cc26xx-rf.c

2149 lines
67 KiB
C

/*
* Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/
* 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 the copyright holder 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.
*/
/*---------------------------------------------------------------------------*/
/**
* \addtogroup cc26xx-rf
* @{
*
* \file
* Implementation of the CC26xx RF driver
*/
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "dev/radio.h"
#include "dev/cc26xx-rf.h"
#include "dev/oscillators.h"
#include "net/packetbuf.h"
#include "net/rime/rimestats.h"
#include "net/linkaddr.h"
#include "net/netstack.h"
#include "sys/energest.h"
#include "sys/clock.h"
#include "sys/rtimer.h"
#include "sys/cc.h"
#include "lpm.h"
#include "ti-lib.h"
/*---------------------------------------------------------------------------*/
/* RF core and RF HAL API */
#include "hw_rfc_dbell.h"
#include "hw_rfc_pwr.h"
/*---------------------------------------------------------------------------*/
/* RF Core Mailbox API */
#include "mailbox.h"
#include "common_cmd.h"
#include "common_cmd_field.h"
#include "ble_cmd.h"
#include "ble_cmd_field.h"
#include "ieee_cmd.h"
#include "ieee_cmd_field.h"
#include "data_entry.h"
#include "ble_mailbox.h"
#include "ieee_mailbox.h"
/*---------------------------------------------------------------------------*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
#define BUSYWAIT_UNTIL(cond, max_time) \
do { \
rtimer_clock_t t0; \
t0 = RTIMER_NOW(); \
while(!(cond) && RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + (max_time))); \
} while(0)
/*---------------------------------------------------------------------------*/
#ifdef __GNUC__
#define CC_ALIGN_ATTR(n) __attribute__ ((aligned(n)))
#else
#define CC_ALIGN_ATTR(n)
#endif
/*---------------------------------------------------------------------------*/
#define DEBUG 0
#if DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
/* Data entry status field constants */
#define DATA_ENTRY_STATUS_PENDING 0x00 /* Not in use by the Radio CPU */
#define DATA_ENTRY_STATUS_ACTIVE 0x01 /* Open for r/w by the radio CPU */
#define DATA_ENTRY_STATUS_BUSY 0x02 /* Ongoing r/w */
#define DATA_ENTRY_STATUS_FINISHED 0x03 /* Free to use and to free */
#define DATA_ENTRY_STATUS_UNFINISHED 0x04 /* Partial RX entry */
/*---------------------------------------------------------------------------*/
/* RF stats data structure */
static uint8_t rf_stats[16] = { 0 };
/*---------------------------------------------------------------------------*/
/* RF Command status constants - Correspond to values in the CMDSTA register */
#define RF_CMD_STATUS_PENDING 0x00
#define RF_CMD_STATUS_DONE 0x01
#define RF_CMD_STATUS_ILLEGAL_PTR 0x81
#define RF_CMD_STATUS_UNKNOWN_CMD 0x82
#define RF_CMD_STATUS_UNKNOWN_DIR_CMD 0x83
#define RF_CMD_STATUS_CONTEXT_ERR 0x85
#define RF_CMD_STATUS_SCHEDULING_ERR 0x86
#define RF_CMD_STATUS_PAR_ERR 0x87
#define RF_CMD_STATUS_QUEUE_ERR 0x88
#define RF_CMD_STATUS_QUEUE_BUSY 0x89
/* Status values starting with 0x8 correspond to errors */
#define RF_CMD_STATUS_ERR_MASK 0x80
/* Return values for rf_send_cmd_ok */
#define RF_CMD_ERROR 0
#define RF_CMD_OK 1
/* The size of the RF commands buffer */
#define RF_CMD_BUFFER_SIZE 128
/*---------------------------------------------------------------------------*/
/* RF Radio Op status constants. Field 'status' in Radio Op command struct */
#define RF_RADIO_OP_STATUS_IDLE 0x0000
#define RF_RADIO_OP_STATUS_PENDING 0x0001
#define RF_RADIO_OP_STATUS_ACTIVE 0x0002
#define RF_RADIO_OP_STATUS_SKIPPED 0x0003
#define RF_RADIO_OP_STATUS_DONE_OK 0x0400
#define RF_RADIO_OP_STATUS_DONE_COUNTDOWN 0x0401
#define RF_RADIO_OP_STATUS_DONE_RXERR 0x0402
#define RF_RADIO_OP_STATUS_DONE_TIMEOUT 0x0403
#define RF_RADIO_OP_STATUS_DONE_STOPPED 0x0404
#define RF_RADIO_OP_STATUS_DONE_ABORT 0x0405
#define RF_RADIO_OP_STATUS_ERROR_PAST_START 0x0800
#define RF_RADIO_OP_STATUS_ERROR_START_TRIG 0x0801
#define RF_RADIO_OP_STATUS_ERROR_CONDITION 0x0802
#define RF_RADIO_OP_STATUS_ERROR_PAR 0x0803
#define RF_RADIO_OP_STATUS_ERROR_POINTER 0x0804
#define RF_RADIO_OP_STATUS_ERROR_CMDID 0x0805
#define RF_RADIO_OP_STATUS_ERROR_NO_SETUP 0x0807
#define RF_RADIO_OP_STATUS_ERROR_NO_FS 0x0808
#define RF_RADIO_OP_STATUS_ERROR_SYNTH_PROG 0x0809
/* Additional Op status values for IEEE mode */
#define RF_RADIO_OP_STATUS_IEEE_SUSPENDED 0x2001
#define RF_RADIO_OP_STATUS_IEEE_DONE_OK 0x2400
#define RF_RADIO_OP_STATUS_IEEE_DONE_BUSY 0x2401
#define RF_RADIO_OP_STATUS_IEEE_DONE_STOPPED 0x2402
#define RF_RADIO_OP_STATUS_IEEE_DONE_ACK 0x2403
#define RF_RADIO_OP_STATUS_IEEE_DONE_ACKPEND 0x2404
#define RF_RADIO_OP_STATUS_IEEE_DONE_TIMEOUT 0x2405
#define RF_RADIO_OP_STATUS_IEEE_DONE_BGEND 0x2406
#define RF_RADIO_OP_STATUS_IEEE_DONE_ABORT 0x2407
#define RF_RADIO_OP_STATUS_ERROR_WRONG_BG 0x0806
#define RF_RADIO_OP_STATUS_IEEE_ERROR_PAR 0x2800
#define RF_RADIO_OP_STATUS_IEEE_ERROR_NO_SETUP 0x2801
#define RF_RADIO_OP_STATUS_IEEE_ERROR_NO_FS 0x2802
#define RF_RADIO_OP_STATUS_IEEE_ERROR_SYNTH_PROG 0x2803
#define RF_RADIO_OP_STATUS_IEEE_ERROR_RXOVF 0x2804
#define RF_RADIO_OP_STATUS_IEEE_ERROR_TXUNF 0x2805
/* Op status values for BLE mode */
#define RF_RADIO_OP_STATUS_BLE_DONE_OK 0x1400
#define RF_RADIO_OP_STATUS_BLE_DONE_RXTIMEOUT 0x1401
#define RF_RADIO_OP_STATUS_BLE_DONE_NOSYNC 0x1402
#define RF_RADIO_OP_STATUS_BLE_DONE_RXERR 0x1403
#define RF_RADIO_OP_STATUS_BLE_DONE_CONNECT 0x1404
#define RF_RADIO_OP_STATUS_BLE_DONE_MAXNACK 0x1405
#define RF_RADIO_OP_STATUS_BLE_DONE_ENDED 0x1406
#define RF_RADIO_OP_STATUS_BLE_DONE_ABORT 0x1407
#define RF_RADIO_OP_STATUS_BLE_DONE_STOPPED 0x1408
#define RF_RADIO_OP_STATUS_BLE_ERROR_PAR 0x1800
#define RF_RADIO_OP_STATUS_BLE_ERROR_RXBUF 0x1801
#define RF_RADIO_OP_STATUS_BLE_ERROR_NO_SETUP 0x1802
#define RF_RADIO_OP_STATUS_BLE_ERROR_NO_FS 0x1803
#define RF_RADIO_OP_STATUS_BLE_ERROR_SYNTH_PROG 0x1804
#define RF_RADIO_OP_STATUS_BLE_ERROR_RXOVF 0x1805
#define RF_RADIO_OP_STATUS_BLE_ERROR_TXUNF 0x1806
/* Bits 15:12 signify the protocol */
#define RF_RADIO_OP_STATUS_PROTO_MASK 0xF000
#define RF_RADIO_OP_STATUS_PROTO_GENERIC 0x0000
#define RF_RADIO_OP_STATUS_PROTO_BLE 0x1000
#define RF_RADIO_OP_STATUS_PROTO_IEEE 0x2000
#define RF_RADIO_OP_STATUS_PROTO_PROP 0x3000
/* Bits 11:10 signify Running / Done OK / Done with error */
#define RF_RADIO_OP_MASKED_STATUS 0x0C00
#define RF_RADIO_OP_MASKED_STATUS_RUNNING 0x0000
#define RF_RADIO_OP_MASKED_STATUS_DONE 0x0400
#define RF_RADIO_OP_MASKED_STATUS_ERROR 0x0800
/*---------------------------------------------------------------------------*/
/**
* \brief Returns the current status of a running Radio Op command
* \param a A pointer with the buffer used to initiate the command
* \return The value of the Radio Op buffer's status field
*
* This macro can be used to e.g. return the status of a previously
* initiated background operation, or of an immediate command
*/
#define RF_RADIO_OP_GET_STATUS(a) GET_FIELD_V(a, radioOp, status)
/*---------------------------------------------------------------------------*/
/* Special value returned by CMD_IEEE_CCA_REQ when an RSSI is not available */
#define RF_CMD_CCA_REQ_RSSI_UNKNOWN -128
/* Used for the return value of channel_clear */
#define RF_CCA_CLEAR 1
#define RF_CCA_BUSY 0
/* Used as an error return value for get_cca_info */
#define RF_GET_CCA_INFO_ERROR 0xFF
/*
* Values of the individual bits of the ccaInfo field in CMD_IEEE_CCA_REQ's
* status struct
*/
#define RF_CMD_CCA_REQ_CCA_STATE_IDLE 0 /* 00 */
#define RF_CMD_CCA_REQ_CCA_STATE_BUSY 1 /* 01 */
#define RF_CMD_CCA_REQ_CCA_STATE_INVALID 2 /* 10 */
/*---------------------------------------------------------------------------*/
#define RF_MODE_BLE 0
#define RF_MODE_IEEE 1
/*---------------------------------------------------------------------------*/
/* How long to wait for an ongoing ACK TX to finish before starting frame TX */
#define TX_WAIT_TIMEOUT (RTIMER_SECOND >> 11)
/* How long to wait for the RF to enter RX in rf_cmd_ieee_rx */
#define ENTER_RX_WAIT_TIMEOUT (RTIMER_SECOND >> 10)
/*---------------------------------------------------------------------------*/
/* TX Power dBm lookup table - values from SmartRF Studio */
typedef struct output_config {
radio_value_t dbm;
uint8_t register_ib;
uint8_t register_gc;
} output_config_t;
static const output_config_t output_power[] = {
{ 5, 0x29, 0x00 },
{ 4, 0x20, 0x00 },
{ 3, 0x19, 0x00 },
{ 2, 0x25, 0x01 },
{ 1, 0x21, 0x01 },
{ 0, 0x1D, 0x01 },
{ -3, 0x19, 0x03 },
{ -6, 0x13, 0x03 },
{ -9, 0x0F, 0x03 },
};
#define OUTPUT_CONFIG_COUNT (sizeof(output_power) / sizeof(output_config_t))
/* Max and Min Output Power in dBm */
#define OUTPUT_POWER_MIN (output_power[OUTPUT_CONFIG_COUNT - 1].dbm)
#define OUTPUT_POWER_MAX (output_power[0].dbm)
#define OUTPUT_POWER_UNKNOWN 0xFFFF
/* Default TX Power - position in output_power[] */
#define CC26XX_RF_TX_POWER 0
const output_config_t *tx_power_current = &output_power[0];
/*---------------------------------------------------------------------------*/
#define RF_CORE_CLOCKS_MASK (RFC_PWR_PWMCLKEN_RFC_M | RFC_PWR_PWMCLKEN_CPE_M \
| RFC_PWR_PWMCLKEN_CPERAM_M)
/*---------------------------------------------------------------------------*/
/* RF interrupts */
#define RX_IRQ IRQ_IEEE_RX_ENTRY_DONE
#define TX_ACK_IRQ IRQ_IEEE_TX_ACK
#define ERROR_IRQ IRQ_INTERNAL_ERROR
/* Those IRQs are enabled all the time */
#define ENABLED_IRQS (RX_IRQ + ERROR_IRQ)
/*
* We only enable this right before starting frame TX, so we can sleep while
* the TX is ongoing
*/
#define LAST_FG_CMD_DONE IRQ_LAST_FG_COMMAND_DONE
#define cc26xx_rf_cpe0_isr RFCCPE0IntHandler
#define cc26xx_rf_cpe1_isr RFCCPE1IntHandler
/*---------------------------------------------------------------------------*/
/*
* Buffers used to send commands to the RF core (generic and IEEE commands).
* Some of those buffers are re-usable, some are not.
*
* If you are uncertain, declare a new buffer.
*/
/*
* A buffer to send a CMD_IEEE_RX and to subsequently monitor its status
* Do not use this buffer for any commands other than CMD_IEEE_RX
*/
static uint8_t cmd_ieee_rx_buf[RF_CMD_BUFFER_SIZE] CC_ALIGN_ATTR(4);
/*
* A buffer used to send immediate and foreground Radio Op (e.g. CMD_IEEE_TX)
* commands.
*
* Do not re-use this buffer to send a command before the previous command
* has been completed.
*
* Do not intermingle preparation of this buffer to send a command with calls
* that might lead to a different command, since the latter will overwrite what
* you have written in preparation for the former.
*/
static uint8_t cmd_immediate_buf[RF_CMD_BUFFER_SIZE] CC_ALIGN_ATTR(4);
/*---------------------------------------------------------------------------*/
/* BLE macros, variables and buffers */
/* BLE Intervals: Send a burst of advertisements every BLE_ADV_INTERVAL secs */
#define BLE_ADV_INTERVAL (CLOCK_SECOND * 5)
#define BLE_ADV_DUTY_CYCLE (CLOCK_SECOND / 10)
#define BLE_ADV_MESSAGES 10
/* BLE Advertisement-related macros */
#define BLE_ADV_TYPE_DEVINFO 0x01
#define BLE_ADV_TYPE_NAME 0x09
#define BLE_ADV_TYPE_MANUFACTURER 0xFF
#define BLE_ADV_NAME_BUF_LEN 32
#define BLE_ADV_PAYLOAD_BUF_LEN 64
#define BLE_UUID_SIZE 16
#if CC26XX_RF_BLE_SUPPORT
/* BLE buffers / variables */
static unsigned char ble_cmd_buf[32] CC_ALIGN_ATTR(4) = { 0 };
static unsigned char ble_tx_rx_buf[128] CC_ALIGN_ATTR(4);
static uint8_t ble_mode_on;
/* BLE beacond config */
static struct ble_beacond_config {
clock_time_t interval;
char adv_name[BLE_ADV_NAME_BUF_LEN];
} beacond_config;
/* BLE overrides */
static uint32_t ble_overrides[] = {
0x00364038, /* Synth: Set RTRIM (POTAILRESTRIM) to 6 */
0x000784A3, /* Synth: Set FREF = 3.43 MHz (24 MHz / 7) */
0xA47E0583, /* Synth: Set loop bandwidth after lock to 80 kHz (K2) */
0xEAE00603, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, LSB) */
0x00010623, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, MSB) */
0x00456088, /* Adjust AGC reference level */
0xFFFFFFFF, /* End of override list */
};
PROCESS(cc26xx_rf_ble_beacon_process, "CC26xx RF BLE Beacon Process");
static void init_ble(void);
#else
#define init_ble(...)
#endif /* CC26XX_RF_BLE_SUPPORT */
/*---------------------------------------------------------------------------*/
#define RX_BUF_SIZE 140
/* Four receive buffers entries with room for 1 IEEE802.15.4 frame in each */
static uint8_t rx_buf_0[RX_BUF_SIZE] CC_ALIGN_ATTR(4);
static uint8_t rx_buf_1[RX_BUF_SIZE] CC_ALIGN_ATTR(4);
static uint8_t rx_buf_2[RX_BUF_SIZE] CC_ALIGN_ATTR(4);
static uint8_t rx_buf_3[RX_BUF_SIZE] CC_ALIGN_ATTR(4);
/* The RX Data Queue */
static dataQueue_t rx_data_queue = { 0 };
/* Receive entry pointer to keep track of read items */
volatile static uint8_t *rx_read_entry;
/*---------------------------------------------------------------------------*/
/* The outgoing frame buffer */
#define TX_BUF_SIZE 180
static uint8_t tx_buf[TX_BUF_SIZE];
/*---------------------------------------------------------------------------*/
/* Overrides for IEEE 802.15.4, differential mode */
static uint32_t ieee_overrides[] = {
0x00354038, /* Synth: Set RTRIM (POTAILRESTRIM) to 5 */
0x4001402D, /* Synth: Correct CKVD latency setting (address) */
0x00608402, /* Synth: Correct CKVD latency setting (value) */
0x4001405D, /* Synth: Set ANADIV DIV_BIAS_MODE to PG1 (address) */
0x1801F800, /* Synth: Set ANADIV DIV_BIAS_MODE to PG1 (value) */
0x000784A3, /* Synth: Set FREF = 3.43 MHz (24 MHz / 7) */
0xA47E0583, /* Synth: Set loop bandwidth after lock to 80 kHz (K2) */
0xEAE00603, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, LSB) */
0x00010623, /* Synth: Set loop bandwidth after lock to 80 kHz (K3, MSB) */
0x002B50DC, /* Adjust AGC DC filter */
0x05000243, /* Increase synth programming timeout */
0x002082C3, /* Increase synth programming timeout */
0xFFFFFFFF, /* End of override list */
};
/*---------------------------------------------------------------------------*/
PROCESS(cc26xx_rf_process, "CC26xx RF driver");
/*---------------------------------------------------------------------------*/
static int on(void);
static int off(void);
static void setup_interrupts(void);
/*---------------------------------------------------------------------------*/
static uint8_t
rf_is_accessible(void)
{
if(ti_lib_prcm_rf_ready() &&
ti_lib_prcm_power_domain_status(PRCM_DOMAIN_RFCORE) ==
PRCM_DOMAIN_POWER_ON) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Sends a command to the RF core.
*
* \param cmd The command value or a pointer to a command buffer
* \param status A pointer to a variable which will hold the status
* \return RF_CMD_OK or RF_CMD_ERROR
*
* This function supports all three types of command (Radio OP, immediate and
* direct)
*
* For immediate and Radio OPs, cmd is a pointer to the data structure
* containing the command and its parameters. This data structure must be
* 4-byte aligned.
*
* For direct commands, cmd contains the value of the command alongside its
* parameters
*
* The caller is responsible of allocating and populating cmd for Radio OP and
* immediate commands
*
* The caller is responsible for allocating status
*/
static uint_fast8_t
rf_send_cmd(uint32_t cmd, uint32_t *status)
{
uint32_t timeout_count = 0;
bool interrupts_disabled;
/*
* Make sure ContikiMAC doesn't turn us off from within an interrupt while
* we are accessing RF Core registers
*/
interrupts_disabled = ti_lib_int_master_disable();
if(!rf_is_accessible()) {
PRINTF("rf_send_cmd: RF was off\n");
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
return RF_CMD_ERROR;
}
HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDR) = cmd;
do {
*status = HWREG(RFC_DBELL_BASE + RFC_DBELL_O_CMDSTA);
if(++timeout_count > 50000) {
PRINTF("rf_send_cmd: Timeout\n");
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
return RF_CMD_ERROR;
}
} while(*status == RF_CMD_STATUS_PENDING);
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/*
* If we reach here the command is no longer pending. It is either completed
* successfully or with error
*/
return *status == RF_CMD_STATUS_DONE;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Checks whether the RFC domain is accessible and the RFC is in IEEE RX
* \return 1: RFC in RX mode (and therefore accessible too). 0 otherwise
*/
static uint8_t
rf_is_on(void)
{
if(!rf_is_accessible()) {
return 0;
}
return RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == RF_RADIO_OP_STATUS_ACTIVE;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Check the RF's TX status
* \return 1 RF is transmitting
* \return 0 RF is not transmitting
*
* TX mode may be triggered either by a CMD_IEEE_TX or by the automatic
* transmission of an ACK frame.
*/
static uint8_t
transmitting(void)
{
uint32_t cmd_status;
/* If we are off, we are not in TX */
if(!rf_is_accessible()) {
return 0;
}
memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_IEEE_CCA_REQ));
GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_IEEE_CCA_REQ;
if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_ERROR) {
PRINTF("transmitting: CMDSTA=0x%08lx, status=0x%04x\n",
cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf));
return 0;
}
if((GET_FIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, currentRssi)
== RF_CMD_CCA_REQ_RSSI_UNKNOWN) &&
(GET_BITFIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, ccaInfo, ccaEnergy)
== RF_CMD_CCA_REQ_CCA_STATE_BUSY)) {
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Returns CCA information
* \return RF_GET_CCA_INFO_ERROR if the RF was not on
* \return On success, the return value is formatted as per the ccaInfo field
* of CMD_IEEE_CCA_REQ
*
* It is the caller's responsibility to make sure the RF is on. This function
* will return RF_GET_CCA_INFO_ERROR if the RF is off
*
* This function will in fact wait for a valid RSSI signal
*/
static uint8_t
get_cca_info(void)
{
uint32_t cmd_status;
int8_t rssi;
if(!rf_is_on()) {
PRINTF("get_cca_info: Not on\n");
return RF_GET_CCA_INFO_ERROR;
}
rssi = RF_CMD_CCA_REQ_RSSI_UNKNOWN;
while(rssi == RF_CMD_CCA_REQ_RSSI_UNKNOWN || rssi == 0) {
memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_IEEE_CCA_REQ));
GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_IEEE_CCA_REQ;
if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_ERROR) {
PRINTF("get_cca_info: CMDSTA=0x%08lx, status=0x%04x\n",
cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf));
return RF_GET_CCA_INFO_ERROR;
}
rssi = GET_FIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, currentRssi);
}
/* We have a valid RSSI signal. Return the CCA Info */
return GET_FIELD(cmd_immediate_buf, CMD_IEEE_CCA_REQ, ccaInfo);
}
/*---------------------------------------------------------------------------*/
/**
* \brief Reads the current signal strength (RSSI)
* \return The current RSSI in dBm or CMD_GET_RSSI_UNKNOWN
*
* This function reads the current RSSI on the currently configured
* channel.
*/
static radio_value_t
get_rssi(void)
{
uint32_t cmd_status;
int8_t rssi;
uint8_t was_off = 0;
/* If we are off, turn on first */
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CMD_OK) {
PRINTF("get_rssi: on() failed\n");
return RF_CMD_CCA_REQ_RSSI_UNKNOWN;
}
}
memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_GET_RSSI));
GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_GET_RSSI;
if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_ERROR) {
rssi = RF_CMD_CCA_REQ_RSSI_UNKNOWN;
}
/* Current RSSI in bits 23:16 of cmd_status */
rssi = (cmd_status >> 16) & 0xFF;
/* If we were off, turn back off */
if(was_off) {
off();
}
return rssi;
}
/*---------------------------------------------------------------------------*/
/* Returns the current TX power in dBm */
static radio_value_t
get_tx_power(void)
{
return tx_power_current->dbm;
}
/*---------------------------------------------------------------------------*/
/*
* Set TX power to 'at least' power dBm
* This works with a lookup table. If the value of 'power' does not exist in
* the lookup table, TXPOWER will be set to the immediately higher available
* value
*/
static void
set_tx_power(radio_value_t power)
{
uint32_t cmd_status;
int i;
/* Send a CMD_SET_TX_POWER command to the RF */
memset(cmd_immediate_buf, 0x00, SIZEOF_STRUCT(CMD_SET_TX_POWER));
GET_FIELD(cmd_immediate_buf, command, commandNo) = CMD_SET_TX_POWER;
for(i = OUTPUT_CONFIG_COUNT - 1; i >= 0; --i) {
if(power <= output_power[i].dbm) {
GET_FIELD(cmd_immediate_buf, CMD_SET_TX_POWER, txPower) =
BITVALUE(CMD_SET_TX_POWER, txPower, IB, output_power[i].register_ib) |
BITVALUE(CMD_SET_TX_POWER, txPower, GC, output_power[i].register_gc) |
BITVALUE(CMD_SET_TX_POWER, txPower, tempCoeff, 0);
if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) == RF_CMD_OK) {
/* Success: Remember the new setting */
tx_power_current = &output_power[i];
} else {
PRINTF("set_tx_power: CMDSTA=0x%08lx\n", cmd_status);
}
return;
}
}
}
/*---------------------------------------------------------------------------*/
/**
* \brief Wait till running radio Op command completes
*
* \return RF_CMD_ERROR or RF_CMD_OK
*
* RF_CMD_OK will be returned if the Radio Op returned with
* RF_RADIO_OP_STATUS_DONE_OK
*
* RF_CMD_ERROR will be returned in the radio op returned with any other
* RF_RADIO_OP_STATUS_DONE_xyz
*/
static uint_fast8_t
rf_wait_cmd_completed_ok(uint8_t *cmd)
{
_TYPE_radioOp_status tmp_status;
uint32_t timeoutCount = 0;
/*
* 0x04XX=DONE, 0x0400=DONE_OK while all other "DONE" values means done
* but with some kind of error (ref. "Common radio operation status codes")
*/
do {
tmp_status = GET_FIELD_V(cmd, radioOp, status);
if(++timeoutCount > 500000) {
return RF_CMD_ERROR;
}
} while((tmp_status & RF_RADIO_OP_MASKED_STATUS) != RF_RADIO_OP_MASKED_STATUS_DONE);
return tmp_status == RF_RADIO_OP_STATUS_DONE_OK;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Builds common radio parameters for radio operations
*
* \param *cmd Pointer to buffer to add parameters to
* \param command Radio command number (e.g. COMMAND_RADIO_SETUP)
*
* \note The buffer must be emptied with memset() before calling this function
*
* \return None
*/
static void
rf_build_radio_op_cmd(uint8_t *cmd, uint16_t command)
{
GET_FIELD(cmd, radioOp, commandNo) = command;
GET_FIELD(cmd, radioOp, status) = IDLE;
GET_FIELD(cmd, radioOp, pNextOp) = NULL;
GET_FIELD(cmd, radioOp, startTime) = 0;
GET_FIELD(cmd, radioOp, startTrigger) = TRIG_NOW;
GET_FIELD(cmd, radioOp, condition) = COND_NEVER;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Sends a CMD_RADIO_SETUP for the selected mode (IEEE or BLE)
* \param mode RF_MODE_BLE or RF_MODE_IEEE
* \return RF_CMD_OK or RF_CMD_ERROR
*
* ToDo: Likely to need one more argument to set bNoAdi<foo> on first startup
* vs when coming back from sleep
*/
static uint8_t
rf_radio_setup(uint8_t mode)
{
uint32_t cmd_status;
/* Create radio setup command */
memset(cmd_immediate_buf, 0x00, SIZEOF_RADIO_OP(CMD_RADIO_SETUP));
rf_build_radio_op_cmd(cmd_immediate_buf, CMD_RADIO_SETUP);
/* Set output power to the current (or default) value */
GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, txPower) =
BITVALUE(CMD_RADIO_SETUP, txPower, IB, tx_power_current->register_ib) |
BITVALUE(CMD_RADIO_SETUP, txPower, GC, tx_power_current->register_gc) |
BITVALUE(CMD_RADIO_SETUP, txPower, tempCoeff, 0);
/* Do mode-dependent things (e.g. apply overrides) */
if(mode == RF_MODE_IEEE) {
/* Add override control pointer */
GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, pRegOverride) = ieee_overrides;
#if CC26XX_RF_BLE_SUPPORT
} else if(mode == RF_MODE_BLE) {
/* Add override control pointer */
GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, pRegOverride) = ble_overrides;
#endif
} else {
PRINTF("rf_radio_setup: Unknown mode %u\n", mode);
return RF_CMD_ERROR;
}
/* Lastly, set the mode */
GET_FIELD(cmd_immediate_buf, CMD_RADIO_SETUP, mode) = mode;
/* Send Radio setup to RF Core */
if(rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status) != RF_CMD_OK) {
PRINTF("rf_radio_setup: CMD_RADIO_SETUP, CMDSTA=0x%08lx, status=0x%04x\n",
cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf));
return RF_CMD_ERROR;
}
/* Wait until radio setup is done */
if(rf_wait_cmd_completed_ok(cmd_immediate_buf) != RF_CMD_OK) {
PRINTF("rf_radio_setup: CMD_RADIO_SETUP wait, CMDSTA=0x%08lx, status=0x%04x\n",
cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf));
return RF_CMD_ERROR;
}
return RF_CMD_OK;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Applies patches (if any)
* \return RF_CMD_OK or RF_CMD_ERROR
*
* Currently patches are not required.
*/
static uint8_t
apply_patches()
{
uint32_t cmd_status;
/* Patch of uninitialized pointer */
*((uint32_t *)0x21000028) = 0x00000000;
/* Start radio timer (RAT) */
if(rf_send_cmd(CMDR_DIR_CMD(CMD_START_RAT), &cmd_status) != RF_CMD_OK) {
PRINTF("apply_patches: START_RAT fail, CMDSTA=0x%08lx\n", cmd_status);
return RF_CMD_ERROR;
}
return RF_CMD_OK;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Set up radio in IEEE802.15.4 RX mode
*
* \return RF_CMD_OK Succeeded
* \return RF_CMD_ERROR Failed
*
* This function assumes that cmd_ieee_rx_buf has been previously populated
* with correct values. This can be done through init_rf_params (sets defaults)
* or through Contiki's extended RF API (set_value, set_object)
*/
static uint8_t
rf_cmd_ieee_rx()
{
uint32_t cmd_status;
rtimer_clock_t t0;
int ret;
ret = rf_send_cmd((uint32_t)cmd_ieee_rx_buf, &cmd_status);
if(ret != RF_CMD_OK) {
PRINTF("rf_cmd_ieee_rx: ret=%d, CMDSTA=0x%08lx, status=0x%04x\n",
ret, cmd_status, RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf));
}
t0 = RTIMER_NOW();
while(RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) != RF_RADIO_OP_STATUS_ACTIVE &&
(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + ENTER_RX_WAIT_TIMEOUT)));
/* Wait to enter RX */
if(RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) != RF_RADIO_OP_STATUS_ACTIVE) {
PRINTF("rf_cmd_ieee_rx: CMDSTA=0x%08lx, status=0x%04x\n",
cmd_status, RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf));
return RF_CMD_ERROR;
}
return ret;
}
/*---------------------------------------------------------------------------*/
static void
init_rx_buffers(void)
{
/* Two-element circular buffer, hardcoded for now.. */
GET_FIELD(rx_buf_0, dataEntry, pNextEntry) = rx_buf_1;
GET_FIELD(rx_buf_0, dataEntry, config) = 0x04;
GET_FIELD(rx_buf_0, dataEntry, length) = sizeof(rx_buf_0) - 8;
GET_FIELD(rx_buf_1, dataEntry, pNextEntry) = rx_buf_2;
GET_FIELD(rx_buf_1, dataEntry, config) = 0x04;
GET_FIELD(rx_buf_1, dataEntry, length) = sizeof(rx_buf_1) - 8;
GET_FIELD(rx_buf_2, dataEntry, pNextEntry) = rx_buf_3;
GET_FIELD(rx_buf_2, dataEntry, config) = 0x04;
GET_FIELD(rx_buf_2, dataEntry, length) = sizeof(rx_buf_2) - 8;
/* Point to first element again */
GET_FIELD(rx_buf_3, dataEntry, pNextEntry) = rx_buf_0;
GET_FIELD(rx_buf_3, dataEntry, config) = 0x04;
GET_FIELD(rx_buf_3, dataEntry, length) = sizeof(rx_buf_3) - 8;
}
/*---------------------------------------------------------------------------*/
static void
init_rf_params(void)
{
memset(cmd_ieee_rx_buf, 0x00, SIZEOF_RADIO_OP(CMD_IEEE_RX));
GET_FIELD(cmd_ieee_rx_buf, radioOp, commandNo) = CMD_IEEE_RX;
GET_FIELD(cmd_ieee_rx_buf, radioOp, status) = IDLE;
GET_FIELD(cmd_ieee_rx_buf, radioOp, pNextOp) = NULL;
GET_FIELD(cmd_ieee_rx_buf, radioOp, startTime) = 0x00000000;
GET_FIELD(cmd_ieee_rx_buf, radioOp, startTrigger) = TRIG_NOW;
GET_FIELD(cmd_ieee_rx_buf, radioOp, condition) = COND_NEVER;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, channel) = CC26XX_RF_CHANNEL;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, rxConfig) =
BITVALUE(CMD_IEEE_RX, rxConfig, bAutoFlushCrc, 1) |
BITVALUE(CMD_IEEE_RX, rxConfig, bAutoFlushIgn, 0) |
BITVALUE(CMD_IEEE_RX, rxConfig, bIncludePhyHdr, 0) |
BITVALUE(CMD_IEEE_RX, rxConfig, bIncludeCrc, 1) |
BITVALUE(CMD_IEEE_RX, rxConfig, bAppendRssi, 1) |
BITVALUE(CMD_IEEE_RX, rxConfig, bAppendCorrCrc, 1) |
BITVALUE(CMD_IEEE_RX, rxConfig, bAppendSrcInd, 0) |
BITVALUE(CMD_IEEE_RX, rxConfig, bAppendTimestamp, 0);
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pRxQ) = &rx_data_queue;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pOutput) = rf_stats;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt) =
#if CC26XX_RF_CONF_PROMISCOUS
BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltEn, 0) |
#else
BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltEn, 1) |
#endif
BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltStop, 1) |
#if CC26XX_RF_CONF_AUTOACK
BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoAckEn, 1) |
#else
BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoAckEn, 0) |
#endif
BITVALUE(CMD_IEEE_RX, frameFiltOpt, slottedAckEn, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoPendEn, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, defaultPend, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPendDataReqOnly, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPanCoord, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, maxFrameVersion, 1) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, bStrictLenFilter, 0);
/* Receive all frame types */
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameTypes) =
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt0Beacon, 1) |
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt1Data, 1) |
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt2Ack, 1) |
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt3MacCmd, 1) |
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt4Reserved, 1) |
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt5Reserved, 1) |
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt6Reserved, 1) |
BITVALUE(CMD_IEEE_RX, frameTypes, bAcceptFt7Reserved, 1);
/* Configure CCA settings */
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaOpt) =
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaEnEnergy, 1) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaEnCorr, 1) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaEnSync, 0) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaCorrOp, 1) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaSyncOp, 1) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaCorrThr, 3);
/* Set CCA RSSI Threshold, 0xA6 corresponds to -90dBm (two's comp.)*/
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaRssiThr) = 0xA6;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, numExtEntries) = 0x00;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, numShortEntries) = 0x00;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pExtEntryList) = 0;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, pShortEntryList) = 0;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, endTrigger) = TRIG_NEVER;
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, endTime) = 0x00000000;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Turn on power to the RFC and boot it.
*
* \return RF_CMD_OK or RF_CMD_ERROR
*/
static int
power_up(void)
{
uint32_t cmd_status;
bool interrupts_disabled = ti_lib_int_master_disable();
ti_lib_int_pend_clear(INT_RF_CPE0);
ti_lib_int_pend_clear(INT_RF_CPE1);
ti_lib_int_disable(INT_RF_CPE0);
ti_lib_int_disable(INT_RF_CPE1);
/* Enable RF Core power domain */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_RFCORE);
while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_RFCORE)
!= PRCM_DOMAIN_POWER_ON);
ti_lib_prcm_domain_enable(PRCM_DOMAIN_RFCORE);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
while(!rf_is_accessible()) {
PRINTF("power_up: Not ready\n");
}
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0;
ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1);
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* Let CPE boot */
HWREG(RFC_PWR_NONBUF_BASE + RFC_PWR_O_PWMCLKEN) = RF_CORE_CLOCKS_MASK;
/* Send ping (to verify RFCore is ready and alive) */
if(rf_send_cmd(CMDR_DIR_CMD(CMD_PING), &cmd_status) != RF_CMD_OK) {
PRINTF("power_up: CMD_PING fail, CMDSTA=0x%08lx\n", cmd_status);
return RF_CMD_ERROR;
}
return RF_CMD_OK;
}
/*---------------------------------------------------------------------------*/
/**
* \brief Disable RFCORE clock domain in the MCU VD and turn off the RFCORE PD
*/
static void
power_down(void)
{
bool interrupts_disabled = ti_lib_int_master_disable();
ti_lib_int_disable(INT_RF_CPE0);
ti_lib_int_disable(INT_RF_CPE1);
if(rf_is_accessible()) {
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0;
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0;
}
/* Shut down the RFCORE clock domain in the MCU VD */
ti_lib_prcm_domain_disable(PRCM_DOMAIN_RFCORE);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/* Turn off RFCORE PD */
ti_lib_prcm_power_domain_off(PRCM_DOMAIN_RFCORE);
while(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_RFCORE)
!= PRCM_DOMAIN_POWER_OFF);
ti_lib_int_pend_clear(INT_RF_CPE0);
ti_lib_int_pend_clear(INT_RF_CPE1);
ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1);
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
}
/*---------------------------------------------------------------------------*/
static int
rx_on(void)
{
int ret;
/* Get status of running IEEE_RX (if any) */
if(rf_is_on()) {
PRINTF("rx_on: We were on. PD=%u, RX=0x%04x \n", rf_is_accessible(),
RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf));
return RF_CMD_OK;
}
/* Put CPE in RX using the currently configured parameters */
ret = rf_cmd_ieee_rx();
if(ret) {
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
}
return ret;
}
/*---------------------------------------------------------------------------*/
static int
rx_off(void)
{
uint32_t cmd_status;
int ret;
/* If we are off, do nothing */
if(!rf_is_on()) {
return RF_CMD_OK;
}
/* Wait for ongoing ACK TX to finish */
while(transmitting());
/* Send a CMD_STOP command to RF Core */
if(rf_send_cmd(CMDR_DIR_CMD(CMD_ABORT), &cmd_status) != RF_CMD_OK) {
PRINTF("RX off: CMD_ABORT status=0x%08lx\n", cmd_status);
/* Continue nonetheless */
}
while(rf_is_on());
if(RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == IEEE_DONE_STOPPED ||
RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf) == IEEE_DONE_ABORT) {
/* Stopped gracefully */
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
ret = RF_CMD_OK;
} else {
PRINTF("RX off: BG status=0x%04x\n", RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf));
ret = RF_CMD_ERROR;
}
return ret;
}
/*---------------------------------------------------------------------------*/
static void
rx_isr(void)
{
process_poll(&cc26xx_rf_process);
}
/*---------------------------------------------------------------------------*/
void
cc26xx_rf_cpe1_isr(void)
{
ENERGEST_ON(ENERGEST_TYPE_IRQ);
ti_lib_int_master_disable();
PRINTF("RF Error\n");
if(!rf_is_accessible()) {
if(power_up() != RF_CMD_OK) {
return;
}
}
/* Clear interrupt flags */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0;
ti_lib_int_master_enable();
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
}
/*---------------------------------------------------------------------------*/
void
cc26xx_rf_cpe0_isr(void)
{
ENERGEST_ON(ENERGEST_TYPE_IRQ);
if(!rf_is_accessible()) {
printf("RF ISR called but RF not ready... PANIC!!\n");
if(power_up() != RF_CMD_OK) {
PRINTF("power_up() failed\n");
return;
}
}
ti_lib_int_master_disable();
if(HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & RX_IRQ) {
rx_isr();
}
/* Clear interrupt flags */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0;
ti_lib_int_master_enable();
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
}
/*---------------------------------------------------------------------------*/
static void
setup_interrupts(void)
{
bool interrupts_disabled;
/* We are already turned on by the caller, so this should not happen */
if(!rf_is_accessible()) {
PRINTF("setup_interrupts: No access\n");
return;
}
/* Disable interrupts */
interrupts_disabled = ti_lib_int_master_disable();
/* Set all interrupt channels to CPE0 channel, error to CPE1 */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEISL) = ERROR_IRQ;
/* Acknowledge TX_Frame, Rx_Entry_Done and ERROR */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = ENABLED_IRQS;
/* Clear interrupt flags, active low clear(?) */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0;
ti_lib_int_pend_clear(INT_RF_CPE0);
ti_lib_int_pend_clear(INT_RF_CPE1);
ti_lib_int_enable(INT_RF_CPE0);
ti_lib_int_enable(INT_RF_CPE1);
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
}
/*---------------------------------------------------------------------------*/
static uint8_t
request(void)
{
/*
* We rely on the RDC layer to turn us on and off. Thus, if we are on we
* will only allow sleep, standby otherwise
*/
if(rf_is_on()) {
return LPM_MODE_SLEEP;
}
return LPM_MODE_MAX_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
LPM_MODULE(cc26xx_rf_lpm_module, request, NULL, NULL, LPM_DOMAIN_NONE);
/*---------------------------------------------------------------------------*/
static int
init(void)
{
lpm_register_module(&cc26xx_rf_lpm_module);
/* Enable IEEE, BLE and Common-CMD APIs */
HWREG(PRCM_BASE + PRCM_O_RFCMODESEL) = PRCM_RFCMODESEL_CURR_MODE5;
/* Wipe out the BLE adv buffer */
init_ble();
/* Initialise RX buffers */
memset(rx_buf_0, 0, RX_BUF_SIZE);
memset(rx_buf_1, 0, RX_BUF_SIZE);
memset(rx_buf_2, 0, RX_BUF_SIZE);
memset(rx_buf_3, 0, RX_BUF_SIZE);
/* Set of RF Core data queue. Circular buffer, no last entry */
rx_data_queue.pCurrEntry = rx_buf_0;
rx_data_queue.pLastEntry = NULL;
/* Initialize current read pointer to first element (used in ISR) */
rx_read_entry = rx_buf_0;
/* Populate the RF parameters data structure with default values */
init_rf_params();
if(on() != RF_CMD_OK) {
PRINTF("init: on() failed\n");
return RF_CMD_ERROR;
}
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
process_start(&cc26xx_rf_process, NULL);
return 1;
}
/*---------------------------------------------------------------------------*/
static int
prepare(const void *payload, unsigned short payload_len)
{
int len = MIN(payload_len, sizeof(tx_buf));
memcpy(tx_buf, payload, len);
return RF_CMD_OK;
}
/*---------------------------------------------------------------------------*/
static int
transmit(unsigned short transmit_len)
{
int ret;
uint8_t was_off = 0;
uint32_t cmd_status;
uint16_t stat;
uint8_t tx_active = 0;
rtimer_clock_t t0;
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CMD_OK) {
PRINTF("transmit: on() failed\n");
return RF_CMD_ERROR;
}
}
/*
* We are certainly not TXing a frame as a result of CMD_IEEE_TX, but we may
* be in the process of TXing an ACK. In that case, wait for the TX to finish
* or return after approx TX_WAIT_TIMEOUT
*/
t0 = RTIMER_NOW();
do {
tx_active = transmitting();
} while(tx_active == 1 &&
(RTIMER_CLOCK_LT(RTIMER_NOW(), t0 + TX_WAIT_TIMEOUT)));
if(tx_active) {
PRINTF("transmit: Already TXing and wait timed out\n");
if(was_off) {
off();
}
return RADIO_TX_COLLISION;
}
/* Send the CMD_IEEE_TX command */
memset(cmd_immediate_buf, 0, SIZEOF_RADIO_OP(CMD_IEEE_TX));
rf_build_radio_op_cmd(cmd_immediate_buf, CMD_IEEE_TX);
GET_FIELD(cmd_immediate_buf, CMD_IEEE_TX, payloadLen) = transmit_len;
GET_FIELD(cmd_immediate_buf, CMD_IEEE_TX, pPayload) = tx_buf;
/* Enable the LAST_FG_COMMAND_DONE interrupt, which will wake us up */
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = ENABLED_IRQS +
LAST_FG_CMD_DONE;
ret = rf_send_cmd((uint32_t)cmd_immediate_buf, &cmd_status);
if(ret) {
/* If we enter here, TX actually started */
ENERGEST_OFF(ENERGEST_TYPE_LISTEN);
ENERGEST_ON(ENERGEST_TYPE_TRANSMIT);
/* Idle away while the command is running */
while((RF_RADIO_OP_GET_STATUS(cmd_immediate_buf) & RF_RADIO_OP_MASKED_STATUS)
== RF_RADIO_OP_MASKED_STATUS_RUNNING) {
lpm_sleep();
}
stat = RF_RADIO_OP_GET_STATUS(cmd_immediate_buf);
if(stat == RF_RADIO_OP_STATUS_IEEE_DONE_OK) {
/* Sent OK */
RIMESTATS_ADD(lltx);
ret = RADIO_TX_OK;
} else {
/* Operation completed, but frame was not sent */
PRINTF("transmit: ret=%d, CMDSTA=0x%08lx, status=0x%04x\n", ret,
cmd_status, stat);
ret = RADIO_TX_ERR;
}
} else {
/* Failure sending the CMD_IEEE_TX command */
PRINTF("transmit: ret=%d, CMDSTA=0x%08lx, status=0x%04x\n",
ret, cmd_status, RF_RADIO_OP_GET_STATUS(cmd_immediate_buf));
ret = RADIO_TX_ERR;
}
/*
* Update ENERGEST state here, before a potential call to off(), which
* will correctly update it if required.
*/
ENERGEST_OFF(ENERGEST_TYPE_TRANSMIT);
ENERGEST_ON(ENERGEST_TYPE_LISTEN);
/*
* Disable LAST_FG_COMMAND_DONE interrupt. We don't really care about it
* except when we are transmitting
*/
HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = ENABLED_IRQS;
return ret;
}
/*---------------------------------------------------------------------------*/
static int
send(const void *payload, unsigned short payload_len)
{
prepare(payload, payload_len);
return transmit(payload_len);
}
/*---------------------------------------------------------------------------*/
static int
read_frame(void *buf, unsigned short buf_len)
{
int len = 0;
if(GET_FIELD_V(rx_read_entry, dataEntry, status) == DATA_ENTRY_STATUS_FINISHED) {
/* Set status to 0 "Pending" in element */
GET_FIELD_V(rx_read_entry, dataEntry, status) = DATA_ENTRY_STATUS_PENDING;
if(rx_read_entry[8] > 0) {
memcpy(buf, (char *)&rx_read_entry[9], buf_len);
/* Remove the footer */
len = MIN(buf_len, rx_read_entry[8] - 4);
int rssi = (int8_t)rx_read_entry[9 + len + 2];
packetbuf_set_attr(PACKETBUF_ATTR_RSSI, rssi);
RIMESTATS_ADD(llrx);
/* Clear the length byte */
rx_read_entry[8] = 0;
}
/* Move read entry pointer to next entry */
rx_read_entry = GET_FIELD_V(rx_read_entry, dataEntry, pNextEntry);
}
return len;
}
/*---------------------------------------------------------------------------*/
static int
channel_clear(void)
{
uint8_t was_off = 0;
uint8_t cca_info;
int ret = RF_CCA_CLEAR;
/*
* If we are in the middle of a BLE operation, we got called by ContikiMAC
* from within an interrupt context. Indicate a clear channel
*/
#if CC26XX_RF_BLE_SUPPORT
if(ble_mode_on) {
PRINTF("channel_clear: Interrupt context but BLE in progress\n");
return RF_CCA_CLEAR;
}
#endif
if(rf_is_on()) {
/*
* Wait for potential leftover ACK still being sent.
* Strictly speaking, if we are TXing an ACK then the channel is not clear.
* However, channel_clear is only ever called to determine whether there is
* someone else's packet in the air, not ours.
*
* We could probably even simply return that the channel is clear
*/
while(transmitting());
} else {
was_off = 1;
if(on() != RF_CMD_OK) {
PRINTF("channel_clear: on() failed\n");
if(was_off) {
off();
}
return RF_CCA_CLEAR;
}
}
cca_info = get_cca_info();
if(cca_info == RF_GET_CCA_INFO_ERROR) {
PRINTF("channel_clear: CCA error\n");
ret = RF_CCA_CLEAR;
} else {
/*
* cca_info bits 1:0 - ccaStatus
* Return 1 (clear) if idle or invalid.
*/
ret = (cca_info & 0x03) != RF_CMD_CCA_REQ_CCA_STATE_BUSY;
}
if(was_off) {
off();
}
return ret;
}
/*---------------------------------------------------------------------------*/
static int
receiving_packet(void)
{
int ret = 0;
uint8_t cca_info;
uint8_t was_off = 0;
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CMD_OK) {
PRINTF("receiving_packet: on() failed\n");
return RF_CMD_ERROR;
}
}
/*
* If we are in the middle of a BLE operation, we got called by ContikiMAC
* from within an interrupt context. We are not receiving
*/
#if CC26XX_RF_BLE_SUPPORT
if(ble_mode_on) {
PRINTF("receiving_packet: Interrupt context but BLE in progress\n");
return 0;
}
#endif
/* If we are off, we are not receiving */
if(!rf_is_on()) {
PRINTF("receiving_packet: We were off\n");
return 0;
}
/* If we are transmitting (can only be an ACK here), we are not receiving */
if(transmitting()) {
PRINTF("receiving_packet: We were TXing\n");
return 0;
}
cca_info = get_cca_info();
if(cca_info == RF_GET_CCA_INFO_ERROR) {
/* If we can't read CCA info, return "not receiving" */
ret = 0;
} else {
/* Return 1 (receiving) if ccaState is busy */
ret = (cca_info & 0x03) == RF_CMD_CCA_REQ_CCA_STATE_BUSY;
}
if(was_off) {
off();
}
return ret;
}
/*---------------------------------------------------------------------------*/
static int
pending_packet(void)
{
volatile uint8_t *current = rx_data_queue.pCurrEntry;
int rv = 0;
/* Go through all RX buffers and check their status */
do {
if(GET_FIELD_V(current, dataEntry, status) ==
DATA_ENTRY_STATUS_FINISHED) {
rv = 1;
process_poll(&cc26xx_rf_process);
}
current = GET_FIELD_V(current, dataEntry, pNextEntry);
} while(current != rx_data_queue.pCurrEntry);
/* If we didn't find an entry at status finished, no frames are pending */
return rv;
}
/*---------------------------------------------------------------------------*/
static int
on(void)
{
/*
* Request the HF XOSC as the source for the HF clock. Needed before we can
* use the FS. This will only request, it will _not_ perform the switch.
*/
oscillators_request_hf_xosc();
/*
* If we are in the middle of a BLE operation, we got called by ContikiMAC
* from within an interrupt context. Abort, but pretend everything is OK.
*/
#if CC26XX_RF_BLE_SUPPORT
if(ble_mode_on) {
PRINTF("on: Interrupt context but BLE in progress\n");
return RF_CMD_OK;
}
#endif
if(rf_is_on()) {
PRINTF("on: We were on. PD=%u, RX=0x%04x \n", rf_is_accessible(),
RF_RADIO_OP_GET_STATUS(cmd_ieee_rx_buf));
return RF_CMD_OK;
}
if(power_up() != RF_CMD_OK) {
PRINTF("on: power_up() failed\n");
return RF_CMD_ERROR;
}
if(apply_patches() != RF_CMD_OK) {
PRINTF("on: apply_patches() failed\n");
return RF_CMD_ERROR;
}
init_rx_buffers();
setup_interrupts();
/*
* Trigger a switch to the XOSC, so that we can subsequently use the RF FS
* This will block until the XOSC is actually ready, but give how we
* requested it early on, this won't be too long a wait/
*/
oscillators_switch_to_hf_xosc();
if(rf_radio_setup(RF_MODE_IEEE) != RF_CMD_OK) {
PRINTF("on: radio_setup() failed\n");
return RF_CMD_ERROR;
}
return rx_on();
}
/*---------------------------------------------------------------------------*/
static int
off(void)
{
/*
* If we are in the middle of a BLE operation, we got called by ContikiMAC
* from within an interrupt context. Abort, but pretend everything is OK.
*/
#if CC26XX_RF_BLE_SUPPORT
if(ble_mode_on) {
PRINTF("off: Interrupt context but BLE in progress\n");
return RF_CMD_OK;
}
#endif
while(transmitting());
power_down();
/* Switch HF clock source to the RCOSC to preserve power */
oscillators_switch_to_hf_rc();
/* We pulled the plug, so we need to restore the status manually */
GET_FIELD(cmd_ieee_rx_buf, radioOp, status) = IDLE;
/*
* Just in case there was an ongoing RX (which started after we begun the
* shutdown sequence), we don't want to leave the buffer in state == ongoing
*/
GET_FIELD_V(rx_buf_0, dataEntry, status) = DATA_ENTRY_STATUS_PENDING;
GET_FIELD_V(rx_buf_1, dataEntry, status) = DATA_ENTRY_STATUS_PENDING;
GET_FIELD_V(rx_buf_2, dataEntry, status) = DATA_ENTRY_STATUS_PENDING;
GET_FIELD_V(rx_buf_3, dataEntry, status) = DATA_ENTRY_STATUS_PENDING;
return RF_CMD_OK;
}
/*---------------------------------------------------------------------------*/
static radio_result_t
get_value(radio_param_t param, radio_value_t *value)
{
if(!value) {
return RADIO_RESULT_INVALID_VALUE;
}
switch(param) {
case RADIO_PARAM_POWER_MODE:
/* On / off */
*value = rf_is_on() ? RADIO_POWER_MODE_ON : RADIO_POWER_MODE_OFF;
return RADIO_RESULT_OK;
case RADIO_PARAM_CHANNEL:
*value = (radio_value_t)GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, channel);
return RADIO_RESULT_OK;
case RADIO_PARAM_PAN_ID:
*value = (radio_value_t)GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localPanID);
return RADIO_RESULT_OK;
case RADIO_PARAM_16BIT_ADDR:
*value = (radio_value_t)GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localShortAddr);
return RADIO_RESULT_OK;
case RADIO_PARAM_RX_MODE:
*value = 0;
if(GET_BITFIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt, frameFiltEn)) {
*value |= RADIO_RX_MODE_ADDRESS_FILTER;
}
if(GET_BITFIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt, autoAckEn)) {
*value |= RADIO_RX_MODE_AUTOACK;
}
return RADIO_RESULT_OK;
case RADIO_PARAM_TXPOWER:
*value = get_tx_power();
return RADIO_RESULT_OK;
case RADIO_PARAM_CCA_THRESHOLD:
*value = GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaRssiThr);
return RADIO_RESULT_OK;
case RADIO_PARAM_RSSI:
*value = get_rssi();
if(*value == RF_CMD_CCA_REQ_RSSI_UNKNOWN) {
return RADIO_RESULT_ERROR;
} else {
return RADIO_RESULT_OK;
}
case RADIO_CONST_CHANNEL_MIN:
*value = CC26XX_RF_CHANNEL_MIN;
return RADIO_RESULT_OK;
case RADIO_CONST_CHANNEL_MAX:
*value = CC26XX_RF_CHANNEL_MAX;
return RADIO_RESULT_OK;
case RADIO_CONST_TXPOWER_MIN:
*value = OUTPUT_POWER_MIN;
return RADIO_RESULT_OK;
case RADIO_CONST_TXPOWER_MAX:
*value = OUTPUT_POWER_MAX;
return RADIO_RESULT_OK;
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
}
/*---------------------------------------------------------------------------*/
static radio_result_t
set_value(radio_param_t param, radio_value_t value)
{
uint8_t was_off = 0;
radio_result_t rv;
switch(param) {
case RADIO_PARAM_POWER_MODE:
if(value == RADIO_POWER_MODE_ON) {
if(on() != RF_CMD_OK) {
PRINTF("set_value: on() failed (1)\n");
return RADIO_RESULT_ERROR;
}
return RADIO_RESULT_OK;
}
if(value == RADIO_POWER_MODE_OFF) {
off();
return RADIO_RESULT_OK;
}
return RADIO_RESULT_INVALID_VALUE;
case RADIO_PARAM_CHANNEL:
if(value < CC26XX_RF_CHANNEL_MIN ||
value > CC26XX_RF_CHANNEL_MAX) {
return RADIO_RESULT_INVALID_VALUE;
}
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, channel) = (uint8_t)value;
break;
case RADIO_PARAM_PAN_ID:
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localPanID) = (uint16_t)value;
break;
case RADIO_PARAM_16BIT_ADDR:
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, localShortAddr) = (uint16_t)value;
break;
case RADIO_PARAM_RX_MODE:
{
if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER |
RADIO_RX_MODE_AUTOACK)) {
return RADIO_RESULT_INVALID_VALUE;
}
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, frameFiltOpt) =
BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltEn,
(value & RADIO_RX_MODE_ADDRESS_FILTER) != 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, frameFiltStop, 1) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoAckEn,
(value & RADIO_RX_MODE_AUTOACK) != 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, slottedAckEn, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, autoPendEn, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, defaultPend, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPendDataReqOnly, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, bPanCoord, 0) |
BITVALUE(CMD_IEEE_RX, frameFiltOpt, bStrictLenFilter, 0);
break;
}
case RADIO_PARAM_TXPOWER:
if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) {
return RADIO_RESULT_INVALID_VALUE;
}
set_tx_power(value);
return RADIO_RESULT_OK;
case RADIO_PARAM_CCA_THRESHOLD:
GET_FIELD(cmd_ieee_rx_buf, CMD_IEEE_RX, ccaRssiThr) = (int8_t)value;
break;
default:
return RADIO_RESULT_NOT_SUPPORTED;
}
/* If we reach here we had no errors. Apply new settings */
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CMD_OK) {
PRINTF("set_value: on() failed (2)\n");
return RADIO_RESULT_ERROR;
}
}
if(rx_off() != RF_CMD_OK) {
PRINTF("set_value: rx_off() failed\n");
rv = RADIO_RESULT_ERROR;
}
if(rx_on() != RF_CMD_OK) {
PRINTF("set_value: rx_on() failed\n");
rv = RADIO_RESULT_ERROR;
}
/* If we were off, turn back off */
if(was_off) {
off();
}
return rv;
}
/*---------------------------------------------------------------------------*/
static radio_result_t
get_object(radio_param_t param, void *dest, size_t size)
{
uint8_t *target;
uint8_t *src;
int i;
if(param == RADIO_PARAM_64BIT_ADDR) {
if(size != 8 || !dest) {
return RADIO_RESULT_INVALID_VALUE;
}
target = dest;
src = (uint8_t *)(GET_FIELD_PTR(cmd_ieee_rx_buf, CMD_IEEE_RX, localExtAddr));
for(i = 0; i < 8; i++) {
target[i] = src[7 - i];
}
return RADIO_RESULT_OK;
}
return RADIO_RESULT_NOT_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
static radio_result_t
set_object(radio_param_t param, const void *src, size_t size)
{
uint8_t was_off = 0;
radio_result_t rv;
int i;
uint8_t *dst;
if(param == RADIO_PARAM_64BIT_ADDR) {
if(size != 8 || !src) {
return RADIO_RESULT_INVALID_VALUE;
}
dst = (uint8_t *)(GET_FIELD_PTR(cmd_ieee_rx_buf, CMD_IEEE_RX,
localExtAddr));
for(i = 0; i < 8; i++) {
dst[i] = ((uint8_t *)src)[7 - i];
}
if(!rf_is_on()) {
was_off = 1;
if(on() != RF_CMD_OK) {
PRINTF("set_object: on() failed\n");
return RADIO_RESULT_ERROR;
}
}
if(rx_off() != RF_CMD_OK) {
PRINTF("set_object: rx_off() failed\n");
rv = RADIO_RESULT_ERROR;
}
if(rx_on() != RF_CMD_OK) {
PRINTF("set_object: rx_on() failed\n");
rv = RADIO_RESULT_ERROR;
}
/* If we were off, turn back off */
if(was_off) {
off();
}
return rv;
}
return RADIO_RESULT_NOT_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
const struct radio_driver cc26xx_rf_driver = {
init,
prepare,
transmit,
send,
read_frame,
channel_clear,
receiving_packet,
pending_packet,
on,
off,
get_value,
set_value,
get_object,
set_object,
};
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(cc26xx_rf_process, ev, data)
{
int len;
PROCESS_BEGIN();
while(1) {
PROCESS_WAIT_EVENT();
do {
packetbuf_clear();
len = read_frame(packetbuf_dataptr(), PACKETBUF_SIZE);
if(len > 0) {
packetbuf_set_datalen(len);
NETSTACK_RDC.input();
}
} while(len > 0);
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
#if CC26XX_RF_BLE_SUPPORT
/*---------------------------------------------------------------------------*/
/**
* \brief Builds common radio parameters for radio operations
*
* \param *cmd Pointer to buffer to add parameters to
* \param command Radio command number (e.g. COMMAND_RADIO_SETUP)
*
* \note The buffer must be emptied with memset() before calling this function
*
* \return None
*/
static void
rf_build_ble_radio_op_cmd(uint8_t *cmd, uint16_t command)
{
GET_FIELD(cmd, radioOp, commandNo) = command;
GET_FIELD(cmd, radioOp, status) = IDLE;
GET_FIELD(cmd, radioOp, pNextOp) = NULL;
GET_FIELD(cmd, radioOp, startTime) = 0;
GET_FIELD(cmd, radioOp, startTrigger) = TRIG_NOW;
GET_FIELD(cmd, radioOp, condition) = COND_NEVER;
}
/*---------------------------------------------------------------------------*/
static void
init_ble()
{
ble_mode_on = 0;
memset(beacond_config.adv_name, 0, BLE_ADV_NAME_BUF_LEN);
beacond_config.interval = BLE_ADV_INTERVAL;
}
/*---------------------------------------------------------------------------*/
static int
send_ble_adv_nc(int channel, uint8_t *output, uint8_t *adv_payload,
int adv_payload_len, uint16_t *dev_address)
{
uint32_t cmd_status;
int ret;
/* Erase ble_tx_rx_buf array */
memset(ble_tx_rx_buf, 0x00, SIZEOF_RADIO_OP(CMD_BLE_ADV_NC));
rf_build_ble_radio_op_cmd(ble_tx_rx_buf, CMD_BLE_ADV_NC);
GET_FIELD(ble_tx_rx_buf, bleRadioOp, channel) = channel;
GET_FIELD(ble_tx_rx_buf, bleRadioOp, whitening) = 0;
memset(ble_cmd_buf, 0x00, SIZEOF_STRUCT(bleAdvPar));
GET_FIELD(ble_tx_rx_buf, bleRadioOp, pParams) = (uint8_t *)ble_cmd_buf;
GET_FIELD(ble_tx_rx_buf, bleRadioOp, pOutput) = output;
/* Set up BLE Advertisement parameters */
GET_FIELD(ble_cmd_buf, bleAdvPar, pRxQ) = NULL;
GET_FIELD(ble_cmd_buf, bleAdvPar, rxConfig) = 0;
GET_FIELD(ble_cmd_buf, bleAdvPar, advConfig) = 0;
GET_FIELD(ble_cmd_buf, bleAdvPar, advLen) = adv_payload_len;
GET_FIELD(ble_cmd_buf, bleAdvPar, scanRspLen) = 0;
GET_FIELD(ble_cmd_buf, bleAdvPar, pAdvData) = adv_payload;
GET_FIELD(ble_cmd_buf, bleAdvPar, pScanRspData) = NULL;
GET_FIELD(ble_cmd_buf, bleAdvPar, pDeviceAddress) = dev_address;
GET_FIELD(ble_cmd_buf, bleAdvPar, pWhiteList) = NULL;
GET_FIELD(ble_cmd_buf, bleAdvPar, endTrigger) = TRIG_NEVER;
GET_FIELD(ble_cmd_buf, bleAdvPar, endTime) = TRIG_NEVER;
if(rf_send_cmd((uint32_t)ble_tx_rx_buf, &cmd_status) == RF_CMD_ERROR) {
PRINTF("send_ble_adv_nc: Chan=%d CMDSTA=0x%08lx, status=0x%04x\n",
channel, cmd_status, RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf));
return RF_CMD_ERROR;
}
/* Wait for the ADV_NC to go out */
while((RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf) & RF_RADIO_OP_MASKED_STATUS)
== RF_RADIO_OP_MASKED_STATUS_RUNNING);
if(RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf) == RF_RADIO_OP_STATUS_BLE_DONE_OK) {
/* Sent OK */
ret = RF_CMD_OK;
} else {
/* Radio Op completed, but ADV NC was not sent */
PRINTF("send_ble_adv_nc: Chan=%d CMDSTA=0x%08lx, status=0x%04x\n",
channel, cmd_status, RF_RADIO_OP_GET_STATUS(ble_tx_rx_buf));
ret = RF_CMD_ERROR;
}
return ret;
}
/*---------------------------------------------------------------------------*/
static int
send_ble_adv(int channel, uint8_t *adv_payload, int adv_payload_len)
{
if(send_ble_adv_nc(channel, rf_stats, adv_payload, adv_payload_len,
(uint16_t *)&linkaddr_node_addr.u8[2]) != RF_CMD_OK) {
PRINTF("send_ble_adv: Channel=%d, Error advertising\n", channel);
/* Break the loop, but don't return just yet */
return RF_CMD_ERROR;
}
return RF_CMD_OK;
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(cc26xx_rf_ble_beacon_process, ev, data)
{
static struct etimer ble_adv_et;
static uint8_t payload[BLE_ADV_PAYLOAD_BUF_LEN];
static int p = 0;
static int i;
uint8_t was_on;
int j;
uint32_t cmd_status;
bool interrupts_disabled;
PROCESS_BEGIN();
while(1) {
etimer_set(&ble_adv_et, beacond_config.interval);
PROCESS_WAIT_EVENT();
if(ev == PROCESS_EVENT_EXIT) {
PROCESS_EXIT();
}
/* Set the adv payload each pass: The device name may have changed */
p = 0;
/* device info */
payload[p++] = 0x02; /* 2 bytes */
payload[p++] = BLE_ADV_TYPE_DEVINFO;
payload[p++] = 0x1a; /* LE general discoverable + BR/EDR */
payload[p++] = 1 + strlen(beacond_config.adv_name);
payload[p++] = BLE_ADV_TYPE_NAME;
memcpy(&payload[p], beacond_config.adv_name,
strlen(beacond_config.adv_name));
p += strlen(beacond_config.adv_name);
for(i = 0; i < BLE_ADV_MESSAGES; i++) {
/*
* Under ContikiMAC, some IEEE-related operations will be called from an
* interrupt context. We need those to see that we are in BLE mode.
*/
interrupts_disabled = ti_lib_int_master_disable();
ble_mode_on = 1;
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/*
* Send BLE_ADV_MESSAGES beacon bursts. Each burst on all three
* channels, with a BLE_ADV_DUTY_CYCLE interval between bursts
*
* First, determine our state:
*
* If we are running NullRDC, we are likely in IEEE RX mode. We need to
* abort the IEEE BG Op before entering BLE mode.
* If we are ContikiMAC, we are likely off, in which case we need to
* boot the CPE before entering BLE mode
*/
was_on = rf_is_accessible();
if(was_on) {
/*
* We were on: If we are in the process of receiving an IEEE frame,
* abort the BLE beacon burst. Otherwise, terminate the IEEE BG Op
* so we can switch to BLE mode
*/
if(receiving_packet()) {
PRINTF("cc26xx_rf_ble_beacon_process: We were receiving\n");
/* Abort this pass */
break;
}
if(rx_off() != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: rx_off() failed\n");
/* Abort this pass */
break;
}
} else {
/* Request the HF XOSC to source the HF clock. */
oscillators_request_hf_xosc();
/* We were off: Boot the CPE */
if(power_up() != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: power_up() failed\n");
/* Abort this pass */
break;
}
if(apply_patches() != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: apply_patches() failed\n");
/* Abort this pass */
break;
}
/* Trigger a switch to the XOSC, so that we can use the FS */
oscillators_switch_to_hf_xosc();
}
/* Enter BLE mode */
if(rf_radio_setup(RF_MODE_BLE) != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: Error entering BLE mode\n");
/* Continue so we can at least try to restore our previous state */
} else {
/* Send advertising packets on all 3 advertising channels */
for(j = 37; j <= 39; j++) {
if(send_ble_adv(j, payload, p) != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: Channel=%d,"
"Error advertising\n", j);
/* Break the loop, but don't return just yet */
break;
}
}
}
/* Send a CMD_STOP command to RF Core */
if(rf_send_cmd(CMDR_DIR_CMD(CMD_STOP), &cmd_status) != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: status=0x%08lx\n", cmd_status);
/* Continue... */
}
if(was_on) {
/* We were on, go back to IEEE mode */
if(rf_radio_setup(RF_MODE_IEEE) != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: radio_setup() failed\n");
}
/* Enter IEEE RX mode */
if(rx_on() != RF_CMD_OK) {
PRINTF("cc26xx_rf_ble_beacon_process: rx_on() failed\n");
}
} else {
power_down();
/* Switch HF clock source to the RCOSC to preserve power */
oscillators_switch_to_hf_rc();
}
etimer_set(&ble_adv_et, BLE_ADV_DUTY_CYCLE);
interrupts_disabled = ti_lib_int_master_disable();
ble_mode_on = 0;
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
/* Wait unless this is the last burst */
if(i < BLE_ADV_MESSAGES - 1) {
PROCESS_WAIT_EVENT();
}
}
interrupts_disabled = ti_lib_int_master_disable();
ble_mode_on = 0;
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
#endif /* CC26XX_RF_BLE_SUPPORT */
/*---------------------------------------------------------------------------*/
void
cc26xx_rf_ble_beacond_config(clock_time_t interval, const char *name)
{
#if CC26XX_RF_BLE_SUPPORT
if(name != NULL) {
memset(beacond_config.adv_name, 0, BLE_ADV_NAME_BUF_LEN);
if(strlen(name) == 0 || strlen(name) >= BLE_ADV_NAME_BUF_LEN) {
return;
}
memcpy(beacond_config.adv_name, name, strlen(name));
}
if(interval != 0) {
beacond_config.interval = interval;
}
#endif
}
/*---------------------------------------------------------------------------*/
uint8_t
cc26xx_rf_ble_beacond_start()
{
#if CC26XX_RF_BLE_SUPPORT
if(beacond_config.adv_name[0] == 0) {
return RF_CMD_ERROR;
}
process_start(&cc26xx_rf_ble_beacon_process, NULL);
return RF_CMD_OK;
#else
return RF_CMD_ERROR;
#endif
}
/*---------------------------------------------------------------------------*/
void
cc26xx_rf_ble_beacond_stop()
{
#if CC26XX_RF_BLE_SUPPORT
process_exit(&cc26xx_rf_ble_beacon_process);
#endif
}
/*---------------------------------------------------------------------------*/
/** @} */