Skip to main content

Driver

Struct Driver 

Source
pub struct Driver<D: CaDevice> { /* private fields */ }
Expand description

Drives a CaDevice with the CiStack.

Implementations§

Source§

impl<D: CaDevice> Driver<D>

Source

pub fn new(device: D) -> Self

New driver over device, single transport connection.

Examples found in repository?
examples/mock_cam_session.rs (line 18)
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}
Source

pub fn with_clock(self, clock: impl Fn() -> Instant + 'static) -> Self

Override Self::pump’s clock source (r10-W-21) — Instant::now by default. A test uses this to simulate an arbitrary elapsed jump deterministically, in place of a real sleep.

Source

pub fn managed_ca(&self) -> &ManagedCa

The slot’s managed CAS-layer state (#763 Layer 1) — the active service set built via add_service.

Source

pub fn device(&self) -> &D

Borrow the underlying device (e.g. to inspect a mock’s recorded ops).

Examples found in repository?
examples/mock_cam_session.rs (line 22)
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}
Source

pub fn device_mut(&mut self) -> &mut D

Mutably borrow the underlying device (e.g. to script a mock’s inbound frames between pumps).

Source

pub fn next_timer(&self) -> Option<Duration>

The poll delay the stack most recently requested, if any.

Source

pub fn take_notifications(&mut self) -> Vec<Notification>

Drain the notifications collected so far.

Examples found in repository?
examples/mock_cam_session.rs (line 32)
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}
Source

pub fn init(&mut self) -> Result<()>

Bring the interface up (reset + open the transport connection).

Examples found in repository?
examples/mock_cam_session.rs (line 21)
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}
Source

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).

Source

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).

Source

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.

Source

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.

Source

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.

Source

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 (ISO/IEC 13818-1 §2.6.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. CaError::NotSent if the stack could not hand it to the CAM (no open CA session, inactive connection, full queue); nothing is recorded.

Source

pub fn update_service(&mut self, pmt: &PmtSection<'_>) -> Result<(), CaError>

Update (re-send) the ca_pmt for a programme already in the managed set, using ca_pmt_list_management = update (ETSI EN 50221 §8.4.3.4 Table 25, ca_pmt_list_management value 0x05 “update”). EN 50221 p. 31: “the CA PMT of a programme already in the list is sent again because the version_number or the current_next_indicator has changed. … Since the list management commands act only at the programme level, any changes at the elementary stream level in an existing programme must be signalled by an ‘update’ command with the complete elementary stream list re-sent.” The same page notes that re-sending add for an already-existing programme is already equivalent to update, so add_service remains spec-conforming; this method exists as the explicit, clearer entry point for that case.

The stored service record is replaced with the new PMT bytes, so later remove_service and the periodic re-query use the new PMT.

§Errors

CaError::NotManaged if pmt.program_number is not currently managed. CaError::NoCaDescriptor if pmt carries no CA_descriptor (ISO/IEC 13818-1 §2.6.16, tag 0x09) — the stored record is left unchanged. CaError::Serialize if the projected ca_pmt has no valid wire encoding — the record is left unchanged and nothing is sent. CaError::Io if sending the built ca_pmt fails.

Source

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.

Source

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 as a list_management = update pair — cmd_id = query immediately followed by cmd_id = ok_descrambling — see requery_tick for why both, #1032. Duration::ZERO disables re-query. Opt-in: defaults to managed::REQUERY_DEFAULT (Duration::ZERO) at construction (the periodic re-query is hardware-verified on an entitled AlphaCrypt module since #1032, but stays opt-in).

Source

pub fn set_cat(&mut self, cat: &CatSection<'_>) -> Result<(), CaError>

Feed a freshly-parsed CAT (ISO/IEC 13818-1 §2.4.4.7) to the managed CAS-layer state: extracts its CA_descriptors (ISO/IEC 13818-1 §2.6.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.

Source

pub fn emm_pids(&self) -> &[Pid]

The EMM PIDs to route into ci0 — the last set_cat’s list of (CAID, EMM PID) entries (a CAID may have several) intersected with the CAM’s advertised CAIDs (#763 Task 4).

Source

pub fn descramble_pids(&self) -> &[Pid]

The PIDs to route into ci0 for descrambling — the union of every actively-managed service’s elementary-stream PIDs (#763 Task 4).

Source

pub fn ca_pids(&self) -> &[Pid]

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).

Source

pub fn required_pids(&self) -> Vec<Pid>

descramble_pids() ∪ ca_pids() ∪ emm_pids() ∪ PCR ∪ CAT — 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.9 — 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.

Source

pub fn mmi_menu_answer(&mut self, choice_ref: u8) -> Result<()>

Answer an MMI menu/list by 1-based choice_ref (0 = back/cancel).

Source

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).

Source

pub fn mmi_cancel(&mut self) -> Result<()>

Abort the current MMI dialogue (answ with answ_id = cancel).

Source

pub fn enter_menu(&mut self) -> Result<()>

Ask the module to open its MMI menu (enter_menu) — e.g. to read card / entitlement info from the module’s own menus.

Source

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?
examples/mock_cam_session.rs (line 27)
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}
Source

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.

Source

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.

Auto Trait Implementations§

§

impl<D> !RefUnwindSafe for Driver<D>

§

impl<D> !Send for Driver<D>

§

impl<D> !Sync for Driver<D>

§

impl<D> !UnwindSafe for Driver<D>

§

impl<D> Forget for Driver<D>

§

impl<D> Freeze for Driver<D>
where D: Freeze,

§

impl<D> Move for Driver<D>

§

impl<D> Unpin for Driver<D>
where D: Unpin,

§

impl<D> UnsafeUnpin for Driver<D>
where D: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.