01e36532c2
This commit: * Moves all cpu files from cpu/cc26xx to cpu/cc26xx-cc13xx * Bumps the CC26xxware submodule to the latest TI release * Adds CC13xxware as a submodule * Adds support for sub-ghz mode / IEEE 802.15.4g * Splits the driver into multiple files for clarity. We now have the following structure: * A common module that handles access to the RF core, interrupts etc * A module that takes care of BLE functionality * A netstack radio driver for IEEE mode (2.4GHz) * A netstack radio driver for PROP mode (sub-ghz - multiple bands) This commit also adds tick suppression functionality, applicable to all chips of the CC26xx and CC13xx families. Instead waking up on every clock tick simply to increment our software counter, we now only wake up just in time to service the next scheduled etimer. ContikiMAC-triggered wakeups are unaffected. Laslty, this commit also applies a number of minor changes: * Addition of missing includes * Removal of stub functions * Removal of a woraround for a CC26xxware bug that has now been fixed
400 lines
13 KiB
C
400 lines
13 KiB
C
/*
|
|
* Copyright (c) 2015, 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 rf-core-ble
|
|
* @{
|
|
*
|
|
* \file
|
|
* Implementation of the CC13xx/CC26xx RF BLE driver
|
|
*/
|
|
/*---------------------------------------------------------------------------*/
|
|
#include "contiki-conf.h"
|
|
#include "sys/process.h"
|
|
#include "sys/clock.h"
|
|
#include "sys/etimer.h"
|
|
#include "net/netstack.h"
|
|
#include "net/linkaddr.h"
|
|
#include "dev/oscillators.h"
|
|
#include "rf-core/rf-core.h"
|
|
#include "rf-core/rf-ble.h"
|
|
#include "rf-core/api/ble_cmd.h"
|
|
#include "rf-core/api/common_cmd.h"
|
|
#include "ti-lib.h"
|
|
/*---------------------------------------------------------------------------*/
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
/*---------------------------------------------------------------------------*/
|
|
#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
|
|
/*---------------------------------------------------------------------------*/
|
|
/* 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
|
|
/*---------------------------------------------------------------------------*/
|
|
static unsigned char ble_params_buf[32] CC_ALIGN_ATTR(4);
|
|
static uint8_t ble_mode_on = RF_BLE_IDLE;
|
|
static struct etimer ble_adv_et;
|
|
static uint8_t payload[BLE_ADV_PAYLOAD_BUF_LEN];
|
|
static int p = 0;
|
|
static int i;
|
|
/*---------------------------------------------------------------------------*/
|
|
typedef struct default_ble_tx_power_s {
|
|
uint16_t ib:6;
|
|
uint16_t gc:2;
|
|
uint16_t boost:1;
|
|
uint16_t temp_coeff:7;
|
|
} default_ble_tx_power_t;
|
|
|
|
static default_ble_tx_power_t tx_power = { 0x29, 0x00, 0x00, 0x00 };
|
|
/*---------------------------------------------------------------------------*/
|
|
/* BLE beacond config */
|
|
static struct ble_beacond_config {
|
|
clock_time_t interval;
|
|
char adv_name[BLE_ADV_NAME_BUF_LEN];
|
|
} beacond_config = { .interval = BLE_ADV_INTERVAL };
|
|
/*---------------------------------------------------------------------------*/
|
|
/* 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(rf_ble_beacon_process, "CC13xx / CC26xx RF BLE Beacon Process");
|
|
/*---------------------------------------------------------------------------*/
|
|
static int
|
|
send_ble_adv_nc(int channel, uint8_t *adv_payload, int adv_payload_len)
|
|
{
|
|
uint32_t cmd_status;
|
|
rfc_CMD_BLE_ADV_NC_t cmd;
|
|
rfc_bleAdvPar_t *params;
|
|
|
|
params = (rfc_bleAdvPar_t *)ble_params_buf;
|
|
|
|
/* Clear both buffers */
|
|
memset(&cmd, 0x00, sizeof(cmd));
|
|
memset(ble_params_buf, 0x00, sizeof(ble_params_buf));
|
|
|
|
/* Adv NC */
|
|
cmd.commandNo = CMD_BLE_ADV_NC;
|
|
cmd.condition.rule = COND_NEVER;
|
|
cmd.whitening.bOverride = 0;
|
|
cmd.whitening.init = 0;
|
|
cmd.pParams = params;
|
|
cmd.channel = channel;
|
|
|
|
/* Set up BLE Advertisement parameters */
|
|
params->pDeviceAddress = (uint16_t *)&linkaddr_node_addr.u8[LINKADDR_SIZE - 2];
|
|
params->endTrigger.triggerType = TRIG_NEVER;
|
|
params->endTime = TRIG_NEVER;
|
|
|
|
/* Set up BLE Advertisement parameters */
|
|
params = (rfc_bleAdvPar_t *)ble_params_buf;
|
|
params->advLen = adv_payload_len;
|
|
params->pAdvData = adv_payload;
|
|
|
|
if(rf_core_send_cmd((uint32_t)&cmd, &cmd_status) == RF_CORE_CMD_ERROR) {
|
|
PRINTF("send_ble_adv_nc: Chan=%d CMDSTA=0x%08lx, status=0x%04x\n",
|
|
channel, cmd_status, cmd.status);
|
|
return RF_CORE_CMD_ERROR;
|
|
}
|
|
|
|
/* Wait until the command is done */
|
|
if(rf_core_wait_cmd_done(&cmd) != RF_CORE_CMD_OK) {
|
|
PRINTF("send_ble_adv_nc: Chan=%d CMDSTA=0x%08lx, status=0x%04x\n",
|
|
channel, cmd_status, cmd.status);
|
|
return RF_CORE_CMD_ERROR;
|
|
}
|
|
|
|
return RF_CORE_CMD_OK;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
rf_ble_beacond_config(clock_time_t interval, const char *name)
|
|
{
|
|
if(RF_BLE_ENABLED == 0) {
|
|
return;
|
|
}
|
|
|
|
if(name != NULL) {
|
|
if(strlen(name) == 0 || strlen(name) >= BLE_ADV_NAME_BUF_LEN) {
|
|
return;
|
|
}
|
|
|
|
memset(beacond_config.adv_name, 0, BLE_ADV_NAME_BUF_LEN);
|
|
memcpy(beacond_config.adv_name, name, strlen(name));
|
|
}
|
|
|
|
if(interval != 0) {
|
|
beacond_config.interval = interval;
|
|
}
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
uint8_t
|
|
rf_ble_beacond_start()
|
|
{
|
|
if(RF_BLE_ENABLED == 0) {
|
|
return RF_CORE_CMD_ERROR;
|
|
}
|
|
|
|
if(ti_lib_chipinfo_supports_ble() == false) {
|
|
return RF_CORE_CMD_ERROR;
|
|
}
|
|
|
|
if(beacond_config.adv_name[0] == 0) {
|
|
return RF_CORE_CMD_ERROR;
|
|
}
|
|
|
|
ble_mode_on = RF_BLE_IDLE;
|
|
|
|
process_start(&rf_ble_beacon_process, NULL);
|
|
|
|
return RF_CORE_CMD_OK;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
uint8_t
|
|
rf_ble_is_active()
|
|
{
|
|
return ble_mode_on;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
void
|
|
rf_ble_beacond_stop()
|
|
{
|
|
process_exit(&rf_ble_beacon_process);
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
rf_radio_setup()
|
|
{
|
|
uint32_t cmd_status;
|
|
rfc_CMD_RADIO_SETUP_t cmd;
|
|
|
|
/* Create radio setup command */
|
|
rf_core_init_radio_op((rfc_radioOp_t *)&cmd, sizeof(cmd), CMD_RADIO_SETUP);
|
|
|
|
cmd.txPower.IB = tx_power.ib;
|
|
cmd.txPower.GC = tx_power.gc;
|
|
cmd.txPower.tempCoeff = tx_power.temp_coeff;
|
|
cmd.txPower.boost = tx_power.boost;
|
|
cmd.pRegOverride = ble_overrides;
|
|
cmd.mode = 0;
|
|
|
|
/* Send Radio setup to RF Core */
|
|
if(rf_core_send_cmd((uint32_t)&cmd, &cmd_status) != RF_CORE_CMD_OK) {
|
|
PRINTF("rf_radio_setup: CMDSTA=0x%08lx, status=0x%04x\n",
|
|
cmd_status, cmd.status);
|
|
return RF_CORE_CMD_ERROR;
|
|
}
|
|
|
|
/* Wait until radio setup is done */
|
|
if(rf_core_wait_cmd_done(&cmd) != RF_CORE_CMD_OK) {
|
|
PRINTF("rf_radio_setup: wait, CMDSTA=0x%08lx, status=0x%04x\n",
|
|
cmd_status, cmd.status);
|
|
return RF_CORE_CMD_ERROR;
|
|
}
|
|
|
|
return RF_CORE_CMD_OK;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
PROCESS_THREAD(rf_ble_beacon_process, ev, data)
|
|
{
|
|
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 */
|
|
memset(payload, 0, BLE_ADV_PAYLOAD_BUF_LEN);
|
|
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 = RF_BLE_ACTIVE;
|
|
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_core_is_accessible();
|
|
|
|
if(was_on) {
|
|
/*
|
|
* We were on: If we are in the process of receiving a frame, abort the
|
|
* BLE beacon burst. Otherwise, terminate the primary radio Op so we
|
|
* can switch to BLE mode
|
|
*/
|
|
if(NETSTACK_RADIO.receiving_packet()) {
|
|
PRINTF("rf_ble_beacon_process: We were receiving\n");
|
|
|
|
/* Abort this pass */
|
|
break;
|
|
}
|
|
|
|
rf_core_primary_mode_abort();
|
|
} else {
|
|
/* Request the HF XOSC to source the HF clock. */
|
|
oscillators_request_hf_xosc();
|
|
|
|
/* We were off: Boot the CPE */
|
|
if(rf_core_boot() != RF_CORE_CMD_OK) {
|
|
PRINTF("rf_ble_beacon_process: rf_core_boot() 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_CORE_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_nc(j, payload, p) != RF_CORE_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_core_send_cmd(CMDR_DIR_CMD(CMD_STOP), &cmd_status) != RF_CORE_CMD_OK) {
|
|
PRINTF("cc26xx_rf_ble_beacon_process: status=0x%08lx\n", cmd_status);
|
|
/* Continue... */
|
|
}
|
|
|
|
if(was_on) {
|
|
/* We were on, go back to previous primary mode */
|
|
rf_core_primary_mode_restore();
|
|
} else {
|
|
/* We were off. Shut back off */
|
|
rf_core_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 = RF_BLE_IDLE;
|
|
|
|
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 = RF_BLE_IDLE;
|
|
|
|
if(!interrupts_disabled) {
|
|
ti_lib_int_master_enable();
|
|
}
|
|
}
|
|
PROCESS_END();
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
/**
|
|
* @}
|
|
* @}
|
|
*/
|