1#![allow(unsafe_code)]
12
13use std::fs::{File, OpenOptions};
14use std::io::{self, Read, Write};
15use std::os::unix::io::AsRawFd;
16use std::time::Duration;
17
18use crate::device::{CaDevice, SlotInfo};
19
20const IOC_NRBITS: u32 = 8;
22const IOC_TYPEBITS: u32 = 8;
23const IOC_SIZEBITS: u32 = 14;
24const IOC_NRSHIFT: u32 = 0;
25const IOC_TYPESHIFT: u32 = IOC_NRSHIFT + IOC_NRBITS;
26const IOC_SIZESHIFT: u32 = IOC_TYPESHIFT + IOC_TYPEBITS;
27const IOC_DIRSHIFT: u32 = IOC_SIZESHIFT + IOC_SIZEBITS;
28const IOC_NONE: u32 = 0;
29const IOC_READ: u32 = 2;
30
31const fn ioc(dir: u32, typ: u32, nr: u32, size: u32) -> u64 {
32 ((dir << IOC_DIRSHIFT) | (typ << IOC_TYPESHIFT) | (nr << IOC_NRSHIFT) | (size << IOC_SIZESHIFT))
33 as u64
34}
35
36const DVB_CA_MAGIC: u32 = b'o' as u32;
38const CA_RESET: u64 = ioc(IOC_NONE, DVB_CA_MAGIC, 128, 0);
39const CA_GET_SLOT_INFO: u64 = ioc(
40 IOC_READ,
41 DVB_CA_MAGIC,
42 130,
43 core::mem::size_of::<CaSlotInfo>() as u32,
44);
45const CA_CI_MODULE_READY: u32 = 1;
47
48#[repr(C)]
49struct CaSlotInfo {
50 num: i32,
51 typ: i32,
52 flags: u32,
53}
54
55#[derive(Debug)]
57pub struct LinuxCaDevice {
58 file: File,
59}
60
61impl LinuxCaDevice {
62 pub fn open(adapter: u32, ca: u32) -> io::Result<Self> {
64 let path = format!("/dev/dvb/adapter{adapter}/ca{ca}");
65 let file = OpenOptions::new().read(true).write(true).open(path)?;
66 Ok(Self { file })
67 }
68
69 #[must_use]
71 pub fn from_file(file: File) -> Self {
72 Self { file }
73 }
74}
75
76impl CaDevice for LinuxCaDevice {
77 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
78 match self.file.read(buf) {
81 Ok(n) => Ok(n),
82 Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
83 Err(e) => Err(e),
84 }
85 }
86
87 fn write(&mut self, buf: &[u8]) -> io::Result<()> {
88 self.file.write_all(buf)
89 }
90
91 fn reset(&mut self) -> io::Result<()> {
92 let r = unsafe { libc::ioctl(self.file.as_raw_fd(), CA_RESET as libc::c_ulong) };
94 if r < 0 {
95 Err(io::Error::last_os_error())
96 } else {
97 Ok(())
98 }
99 }
100
101 fn slot_info(&mut self) -> io::Result<SlotInfo> {
102 let mut si = CaSlotInfo {
103 num: 0,
104 typ: 0,
105 flags: 0,
106 };
107 let r = unsafe {
110 libc::ioctl(
111 self.file.as_raw_fd(),
112 CA_GET_SLOT_INFO as libc::c_ulong,
113 &mut si as *mut CaSlotInfo,
114 )
115 };
116 if r < 0 {
117 return Err(io::Error::last_os_error());
118 }
119 Ok(SlotInfo {
120 num: si.num as u8,
121 module_ready: si.flags & CA_CI_MODULE_READY != 0,
122 })
123 }
124
125 fn poll(&mut self, timeout: Duration) -> io::Result<bool> {
126 let mut pfd = libc::pollfd {
127 fd: self.file.as_raw_fd(),
128 events: libc::POLLIN,
129 revents: 0,
130 };
131 let ms = i32::try_from(timeout.as_millis()).unwrap_or(i32::MAX);
132 let r = unsafe { libc::poll(&mut pfd as *mut libc::pollfd, 1, ms) };
134 if r < 0 {
135 Err(io::Error::last_os_error())
136 } else {
137 Ok(pfd.revents & libc::POLLIN != 0)
138 }
139 }
140}