contiki/cpu/cc26xx-cc13xx/rf-core/rf-ble.c

401 lines
13 KiB
C
Raw Normal View History

/*
* 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();
}
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/