nes-proj/cpu/cc26xx/dev/cc26xx-rf.c
George Oikonomou 421fbfae25 Change the LPM locks API:
Instead of using a separate data structure to request that a PD remain powered during deep sleep,
we do the same within the main LPM data structure through an additional field.

This allows us to maintain only one linked list of LPM modules and overall improves code clarity
2015-05-15 09:21:02 +01:00

2151 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 "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)
/*---------------------------------------------------------------------------*/
#ifndef MIN
#define MIN(n, m) (((n) < (m)) ? (n) : (m))
#endif
/*---------------------------------------------------------------------------*/
#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, 0) |
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, 0) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaEnSync, 0) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaCorrOp, 0) |
BITVALUE(CMD_IEEE_RX, ccaOpt, ccaSyncOp, 0);
/* 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
}
/*---------------------------------------------------------------------------*/
/** @} */