1#![allow(dead_code)]
2mod apdu;
3mod asn1;
4mod spdu;
5pub mod sys;
6mod tpdu;
7
8use {
9 anyhow::{Context, Result},
10 nix::{ioctl_none, ioctl_read},
11 std::{
12 fs::{File, OpenOptions},
13 os::unix::{
14 fs::OpenOptionsExt,
15 io::{AsRawFd, RawFd},
16 },
17 thread,
18 time::Duration,
19 },
20 sys::*,
21};
22
23const CA_DELAY: Duration = Duration::from_millis(100);
24
25#[derive(Debug)]
26pub struct CaDevice {
27 adapter: u32,
28 device: u32,
29
30 file: File,
31 slot: CaSlotInfo,
32}
33
34impl AsRawFd for CaDevice {
35 #[inline]
36 fn as_raw_fd(&self) -> RawFd {
37 self.file.as_raw_fd()
38 }
39}
40
41impl CaDevice {
42 #[inline]
44 pub fn reset(&mut self) -> Result<()> {
45 ioctl_none!(
47 #[inline]
48 ca_reset,
49 b'o',
50 128
51 );
52 unsafe { ca_reset(self.as_raw_fd()) }.context("CA: failed to reset")?;
53
54 Ok(())
55 }
56
57 #[inline]
59 pub fn get_caps(&self, caps: &mut CaCaps) -> Result<()> {
60 ioctl_read!(
62 #[inline]
63 ca_get_cap,
64 b'o',
65 129,
66 CaCaps
67 );
68 unsafe { ca_get_cap(self.as_raw_fd(), caps as *mut _) }
69 .context("CA: failed to get caps")?;
70
71 Ok(())
72 }
73
74 #[inline]
76 pub fn get_slot_info(&mut self) -> Result<()> {
77 ioctl_read!(
79 #[inline]
80 ca_get_slot_info,
81 b'o',
82 130,
83 CaSlotInfo
84 );
85 unsafe { ca_get_slot_info(self.as_raw_fd(), &mut self.slot as *mut _) }
86 .context("CA: failed to get slot info")?;
87
88 Ok(())
89 }
90
91 pub fn open(adapter: u32, device: u32, slot: u32) -> Result<CaDevice> {
93 let path = format!("/dev/dvb/adapter{}/ca{}", adapter, device);
94 let file = OpenOptions::new()
95 .read(true)
96 .write(true)
97 .custom_flags(::nix::libc::O_NONBLOCK)
98 .open(&path)
99 .with_context(|| format!("CA: failed to open device {}", &path))?;
100
101 let mut ca = CaDevice {
102 adapter,
103 device,
104
105 file,
106 slot: CaSlotInfo::default(),
107 };
108
109 ca.reset()?;
110
111 thread::sleep(CA_DELAY);
112
113 let mut caps = CaCaps::default();
114
115 for _ in 0..5 {
116 ca.get_caps(&mut caps)?;
117
118 if caps.slot_num != 0 {
119 break;
120 }
121
122 thread::sleep(CA_DELAY);
123 }
124
125 if slot >= caps.slot_num {
126 return Err(anyhow!("CA: slot {} not found", slot));
127 }
128
129 ca.slot.slot_num = slot;
130 ca.get_slot_info()?;
131
132 if ca.slot.slot_type != CA_CI_LINK {
133 return Err(anyhow!("CA: incompatible interface"));
134 }
135
136 ca.slot.flags = CA_CI_MODULE_NOT_FOUND;
138
139 Ok(ca)
140 }
141
142 fn poll_timer(&mut self) -> Result<()> {
143 let flags = self.slot.flags;
144
145 self.get_slot_info()?;
146
147 match self.slot.flags {
148 CA_CI_MODULE_PRESENT => {
149 if flags == CA_CI_MODULE_READY {
150 }
152 return Ok(());
153 }
154 CA_CI_MODULE_READY => {
155 if flags != CA_CI_MODULE_READY {
156 tpdu::init(self, self.slot.slot_num as u8)?;
157 }
158 }
159 CA_CI_MODULE_NOT_FOUND => {
160 return Err(anyhow!("CA: module not found"));
161 }
162 _ => {
163 return Err(anyhow!("CA: invalid slot flags"));
164 }
165 };
166
167 Ok(())
170 }
171
172 fn poll_event(&mut self) -> Result<()> {
173 Ok(())
176 }
177}