1 //! Safe wrapper for the `VIDIOC_QUERYCTRL` and `VIDIOC_QUERY_EXT_CTRL` ioctls.
2 use std::os::unix::io::AsRawFd;
3 
4 use bitflags::bitflags;
5 use nix::errno::Errno;
6 use thiserror::Error;
7 
8 use crate::bindings;
9 use crate::bindings::v4l2_query_ext_ctrl;
10 use crate::bindings::v4l2_queryctrl;
11 
12 /// Index of a control that has been validated, i.e. which ID is within the range of
13 /// `V4L2_CTRL_ID_MASK`.
14 #[derive(Debug, PartialEq, Eq)]
15 pub struct CtrlId(u32);
16 
17 #[derive(Debug, Error, PartialEq, Eq)]
18 pub enum CtrlIdError {
19     #[error("invalid control number: 0x{0:08x}")]
20     InvalidControl(u32),
21 }
22 
23 impl CtrlId {
24     /// Create a new control index from its u32 representation, after validation.
new(ctrl: u32) -> Result<Self, CtrlIdError>25     pub fn new(ctrl: u32) -> Result<Self, CtrlIdError> {
26         if (ctrl & bindings::V4L2_CTRL_ID_MASK) != ctrl {
27             Err(CtrlIdError::InvalidControl(ctrl))
28         } else {
29             Ok(CtrlId(ctrl))
30         }
31     }
32 }
33 
34 bitflags! {
35     #[derive(Clone, Copy, Debug)]
36     pub struct QueryCtrlFlags: u32 {
37         const NEXT = bindings::V4L2_CTRL_FLAG_NEXT_CTRL;
38         const COMPOUND = bindings::V4L2_CTRL_FLAG_NEXT_COMPOUND;
39     }
40 }
41 
42 /// Decompose a u32 between its control ID and query flags parts.
parse_ctrl_id_and_flags(ctrl: u32) -> (CtrlId, QueryCtrlFlags)43 pub fn parse_ctrl_id_and_flags(ctrl: u32) -> (CtrlId, QueryCtrlFlags) {
44     (
45         CtrlId(ctrl & bindings::V4L2_CTRL_ID_MASK),
46         QueryCtrlFlags::from_bits_truncate(ctrl),
47     )
48 }
49 
50 #[doc(hidden)]
51 mod ioctl {
52     use crate::bindings::v4l2_queryctrl;
53     nix::ioctl_readwrite!(vidioc_queryctrl, b'V', 36, v4l2_queryctrl);
54 
55     use crate::bindings::v4l2_query_ext_ctrl;
56     nix::ioctl_readwrite!(vidioc_query_ext_ctrl, b'V', 103, v4l2_query_ext_ctrl);
57 }
58 
59 #[derive(Debug, Error)]
60 pub enum QueryCtrlError {
61     #[error("ioctl error: {0}")]
62     IoctlError(Errno),
63 }
64 
65 impl From<QueryCtrlError> for Errno {
from(err: QueryCtrlError) -> Self66     fn from(err: QueryCtrlError) -> Self {
67         match err {
68             QueryCtrlError::IoctlError(e) => e,
69         }
70     }
71 }
72 
73 /// Safe wrapper around the `VIDIOC_QUERYCTRL` ioctl.
queryctrl<T: From<v4l2_queryctrl>>( fd: &impl AsRawFd, id: CtrlId, flags: QueryCtrlFlags, ) -> Result<T, QueryCtrlError>74 pub fn queryctrl<T: From<v4l2_queryctrl>>(
75     fd: &impl AsRawFd,
76     id: CtrlId,
77     flags: QueryCtrlFlags,
78 ) -> Result<T, QueryCtrlError> {
79     let mut qctrl: v4l2_queryctrl = v4l2_queryctrl {
80         id: id.0 | flags.bits(),
81         ..Default::default()
82     };
83 
84     match unsafe { ioctl::vidioc_queryctrl(fd.as_raw_fd(), &mut qctrl) } {
85         Ok(_) => Ok(T::from(qctrl)),
86         Err(e) => Err(QueryCtrlError::IoctlError(e)),
87     }
88 }
89 
90 /// Safe wrapper around the `VIDIOC_QUERYCTRL` ioctl.
query_ext_ctrl<T: From<v4l2_query_ext_ctrl>>( fd: &impl AsRawFd, id: CtrlId, flags: QueryCtrlFlags, ) -> Result<T, QueryCtrlError>91 pub fn query_ext_ctrl<T: From<v4l2_query_ext_ctrl>>(
92     fd: &impl AsRawFd,
93     id: CtrlId,
94     flags: QueryCtrlFlags,
95 ) -> Result<T, QueryCtrlError> {
96     let mut qctrl: v4l2_query_ext_ctrl = v4l2_query_ext_ctrl {
97         id: id.0 | flags.bits(),
98         ..Default::default()
99     };
100 
101     match unsafe { ioctl::vidioc_query_ext_ctrl(fd.as_raw_fd(), &mut qctrl) } {
102         Ok(_) => Ok(T::from(qctrl)),
103         Err(e) => Err(QueryCtrlError::IoctlError(e)),
104     }
105 }
106 
107 #[cfg(test)]
108 mod tests {
109     use super::*;
110 
111     #[test]
test_ctrlid()112     fn test_ctrlid() {
113         assert_eq!(
114             CtrlId::new(bindings::V4L2_CID_AUDIO_VOLUME),
115             Ok(CtrlId(bindings::V4L2_CID_AUDIO_VOLUME))
116         );
117         assert_eq!(
118             CtrlId::new(bindings::V4L2_CTRL_FLAG_NEXT_CTRL),
119             Err(CtrlIdError::InvalidControl(
120                 bindings::V4L2_CTRL_FLAG_NEXT_CTRL
121             ))
122         );
123     }
124 }
125