#![allow(unsafe_code)]
use std::fs::{File, OpenOptions};
use std::io::{self, Read, Write};
use std::os::unix::io::AsRawFd;
use std::time::Duration;
use crate::device::{CaDevice, SlotInfo};
const IOC_NRBITS: u32 = 8;
const IOC_TYPEBITS: u32 = 8;
const IOC_SIZEBITS: u32 = 14;
const IOC_NRSHIFT: u32 = 0;
const IOC_TYPESHIFT: u32 = IOC_NRSHIFT + IOC_NRBITS;
const IOC_SIZESHIFT: u32 = IOC_TYPESHIFT + IOC_TYPEBITS;
const IOC_DIRSHIFT: u32 = IOC_SIZESHIFT + IOC_SIZEBITS;
const IOC_NONE: u32 = 0;
const IOC_READ: u32 = 2;
const fn ioc(dir: u32, typ: u32, nr: u32, size: u32) -> u64 {
((dir << IOC_DIRSHIFT) | (typ << IOC_TYPESHIFT) | (nr << IOC_NRSHIFT) | (size << IOC_SIZESHIFT))
as u64
}
const DVB_CA_MAGIC: u32 = b'o' as u32;
const CA_RESET: u64 = ioc(IOC_NONE, DVB_CA_MAGIC, 128, 0);
const CA_GET_SLOT_INFO: u64 = ioc(
IOC_READ,
DVB_CA_MAGIC,
130,
core::mem::size_of::<CaSlotInfo>() as u32,
);
const CA_CI_MODULE_READY: u32 = 1;
#[repr(C)]
struct CaSlotInfo {
num: i32,
typ: i32,
flags: u32,
}
#[derive(Debug)]
pub struct LinuxCaDevice {
file: File,
}
impl LinuxCaDevice {
pub fn open(adapter: u32, ca: u32) -> io::Result<Self> {
let path = format!("/dev/dvb/adapter{adapter}/ca{ca}");
let file = OpenOptions::new().read(true).write(true).open(path)?;
Ok(Self { file })
}
#[must_use]
pub fn from_file(file: File) -> Self {
Self { file }
}
}
impl CaDevice for LinuxCaDevice {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.file.read(buf) {
Ok(n) => Ok(n),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(0),
Err(e) => Err(e),
}
}
fn write(&mut self, buf: &[u8]) -> io::Result<()> {
self.file.write_all(buf)
}
fn reset(&mut self) -> io::Result<()> {
let r = unsafe { libc::ioctl(self.file.as_raw_fd(), CA_RESET as libc::c_ulong) };
if r < 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
fn slot_info(&mut self) -> io::Result<SlotInfo> {
let mut si = CaSlotInfo {
num: 0,
typ: 0,
flags: 0,
};
let r = unsafe {
libc::ioctl(
self.file.as_raw_fd(),
CA_GET_SLOT_INFO as libc::c_ulong,
&mut si as *mut CaSlotInfo,
)
};
if r < 0 {
return Err(io::Error::last_os_error());
}
Ok(SlotInfo {
num: si.num as u8,
module_ready: si.flags & CA_CI_MODULE_READY != 0,
})
}
fn poll(&mut self, timeout: Duration) -> io::Result<bool> {
let mut pfd = libc::pollfd {
fd: self.file.as_raw_fd(),
events: libc::POLLIN,
revents: 0,
};
let ms = i32::try_from(timeout.as_millis()).unwrap_or(i32::MAX);
let r = unsafe { libc::poll(&mut pfd as *mut libc::pollfd, 1, ms) };
if r < 0 {
Err(io::Error::last_os_error())
} else {
Ok(pfd.revents & libc::POLLIN != 0)
}
}
}