blob: 27f08c6862ce65f185f5066c98f5dd808ae8e8f6 [file] [log] [blame]
use nix::libc::c_int;
use nix::poll::{PollFd, PollTimeout};
use std::convert::TryFrom;
use std::fs::File;
use std::os::unix::io::{AsFd, AsRawFd, RawFd};
use std::os::unix::prelude::FromRawFd;
use thiserror::Error;
pub use nix::poll::PollFlags;
#[doc(hidden)]
mod ioctl {
use nix::libc::c_int;
nix::ioctl_read!(media_ioc_request_alloc, b'|', 5, c_int);
nix::ioctl_none!(media_request_ioc_queue, b'|', 0x80);
nix::ioctl_none!(media_request_ioc_reinit, b'|', 0x81);
}
#[derive(Debug, Error)]
pub enum RequestError {
#[error("Unexpected ioctl error: {0}")]
IoctlError(nix::Error),
#[error("Unknown poll flag returned")]
UnknownPollFlagReturned,
#[error("Duration is too large")]
InvalidDuration,
}
#[derive(Debug)]
pub struct Request {
fd: File,
}
impl Request {
pub fn alloc(media_fd: &impl AsRawFd) -> Result<Self, RequestError> {
let mut request_fd: RawFd = 0;
// SAFETY: the 'data' argument is an address of a variable compatible with a C integer
match unsafe {
ioctl::media_ioc_request_alloc(
media_fd.as_raw_fd(),
&mut request_fd as *mut _ as *mut c_int,
)
} {
// SAFETY: correctly executed ioctl guarantees request_fd is a correct file descriptor
Ok(_) => Ok(Request {
fd: unsafe { File::from_raw_fd(request_fd) },
}),
Err(e) => Err(RequestError::IoctlError(e)),
}
}
pub fn queue(&self) -> Result<(), RequestError> {
// SAFETY: the file descriptor is correct
match unsafe { ioctl::media_request_ioc_queue(self.as_raw_fd()) } {
Ok(_) => Ok(()),
Err(e) => Err(RequestError::IoctlError(e)),
}
}
pub fn reinit(&self) -> Result<(), RequestError> {
// SAFETY: the file descriptor is correct
match unsafe { ioctl::media_request_ioc_reinit(self.as_raw_fd()) } {
Ok(_) => Ok(()),
Err(e) => Err(RequestError::IoctlError(e)),
}
}
pub fn poll(
&self,
video_fd: &impl AsFd,
events: PollFlags,
timeout: Option<u32>,
) -> Result<PollFlags, RequestError> {
let mut poll_fd = [PollFd::new(video_fd.as_fd(), events)];
let timeout = timeout.map_or(PollTimeout::NONE, |d| PollTimeout::try_from(d).unwrap());
match nix::poll::poll(&mut poll_fd, timeout) {
Ok(0) => Ok(PollFlags::empty()),
Ok(_) => {
if let Some(poll_flags) = poll_fd[0].revents() {
Ok(poll_flags)
} else {
Err(RequestError::UnknownPollFlagReturned)
}
}
Err(e) => Err(RequestError::IoctlError(e)),
}
}
}
impl AsRawFd for Request {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}