1 use std::ffi::c_int;
2 use std::os::unix::io::AsRawFd;
3 
4 use nix::errno::Errno;
5 use thiserror::Error;
6 
7 use crate::bindings::v4l2_input;
8 use crate::bindings::v4l2_output;
9 
10 #[doc(hidden)]
11 mod ioctl {
12     use std::ffi::c_int;
13 
14     use crate::bindings::v4l2_input;
15     use crate::bindings::v4l2_output;
16 
17     nix::ioctl_readwrite!(vidioc_enuminput, b'V', 26, v4l2_input);
18     nix::ioctl_read!(vidioc_g_input, b'V', 38, c_int);
19     nix::ioctl_readwrite!(vidioc_s_input, b'V', 39, c_int);
20 
21     nix::ioctl_read!(vidioc_g_output, b'V', 46, c_int);
22     nix::ioctl_readwrite!(vidioc_s_output, b'V', 47, c_int);
23     nix::ioctl_readwrite!(vidioc_enumoutput, b'V', 48, v4l2_output);
24 }
25 
26 #[derive(Debug, Error)]
27 pub enum SelectionError {
28     #[error("selection {0} is out of range")]
29     OutOfRange(usize),
30     #[error("ioctl error: {0}")]
31     IoctlError(Errno),
32 }
33 
34 impl From<SelectionError> for Errno {
from(err: SelectionError) -> Self35     fn from(err: SelectionError) -> Self {
36         match err {
37             SelectionError::OutOfRange(_) => Errno::EINVAL,
38             SelectionError::IoctlError(e) => e,
39         }
40     }
41 }
42 
43 /// Safe wrapper around the `VIDIOC_ENUMINPUT` ioctl.
enuminput<R: From<v4l2_input>>( fd: &impl AsRawFd, index: usize, ) -> Result<R, SelectionError>44 pub fn enuminput<R: From<v4l2_input>>(
45     fd: &impl AsRawFd,
46     index: usize,
47 ) -> Result<R, SelectionError> {
48     let mut input = v4l2_input {
49         index: index as u32,
50         ..Default::default()
51     };
52 
53     match unsafe { ioctl::vidioc_enuminput(fd.as_raw_fd(), &mut input) } {
54         Ok(_) => Ok(R::from(input)),
55         Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
56         Err(e) => Err(SelectionError::IoctlError(e)),
57     }
58 }
59 
60 /// Safe wrapper around the `VIDIOC_G_INPUT` ioctl.
g_input(fd: &impl AsRawFd) -> Result<usize, Errno>61 pub fn g_input(fd: &impl AsRawFd) -> Result<usize, Errno> {
62     let mut input: c_int = 0;
63 
64     unsafe { ioctl::vidioc_g_input(fd.as_raw_fd(), &mut input) }.map(|r| r as usize)
65 }
66 
67 /// Safe wrapper around the `VIDIOC_S_INPUT` ioctl.
68 ///
69 /// Returns the updated `index` upon success.
s_input(fd: &impl AsRawFd, index: usize) -> Result<usize, SelectionError>70 pub fn s_input(fd: &impl AsRawFd, index: usize) -> Result<usize, SelectionError> {
71     let mut input: c_int = index as c_int;
72 
73     match unsafe { ioctl::vidioc_s_input(fd.as_raw_fd(), &mut input) } {
74         Ok(_) => Ok(input as usize),
75         Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
76         Err(e) => Err(SelectionError::IoctlError(e)),
77     }
78 }
79 
80 /// Safe wrapper around the `VIDIOC_ENUMOUTPUT` ioctl.
enumoutput<R: From<v4l2_output>>( fd: &impl AsRawFd, index: usize, ) -> Result<R, SelectionError>81 pub fn enumoutput<R: From<v4l2_output>>(
82     fd: &impl AsRawFd,
83     index: usize,
84 ) -> Result<R, SelectionError> {
85     let mut output = v4l2_output {
86         index: index as u32,
87         ..Default::default()
88     };
89 
90     match unsafe { ioctl::vidioc_enumoutput(fd.as_raw_fd(), &mut output) } {
91         Ok(_) => Ok(R::from(output)),
92         Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
93         Err(e) => Err(SelectionError::IoctlError(e)),
94     }
95 }
96 
97 /// Safe wrapper around the `VIDIOC_G_OUTPUT` ioctl.
g_output(fd: &impl AsRawFd) -> Result<usize, Errno>98 pub fn g_output(fd: &impl AsRawFd) -> Result<usize, Errno> {
99     let mut output: c_int = 0;
100 
101     unsafe { ioctl::vidioc_g_output(fd.as_raw_fd(), &mut output) }.map(|r| r as usize)
102 }
103 
104 /// Safe wrapper around the `VIDIOC_S_OUTPUT` ioctl.
s_output(fd: &impl AsRawFd, index: usize) -> Result<(), SelectionError>105 pub fn s_output(fd: &impl AsRawFd, index: usize) -> Result<(), SelectionError> {
106     let mut output: c_int = index as c_int;
107 
108     match unsafe { ioctl::vidioc_s_output(fd.as_raw_fd(), &mut output) } {
109         Ok(_) => Ok(()),
110         Err(Errno::EINVAL) => Err(SelectionError::OutOfRange(index)),
111         Err(e) => Err(SelectionError::IoctlError(e)),
112     }
113 }
114