Expand description
DVB Common Interface (EN 50221) — the host↔CICAM wire protocol.
Parses + builds the EN 50221 protocol objects: resource APDUs (Resource
Manager, Application Information, CA support incl. ca_pmt/ca_pmt_reply,
Date-Time, Host Control, the full MMI set — low-level/display/keypad/subtitle/
download and high-level menu/list/enq — and Low-Speed Communications), the
session-layer SPDUs and transport-layer TPDUs, plus a CA_PMT builder that
turns a dvb-si PMT into the object handed to a CICAM.
Every wire structure implements broadcast_common::Parse / broadcast_common::Serialize
symmetrically (parse → serialize is byte-identical), with all length fields
computed from content. Spec citations live in each module doc; the
render-verified transcription is in docs/en_50221/.
Scope: the wire/protocol layer only. The physical PC-Card transport and CI+
crypto (the CC resource) are out of scope. #![no_std] (+ alloc).
§Layers
tag/length— the 3-byteapdu_tagand the ASN.1-stylelength_fieldshared by all PDUs.resource— the 4-octetresource_identifier().objects— application-layer APDU objects, dispatched byAnyApdu.spdu— session-layer SPDUs (open/create/close session, session number).tpdu— transport-layer framing (C_TPDU/R_TPDU + connection mgmt).builder— theCA_PMTprojection from advb-siPMT.
All Table 58 apdu_tags are now typed; an unallocated/private tag is produced
by AnyApdu::parse as AnyApdu::Unknown (lossless round-trip). The CI+
crypto (CC resource) and the PC-Card hardware transport remain out of scope.
§Examples
Two runnable examples ship with this crate (cargo run -p dvb-ci --example <name>).
§build_ca_pmt
//! Build a `ca_pmt` object from a `dvb-si` PMT and print the wire bytes.
//!
//! Run with: `cargo run -p dvb-ci --example build_ca_pmt`
//!
//! Shows the headline workflow: a host takes a parsed MPEG-2 PMT, strips every
//! non-CA descriptor, and hands the CA-only `ca_pmt` object to a CICAM.
use broadcast_common::Parse;
use dvb_ci::builder::build_ca_pmt;
use dvb_ci::objects::ca_pmt::{CaPmt, CaPmtCmdId, CaPmtListManagement};
use dvb_si::tables::pmt::PmtSection;
fn main() {
// A small PMT (program 1): a programme-level CA_descriptor (tag 0x09) plus a
// non-CA registration descriptor, one scrambled video ES with an ES-level
// CA_descriptor, and one clear audio ES with only a language descriptor.
let pmt_bytes = sample_pmt();
let pmt = PmtSection::parse(&pmt_bytes).expect("valid PMT");
let built = build_ca_pmt(&pmt, CaPmtListManagement::Only, CaPmtCmdId::OkDescrambling);
let wire = built.to_bytes();
println!("ca_pmt APDU ({} bytes): {:02X?}", wire.len(), wire);
// Re-parse to confirm the projection round-trips.
let parsed = CaPmt::parse(&wire).expect("valid ca_pmt");
println!("list_management : {}", parsed.list_management);
println!("program_number : {}", parsed.program_number);
println!(
"program CA desc : {} byte(s) (non-CA descriptors stripped)",
parsed.program_ca_descriptors.len()
);
for (i, s) in parsed.streams.iter().enumerate() {
println!(
" ES[{i}] type=0x{:02X} pid=0x{:04X} ca_descriptors={} cmd_id={:?}",
s.stream_type,
s.elementary_pid,
s.ca_descriptors.len(),
s.cmd_id.map(|c| c.name())
);
}
}
fn sample_pmt() -> Vec<u8> {
let prog_ca = [0x09, 0x04, 0x05, 0x00, 0xE1, 0x00]; // CA_system_id 0x0500, PID 0x0100
let reg = [0x05, 0x04, b'H', b'D', b'M', b'V']; // non-CA, will be stripped
let mut program_info = Vec::new();
program_info.extend_from_slice(&prog_ca);
program_info.extend_from_slice(®);
let es0_ca = [0x09, 0x04, 0x05, 0x00, 0xE1, 0x01];
let lang = [0x0A, 0x04, b'e', b'n', b'g', 0x00];
let mut body = vec![0x02, 0x00, 0x00, 0x00, 0x01, 0xC3, 0x00, 0x00, 0xE2, 0x00];
let pil = program_info.len();
body.push(0xF0 | ((pil >> 8) as u8 & 0x0F));
body.push(pil as u8);
body.extend_from_slice(&program_info);
body.extend_from_slice(&[
0x02,
0xE2,
0x00,
0xF0 | ((es0_ca.len() >> 8) as u8),
es0_ca.len() as u8,
]);
body.extend_from_slice(&es0_ca);
body.extend_from_slice(&[
0x03,
0xE2,
0x01,
0xF0 | ((lang.len() >> 8) as u8),
lang.len() as u8,
]);
body.extend_from_slice(&lang);
let section_length = body.len() - 3 + 4;
body[1] = 0xB0 | ((section_length >> 8) as u8 & 0x0F);
body[2] = section_length as u8;
let crc = broadcast_common::crc32_mpeg2::compute(&body);
body.extend_from_slice(&crc.to_be_bytes());
body
}§parse_apdu
//! Parse an APDU with the unified [`AnyApdu`] dispatcher and round-trip it.
//!
//! Run with: `cargo run -p dvb-ci --example parse_apdu`
use broadcast_common::Serialize;
use dvb_ci::AnyApdu;
use dvb_ci::objects::ca_info::CaInfo;
fn main() {
// A ca_info() APDU (9F 80 31) advertising two CA_system_ids: 0x0500, 0x0B00.
let info = CaInfo {
ca_system_ids: vec![0x0500, 0x0B00],
};
let wire = info.to_bytes();
println!("ca_info APDU : {wire:02X?}");
// Route it through the tag dispatcher without knowing the type in advance.
let any = AnyApdu::parse(&wire).expect("valid APDU");
println!("dispatched as : {} (tag {})", any.name(), any.tag());
if let AnyApdu::CaInfo(ci) = &any {
println!("CA_system_ids : {:04X?}", ci.ca_system_ids);
}
// Symmetric serialize: AnyApdu round-trips byte-for-byte.
let back = any.to_bytes();
assert_eq!(back, wire);
println!("round-trip : OK ({} bytes)", back.len());
// An unrecognised tag (Tenq 9F 88 07, an MMI high-level object not yet
// implemented) is preserved losslessly as AnyApdu::Unknown.
let unknown_wire = [0x9F, 0x88, 0x07, 0x02, 0xAA, 0xBB];
let unknown = AnyApdu::parse(&unknown_wire).expect("valid APDU framing");
println!(
"unknown tag : {} -> preserved, round-trips: {}",
unknown.name(),
unknown.to_bytes() == unknown_wire
);
}Re-exports§
pub use any::AnyApdu;pub use error::Error;pub use error::Result;pub use resource::ResourceId;pub use tag::ApduTag;pub use traits::ApduDef;
Modules§
- any
- Unified APDU dispatch:
AnyApdu. - builder
CA_PMTbuilder — project advb-siPMT into theca_pmtobject handed to a CICAM — ETSI EN 50221 §8.4.3.4 (Table 25), perdocs/en_50221/ca-pmt.md.- ci_ext
- DVB CI Extensions (ETSI TS 101 699) — the resource-scoped APDU layer.
- ci_plus
- CI Plus extensions (ETSI TS 103 205) — the resource-scoped APDU layer.
- error
- Error type for EN 50221 Common Interface parsing/serialization.
- length
- The EN 50221
length_field— the ASN.1-style length used by every PDU at the Transport, Session and Application layers — ETSI EN 50221 §7, Table 1 (PDF p. 11). - objects
- Application-layer APDU objects (resource APDUs) — ETSI EN 50221 §8.4-§8.6.
- resource
resource_identifier()— the 4-octet resource identity — ETSI EN 50221 §8.2.2, Table 15 (PDF p. 24) + §8.8.1, Table 57 (PDF p. 54).- spdu
- Session Protocol Data Unit (SPDU) framing — ETSI EN 50221 §7.2.4-7.2.7, Tables 4-14 (PDF pp. 19-23).
- tag
apdu_tag— the 3-byte ASN.1 application-object tag — ETSI EN 50221 §8.8.2, Table 58 + Figure 16 (PDF pp. 56-57).- tpdu
- Transport Protocol Data Unit (TPDU) framing — ETSI EN 50221 Annex A §A.4.1, Tables A.1-A.16 (PDF pp. 63-70).
- traits
dvb-ci-specific dispatch trait.Parse/Serializecome frombroadcast_commonand are imported at the call sites.