nrf/rng: expose all functionality as inherent methods.
This commit is contained in:
parent
df00c83984
commit
6eec3d8acc
@ -1,5 +1,3 @@
|
||||
use core::convert::Infallible;
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomData;
|
||||
use core::ptr;
|
||||
use core::sync::atomic::AtomicPtr;
|
||||
@ -7,13 +5,11 @@ use core::sync::atomic::Ordering;
|
||||
use core::task::Poll;
|
||||
|
||||
use embassy::interrupt::InterruptExt;
|
||||
use embassy::traits;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy::waitqueue::AtomicWaker;
|
||||
use embassy_hal_common::drop::OnDrop;
|
||||
use embassy_hal_common::unborrow;
|
||||
use futures::future::poll_fn;
|
||||
use rand_core::RngCore;
|
||||
|
||||
use crate::interrupt;
|
||||
use crate::pac;
|
||||
@ -39,7 +35,7 @@ struct State {
|
||||
|
||||
/// A wrapper around an nRF RNG peripheral.
|
||||
///
|
||||
/// It has a non-blocking API, through `embassy::traits::Rng`, and a blocking api through `rand`.
|
||||
/// It has a non-blocking API, and a blocking api through `rand`.
|
||||
pub struct Rng<'d> {
|
||||
irq: interrupt::RNG,
|
||||
phantom: PhantomData<(&'d mut RNG, &'d mut interrupt::RNG)>,
|
||||
@ -146,72 +142,51 @@ impl<'d> Rng<'d> {
|
||||
pub fn bias_correction(&self, enable: bool) {
|
||||
RNG::regs().config.write(|w| w.dercen().bit(enable))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for Rng<'d> {
|
||||
fn drop(&mut self) {
|
||||
self.irq.disable()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> traits::rng::Rng for Rng<'d> {
|
||||
type Error = Infallible;
|
||||
|
||||
type RngFuture<'a>
|
||||
where
|
||||
'd: 'a,
|
||||
= impl Future<Output = Result<(), Self::Error>> + 'a;
|
||||
|
||||
fn fill_bytes<'a>(&'a mut self, dest: &'a mut [u8]) -> Self::RngFuture<'a> {
|
||||
async move {
|
||||
if dest.len() == 0 {
|
||||
return Ok(()); // Nothing to fill
|
||||
}
|
||||
|
||||
let range = dest.as_mut_ptr_range();
|
||||
// Even if we've preempted the interrupt, it can't preempt us again,
|
||||
// so we don't need to worry about the order we write these in.
|
||||
STATE.ptr.store(range.start, Ordering::Relaxed);
|
||||
STATE.end.store(range.end, Ordering::Relaxed);
|
||||
|
||||
self.enable_irq();
|
||||
self.start();
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
self.stop();
|
||||
self.disable_irq();
|
||||
|
||||
// The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here.
|
||||
STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed);
|
||||
STATE.end.store(ptr::null_mut(), Ordering::Relaxed);
|
||||
});
|
||||
|
||||
poll_fn(|cx| {
|
||||
STATE.waker.register(cx.waker());
|
||||
|
||||
// The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`.
|
||||
let end = STATE.end.load(Ordering::Relaxed);
|
||||
let ptr = STATE.ptr.load(Ordering::Relaxed);
|
||||
|
||||
if ptr == end {
|
||||
// We're done.
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Trigger the teardown
|
||||
drop(on_drop);
|
||||
|
||||
Ok(())
|
||||
pub async fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
if dest.len() == 0 {
|
||||
return; // Nothing to fill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> RngCore for Rng<'d> {
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
let range = dest.as_mut_ptr_range();
|
||||
// Even if we've preempted the interrupt, it can't preempt us again,
|
||||
// so we don't need to worry about the order we write these in.
|
||||
STATE.ptr.store(range.start, Ordering::Relaxed);
|
||||
STATE.end.store(range.end, Ordering::Relaxed);
|
||||
|
||||
self.enable_irq();
|
||||
self.start();
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
self.stop();
|
||||
self.disable_irq();
|
||||
|
||||
// The interrupt is now disabled and can't preempt us anymore, so the order doesn't matter here.
|
||||
STATE.ptr.store(ptr::null_mut(), Ordering::Relaxed);
|
||||
STATE.end.store(ptr::null_mut(), Ordering::Relaxed);
|
||||
});
|
||||
|
||||
poll_fn(|cx| {
|
||||
STATE.waker.register(cx.waker());
|
||||
|
||||
// The interrupt will never modify `end`, so load it first and then get the most up-to-date `ptr`.
|
||||
let end = STATE.end.load(Ordering::Relaxed);
|
||||
let ptr = STATE.ptr.load(Ordering::Relaxed);
|
||||
|
||||
if ptr == end {
|
||||
// We're done.
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
// Trigger the teardown
|
||||
drop(on_drop);
|
||||
}
|
||||
|
||||
pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.start();
|
||||
|
||||
for byte in dest.iter_mut() {
|
||||
@ -223,24 +198,36 @@ impl<'d> RngCore for Rng<'d> {
|
||||
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> Drop for Rng<'d> {
|
||||
fn drop(&mut self) {
|
||||
self.irq.disable()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> rand_core::RngCore for Rng<'d> {
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
self.blocking_fill_bytes(dest);
|
||||
}
|
||||
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
let mut bytes = [0; 4];
|
||||
self.fill_bytes(&mut bytes);
|
||||
self.blocking_fill_bytes(&mut bytes);
|
||||
// We don't care about the endianness, so just use the native one.
|
||||
u32::from_ne_bytes(bytes)
|
||||
}
|
||||
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
let mut bytes = [0; 8];
|
||||
self.fill_bytes(&mut bytes);
|
||||
self.blocking_fill_bytes(&mut bytes);
|
||||
u64::from_ne_bytes(bytes)
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||
self.fill_bytes(dest);
|
||||
self.blocking_fill_bytes(dest);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Should `Rng` implement `CryptoRng`? It's 'suitable for cryptographic purposes' according to the specification.
|
||||
impl<'d> rand_core::CryptoRng for Rng<'d> {}
|
||||
|
@ -5,9 +5,7 @@
|
||||
#[path = "../example_common.rs"]
|
||||
mod example_common;
|
||||
|
||||
use defmt::unwrap;
|
||||
use embassy::executor::Spawner;
|
||||
use embassy::traits::rng::Rng as _;
|
||||
use embassy_nrf::interrupt;
|
||||
use embassy_nrf::rng::Rng;
|
||||
use embassy_nrf::Peripherals;
|
||||
@ -19,14 +17,14 @@ async fn main(_spawner: Spawner, p: Peripherals) {
|
||||
|
||||
// Async API
|
||||
let mut bytes = [0; 4];
|
||||
unwrap!(rng.fill_bytes(&mut bytes).await); // nRF RNG is infallible
|
||||
rng.fill_bytes(&mut bytes).await;
|
||||
defmt::info!("Some random bytes: {:?}", bytes);
|
||||
|
||||
// Sync API with `rand`
|
||||
defmt::info!("A random number from 1 to 10: {:?}", rng.gen_range(1..=10));
|
||||
|
||||
let mut bytes = [0; 1024];
|
||||
unwrap!(rng.fill_bytes(&mut bytes).await);
|
||||
rng.fill_bytes(&mut bytes).await;
|
||||
let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros());
|
||||
let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones());
|
||||
defmt::info!(
|
||||
|
Loading…
Reference in New Issue
Block a user