use std::io;
use std::time::Duration;
use crate::device::CaDevice;
use crate::event::{Action, Event, HostRequest, Notification};
use crate::stack::CiStack;
pub struct Driver<D: CaDevice> {
device: D,
stack: CiStack,
notifications: Vec<Notification>,
next_timer: Option<Duration>,
buf: Vec<u8>,
}
impl<D: CaDevice> Driver<D> {
#[must_use]
pub fn new(device: D) -> Self {
Self {
device,
stack: CiStack::new(),
notifications: Vec::new(),
next_timer: None,
buf: vec![0u8; 4096],
}
}
pub fn device(&self) -> &D {
&self.device
}
pub fn next_timer(&self) -> Option<Duration> {
self.next_timer
}
pub fn take_notifications(&mut self) -> Vec<Notification> {
core::mem::take(&mut self.notifications)
}
pub fn init(&mut self) -> io::Result<()> {
let actions = self.stack.handle(Event::Host(HostRequest::Init));
self.run(actions)
}
pub fn send_ca_pmt(&mut self, ca_pmt: &[u8]) -> io::Result<()> {
let actions = self
.stack
.handle(Event::Host(HostRequest::SendCaPmt(ca_pmt)));
self.run(actions)
}
pub fn pump(&mut self, timeout: Duration) -> io::Result<bool> {
if self.device.poll(timeout)? {
let n = self.device.read(&mut self.buf)?;
if n > 0 {
let frame = self.buf[..n].to_vec();
let actions = self.stack.handle(Event::Readable(&frame));
self.run(actions)?;
return Ok(true);
}
}
let actions = self.stack.handle(Event::Tick { elapsed: timeout });
self.run(actions)?;
Ok(false)
}
fn run(&mut self, actions: Vec<Action>) -> io::Result<()> {
for action in actions {
match action {
Action::Write(bytes) => self.device.write(&bytes)?,
Action::Reset => self.device.reset()?,
Action::QuerySlot => {
self.device.slot_info()?;
}
Action::SetTimer { after } => self.next_timer = Some(after),
Action::Notify(n) => self.notifications.push(n),
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::device::{DeviceOp, MockCaDevice};
use dvb_ci::tpdu::tags;
#[test]
fn init_drives_reset_slotinfo_and_create_tc_to_device() {
let mut d = Driver::new(MockCaDevice::new([]));
d.init().unwrap();
let ops = &d.device().ops;
assert_eq!(ops[0], DeviceOp::Reset);
assert_eq!(ops[1], DeviceOp::SlotInfo);
assert!(matches!(&ops[2], DeviceOp::Write(w) if w[0] == tags::CREATE_T_C));
}
#[test]
fn reads_reply_then_polls_on_pump() {
let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
let mut d = Driver::new(dev);
d.init().unwrap();
assert!(d.pump(Duration::from_millis(100)).unwrap());
assert!(!d.pump(Duration::from_millis(100)).unwrap());
let last = d.device().ops.last().unwrap();
assert!(matches!(last, DeviceOp::Write(w) if w.first() == Some(&tags::DATA_LAST)));
}
}