1use std::io;
7use std::time::Duration;
8
9use crate::device::CaDevice;
10use crate::event::{Action, Event, HostRequest, Notification};
11use crate::stack::CiStack;
12
13pub struct Driver<D: CaDevice> {
15 device: D,
16 stack: CiStack,
17 notifications: Vec<Notification>,
18 next_timer: Option<Duration>,
20 buf: Vec<u8>,
22}
23
24impl<D: CaDevice> Driver<D> {
25 #[must_use]
27 pub fn new(device: D) -> Self {
28 Self {
29 device,
30 stack: CiStack::new(),
31 notifications: Vec::new(),
32 next_timer: None,
33 buf: vec![0u8; 4096],
34 }
35 }
36
37 pub fn device(&self) -> &D {
39 &self.device
40 }
41
42 pub fn next_timer(&self) -> Option<Duration> {
44 self.next_timer
45 }
46
47 pub fn take_notifications(&mut self) -> Vec<Notification> {
49 core::mem::take(&mut self.notifications)
50 }
51
52 pub fn init(&mut self) -> io::Result<()> {
54 let actions = self.stack.handle(Event::Host(HostRequest::Init));
55 self.run(actions)
56 }
57
58 pub fn send_ca_pmt(&mut self, ca_pmt: &[u8]) -> io::Result<()> {
61 let actions = self
62 .stack
63 .handle(Event::Host(HostRequest::SendCaPmt(ca_pmt)));
64 self.run(actions)
65 }
66
67 pub fn descramble(&mut self, pmt_section: &[u8]) -> io::Result<()> {
73 let actions = self
74 .stack
75 .handle(Event::Host(HostRequest::Descramble(pmt_section)));
76 self.run(actions)
77 }
78
79 pub fn descramble_programs(&mut self, pmt_sections: &[&[u8]]) -> io::Result<()> {
82 let actions = self
83 .stack
84 .handle(Event::Host(HostRequest::DescramblePrograms(pmt_sections)));
85 self.run(actions)
86 }
87
88 pub fn add_program(&mut self, pmt_section: &[u8]) -> io::Result<()> {
91 let actions = self
92 .stack
93 .handle(Event::Host(HostRequest::AddProgram(pmt_section)));
94 self.run(actions)
95 }
96
97 pub fn remove_program(&mut self, pmt_section: &[u8]) -> io::Result<()> {
100 let actions = self
101 .stack
102 .handle(Event::Host(HostRequest::RemoveProgram(pmt_section)));
103 self.run(actions)
104 }
105
106 pub fn mmi_menu_answer(&mut self, choice_ref: u8) -> io::Result<()> {
108 let actions = self
109 .stack
110 .handle(Event::Host(HostRequest::MmiMenuAnswer(choice_ref)));
111 self.run(actions)
112 }
113
114 pub fn mmi_enquiry_answer(&mut self, text: &[u8]) -> io::Result<()> {
116 let actions = self
117 .stack
118 .handle(Event::Host(HostRequest::MmiEnquiryAnswer(text)));
119 self.run(actions)
120 }
121
122 pub fn mmi_cancel(&mut self) -> io::Result<()> {
124 let actions = self.stack.handle(Event::Host(HostRequest::MmiCancel));
125 self.run(actions)
126 }
127
128 pub fn enter_menu(&mut self) -> io::Result<()> {
131 let actions = self.stack.handle(Event::Host(HostRequest::EnterMenu));
132 self.run(actions)
133 }
134
135 pub fn pump(&mut self, timeout: Duration) -> io::Result<bool> {
139 if self.device.poll(timeout)? {
140 let n = self.device.read(&mut self.buf)?;
141 if n > 0 {
142 let frame = self.buf[..n].to_vec();
143 let actions = self.stack.handle(Event::Readable(&frame));
144 self.run(actions)?;
145 return Ok(true);
146 }
147 }
148 let actions = self.stack.handle(Event::Tick { elapsed: timeout });
149 self.run(actions)?;
150 Ok(false)
151 }
152
153 fn run(&mut self, actions: Vec<Action>) -> io::Result<()> {
155 for action in actions {
156 match action {
157 Action::Write(bytes) => self.device.write(&bytes)?,
158 Action::Reset => self.device.reset()?,
159 Action::QuerySlot => {
160 self.device.slot_info()?;
161 }
162 Action::SetTimer { after } => self.next_timer = Some(after),
163 Action::Notify(n) => self.notifications.push(n),
164 }
165 }
166 Ok(())
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate::device::{DeviceOp, MockCaDevice};
174 use dvb_ci::tpdu::tags;
175
176 #[test]
177 fn init_drives_reset_slotinfo_and_create_tc_to_device() {
178 let mut d = Driver::new(MockCaDevice::new([]));
179 d.init().unwrap();
180 let ops = &d.device().ops;
181 assert_eq!(ops[0], DeviceOp::Reset);
182 assert_eq!(ops[1], DeviceOp::SlotInfo);
183 assert!(matches!(&ops[2], DeviceOp::Write(w) if w[0] == tags::CREATE_T_C));
184 }
185
186 #[test]
187 fn reads_reply_then_polls_on_pump() {
188 let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
190 let mut d = Driver::new(dev);
191 d.init().unwrap();
192 assert!(d.pump(Duration::from_millis(100)).unwrap());
194 assert!(!d.pump(Duration::from_millis(100)).unwrap());
196 let last = d.device().ops.last().unwrap();
197 assert!(matches!(last, DeviceOp::Write(w) if w.first() == Some(&tags::DATA_LAST)));
198 }
199}