1 use std::convert::Infallible;
2 use std::convert::TryFrom;
3 use std::os::unix::io::AsRawFd;
4 
5 use nix::errno::Errno;
6 use thiserror::Error;
7 
8 use crate::ioctl::ioctl_and_convert;
9 use crate::ioctl::BufferFlags;
10 use crate::ioctl::IoctlConvertError;
11 use crate::ioctl::IoctlConvertResult;
12 use crate::ioctl::UncheckedV4l2Buffer;
13 use crate::QueueType;
14 
15 #[derive(Debug)]
16 pub struct QueryBufPlane {
17     /// Offset to pass to `mmap()` in order to obtain a mapping for this plane.
18     pub mem_offset: u32,
19     /// Length of this plane.
20     pub length: u32,
21 }
22 
23 /// Contains information about a buffer's layout, as obtained from [`crate::ioctl::querybuf`].
24 ///
25 /// It is a subset of [`crate::ioctl::V4l2Buffer`], only more convenient on occasion because its
26 /// conversion from an unchecked v4l2_buffer cannot fail.
27 ///
28 /// Single-planar buffers have one entry in [`planes`] representing the layout of their unique
29 /// plane.
30 #[derive(Debug)]
31 pub struct QueryBuffer {
32     pub index: usize,
33     pub flags: BufferFlags,
34     pub planes: Vec<QueryBufPlane>,
35 }
36 
37 impl TryFrom<UncheckedV4l2Buffer> for QueryBuffer {
38     type Error = Infallible;
39 
try_from(buffer: UncheckedV4l2Buffer) -> Result<Self, Self::Error>40     fn try_from(buffer: UncheckedV4l2Buffer) -> Result<Self, Self::Error> {
41         let v4l2_buf = buffer.0;
42         let planes = match buffer.1 {
43             None => vec![QueryBufPlane {
44                 mem_offset: unsafe { v4l2_buf.m.offset },
45                 length: v4l2_buf.length,
46             }],
47             Some(v4l2_planes) => v4l2_planes
48                 .iter()
49                 .take(v4l2_buf.length as usize)
50                 .map(|v4l2_plane| QueryBufPlane {
51                     mem_offset: unsafe { v4l2_plane.m.mem_offset },
52                     length: v4l2_plane.length,
53                 })
54                 .collect(),
55         };
56 
57         Ok(QueryBuffer {
58             index: v4l2_buf.index as usize,
59             flags: BufferFlags::from_bits_truncate(v4l2_buf.flags),
60             planes,
61         })
62     }
63 }
64 
65 #[doc(hidden)]
66 mod ioctl {
67     use crate::bindings::v4l2_buffer;
68     nix::ioctl_readwrite!(vidioc_querybuf, b'V', 9, v4l2_buffer);
69 }
70 
71 #[derive(Debug, Error)]
72 pub enum QueryBufIoctlError {
73     #[error("unsupported queue or out-of-bounds index")]
74     InvalidInput,
75     #[error("unexpected ioctl error: {0}")]
76     Other(Errno),
77 }
78 
79 impl From<Errno> for QueryBufIoctlError {
from(err: Errno) -> Self80     fn from(err: Errno) -> Self {
81         match err {
82             Errno::EINVAL => QueryBufIoctlError::InvalidInput,
83             e => QueryBufIoctlError::Other(e),
84         }
85     }
86 }
87 
88 impl From<QueryBufIoctlError> for Errno {
from(err: QueryBufIoctlError) -> Self89     fn from(err: QueryBufIoctlError) -> Self {
90         match err {
91             QueryBufIoctlError::InvalidInput => Errno::EINVAL,
92             QueryBufIoctlError::Other(e) => e,
93         }
94     }
95 }
96 
97 pub type QueryBufError<CE> = IoctlConvertError<QueryBufIoctlError, CE>;
98 pub type QueryBufResult<O, CE> = IoctlConvertResult<O, QueryBufIoctlError, CE>;
99 
100 /// Safe wrapper around the `VIDIOC_QUERYBUF` ioctl.
querybuf<O>(fd: &impl AsRawFd, queue: QueueType, index: usize) -> QueryBufResult<O, O::Error> where O: TryFrom<UncheckedV4l2Buffer>, O::Error: std::fmt::Debug,101 pub fn querybuf<O>(fd: &impl AsRawFd, queue: QueueType, index: usize) -> QueryBufResult<O, O::Error>
102 where
103     O: TryFrom<UncheckedV4l2Buffer>,
104     O::Error: std::fmt::Debug,
105 {
106     let mut v4l2_buf = UncheckedV4l2Buffer::new_for_querybuf(queue, Some(index as u32));
107 
108     ioctl_and_convert(
109         unsafe { ioctl::vidioc_querybuf(fd.as_raw_fd(), v4l2_buf.as_mut()) }
110             .map(|_| v4l2_buf)
111             .map_err(Into::into),
112     )
113 }
114