libdvb_rs/ca/
mod.rs

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    /// Sends reset command to CA device
43    #[inline]
44    pub fn reset(&mut self) -> Result<()> {
45        // CA_RESET
46        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    /// Gets CA capabilities
58    #[inline]
59    pub fn get_caps(&self, caps: &mut CaCaps) -> Result<()> {
60        // CA_GET_CAP
61        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    /// Gets CA slot information
75    #[inline]
76    pub fn get_slot_info(&mut self) -> Result<()> {
77        // CA_GET_SLOT_INFO
78        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    /// Attempts to open a CA device
92    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        // reset flags
137        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                    // TODO: de-init
151                }
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        // TODO: check queue?
168
169        Ok(())
170    }
171
172    fn poll_event(&mut self) -> Result<()> {
173        // TODO: tpdu read
174
175        Ok(())
176    }
177}