use core::cell::Cell;
use kernel::collections::list::{List, ListLink, ListNode};
use kernel::hil::rng::{Client, Continue, Rng};
use kernel::utilities::cells::OptionalCell;
use kernel::ErrorCode;
#[derive(Copy, Clone, PartialEq)]
enum Op {
Idle,
Get,
}
pub struct MuxRngMaster<'a> {
rng: &'a dyn Rng<'a>,
devices: List<'a, VirtualRngMasterDevice<'a>>,
inflight: OptionalCell<&'a VirtualRngMasterDevice<'a>>,
}
impl<'a> MuxRngMaster<'a> {
pub const fn new(rng: &'a dyn Rng<'a>) -> MuxRngMaster<'a> {
MuxRngMaster {
rng,
devices: List::new(),
inflight: OptionalCell::empty(),
}
}
fn do_next_op(&self) -> Result<(), ErrorCode> {
if self.inflight.is_none() {
let mnode = self
.devices
.iter()
.find(|node| node.operation.get() != Op::Idle);
let return_code = mnode.map(|node| {
let op = node.operation.get();
let operation_code = match op {
Op::Get => {
let success_code = self.rng.get();
if success_code == Ok(()) {
self.inflight.set(node);
}
success_code
}
Op::Idle => unreachable!("Attempted to run idle operation in virtual_rng!"), };
node.operation.set(Op::Idle);
operation_code
});
if let Some(r) = return_code {
r
} else {
Err(ErrorCode::FAIL)
}
} else {
Ok(())
}
}
}
impl<'a> Client for MuxRngMaster<'a> {
fn randomness_available(
&self,
_randomness: &mut dyn Iterator<Item = u32>,
_error: Result<(), ErrorCode>,
) -> Continue {
self.inflight.take().map_or(Continue::Done, |device| {
let cont_code = device.randomness_available(_randomness, _error);
if cont_code == Continue::Done {
let _ = self.do_next_op();
}
cont_code
})
}
}
pub struct VirtualRngMasterDevice<'a> {
mux: &'a MuxRngMaster<'a>,
next: ListLink<'a, VirtualRngMasterDevice<'a>>,
client: OptionalCell<&'a dyn Client>,
operation: Cell<Op>,
}
impl<'a> ListNode<'a, VirtualRngMasterDevice<'a>> for VirtualRngMasterDevice<'a> {
fn next(&self) -> &'a ListLink<VirtualRngMasterDevice<'a>> {
&self.next
}
}
impl<'a> VirtualRngMasterDevice<'a> {
pub const fn new(mux: &'a MuxRngMaster<'a>) -> VirtualRngMasterDevice<'a> {
VirtualRngMasterDevice {
mux,
next: ListLink::empty(),
client: OptionalCell::empty(),
operation: Cell::new(Op::Idle),
}
}
}
impl<'a> PartialEq<VirtualRngMasterDevice<'a>> for VirtualRngMasterDevice<'a> {
fn eq(&self, other: &VirtualRngMasterDevice<'a>) -> bool {
core::ptr::eq(self, other)
}
}
impl<'a> Rng<'a> for VirtualRngMasterDevice<'a> {
fn get(&self) -> Result<(), ErrorCode> {
self.operation.set(Op::Get);
self.mux.do_next_op()
}
fn cancel(&self) -> Result<(), ErrorCode> {
self.operation.set(Op::Idle);
self.mux.inflight.map_or_else(
|| {
Ok(())
},
|current_node| {
if current_node == self {
self.mux.rng.cancel()
} else {
Ok(())
}
},
)
}
fn set_client(&'a self, client: &'a dyn Client) {
self.mux.devices.push_head(self);
self.client.set(client);
self.mux.rng.set_client(self.mux);
}
}
impl<'a> Client for VirtualRngMasterDevice<'a> {
fn randomness_available(
&self,
randomness: &mut dyn Iterator<Item = u32>,
error: Result<(), ErrorCode>,
) -> Continue {
self.client.map_or(Continue::Done, move |client| {
client.randomness_available(randomness, error)
})
}
}