pub struct Driver<D: CaDevice> { /* private fields */ }Implementations§
Source§impl<D: CaDevice> Driver<D>
impl<D: CaDevice> Driver<D>
Sourcepub fn new(device: D) -> Self
pub fn new(device: D) -> Self
New driver over device, single transport connection.
Examples found in repository?
15fn main() -> std::io::Result<()> {
16 // Script a module that accepts the transport connection (C_T_C_Reply).
17 let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
18 let mut driver = Driver::new(dev);
19
20 // Bring the interface up: reset + open the transport connection.
21 driver.init()?;
22 println!("init: sent {} device op(s)", driver.device().ops.len());
23
24 // Pump the device. When readable it reads a frame and feeds the stack;
25 // otherwise it advances the EN 50221 poll cadence by the timeout.
26 for step in 0..5 {
27 let read = driver.pump(Duration::from_millis(100))?;
28 println!("pump {step}: processed_frame={read}");
29 }
30
31 // Anything the host application needs to act on surfaces as a Notification.
32 for note in driver.take_notifications() {
33 match note {
34 Notification::CamReady => println!("note: CAM ready — safe to send ca_pmt"),
35 Notification::ApplicationInfo { menu, .. } => {
36 println!("note: application_information menu={menu:?}")
37 }
38 Notification::CaInfo { ca_system_ids } => {
39 println!("note: ca_info system_ids={ca_system_ids:?}")
40 }
41 other => println!("note: {other:?}"),
42 }
43 }
44
45 // The mock records every device op (writes/ioctls) — handy for assertions.
46 println!("total recorded device ops: {}", driver.device().ops.len());
47 Ok(())
48}Sourcepub fn managed_ca(&self) -> &ManagedCa
pub fn managed_ca(&self) -> &ManagedCa
The slot’s managed CAS-layer state (#763 Layer 1) — the active
service set built via add_service.
Sourcepub fn device(&self) -> &D
pub fn device(&self) -> &D
Borrow the underlying device (e.g. to inspect a mock’s recorded ops).
Examples found in repository?
15fn main() -> std::io::Result<()> {
16 // Script a module that accepts the transport connection (C_T_C_Reply).
17 let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
18 let mut driver = Driver::new(dev);
19
20 // Bring the interface up: reset + open the transport connection.
21 driver.init()?;
22 println!("init: sent {} device op(s)", driver.device().ops.len());
23
24 // Pump the device. When readable it reads a frame and feeds the stack;
25 // otherwise it advances the EN 50221 poll cadence by the timeout.
26 for step in 0..5 {
27 let read = driver.pump(Duration::from_millis(100))?;
28 println!("pump {step}: processed_frame={read}");
29 }
30
31 // Anything the host application needs to act on surfaces as a Notification.
32 for note in driver.take_notifications() {
33 match note {
34 Notification::CamReady => println!("note: CAM ready — safe to send ca_pmt"),
35 Notification::ApplicationInfo { menu, .. } => {
36 println!("note: application_information menu={menu:?}")
37 }
38 Notification::CaInfo { ca_system_ids } => {
39 println!("note: ca_info system_ids={ca_system_ids:?}")
40 }
41 other => println!("note: {other:?}"),
42 }
43 }
44
45 // The mock records every device op (writes/ioctls) — handy for assertions.
46 println!("total recorded device ops: {}", driver.device().ops.len());
47 Ok(())
48}Sourcepub fn device_mut(&mut self) -> &mut D
pub fn device_mut(&mut self) -> &mut D
Mutably borrow the underlying device (e.g. to script a mock’s inbound frames between pumps).
Sourcepub fn next_timer(&self) -> Option<Duration>
pub fn next_timer(&self) -> Option<Duration>
The poll delay the stack most recently requested, if any.
Sourcepub fn take_notifications(&mut self) -> Vec<Notification>
pub fn take_notifications(&mut self) -> Vec<Notification>
Drain the notifications collected so far.
Examples found in repository?
15fn main() -> std::io::Result<()> {
16 // Script a module that accepts the transport connection (C_T_C_Reply).
17 let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
18 let mut driver = Driver::new(dev);
19
20 // Bring the interface up: reset + open the transport connection.
21 driver.init()?;
22 println!("init: sent {} device op(s)", driver.device().ops.len());
23
24 // Pump the device. When readable it reads a frame and feeds the stack;
25 // otherwise it advances the EN 50221 poll cadence by the timeout.
26 for step in 0..5 {
27 let read = driver.pump(Duration::from_millis(100))?;
28 println!("pump {step}: processed_frame={read}");
29 }
30
31 // Anything the host application needs to act on surfaces as a Notification.
32 for note in driver.take_notifications() {
33 match note {
34 Notification::CamReady => println!("note: CAM ready — safe to send ca_pmt"),
35 Notification::ApplicationInfo { menu, .. } => {
36 println!("note: application_information menu={menu:?}")
37 }
38 Notification::CaInfo { ca_system_ids } => {
39 println!("note: ca_info system_ids={ca_system_ids:?}")
40 }
41 other => println!("note: {other:?}"),
42 }
43 }
44
45 // The mock records every device op (writes/ioctls) — handy for assertions.
46 println!("total recorded device ops: {}", driver.device().ops.len());
47 Ok(())
48}Sourcepub fn init(&mut self) -> Result<()>
pub fn init(&mut self) -> Result<()>
Bring the interface up (reset + open the transport connection).
Examples found in repository?
15fn main() -> std::io::Result<()> {
16 // Script a module that accepts the transport connection (C_T_C_Reply).
17 let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
18 let mut driver = Driver::new(dev);
19
20 // Bring the interface up: reset + open the transport connection.
21 driver.init()?;
22 println!("init: sent {} device op(s)", driver.device().ops.len());
23
24 // Pump the device. When readable it reads a frame and feeds the stack;
25 // otherwise it advances the EN 50221 poll cadence by the timeout.
26 for step in 0..5 {
27 let read = driver.pump(Duration::from_millis(100))?;
28 println!("pump {step}: processed_frame={read}");
29 }
30
31 // Anything the host application needs to act on surfaces as a Notification.
32 for note in driver.take_notifications() {
33 match note {
34 Notification::CamReady => println!("note: CAM ready — safe to send ca_pmt"),
35 Notification::ApplicationInfo { menu, .. } => {
36 println!("note: application_information menu={menu:?}")
37 }
38 Notification::CaInfo { ca_system_ids } => {
39 println!("note: ca_info system_ids={ca_system_ids:?}")
40 }
41 other => println!("note: {other:?}"),
42 }
43 }
44
45 // The mock records every device op (writes/ioctls) — handy for assertions.
46 println!("total recorded device ops: {}", driver.device().ops.len());
47 Ok(())
48}Sourcepub fn send_ca_pmt(&mut self, ca_pmt: &[u8]) -> Result<()>
pub fn send_ca_pmt(&mut self, ca_pmt: &[u8]) -> Result<()>
Request the module descramble the services in ca_pmt (a serialized
ca_pmt APDU body, e.g. from dvb_ci::build_ca_pmt).
Sourcepub fn descramble(&mut self, pmt_section: &[u8]) -> Result<()>
pub fn descramble(&mut self, pmt_section: &[u8]) -> Result<()>
Descramble the services in a PMT section: the stack filters the PMT’s
CA_descriptors to the CAM’s advertised CAIDs and sends a ca_pmt
(list_management = only, cmd_id = ok_descrambling). The outcome
surfaces as Notification::CaPmtReply. Call after the CAM is ready and
its ca_info has been received (otherwise no CAID filter is applied).
Sourcepub fn descramble_programs(&mut self, pmt_sections: &[&[u8]]) -> Result<()>
pub fn descramble_programs(&mut self, pmt_sections: &[&[u8]]) -> Result<()>
Descramble a set of programmes in one CA-PMT list (first/more/last),
replacing any previously selected set. Each element is a raw PMT section.
Sourcepub fn add_program(&mut self, pmt_section: &[u8]) -> Result<()>
pub fn add_program(&mut self, pmt_section: &[u8]) -> Result<()>
Add one programme to the descrambled set (list_management = add) without
re-listing the others — for a capacity manager adding a viewer’s service.
Sourcepub fn remove_program(&mut self, pmt_section: &[u8]) -> Result<()>
pub fn remove_program(&mut self, pmt_section: &[u8]) -> Result<()>
Remove one programme from the descrambled set (list_management = update,
cmd_id = not_selected) — tells the CAM to stop descrambling it.
Sourcepub fn add_service(&mut self, pmt: &PmtSection<'_>) -> Result<(), CaError>
pub fn add_service(&mut self, pmt: &PmtSection<'_>) -> Result<(), CaError>
Build + send the ca_pmt for pmt (via
dvb_ci::builder::build_ca_pmt, ETSI EN 50221 §8.4.3.4 Table 25) and
track it in the slot’s managed active-service set (#763 Layer 1).
Additive alongside the raw send_ca_pmt and the
existing multi-programme API
(descramble_programs/
add_program).
list_management (EN 50221 Table 25) is auto-selected from the tracked
set: Only when this is the first service added to an empty managed
set, Add when joining an already-active set. (Contrast the raw
add_program, which always sends Add and leaves
list-management sequencing to the caller.)
§Errors
CaError::NoCaDescriptor if pmt carries no CA_descriptor
(ETSI EN 300 468 §6.2.16, tag 0x09) at programme or
elementary-stream level — there would be nothing for the CAM to
descramble. CaError::Io if sending the built ca_pmt fails.
Sourcepub fn remove_service(&mut self, program_number: u16) -> Result<(), CaError>
pub fn remove_service(&mut self, program_number: u16) -> Result<(), CaError>
Stop descrambling a previously-added service (#763 Task 6): sends the
removal ca_pmt (list_management = update, cmd_id = not_selected,
EN 50221 §8.4.3.4 Table 25) via the existing
remove_program path — re-driving it with the
raw PMT bytes stashed at add_service time — then
drops the service from the managed set.
Removing a program_number that isn’t currently tracked (never
add_service’d, or already removed) is a no-op, not an error:
CaError has no not-found arm, and remove_service is idempotent.
§Errors
CaError::Io if sending the removal ca_pmt fails.
Sourcepub fn set_requery_interval(&mut self, interval: Duration)
pub fn set_requery_interval(&mut self, interval: Duration)
Set the entitlement re-query cadence (#763 Task 5): every
interval, the driver re-sends each actively-managed service’s
ca_pmt (EN 50221 §8.4.3.4 Table 25, cmd_id = query — not the
ok_descrambling variant originally sent to start descrambling; per
§8.4.3.5, ok_descrambling solicits no reply) so the CAM re-evaluates
and replies, surfacing as Notification::CaPmtReply and — on a
status change — Notification::Entitlement. Duration::ZERO
disables re-query. Defaults to managed::REQUERY_DEFAULT (10s) at
construction.
Sourcepub fn set_cat(&mut self, cat: &CatSection<'_>) -> Result<(), CaError>
pub fn set_cat(&mut self, cat: &CatSection<'_>) -> Result<(), CaError>
Feed a freshly-parsed CAT (ISO/IEC 13818-1 §2.4.4.5) to the managed
CAS-layer state: extracts its CA_descriptors (EN 300 468 §6.2.16,
CAID → EMM PID) and recomputes emm_pids against
the CAM’s advertised CAIDs (last Notification::CaInfo, captured
automatically as it arrives — see pump).
Calling this before any ca_info has been observed is not an
error: emm_pids stays empty until the CAM
advertises its CAIDs, then recomputes against the CAT stored here —
set_cat need not be re-called once ca_info arrives.
§Errors
CaError::Cat if the CAT’s descriptor loop carries a truncated
CA_descriptor.
Sourcepub fn emm_pids(&self) -> &[u16]
pub fn emm_pids(&self) -> &[u16]
The EMM PIDs to route into ci0 — the last set_cat’s
CAID → EMM-PID map intersected with the CAM’s advertised CAIDs (#763
Task 4).
Sourcepub fn descramble_pids(&self) -> &[u16]
pub fn descramble_pids(&self) -> &[u16]
The PIDs to route into ci0 for descrambling — the union of every
actively-managed service’s elementary-stream PIDs (#763 Task 4).
Sourcepub fn ca_pids(&self) -> &[u16]
pub fn ca_pids(&self) -> &[u16]
The union of every actively-managed service’s CA_PIDs (ECM PIDs —
ISO/IEC 13818-1 §2.6.16 CA_descriptor CA_PID, programme + ES level
combined) — the control-word channel, without which the module has ES
to descramble but no control words to do it with (#763 Task 7).
Sourcepub fn required_pids(&self) -> Vec<u16>
pub fn required_pids(&self) -> Vec<u16>
descramble_pids() ∪ ca_pids() ∪ emm_pids() ∪ PCR — every PID class
this slot needs on ci0 (ES to descramble ∪ ECM for control words ∪
EMM for entitlements ∪ each active service’s PCR PID — ISO/IEC
13818-1 §2.4.4.8 — so the descrambled TS keeps its clock reference
even when the PCR rides a dedicated PID). #763 Task 7’s turnkey
CaDescrambler filters its
feed_ts input to exactly this set.
Answer an MMI menu/list by 1-based choice_ref (0 = back/cancel).
Sourcepub fn mmi_enquiry_answer(&mut self, text: &[u8]) -> Result<()>
pub fn mmi_enquiry_answer(&mut self, text: &[u8]) -> Result<()>
Answer an MMI enquiry with the user’s input (EN 300 468 Annex A bytes).
Sourcepub fn mmi_cancel(&mut self) -> Result<()>
pub fn mmi_cancel(&mut self) -> Result<()>
Abort the current MMI dialogue (answ with answ_id = cancel).
Ask the module to open its MMI menu (enter_menu) — e.g. to read card /
entitlement info from the module’s own menus.
Sourcepub fn pump(&mut self, timeout: Duration) -> Result<bool>
pub fn pump(&mut self, timeout: Duration) -> Result<bool>
One pump step: if the device is readable within timeout, read a frame
and feed it; otherwise advance the stack’s timers by timeout (driving
the poll cadence). Returns whether a frame was processed.
Also samples SlotInfo once per call (the DVB-CA slot has no
interrupt/event of its own; CA_GET_SLOT_INFO is a poll) so a hot-plug
edge is caught between reads — see Notification::HotPlug carrying
HotPlug::CamPresent/CamRemoved (#726).
Examples found in repository?
15fn main() -> std::io::Result<()> {
16 // Script a module that accepts the transport connection (C_T_C_Reply).
17 let dev = MockCaDevice::new([vec![tags::C_T_C_REPLY, 0x01, 0x01]]);
18 let mut driver = Driver::new(dev);
19
20 // Bring the interface up: reset + open the transport connection.
21 driver.init()?;
22 println!("init: sent {} device op(s)", driver.device().ops.len());
23
24 // Pump the device. When readable it reads a frame and feeds the stack;
25 // otherwise it advances the EN 50221 poll cadence by the timeout.
26 for step in 0..5 {
27 let read = driver.pump(Duration::from_millis(100))?;
28 println!("pump {step}: processed_frame={read}");
29 }
30
31 // Anything the host application needs to act on surfaces as a Notification.
32 for note in driver.take_notifications() {
33 match note {
34 Notification::CamReady => println!("note: CAM ready — safe to send ca_pmt"),
35 Notification::ApplicationInfo { menu, .. } => {
36 println!("note: application_information menu={menu:?}")
37 }
38 Notification::CaInfo { ca_system_ids } => {
39 println!("note: ca_info system_ids={ca_system_ids:?}")
40 }
41 other => println!("note: {other:?}"),
42 }
43 }
44
45 // The mock records every device op (writes/ioctls) — handy for assertions.
46 println!("total recorded device ops: {}", driver.device().ops.len());
47 Ok(())
48}Sourcepub fn pump_with<F: FnMut(&Notification)>(
&mut self,
timeout: Duration,
handler: F,
) -> Result<bool>
pub fn pump_with<F: FnMut(&Notification)>( &mut self, timeout: Duration, handler: F, ) -> Result<bool>
Pump once (pump), then invoke handler for each
Notification produced this cycle (drain-and-dispatch via
take_notifications). Returns the same
bool as pump. The closure is per-call — nothing is stored, so there
are no lifetime constraints beyond the call itself. This crate is
sync/sans-IO (no channels/async runtime), so a closure callback is the
idiomatic push-style alternative to poll-draining take_notifications
yourself.
Sourcepub fn pump_hotplug<F: FnMut(HotPlug)>(
&mut self,
timeout: Duration,
handler: F,
) -> Result<bool>
pub fn pump_hotplug<F: FnMut(HotPlug)>( &mut self, timeout: Duration, handler: F, ) -> Result<bool>
Convenience over pump_with: invoke handler only
for HotPlug transitions, ignoring every other Notification
produced this cycle.