/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
|
#include <stdint.h>
|
#include <assert.h>
|
#include <string.h>
|
#include "nimble_syscfg.h"
|
#include "os/os.h"
|
#include "ble/xcvr.h"
|
#include "nimble/ble.h"
|
#include "nimble/nimble_opt.h"
|
#include "nrfx.h"
|
#include "controller/ble_hw.h"
|
#if MYNEWT
|
#include "mcu/cmsis_nvic.h"
|
#else
|
#include "core_cm4.h"
|
#include <nimble/nimble_npl_os.h>
|
#endif
|
#include "os/os_trace_api.h"
|
#include <hal/nrf_rng.h>
|
#include "hal/nrf_ecb.h"
|
|
/* Total number of resolving list elements */
|
#define BLE_HW_RESOLV_LIST_SIZE (16)
|
|
/* We use this to keep track of which entries are set to valid addresses */
|
static uint8_t g_ble_hw_whitelist_mask;
|
|
/* Random number generator isr callback */
|
ble_rng_isr_cb_t g_ble_rng_isr_cb;
|
|
#if BABBLESIM
|
extern void tm_tick(void);
|
#endif
|
|
/* If LL privacy is enabled, allocate memory for AAR */
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
|
/* The NRF51 supports up to 16 IRK entries */
|
#if (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE) < 16)
|
#define NRF_IRK_LIST_ENTRIES (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE))
|
#else
|
#define NRF_IRK_LIST_ENTRIES (16)
|
#endif
|
|
/* NOTE: each entry is 16 bytes long. */
|
uint32_t g_nrf_irk_list[NRF_IRK_LIST_ENTRIES * 4];
|
|
/* Current number of IRK entries */
|
uint8_t g_nrf_num_irks;
|
|
#endif
|
|
/* Returns public device address or -1 if not present */
|
int
|
ble_hw_get_public_addr(ble_addr_t *addr)
|
{
|
uint32_t addr_high;
|
uint32_t addr_low;
|
|
#if MYNEWT_VAL(BLE_PHY_UBLOX_BMD345_PUBLIC_ADDR)
|
/*
|
* The BMD-345 modules are preprogrammed from the factory with a unique public
|
* The Bluetooth device address stored in the CUSTOMER[0] and CUSTOMER[1]
|
* registers of the User Information Configuration Registers (UICR).
|
* The Bluetooth device address consists of the IEEE Organizationally Unique
|
* Identifier (OUI) combined with the hexadecimal digits that are printed on
|
* a 2D barcode and in human-readable text on the module label.The Bluetooth
|
* device address is stored in little endian format. The most significant
|
* bytes of the CUSTOMER[1] register are 0xFF to complete the 32-bit register.
|
*/
|
|
/* Copy into device address. We can do this because we know platform */
|
addr_low = NRF_UICR->CUSTOMER[0];
|
addr_high = NRF_UICR->CUSTOMER[1];
|
#else
|
/* Does FICR have a public address */
|
if ((NRF_FICR->DEVICEADDRTYPE & 1) != 0) {
|
return -1;
|
}
|
|
/* Copy into device address. We can do this because we know platform */
|
addr_low = NRF_FICR->DEVICEADDR[0];
|
addr_high = NRF_FICR->DEVICEADDR[1];
|
#endif
|
|
memcpy(addr->val, &addr_low, 4);
|
memcpy(&addr->val[4], &addr_high, 2);
|
addr->type = BLE_ADDR_PUBLIC;
|
|
return 0;
|
}
|
|
/* Returns random static address or -1 if not present */
|
int
|
ble_hw_get_static_addr(ble_addr_t *addr)
|
{
|
int rc;
|
|
if ((NRF_FICR->DEVICEADDRTYPE & 1) == 1) {
|
memcpy(addr->val, (void *)&NRF_FICR->DEVICEADDR[0], 4);
|
memcpy(&addr->val[4], (void *)&NRF_FICR->DEVICEADDR[1], 2);
|
addr->val[5] |= 0xc0;
|
addr->type = BLE_ADDR_RANDOM;
|
rc = 0;
|
} else {
|
rc = -1;
|
}
|
|
return rc;
|
}
|
|
/**
|
* Clear the whitelist
|
*
|
* @return int
|
*/
|
void
|
ble_hw_whitelist_clear(void)
|
{
|
NRF_RADIO->DACNF = 0;
|
g_ble_hw_whitelist_mask = 0;
|
}
|
|
/**
|
* Add a device to the hw whitelist
|
*
|
* @param addr
|
* @param addr_type
|
*
|
* @return int 0: success, BLE error code otherwise
|
*/
|
int
|
ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type)
|
{
|
int i;
|
uint32_t mask;
|
|
/* Find first ununsed device address match element */
|
mask = 0x01;
|
for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) {
|
if ((mask & g_ble_hw_whitelist_mask) == 0) {
|
NRF_RADIO->DAB[i] = get_le32(addr);
|
NRF_RADIO->DAP[i] = get_le16(addr + 4);
|
if (addr_type == BLE_ADDR_RANDOM) {
|
NRF_RADIO->DACNF |= (mask << 8);
|
}
|
g_ble_hw_whitelist_mask |= mask;
|
return BLE_ERR_SUCCESS;
|
}
|
mask <<= 1;
|
}
|
|
return BLE_ERR_MEM_CAPACITY;
|
}
|
|
/**
|
* Remove a device from the hw whitelist
|
*
|
* @param addr
|
* @param addr_type
|
*
|
*/
|
void
|
ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type)
|
{
|
int i;
|
uint8_t cfg_addr;
|
uint16_t dap;
|
uint16_t txadd;
|
uint32_t dab;
|
uint32_t mask;
|
|
/* Find first ununsed device address match element */
|
dab = get_le32(addr);
|
dap = get_le16(addr + 4);
|
txadd = NRF_RADIO->DACNF >> 8;
|
mask = 0x01;
|
for (i = 0; i < BLE_HW_WHITE_LIST_SIZE; ++i) {
|
if (mask & g_ble_hw_whitelist_mask) {
|
if ((dab == NRF_RADIO->DAB[i]) && (dap == NRF_RADIO->DAP[i])) {
|
cfg_addr = txadd & mask;
|
if (addr_type == BLE_ADDR_RANDOM) {
|
if (cfg_addr != 0) {
|
break;
|
}
|
} else {
|
if (cfg_addr == 0) {
|
break;
|
}
|
}
|
}
|
}
|
mask <<= 1;
|
}
|
|
if (i < BLE_HW_WHITE_LIST_SIZE) {
|
g_ble_hw_whitelist_mask &= ~mask;
|
NRF_RADIO->DACNF &= ~mask;
|
}
|
}
|
|
/**
|
* Returns the size of the whitelist in HW
|
*
|
* @return int Number of devices allowed in whitelist
|
*/
|
uint8_t
|
ble_hw_whitelist_size(void)
|
{
|
return BLE_HW_WHITE_LIST_SIZE;
|
}
|
|
/**
|
* Enable the whitelisted devices
|
*/
|
void
|
ble_hw_whitelist_enable(void)
|
{
|
/* Enable the configured device addresses */
|
NRF_RADIO->DACNF |= g_ble_hw_whitelist_mask;
|
}
|
|
/**
|
* Disables the whitelisted devices
|
*/
|
void
|
ble_hw_whitelist_disable(void)
|
{
|
/* Disable all whitelist devices */
|
NRF_RADIO->DACNF &= 0x0000ff00;
|
}
|
|
/**
|
* Boolean function which returns true ('1') if there is a match on the
|
* whitelist.
|
*
|
* @return int
|
*/
|
int
|
ble_hw_whitelist_match(void)
|
{
|
return (int)NRF_RADIO->EVENTS_DEVMATCH;
|
}
|
|
/* Encrypt data */
|
int
|
ble_hw_encrypt_block(struct ble_encryption_block *ecb)
|
{
|
int rc;
|
uint32_t end;
|
uint32_t err;
|
|
/* Stop ECB */
|
nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STOPECB);
|
/* XXX: does task stop clear these counters? Anyway to do this quicker? */
|
NRF_ECB->EVENTS_ENDECB = 0;
|
NRF_ECB->EVENTS_ERRORECB = 0;
|
NRF_ECB->ECBDATAPTR = (uint32_t)ecb;
|
|
/* Start ECB */
|
nrf_ecb_task_trigger(NRF_ECB, NRF_ECB_TASK_STARTECB);
|
|
/* Wait till error or done */
|
rc = 0;
|
while (1) {
|
end = NRF_ECB->EVENTS_ENDECB;
|
err = NRF_ECB->EVENTS_ERRORECB;
|
if (end || err) {
|
if (err) {
|
rc = -1;
|
}
|
break;
|
}
|
#if BABBLESIM
|
tm_tick();
|
#endif
|
}
|
|
return rc;
|
}
|
|
/**
|
* Random number generator ISR.
|
*/
|
static void
|
ble_rng_isr(void)
|
{
|
uint8_t rnum;
|
|
os_trace_isr_enter();
|
|
/* No callback? Clear and disable interrupts */
|
if (g_ble_rng_isr_cb == NULL) {
|
nrf_rng_int_disable(NRF_RNG, NRF_RNG_INT_VALRDY_MASK);
|
NRF_RNG->EVENTS_VALRDY = 0;
|
(void)NRF_RNG->SHORTS;
|
os_trace_isr_exit();
|
return;
|
}
|
|
/* If there is a value ready grab it */
|
if (NRF_RNG->EVENTS_VALRDY) {
|
NRF_RNG->EVENTS_VALRDY = 0;
|
rnum = (uint8_t)NRF_RNG->VALUE;
|
(*g_ble_rng_isr_cb)(rnum);
|
}
|
|
os_trace_isr_exit();
|
}
|
|
/**
|
* Initialize the random number generator
|
*
|
* @param cb
|
* @param bias
|
*
|
* @return int
|
*/
|
int
|
ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias)
|
{
|
/* Set bias */
|
if (bias) {
|
NRF_RNG->CONFIG = 1;
|
} else {
|
NRF_RNG->CONFIG = 0;
|
}
|
|
/* If we were passed a function pointer we need to enable the interrupt */
|
if (cb != NULL) {
|
#ifndef RIOT_VERSION
|
NVIC_SetPriority(RNG_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
|
#endif
|
#if MYNEWT
|
NVIC_SetVector(RNG_IRQn, (uint32_t)ble_rng_isr);
|
#else
|
ble_npl_hw_set_isr(RNG_IRQn, ble_rng_isr);
|
#endif
|
NVIC_EnableIRQ(RNG_IRQn);
|
g_ble_rng_isr_cb = cb;
|
}
|
|
return 0;
|
}
|
|
/**
|
* Start the random number generator
|
*
|
* @return int
|
*/
|
int
|
ble_hw_rng_start(void)
|
{
|
os_sr_t sr;
|
|
/* No need for interrupt if there is no callback */
|
OS_ENTER_CRITICAL(sr);
|
NRF_RNG->EVENTS_VALRDY = 0;
|
|
if (g_ble_rng_isr_cb) {
|
nrf_rng_int_enable(NRF_RNG, NRF_RNG_INT_VALRDY_MASK);
|
}
|
nrf_rng_task_trigger(NRF_RNG, NRF_RNG_TASK_START);
|
OS_EXIT_CRITICAL(sr);
|
|
return 0;
|
}
|
|
/**
|
* Stop the random generator
|
*
|
* @return int
|
*/
|
int
|
ble_hw_rng_stop(void)
|
{
|
os_sr_t sr;
|
|
/* No need for interrupt if there is no callback */
|
OS_ENTER_CRITICAL(sr);
|
nrf_rng_int_disable(NRF_RNG, NRF_RNG_INT_VALRDY_MASK);
|
nrf_rng_task_trigger(NRF_RNG, NRF_RNG_TASK_STOP);
|
NRF_RNG->EVENTS_VALRDY = 0;
|
OS_EXIT_CRITICAL(sr);
|
|
return 0;
|
}
|
|
/**
|
* Read the random number generator.
|
*
|
* @return uint8_t
|
*/
|
uint8_t
|
ble_hw_rng_read(void)
|
{
|
uint8_t rnum;
|
|
/* Wait for a sample */
|
while (NRF_RNG->EVENTS_VALRDY == 0) {
|
}
|
|
NRF_RNG->EVENTS_VALRDY = 0;
|
rnum = (uint8_t)NRF_RNG->VALUE;
|
|
return rnum;
|
}
|
|
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY)
|
/**
|
* Clear the resolving list
|
*
|
* @return int
|
*/
|
void
|
ble_hw_resolv_list_clear(void)
|
{
|
g_nrf_num_irks = 0;
|
}
|
|
/**
|
* Add a device to the hw resolving list
|
*
|
* @param irk Pointer to IRK to add
|
*
|
* @return int 0: success, BLE error code otherwise
|
*/
|
int
|
ble_hw_resolv_list_add(uint8_t *irk)
|
{
|
uint32_t *nrf_entry;
|
|
/* Find first ununsed device address match element */
|
if (g_nrf_num_irks == NRF_IRK_LIST_ENTRIES) {
|
return BLE_ERR_MEM_CAPACITY;
|
}
|
|
/* Copy into irk list */
|
nrf_entry = &g_nrf_irk_list[4 * g_nrf_num_irks];
|
memcpy(nrf_entry, irk, 16);
|
|
/* Add to total */
|
++g_nrf_num_irks;
|
return BLE_ERR_SUCCESS;
|
}
|
|
/**
|
* Remove a device from the hw resolving list
|
*
|
* @param index Index of IRK to remove
|
*/
|
void
|
ble_hw_resolv_list_rmv(int index)
|
{
|
uint32_t *irk_entry;
|
|
if (index < g_nrf_num_irks) {
|
--g_nrf_num_irks;
|
irk_entry = &g_nrf_irk_list[index];
|
if (g_nrf_num_irks > index) {
|
memmove(irk_entry, irk_entry + 4, 16 * (g_nrf_num_irks - index));
|
}
|
}
|
}
|
|
/**
|
* Returns the size of the resolving list. NOTE: this returns the maximum
|
* allowable entries in the HW. Configuration options may limit this.
|
*
|
* @return int Number of devices allowed in resolving list
|
*/
|
uint8_t
|
ble_hw_resolv_list_size(void)
|
{
|
return BLE_HW_RESOLV_LIST_SIZE;
|
}
|
|
/**
|
* Called to determine if the address received was resolved.
|
*
|
* @return int Negative values indicate unresolved address; positive values
|
* indicate index in resolving list of resolved address.
|
*/
|
int
|
ble_hw_resolv_list_match(void)
|
{
|
if (NRF_AAR->ENABLE && NRF_AAR->EVENTS_END && NRF_AAR->EVENTS_RESOLVED) {
|
return (int)NRF_AAR->STATUS;
|
}
|
|
return -1;
|
}
|
#endif
|