use std::fs::File;
use std::io;
use std::io::Read;
use std::os::fd::AsRawFd;
use std::path::{Path, PathBuf};
use error_backtrace::{Result as TracedResult, IntoTraced};
use media_subsystem::{media_ioc_device_info, media_ioc_setup_link, EntityId, InterfaceId, MediaDeviceInfo, MediaEntF, MediaIntfT, MediaLinkDesc, MediaV2Entity, MediaV2Interface, MediaV2Pad, MediaV2Topology, Zeroed};
use super::flock::{Locked, FileLike};
use tracing::debug;
pub struct Device(pub File);
impl FileLike for Device {
fn get_metadata(&self) -> io::Result<std::fs::Metadata> {
self.0.get_metadata()
}
fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
AsRawFd::as_raw_fd(&self.0)
}
}
impl Device {
pub fn new(p: impl AsRef<Path>) -> Result<Self, io::Error> {
Ok(Self(File::open(p)?))
}
pub fn get_device_info(&self) -> Result<MediaDeviceInfo, nix::errno::Errno>{
let device_pointer = AsRawFd::as_raw_fd(&self.0);
let mut media_device_info = MediaDeviceInfo::zeroed();
unsafe {
media_ioc_device_info(
device_pointer,
&mut media_device_info,
)
}?;
Ok(media_device_info)
}
pub fn try_lock(self) -> Result<Locked<Self>, Self> {
Locked::new(self)
}
pub fn get_topology(&self) -> io::Result<Topology> {
let device_pointer = AsRawFd::as_raw_fd(&self.0);
MediaV2Topology::read_from_rawfd(device_pointer).map(Topology)
}
pub fn setup_link(&mut self, link: MediaLinkDesc) -> TracedResult<(), nix::errno::Errno> {
let device_pointer = AsRawFd::as_raw_fd(&self.0);
debug!("Setup link: {:?}", link);
let mut v = link.into();
unsafe {
media_ioc_setup_link(device_pointer, &mut v)
}.map(|_| ()).with_trace()
}
}
pub struct Io;
impl Io {
pub fn find_path_by_devnode(&mut self, major: u32, minor: u32) -> Result<PathBuf, io::Error> {
let mut data = String::new();
File::open(format!("/sys/dev/char/{}:{}/uevent", major, minor))?
.read_to_string(&mut data)?;
let devline = data.lines()
.find(|line| line.starts_with("DEVNAME="))
.ok_or(io::Error::other("File does not contain a \"DEVNAME=\" line"))?;
Ok(format!("/dev/{}", devline.split_at("DEVNAME=".len()).1).into())
}
pub fn interface_find_path(&mut self, interface: &MediaV2Interface) -> Result<PathBuf, io::Error> {
self.find_path_by_devnode(interface.devnode.major, interface.devnode.minor)
}
}
#[derive(Debug, Clone)]
pub struct Topology(pub MediaV2Topology);
impl Topology {
pub fn interface(&self, id: InterfaceId) -> Option<&MediaV2Interface> {
self.0.interfaces.iter().find(|i| i.id == id)
}
pub fn pads(&self, id: EntityId) -> impl Iterator<Item=&MediaV2Pad> {
self.0.pads.iter().filter(move |p| p.entity_id == id)
}
pub fn pad_indices(&self, id: EntityId) -> impl Iterator<Item=u32> + use<'_> {
self.pads(id).map(|p| p.index)
}
pub fn get_sensors(&self) -> impl Iterator<Item=&MediaV2Entity> {
self.0.entities.iter()
.filter(|e| e.function == MediaEntF::CamSensor)
}
}