v4l2r 0.0.7

Safe and flexible abstraction over V4L2
Documentation
use std::ffi::c_int;
use std::os::unix::io::AsRawFd;

use nix::errno::Errno;
use thiserror::Error;

use crate::bindings::v4l2_input;
use crate::bindings::v4l2_output;

#[doc(hidden)]
mod ioctl {
    use std::ffi::c_int;

    use crate::bindings::v4l2_input;
    use crate::bindings::v4l2_output;

    nix::ioctl_readwrite!(vidioc_enuminput, b'V', 26, v4l2_input);
    nix::ioctl_read!(vidioc_g_input, b'V', 38, c_int);
    nix::ioctl_readwrite!(vidioc_s_input, b'V', 39, c_int);

    nix::ioctl_read!(vidioc_g_output, b'V', 46, c_int);
    nix::ioctl_readwrite!(vidioc_s_output, b'V', 47, c_int);
    nix::ioctl_readwrite!(vidioc_enumoutput, b'V', 48, v4l2_output);
}

#[derive(Debug, Error)]
pub enum SelectionError {
    #[error("selection {0} is out of range")]
    OutOfRange(usize),
    #[error("ioctl error: {0}")]
    IoctlError(Errno),
}

impl From<SelectionError> for Errno {
    fn from(err: SelectionError) -> Self {
        match err {
            SelectionError::OutOfRange(_) => Errno::EINVAL,
            SelectionError::IoctlError(e) => e,
        }
    }
}

/// Safe wrapper around the `VIDIOC_ENUMINPUT` ioctl.
pub fn enuminput<R: From<v4l2_input>>(
    fd: &impl AsRawFd,
    index: usize,
) -> Result<R, SelectionError> {
    let mut input = v4l2_input {
        index: index as u32,
        ..Default::default()
    };

    match unsafe { ioctl::vidioc_enuminput(fd.as_raw_fd(), &mut input) } {
        Ok(_) => Ok(R::from(input)),
        Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
        Err(e) => Err(SelectionError::IoctlError(e)),
    }
}

/// Safe wrapper around the `VIDIOC_G_INPUT` ioctl.
pub fn g_input(fd: &impl AsRawFd) -> Result<usize, Errno> {
    let mut input: c_int = 0;

    unsafe { ioctl::vidioc_g_input(fd.as_raw_fd(), &mut input) }.map(|r| r as usize)
}

/// Safe wrapper around the `VIDIOC_S_INPUT` ioctl.
///
/// Returns the updated `index` upon success.
pub fn s_input(fd: &impl AsRawFd, index: usize) -> Result<usize, SelectionError> {
    let mut input: c_int = index as c_int;

    match unsafe { ioctl::vidioc_s_input(fd.as_raw_fd(), &mut input) } {
        Ok(_) => Ok(input as usize),
        Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
        Err(e) => Err(SelectionError::IoctlError(e)),
    }
}

/// Safe wrapper around the `VIDIOC_ENUMOUTPUT` ioctl.
pub fn enumoutput<R: From<v4l2_output>>(
    fd: &impl AsRawFd,
    index: usize,
) -> Result<R, SelectionError> {
    let mut output = v4l2_output {
        index: index as u32,
        ..Default::default()
    };

    match unsafe { ioctl::vidioc_enumoutput(fd.as_raw_fd(), &mut output) } {
        Ok(_) => Ok(R::from(output)),
        Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
        Err(e) => Err(SelectionError::IoctlError(e)),
    }
}

/// Safe wrapper around the `VIDIOC_G_OUTPUT` ioctl.
pub fn g_output(fd: &impl AsRawFd) -> Result<usize, Errno> {
    let mut output: c_int = 0;

    unsafe { ioctl::vidioc_g_output(fd.as_raw_fd(), &mut output) }.map(|r| r as usize)
}

/// Safe wrapper around the `VIDIOC_S_OUTPUT` ioctl.
pub fn s_output(fd: &impl AsRawFd, index: usize) -> Result<(), SelectionError> {
    let mut output: c_int = index as c_int;

    match unsafe { ioctl::vidioc_s_output(fd.as_raw_fd(), &mut output) } {
        Ok(_) => Ok(()),
        Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
        Err(e) => Err(SelectionError::IoctlError(e)),
    }
}