1 //! Safe wrapper for the `VIDIOC_QUERYCAP` ioctl.
2 use super::string_from_cstr;
3 use crate::bindings;
4 use crate::bindings::v4l2_capability;
5 use bitflags::bitflags;
6 use nix::errno::Errno;
7 use std::fmt;
8 use std::os::unix::io::AsRawFd;
9 use thiserror::Error;
10 
11 bitflags! {
12     /// Flags returned by the `VIDIOC_QUERYCAP` ioctl into the `capabilities`
13     /// or `device_capabilities` field of `v4l2_capability`.
14     #[derive(Clone, Copy, Debug)]
15     pub struct Capabilities: u32 {
16         const VIDEO_CAPTURE = bindings::V4L2_CAP_VIDEO_CAPTURE;
17         const VIDEO_OUTPUT = bindings::V4L2_CAP_VIDEO_OUTPUT;
18         const VIDEO_OVERLAY = bindings::V4L2_CAP_VIDEO_OVERLAY;
19         const VBI_CAPTURE = bindings::V4L2_CAP_VBI_CAPTURE;
20         const VBI_OUTPUT = bindings::V4L2_CAP_VBI_OUTPUT;
21         const SLICED_VBI_CAPTURE = bindings::V4L2_CAP_SLICED_VBI_CAPTURE;
22         const SLICED_VBI_OUTPUT = bindings::V4L2_CAP_SLICED_VBI_OUTPUT;
23         const RDS_CAPTURE = bindings::V4L2_CAP_RDS_CAPTURE;
24         const VIDEO_OUTPUT_OVERLAY = bindings::V4L2_CAP_VIDEO_OUTPUT_OVERLAY;
25         const HW_FREQ_SEEK = bindings::V4L2_CAP_HW_FREQ_SEEK;
26         const RDS_OUTPUT = bindings::V4L2_CAP_RDS_OUTPUT;
27 
28         const VIDEO_CAPTURE_MPLANE = bindings::V4L2_CAP_VIDEO_CAPTURE_MPLANE;
29         const VIDEO_OUTPUT_MPLANE = bindings::V4L2_CAP_VIDEO_OUTPUT_MPLANE;
30         const VIDEO_M2M_MPLANE = bindings::V4L2_CAP_VIDEO_M2M_MPLANE;
31         const VIDEO_M2M = bindings::V4L2_CAP_VIDEO_M2M;
32 
33         const TUNER = bindings::V4L2_CAP_TUNER;
34         const AUDIO = bindings::V4L2_CAP_AUDIO;
35         const RADIO = bindings::V4L2_CAP_RADIO;
36         const MODULATOR = bindings::V4L2_CAP_MODULATOR;
37 
38         const SDR_CAPTURE = bindings::V4L2_CAP_SDR_CAPTURE;
39         const EXT_PIX_FORMAT = bindings::V4L2_CAP_EXT_PIX_FORMAT;
40         const SDR_OUTPUT = bindings::V4L2_CAP_SDR_OUTPUT;
41         const META_CAPTURE = bindings::V4L2_CAP_META_CAPTURE;
42 
43         const READWRITE = bindings::V4L2_CAP_READWRITE;
44         const ASYNCIO = bindings::V4L2_CAP_ASYNCIO;
45         const STREAMING = bindings::V4L2_CAP_STREAMING;
46         const META_OUTPUT = bindings::V4L2_CAP_META_OUTPUT;
47 
48         const TOUCH = bindings::V4L2_CAP_TOUCH;
49 
50         const DEVICE_CAPS = bindings::V4L2_CAP_DEVICE_CAPS;
51     }
52 }
53 
54 impl fmt::Display for Capabilities {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result55     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56         fmt::Debug::fmt(self, f)
57     }
58 }
59 
60 /// Used to get the capability flags from a `VIDIOC_QUERYCAP` ioctl.
61 impl From<v4l2_capability> for Capabilities {
from(qcap: v4l2_capability) -> Self62     fn from(qcap: v4l2_capability) -> Self {
63         Capabilities::from_bits_truncate(qcap.capabilities)
64     }
65 }
66 
67 /// Safe variant of the `v4l2_capability` struct, to be used with `querycap`.
68 #[derive(Debug)]
69 pub struct Capability {
70     pub driver: String,
71     pub card: String,
72     pub bus_info: String,
73     pub version: u32,
74     pub capabilities: Capabilities,
75     pub device_caps: Option<Capabilities>,
76 }
77 
78 impl Capability {
79     /// Returns the set of capabilities of the hardware as a whole.
capabilities(&self) -> Capabilities80     pub fn capabilities(&self) -> Capabilities {
81         self.capabilities
82     }
83 
84     /// Returns the capabilities that apply to the currently opened V4L2 node.
device_caps(&self) -> Capabilities85     pub fn device_caps(&self) -> Capabilities {
86         self.device_caps
87             .unwrap_or_else(|| self.capabilities.difference(Capabilities::DEVICE_CAPS))
88     }
89 }
90 
91 impl From<v4l2_capability> for Capability {
from(qcap: v4l2_capability) -> Self92     fn from(qcap: v4l2_capability) -> Self {
93         Capability {
94             driver: string_from_cstr(&qcap.driver).unwrap_or_else(|_| "".into()),
95             card: string_from_cstr(&qcap.card).unwrap_or_else(|_| "".into()),
96             bus_info: string_from_cstr(&qcap.bus_info).unwrap_or_else(|_| "".into()),
97             version: qcap.version,
98             capabilities: Capabilities::from_bits_truncate(qcap.capabilities),
99             device_caps: if qcap.capabilities & bindings::V4L2_CAP_DEVICE_CAPS != 0 {
100                 Some(Capabilities::from_bits_truncate(qcap.device_caps))
101             } else {
102                 None
103             },
104         }
105     }
106 }
107 
108 #[doc(hidden)]
109 mod ioctl {
110     use crate::bindings::v4l2_capability;
111     nix::ioctl_read!(vidioc_querycap, b'V', 0, v4l2_capability);
112 }
113 
114 #[derive(Debug, Error)]
115 pub enum QueryCapError {
116     #[error("ioctl error: {0}")]
117     IoctlError(Errno),
118 }
119 
120 impl From<QueryCapError> for Errno {
from(err: QueryCapError) -> Self121     fn from(err: QueryCapError) -> Self {
122         match err {
123             QueryCapError::IoctlError(e) => e,
124         }
125     }
126 }
127 
128 /// Safe wrapper around the `VIDIOC_QUERYCAP` ioctl.
querycap<T: From<v4l2_capability>>(fd: &impl AsRawFd) -> Result<T, QueryCapError>129 pub fn querycap<T: From<v4l2_capability>>(fd: &impl AsRawFd) -> Result<T, QueryCapError> {
130     let mut qcap: v4l2_capability = Default::default();
131 
132     match unsafe { ioctl::vidioc_querycap(fd.as_raw_fd(), &mut qcap) } {
133         Ok(_) => Ok(T::from(qcap)),
134         Err(e) => Err(QueryCapError::IoctlError(e)),
135     }
136 }
137