/*
|
* 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.
|
*/
|
|
/* for jrand48 */
|
#define _XOPEN_SOURCE
|
#include <stdlib.h>
|
#include <stdint.h>
|
#include <assert.h>
|
#include <string.h>
|
#include "nimble_syscfg.h"
|
#include "os/os.h"
|
#include "nimble/ble.h"
|
#include "nimble/nimble_opt.h"
|
#include "controller/ble_hw.h"
|
#include "controller/ble_ll.h"
|
#if MYNEWT_VAL(TRNG)
|
#include "trng/trng.h"
|
#endif
|
|
#if BABBLESIM
|
extern void tm_tick(void);
|
#endif
|
|
#if MYNEWT_VAL(TRNG)
|
static struct trng_dev *g_trng;
|
#else
|
/* This is a simple circular buffer for holding N samples of random data */
|
struct ble_ll_rnum_data
|
{
|
uint8_t *rnd_in;
|
uint8_t *rnd_out;
|
volatile uint8_t rnd_size;
|
};
|
|
struct ble_ll_rnum_data g_ble_ll_rnum_data;
|
uint8_t g_ble_ll_rnum_buf[MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)];
|
|
#define IS_RNUM_BUF_END(x) \
|
(x == &g_ble_ll_rnum_buf[MYNEWT_VAL(BLE_LL_RNG_BUFSIZE) - 1])
|
|
void
|
ble_ll_rand_sample(uint8_t rnum)
|
{
|
os_sr_t sr;
|
|
OS_ENTER_CRITICAL(sr);
|
if (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)) {
|
++g_ble_ll_rnum_data.rnd_size;
|
g_ble_ll_rnum_data.rnd_in[0] = rnum;
|
if (IS_RNUM_BUF_END(g_ble_ll_rnum_data.rnd_in)) {
|
g_ble_ll_rnum_data.rnd_in = g_ble_ll_rnum_buf;
|
} else {
|
++g_ble_ll_rnum_data.rnd_in;
|
}
|
} else {
|
/* Stop generating random numbers as we are full */
|
ble_hw_rng_stop();
|
}
|
OS_EXIT_CRITICAL(sr);
|
}
|
#endif
|
|
/* Get 'len' bytes of random data */
|
int
|
ble_ll_rand_data_get(uint8_t *buf, uint8_t len)
|
{
|
#if MYNEWT_VAL(TRNG)
|
size_t num;
|
|
while (len) {
|
num = trng_read(g_trng, buf, len);
|
buf += num;
|
len -= num;
|
}
|
#else
|
uint8_t rnums;
|
os_sr_t sr;
|
|
while (len != 0) {
|
OS_ENTER_CRITICAL(sr);
|
rnums = g_ble_ll_rnum_data.rnd_size;
|
if (rnums > len) {
|
rnums = len;
|
}
|
len -= rnums;
|
g_ble_ll_rnum_data.rnd_size -= rnums;
|
while (rnums) {
|
buf[0] = g_ble_ll_rnum_data.rnd_out[0];
|
if (IS_RNUM_BUF_END(g_ble_ll_rnum_data.rnd_out)) {
|
g_ble_ll_rnum_data.rnd_out = g_ble_ll_rnum_buf;
|
} else {
|
++g_ble_ll_rnum_data.rnd_out;
|
}
|
++buf;
|
--rnums;
|
}
|
OS_EXIT_CRITICAL(sr);
|
|
/* Make sure rng is started! */
|
ble_hw_rng_start();
|
|
/* Wait till bytes are in buffer. */
|
if (len) {
|
while ((g_ble_ll_rnum_data.rnd_size < len) &&
|
(g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE))) {
|
/* Spin here */
|
#if BABBLESIM
|
tm_tick();
|
#endif
|
}
|
}
|
}
|
#endif
|
return BLE_ERR_SUCCESS;
|
}
|
|
/* Simple wrapper to allow easy replacement of rand() */
|
uint32_t
|
ble_ll_rand(void)
|
{
|
static unsigned short xsubi[3];
|
static bool init = true;
|
|
if (init) {
|
init = false;
|
ble_ll_rand_data_get((uint8_t *)xsubi, sizeof(xsubi));
|
}
|
|
return (uint32_t) jrand48(xsubi);
|
}
|
|
/**
|
* Called to obtain a "prand" as defined in core V4.2 Vol 6 Part B 1.3.2.2
|
*
|
* @param prand
|
*/
|
void
|
ble_ll_rand_prand_get(uint8_t *prand)
|
{
|
uint16_t sum;
|
|
while (1) {
|
/* Get 24 bits of random data */
|
ble_ll_rand_data_get(prand, 3);
|
|
/* Prand cannot be all zeros or 1's. */
|
sum = prand[0] + prand[1] + prand[2];
|
if ((sum != 0) && (sum != (3 * 0xff))) {
|
break;
|
}
|
}
|
|
/* Upper two bits must be 01 */
|
prand[2] &= ~0xc0;
|
prand[2] |= 0x40;
|
}
|
|
/**
|
* Start the generation of random numbers
|
*
|
* @return int
|
*/
|
int
|
ble_ll_rand_start(void)
|
{
|
#if MYNEWT_VAL(TRNG)
|
/* Nothing to do - this is handled by driver */
|
#else
|
/* Start the generation of numbers if we are not full */
|
if (g_ble_ll_rnum_data.rnd_size < MYNEWT_VAL(BLE_LL_RNG_BUFSIZE)) {
|
ble_hw_rng_start();
|
}
|
#endif
|
return 0;
|
}
|
|
/**
|
* Initialize LL random number generation. Should be called only once on
|
* initialization.
|
*
|
* @return int
|
*/
|
int
|
ble_ll_rand_init(void)
|
{
|
#if MYNEWT_VAL(TRNG)
|
g_trng = (struct trng_dev *) os_dev_open("trng", OS_TIMEOUT_NEVER, NULL);
|
#else
|
g_ble_ll_rnum_data.rnd_in = g_ble_ll_rnum_buf;
|
g_ble_ll_rnum_data.rnd_out = g_ble_ll_rnum_buf;
|
ble_hw_rng_init(ble_ll_rand_sample, 1);
|
#endif
|
return 0;
|
}
|