dvb_ci_runtime/lib.rs
1//! Pure-Rust EN 50221 DVB Common Interface **runtime** — the driver loop over
2//! the [`dvb_ci`] codecs.
3//!
4//! [`dvb_ci`] is `no_std` and owns the *wire* layer (TPDU / SPDU / APDU
5//! parse+serialize, CA_PMT building, CI Plus extensions). This crate adds the
6//! *runtime*: device I/O, the TPDU poll loop, SPDU session management, and the
7//! per-resource state machines that together drive a physical CAM, per
8//! ETSI EN 50221 and TS 101 699.
9//!
10//! # Design
11//!
12//! The whole runtime is written against the [`CaDevice`] trait (the
13//! hardware-abstraction boundary), so it runs against either:
14//! - a real Linux CA device (`/dev/dvb/adapterN/caM`, the `linux` feature), or
15//! - the in-memory [`MockCaDevice`], which makes the state machines testable
16//! without hardware and enables differential testing against an external
17//! reference (drive both with the same scripted mock CAM and assert the
18//! emitted `write`/ioctl byte sequences match).
19//!
20//! Implemented from the EN 50221 specification.
21//!
22//! # What's implemented
23//!
24//! - **Transport** (TPDU, §A.4): `Create_T_C` handshake, empty-`T_Data_Last`
25//! poll cadence, `T_SB` data-available → `T_RCV`, `T_Data_More/Last`
26//! reassembly, reply timeout.
27//! - **Session** (SPDU, §7.2): session table; `open_session_request`/response,
28//! host-initiated `create_session`, `close_session`; `session_number` + APDU
29//! routing.
30//! - **Resources** (§8): Resource Manager handshake (profile exchange →
31//! [`Notification::CamReady`], then opens module resources),
32//! application_information, conditional_access (`ca_pmt`/`ca_pmt_reply`),
33//! date_time (MJD + BCD), and mmi (surfaces module menus/enquiries as
34//! [`Notification::Mmi`]).
35//! - **Devices**: the in-memory [`MockCaDevice`], and a Linux
36//! `/dev/dvb/adapterN/caM` `CaDevice` behind the `linux` feature.
37//!
38//! Roadmap: the `host_control` resource, MMI answering (`menu_answ`/`answ`), and
39//! a differential test harness against an external reference.
40//!
41//! # Example
42//!
43//! Drive a CAM with the [`MockCaDevice`] — the same `Driver` API works against
44//! the real Linux device:
45//!
46//! ```
47//! use std::time::Duration;
48//! use dvb_ci_runtime::{Driver, MockCaDevice, Notification};
49//! use dvb_ci_runtime::dvb_ci::tpdu::tags;
50//!
51//! # fn main() -> std::io::Result<()> {
52//! // Script a module that accepts the transport connection.
53//! let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
54//! let mut driver = Driver::new(dev);
55//!
56//! driver.init()?; // reset + open the transport connection
57//!
58//! // Pump the device: reads frames when readable, otherwise advances the
59//! // EN 50221 poll cadence by the timeout.
60//! for _ in 0..4 {
61//! driver.pump(Duration::from_millis(100))?;
62//! }
63//!
64//! // Host-facing events (CamReady, ApplicationInfo, CaInfo, Mmi, …) surface here.
65//! for note in driver.take_notifications() {
66//! match note {
67//! Notification::CamReady => { /* now safe to send a ca_pmt */ }
68//! other => { let _ = other; }
69//! }
70//! }
71//! # Ok(())
72//! # }
73//! ```
74
75// The portable core is unsafe-free; only the optional Linux device leaf
76// (`#[allow(unsafe_code)]` in `linux`) uses ioctls, so this is `deny`, not
77// `forbid`.
78#![deny(unsafe_code)]
79#![warn(missing_docs)]
80
81pub mod device;
82pub mod driver;
83pub mod event;
84pub mod resource;
85pub mod session;
86pub mod stack;
87pub mod transport;
88
89#[cfg(all(feature = "linux", target_os = "linux"))]
90pub mod linux;
91
92pub use device::{CaDevice, DeviceOp, MockCaDevice, SlotInfo};
93pub use driver::Driver;
94pub use event::{Action, Event, HostRequest, Notification};
95#[cfg(all(feature = "linux", target_os = "linux"))]
96pub use linux::LinuxCaDevice;
97pub use stack::CiStack;
98
99/// Re-export of the wire-codec crate this runtime drives.
100pub use dvb_ci;