Optimised the get() function in nRF5 hal rand implementation to reduce number of probable branching operations. This is needed to reduce nRF51 platform's CPU use in radio ISR when using the fast encryption setup implementation. Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
209 lines
3.2 KiB
C
209 lines
3.2 KiB
C
/*
|
|
* Copyright (c) 2016 Nordic Semiconductor ASA
|
|
* Copyright (c) 2016 Vinayak Kariappa Chettimada
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <soc.h>
|
|
|
|
#include "hal/rand.h"
|
|
|
|
#include "common/log.h"
|
|
#include "hal/debug.h"
|
|
|
|
struct rand {
|
|
u8_t count;
|
|
u8_t threshold;
|
|
u8_t first;
|
|
u8_t last;
|
|
u8_t rand[1];
|
|
};
|
|
|
|
static struct rand *rng_isr;
|
|
static struct rand *rng_thr;
|
|
|
|
static void init(struct rand **rng, u8_t *context, u8_t len, u8_t threshold)
|
|
{
|
|
struct rand *p;
|
|
|
|
LL_ASSERT(len > (offsetof(struct rand, rand) + threshold));
|
|
|
|
*rng = (struct rand *)context;
|
|
|
|
p = *rng;
|
|
p->count = len - offsetof(struct rand, rand);
|
|
p->threshold = threshold;
|
|
p->first = p->last = 0;
|
|
|
|
if (!rng_isr || !rng_thr) {
|
|
NRF_RNG->CONFIG = RNG_CONFIG_DERCEN_Msk;
|
|
NRF_RNG->EVENTS_VALRDY = 0;
|
|
NRF_RNG->INTENSET = RNG_INTENSET_VALRDY_Msk;
|
|
|
|
NRF_RNG->TASKS_START = 1;
|
|
}
|
|
}
|
|
|
|
void rand_init(u8_t *context, u8_t context_len, u8_t threshold)
|
|
{
|
|
init(&rng_thr, context, context_len, threshold);
|
|
}
|
|
|
|
void rand_isr_init(u8_t *context, u8_t context_len, u8_t threshold)
|
|
{
|
|
init(&rng_isr, context, context_len, threshold);
|
|
}
|
|
|
|
static size_t get(struct rand *rng, size_t octets, u8_t *rand)
|
|
{
|
|
u8_t first, last, remaining;
|
|
|
|
LL_ASSERT(rng);
|
|
|
|
first = rng->first;
|
|
last = rng->last;
|
|
|
|
if (first <= last) {
|
|
u8_t *d, *s;
|
|
u8_t avail;
|
|
|
|
d = &rand[octets];
|
|
s = &rng->rand[first];
|
|
|
|
avail = last - first;
|
|
if (octets < avail) {
|
|
remaining = avail - octets;
|
|
avail = octets;
|
|
} else {
|
|
remaining = 0;
|
|
}
|
|
|
|
first += avail;
|
|
octets -= avail;
|
|
|
|
while (avail--) {
|
|
*(--d) = *s++;
|
|
}
|
|
|
|
rng->first = first;
|
|
} else {
|
|
u8_t *d, *s;
|
|
u8_t avail;
|
|
|
|
d = &rand[octets];
|
|
s = &rng->rand[first];
|
|
|
|
avail = rng->count - first;
|
|
if (octets < avail) {
|
|
remaining = avail + last - octets;
|
|
avail = octets;
|
|
first += avail;
|
|
} else {
|
|
remaining = last;
|
|
first = 0;
|
|
}
|
|
|
|
octets -= avail;
|
|
|
|
while (avail--) {
|
|
*(--d) = *s++;
|
|
}
|
|
|
|
if (octets && last) {
|
|
s = &rng->rand[0];
|
|
|
|
if (octets < last) {
|
|
remaining = last - octets;
|
|
last = octets;
|
|
} else {
|
|
remaining = 0;
|
|
}
|
|
|
|
first = last;
|
|
octets -= last;
|
|
|
|
while (last--) {
|
|
*(--d) = *s++;
|
|
}
|
|
}
|
|
|
|
rng->first = first;
|
|
}
|
|
|
|
if (remaining < rng->threshold) {
|
|
NRF_RNG->TASKS_START = 1;
|
|
}
|
|
|
|
return octets;
|
|
}
|
|
|
|
size_t rand_get(size_t octets, u8_t *rand)
|
|
{
|
|
return get(rng_thr, octets, rand);
|
|
}
|
|
|
|
size_t rand_isr_get(size_t octets, u8_t *rand)
|
|
{
|
|
return get(rng_isr, octets, rand);
|
|
}
|
|
|
|
static int isr(struct rand *rng, bool store)
|
|
{
|
|
u8_t last;
|
|
|
|
if (!rng) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
last = rng->last + 1;
|
|
if (last == rng->count) {
|
|
last = 0;
|
|
}
|
|
|
|
if (last == rng->first) {
|
|
/* this condition should not happen, but due to probable race,
|
|
* new value could be generated before NRF_RNG task is stopped.
|
|
*/
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
if (!store) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
rng->rand[rng->last] = NRF_RNG->VALUE;
|
|
rng->last = last;
|
|
|
|
last = rng->last + 1;
|
|
if (last == rng->count) {
|
|
last = 0;
|
|
}
|
|
|
|
if (last == rng->first) {
|
|
return 0;
|
|
}
|
|
|
|
return -EBUSY;
|
|
}
|
|
|
|
void isr_rand(void *param)
|
|
{
|
|
ARG_UNUSED(param);
|
|
|
|
if (NRF_RNG->EVENTS_VALRDY) {
|
|
int ret;
|
|
|
|
ret = isr(rng_isr, true);
|
|
if (ret != -EBUSY) {
|
|
ret = isr(rng_thr, (ret == -ENOBUFS));
|
|
}
|
|
|
|
NRF_RNG->EVENTS_VALRDY = 0;
|
|
|
|
if (ret != -EBUSY) {
|
|
NRF_RNG->TASKS_STOP = 1;
|
|
}
|
|
}
|
|
}
|