dvb_ci_runtime/event.rs
1//! The sans-IO event/action model.
2//!
3//! The protocol core is pure: it consumes [`Event`]s and produces [`Action`]s,
4//! with no device, threads, or clock of its own. The driver loop executes the
5//! actions against a [`CaDevice`](crate::CaDevice) and feeds events back. This
6//! keeps every state machine deterministic and testable without
7//! hardware — a test (or a differential comparison against an external
8//! reference) drives a sequence of events and asserts the emitted action
9//! sequence.
10
11use std::time::Duration;
12
13use dvb_ci::resource::ResourceId;
14
15/// An input to the protocol core.
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum Event<'a> {
19 /// One link-layer frame was read from the device.
20 Readable(&'a [u8]),
21 /// Logical time advanced by `elapsed` since the last tick (drives poll
22 /// cadence and resource timers without a real clock in the core).
23 Tick {
24 /// Time since the previous tick.
25 elapsed: Duration,
26 },
27 /// A request from the host application.
28 Host(HostRequest<'a>),
29}
30
31/// A request the host application makes of the stack.
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[non_exhaustive]
34pub enum HostRequest<'a> {
35 /// Bring the interface up: reset the slot and open the transport connection.
36 Init,
37 /// Send a serialized `ca_pmt` APDU body to the CAM's conditional-access
38 /// resource (descrambling request).
39 SendCaPmt(&'a [u8]),
40 /// Tear the interface down (close sessions + transport connection).
41 Shutdown,
42}
43
44/// An output the driver loop must perform.
45#[derive(Debug, Clone, PartialEq, Eq)]
46#[non_exhaustive]
47pub enum Action {
48 /// Write one link-layer frame to the device.
49 Write(Vec<u8>),
50 /// Issue the `CA_RESET` ioctl.
51 Reset,
52 /// Issue the `CA_GET_SLOT_INFO` ioctl.
53 QuerySlot,
54 /// Arm the poll/timer to fire after `after` (coalesced: the latest wins).
55 SetTimer {
56 /// Delay before the next [`Event::Tick`] should be delivered.
57 after: Duration,
58 },
59 /// Surface a host-facing [`Notification`].
60 Notify(Notification),
61}
62
63/// A host-facing event surfaced by the stack (the useful outputs of a CI
64/// session — what an application reacts to).
65#[derive(Debug, Clone, PartialEq, Eq)]
66#[non_exhaustive]
67pub enum Notification {
68 /// The module is present and the resource-manager handshake completed.
69 CamReady,
70 /// `application_information` was received.
71 ApplicationInfo {
72 /// `application_type` (0x01 = CA).
73 application_type: u8,
74 /// `application_manufacturer`.
75 manufacturer: u16,
76 /// `manufacturer_code`.
77 code: u16,
78 /// The decoded menu string.
79 menu: String,
80 },
81 /// `ca_info` was received — the CA system ids the module supports.
82 CaInfo {
83 /// `CA_system_id` values the CAM can descramble.
84 ca_system_ids: Vec<u16>,
85 },
86 /// A `ca_pmt_reply` was received for a prior CA_PMT.
87 CaPmtReply {
88 /// `program_number` the reply pertains to.
89 program_number: u16,
90 /// Raw `CA_enable`/descrambling-possibility bytes (per-program/ES).
91 descrambling_ok: bool,
92 },
93 /// An MMI menu/enquiry the host should display.
94 Mmi(MmiEvent),
95 /// A session for `resource` was opened.
96 SessionOpened {
97 /// The resource the session serves.
98 resource: ResourceId,
99 },
100 /// A session closed.
101 SessionClosed {
102 /// The `session_nb` that closed.
103 session_nb: u16,
104 },
105 /// A protocol error surfaced by the stack (non-fatal; informational).
106 Error {
107 /// Human-readable detail.
108 detail: String,
109 },
110}
111
112/// MMI (man-machine interface) host events.
113#[derive(Debug, Clone, PartialEq, Eq)]
114#[non_exhaustive]
115pub enum MmiEvent {
116 /// A `menu`/`list` to display: title + items.
117 Menu {
118 /// Menu title text.
119 title: String,
120 /// Selectable item texts.
121 items: Vec<String>,
122 },
123 /// An `enquiry` (text prompt) expecting an answer.
124 Enquiry {
125 /// Prompt text.
126 prompt: String,
127 /// Whether the answer should be hidden (e.g. PIN).
128 blind: bool,
129 /// Expected answer length.
130 answer_len: u8,
131 },
132 /// The module closed the MMI dialog.
133 Close,
134}