Expand description
§dvb-ci-runtime
Pure-Rust EN 50221 DVB Common Interface driver runtime: device I/O, the TPDU
poll loop, SPDU session management and the per-resource state machines that drive
a physical CAM, over the dvb-ci wire codecs.
§What it does / what it does not
Does. Drives a Common Interface module end to end: resets the slot, opens the
transport connection, runs the Resource Manager handshake, answers the module’s
session opens for the six host-provided resources, surfaces application_info,
ca_info, ca_pmt_reply, MMI and host-control events as Notifications, builds
and sends ca_pmt for the services you add, and (with a ci0 data device)
pushes a scrambled TS through the CAM and returns the descrambled TS. It is
sync and sans-IO at its core: the caller owns the clock and the loop.
Does not.
- No CI Plus: the runtime does not run CI+ authentication, the Content Control
resource, multi-stream or any TS 103 205 resource (the
dvb-cicrate types those APDUs; nothing here drives them). - No Low-Speed Communications, no TS 101 699 extension resources at runtime (Resource Manager v2, Power Manager, Download and so on); only the six EN 50221 resources listed below are served.
- It does not re-tune on a
host_controltune/replacerequest: it surfaces the request and the host acts on it. - It does not descramble itself; the CAM does. For software CSA see
dvb-csa. - Linux only for real hardware (the DVB CA/CI devices); other platforms get the portable core and the in-memory mocks.
§Standards supported
| Standard | Edition | Scope | Status | Notes |
|---|---|---|---|---|
| CENELEC EN 50221 — Common Interface Specification for CA and other DVB Decoder Applications | February 1997 (vendored text) | Transport (Annex A TPDU poll loop), session layer, and the host-provided resources: Resource Manager, Application Information, Conditional Access Support, Date-Time, MMI, Host Control | Partial | Low-Speed Communications not served; Host Control surfaced, not acted on |
| ETSI TS 101 699 — Extensions to the Common Interface Specification | V1.1.1 (1999-11) | None at runtime (wire objects are typed in dvb-ci) | Not implemented | Cited by the crate docs for context only |
Linux DVB CA API (linux/dvb/ca.h, kernel dvb_ca_en50221) | n/a (kernel ABI) | CA_RESET, CA_GET_SLOT_INFO, [slot, connection_id, ...] frame read/write, ciM TS data plane | Full for the calls used | linux feature, Linux only |
§Features
| Feature | Default | Effect |
|---|---|---|
| (none) | yes | Portable sync core: Driver, CiStack, ManagedCa, CaDescrambler, MockCaDevice, MockCiDataDevice, RecordingCaDevice, trace. Needs std (no no_std claim). |
linux | no | LinuxCaDevice (/dev/dvb/adapterN/caM), LinuxCiDataDevice (ciM), DeviceOpError, and the ci-probe binary (pulls libc, rustix, clap). |
§Quick start
use std::time::Duration;
use dvb_ci_runtime::{Driver, HotPlug, MockCaDevice, Notification};
use dvb_ci_runtime::dvb_ci::tpdu::tags;
fn main() -> std::io::Result<()> {
// Script a module that accepts the transport connection.
let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
let mut driver = Driver::new(dev);
driver.init()?; // reset + open the transport connection
// Pump the device: reads frames when readable, otherwise advances the
// EN 50221 poll cadence by the timeout.
for _ in 0..4 {
driver.pump(Duration::from_millis(100))?;
}
// Host-facing events surface here, poll-drained:
for note in driver.take_notifications() {
match note {
Notification::CamReady => { /* now safe to send a ca_pmt */ }
other => { let _ = other; }
}
}
// ...or delivered by a closure per pump:
driver.pump_hotplug(Duration::from_millis(100), |hp| match hp {
HotPlug::CamPresent => { /* CAM (re)inserted: re-send any pending ca_pmt */ }
HotPlug::CamRemoved => { /* CAM removed: stop descrambling */ }
_ => {}
})?;
Ok(())
}Runnable versions: examples/mock_cam_session.rs, examples/sans_io_core.rs.
§What is implemented
- Transport (EN 50221 Annex A TPDU):
Create_T_Chandshake, poll cadence (oneT_Data_Lastper moduleT_SB, half-duplex),T_SBdata-available toT_RCV,T_Data_More/Lastreassembly, reply timeout. - Session (SPDU): session table,
open_session/create_session/close,session_numberAPDU routing. - Resources, all host-provided (the module opens the session; the host
advertises them in its Resource Manager
profile): Resource Manager (handshake toCamReady), Application Information, Conditional Access (ca_pmt/ca_pmt_reply), Date-Time (MJD + BCD), MMI (menus and enquiries surfaced asNotification::Mmi;Driver::mmi_menu_answer/mmi_enquiry_answer/mmi_cancelanswer them), Host Control (tune/replace/clear_replace/ask_releasesurfaced asNotification::HostControl). - Descramble helper:
Driver::descramble(pmt)/HostRequest::Descrambleruns theca_pmtquery, reply andok_descramblingsequence, filtered to the CAM’s advertised CAIDs (fromca_info). - Managed CAS layer: a single-slot orchestration layer fed parsed
dvb-sistructs (never raw bytes).Driver::add_service(&PmtSection)/remove_service(pn)build and send theca_pmtand track the active service set;set_cat(&CatSection)computes the EMM-PID feed (CAT EMM PIDs intersected with the CAM’sca_infoCAIDs; the CAT keeps one(CAID, EMM PID)entry perCA_descriptor, so a CAID may have several);emm_pids()/descramble_pids()/ca_pids()/required_pids()give the PIDs to route intoci0.required_pids()always includes the CAT PID (0x0001): hardware-evidenced on a CXD2099 with an AlphaCrypt One4All (CAID 0x0650), not mandated by EN 50221.set_requery_interval(dt)is opt-in (defaultREQUERY_DEFAULTisDuration::ZERO) because no CAM verified so far answers thequeryhalf; a per-programme change surfaces as an edge-triggeredNotification::Entitlement.CaDescrambler<D, C>is the turnkey wrapper that also owns theci0CiDataDevice:feed_ts(scrambled)filters torequired_pids()and returns the descrambled TS. OneCaDescrambleris one CI slot and one TS path. - Hot-plug:
Notification::HotPlug(HotPlug)withCamPresent/CamRemoved(real DVB-CA slot-status edges; the driver re-drives the handshake on insert and tears down on remove) andCardInserted/CardRemoved/CardChanged(best-effort EN 50221 app-layer inference; CI slots have no card-detect line). React withDriver::pump_hotplug(dt, |hp| ...)orpump_with. - Devices: in-memory
MockCaDevice+MockCiDataDevice; LinuxLinuxCaDevice(control plane) andLinuxCiDataDevice(TS data plane, whole packets only,WouldBlock+PartialWriteon backpressure) behindlinux.LinuxCiDataDevice::opentriesO_RDWRand falls back to a splitO_WRONLY/O_RDONLY | O_NONBLOCKopen onEINVAL(open_split/from_splitare public). - Named device errors: every failing Linux device operation returns a
DeviceOpErrornaming the operation, device path and errno (DeviceOpError::errno_of(&err)); theio::ErrorKindis unchanged. A firstCreate_T_Cwrite that failsEINVALwhile the kernel slot is still coming up is retried every 250 ms for up to 12 s past the settle window. - Diagnostics:
RecordingCaDevicecaptures the link both ways;trace::decode_frame/decode_logannotate a capture (TPDU, SPDU, APDU).
§Hardware verification
Only what the CHANGELOG records:
- An AlphaCrypt / Irdeto module (live): Resource Manager handshake,
application_information(“AlphaCrypt”) andca_infowith 18 CA_system_ids were verified against the real module (#337, #340). That module rejects a hostcreate_sessionfor the host-provided resources and sends noresource_identifiers in its firstprofile; the runtime handles both. It also drops a second link block sent before it answers the first. - A Digital Devices CXD2099
ci0(dddvb, Ubuntu 5.15): the split-fd data plane was run for 150 s, 111kfeed_tscalls, max 249 us, 0 errors (#1066); the device rejectsO_RDWRwithEINVALandCA_GET_SLOT_INFOmay returnEINVAL(the slot is then assumed ready). - Same CXD2099
ci0with an entitled AlphaCrypt One4All V2.5 (CAID 0x0650, ORF1 on 19.2E 11302H), 180 s runs, 2026-10-16: the opt-in 10 sca_pmtre-query is sent asupdate/queryimmediately followed byupdate/ok_descrambling(12 pairs in the decrypted span); the module answered most withca_enable = 0x81(possible), descrambling continued with no gap in any 10 s window, andfeed_tsnever stalled (max 412 us over 133k calls). Before theok_descramblingfollow-up, the lonequeryevery 10 s stopped descrambling (#1032). StockCaDescrambler::feed_tsdecrypts from about 55 s (card warm-up after the CA reset) once the CAT PID is routed (#1296). - Not verified on hardware: CardInserted/Removed/Changed inference, and every path not listed above.
§ci-probe — discover and engage an installed CAM
A command-line tool that drives a real Common Interface module end-to-end over a
Linux DVB CA device. It is the reference consumer of this crate: it wires
Driver + LinuxCaDevice + RecordingCaDevice + trace together behind a
clap CLI (the workspace CLI standard — docs/CLI-STANDARD.md).
§Build / install
Linux only; gated behind the linux feature (it opens /dev/dvb/... and uses
libc ioctls):
cargo build -p dvb-ci-runtime --features linux --release # target/release/ci-probe
cargo install dvb-ci-runtime --features linux # ~/.cargo/bin/ci-probeDevice access usually needs membership of the video group (or root).
§Commands
All device-addressed commands take -a/--adapter <N> and -c/--ca <N> (both
default 0). --help and --version are auto-generated; <command> --help
lists that command’s flags.
| Command | What it does |
|---|---|
ci-probe list | Enumerate /dev/dvb/adapterN/caM and print each slot’s number + module_ready. |
ci-probe info [--adapter N] [--ca N] | Run the EN 50221 handshake and print application_info + the CAM’s CA_system_ids. |
ci-probe descramble [--adapter N] [--ca N] --pmt <FILE> | Read a PMT section (raw bytes), then run the ca_pmt query → reply → ok_descrambling sequence and report the result. |
ci-probe mmi [--adapter N] [--ca N] | Interactive MMI: display the module’s menus / enquiries and send your answers back (menu_answ / answ). |
Append --trace to any command to dump an annotated link trace on exit
(every TPDU/SPDU/APDU both directions) — invaluable for diagnosing a CAM that
won’t complete a handshake.
§Examples
Discover what’s installed:
$ ci-probe list
/dev/dvb/adapter0/ca0 slot 0 module_ready=trueIdentify the module and its CA systems:
$ ci-probe info --adapter 0 --ca 0
CAM ready (resource-manager handshake complete)
application_info: type=0x01 manufacturer=0x1234 code=0x5678 menu="Irdeto CAM"
ca_info: 18 CA_system_id(s): 0x0604, 0x0606, 0x0608, ...Diagnose a stuck handshake (annotated trace):
$ ci-probe info --adapter 0 --ca 0 --trace
...
--- link trace ---
reset()
slot_info() -> ready=true
W Create_T_C tcid=1
R C_T_C_Reply tcid=1
R T_Data_Last tcid=1 · open_session_request
W T_Data_Last tcid=1 · open_session_response
W T_Data_Last tcid=1 · session 1 · profile_enq (9F8010)
R T_Data_Last tcid=1 · session 1 · profile (9F8011)
...Request descrambling for a service (PMT extracted with e.g. dvbsnoop or the
dvb-si tools):
$ ci-probe descramble --adapter 0 --ca 0 --pmt service.pmt
CAM ready (resource-manager handshake complete)
ca_info: 18 CA_system_id(s): ...
ca_info received → sending descramble request
ca_pmt_reply: program 1019 descrambling_ok=true§Hardware notes
- The device
read/writeexchange one whole kernel link frame[slot, connection_id, <TPDU + T_SB>]; theLinuxCaDevicehandles that framing. - EN 50221’s link is polled half-duplex — one
T_Data_Lastper moduleT_SB. The transport enforces this (a real CAM drops a second block sent before it answers the first — see #337). - If
infotimes out beforeca_info, re-run with--traceand read the last few lines — they show exactly which step the module stopped responding at.
The EN 50221 link is polled half-duplex — the host sends one T_Data_Last per
module T_SB. The transport queues outbound SPDUs and releases one per turn (a
real CAM drops a second block sent before it answers the first — #337).
§Design notes
- Everything is written against the
CaDevicetrait, so the runtime runs against a real Linux CA device or the in-memoryMockCaDevice, which makes the state machines testable without hardware. #![deny(unsafe_code)]: the Linux device leaf is the sole#[allow]; the sans-IO core is unsafe-free. Linux ioctl request numbers come fromrustix::ioctl::opcode; readiness polling isrustix::event::poll.- Long-form
length_fieldsafe: followsdvb-ci’s preservedLengthForm, so a CAM coding82 00 09does not misframe the session or transport layer. PID accessors are typedmpeg_ts::pid::Pid. Debugredacts MMI enquiry answers (whatever the user typed).thiserrorerror types; dispatch enums are#[non_exhaustive].MAX_MMI_CHAIN_LENcaps a chainedmenu/listbody (EN 50221 §8.3.2) so a module that never sends the_lastblock cannot grow host memory past it;MAX_SETUP_ATTEMPTSis the number ofCreate_T_Cattempts (first plus retries) made beforeTransportError::SetupTimeout.
§Related crates
Depends on dvb-ci (wire codecs, re-exported
as dvb_ci_runtime::dvb_ci), dvb-si (PMT / CAT inputs), mpeg-ts and
broadcast-common.
§MSRV and licence
MSRV 1.95.0. Licensed under MIT OR Apache-2.0.
Re-exports§
pub use dataplane::CiDataDevice;pub use dataplane::MockCiDataDevice;pub use dataplane::PartialWrite;pub use dataplane::partial_write_progress;pub use descrambler::CaDescrambler;pub use device::CaDevice;pub use device::DeviceOp;pub use device::DeviceOpError;pub use device::LinkEvent;pub use device::MockCaDevice;pub use device::RecordingCaDevice;pub use device::SlotInfo;pub use driver::Driver;pub use event::Action;pub use event::Event;pub use event::HostRequest;pub use event::HotPlug;pub use event::Notification;pub use linux::LinuxCaDevice;Linux and linuxpub use linux::LinuxCiDataDevice;Linux and linuxpub use managed::CaError;pub use managed::ManagedCa;pub use managed::ManagedService;pub use managed::REQUERY_DEFAULT;pub use stack::CiStack;pub use dvb_ci;
Modules§
- dataplane
- The CI TS data-plane device:
CiDataDevice. - descrambler
- Turnkey CAS descrambler — #763 Layer 2.
- device
- The hardware-abstraction boundary:
CaDevice. - driver
- The driver — the one place I/O happens. It pumps a
CaDeviceagainst the sans-IOCiStack: reads frames in, executes the stack’sActions (writes/ioctls) out, tracks the requested poll timer, and collectsNotifications for the host application. - event
- The sans-IO event/action model.
- linux
Linux and linux - Linux
/dev/dvb/adapterN/caMCaDeviceimplementation (thelinuxfeature). - managed
- Managed CAS-layer state (#763) — the
Driver’s owned view of the slot’s active descrambled-service set. - resource
- The resource layer — application-layer state machines (ETSI EN 50221 §8), one per resource, driven by the session layer’s APDUs.
- session
- SPDU session layer — a sans-IO mechanism over the transport layer (ETSI EN 50221 §7.2).
- stack
- The CI protocol stack — composes the transport + session layers and the resource state machines (Resource Manager, application_information, conditional_access, date_time, mmi, host_control) into one sans-IO core.
- trace
- Human-readable decoding of CI link frames, for diagnosing live-CAM exchanges (e.g. issue #337).
- transport
- TPDU transport layer — a sans-IO state machine for the single transport connection per CI slot (ETSI EN 50221 §A.4).
Constants§
- TS_
PACKET_ LEN - MPEG-2 TS packet length (ISO/IEC 13818-1): 188 bytes. Size of one MPEG-TS packet (ETSI EN 300 468 §3.2, ISO/IEC 13818-1 §2.4.3.2).