1 //! Safe wrapper for the VIDIOC_(D)QBUF and VIDIOC_QUERYBUF ioctls.
2 use nix::errno::Errno;
3 use nix::libc::{suseconds_t, time_t};
4 use nix::sys::time::{TimeVal, TimeValLike};
5 use std::convert::TryFrom;
6 use std::fmt::Debug;
7 use std::os::unix::io::AsRawFd;
8 use std::os::unix::prelude::RawFd;
9 use thiserror::Error;
10 
11 use crate::bindings;
12 use crate::ioctl::ioctl_and_convert;
13 use crate::ioctl::BufferFlags;
14 use crate::ioctl::IoctlConvertError;
15 use crate::ioctl::IoctlConvertResult;
16 use crate::ioctl::UncheckedV4l2Buffer;
17 use crate::memory::Memory;
18 use crate::memory::PlaneHandle;
19 use crate::QueueType;
20 
21 #[derive(Debug, Error)]
22 pub enum QBufIoctlError {
23     #[error("invalid number of planes specified for the buffer: got {0}, expected {1}")]
24     NumPlanesMismatch(usize, usize),
25     #[error("data offset specified while using the single-planar API")]
26     DataOffsetNotSupported,
27     #[error("unexpected ioctl error: {0}")]
28     Other(Errno),
29 }
30 
31 impl From<Errno> for QBufIoctlError {
from(errno: Errno) -> Self32     fn from(errno: Errno) -> Self {
33         Self::Other(errno)
34     }
35 }
36 
37 impl From<QBufIoctlError> for Errno {
from(err: QBufIoctlError) -> Self38     fn from(err: QBufIoctlError) -> Self {
39         match err {
40             QBufIoctlError::NumPlanesMismatch(_, _) => Errno::EINVAL,
41             QBufIoctlError::DataOffsetNotSupported => Errno::EINVAL,
42             QBufIoctlError::Other(e) => e,
43         }
44     }
45 }
46 
47 /// Representation of a single plane of a V4L2 buffer.
48 pub struct QBufPlane(pub bindings::v4l2_plane);
49 
50 impl QBufPlane {
51     // TODO remove as this is not safe - we should always specify a handle.
new(bytes_used: usize) -> Self52     pub fn new(bytes_used: usize) -> Self {
53         QBufPlane(bindings::v4l2_plane {
54             bytesused: bytes_used as u32,
55             data_offset: 0,
56             ..Default::default()
57         })
58     }
59 
new_from_handle<H: PlaneHandle>(handle: &H, bytes_used: usize) -> Self60     pub fn new_from_handle<H: PlaneHandle>(handle: &H, bytes_used: usize) -> Self {
61         let mut plane = Self::new(bytes_used);
62         handle.fill_v4l2_plane(&mut plane.0);
63         plane
64     }
65 }
66 
67 impl Debug for QBufPlane {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result68     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69         f.debug_struct("QBufPlane")
70             .field("bytesused", &self.0.bytesused)
71             .field("data_offset", &self.0.data_offset)
72             .finish()
73     }
74 }
75 
76 /// Contains all the information that can be passed to the `qbuf` ioctl.
77 // TODO Change this to contain a v4l2_buffer, and create constructors/methods
78 // to change it? Then during qbuf we just need to set m.planes to planes
79 // (after resizing it to 8) and we are good to use it as-is.
80 // We could even turn the trait into AsRef<v4l2_buffer> for good measure.
81 #[derive(Debug)]
82 pub struct QBuffer<H: PlaneHandle> {
83     index: u32,
84     queue: QueueType,
85     pub flags: BufferFlags,
86     pub field: u32,
87     pub sequence: u32,
88     pub timestamp: TimeVal,
89     pub planes: Vec<QBufPlane>,
90     pub request: Option<RawFd>,
91     pub _h: std::marker::PhantomData<H>,
92 }
93 
94 impl<H: PlaneHandle> QBuffer<H> {
new(queue: QueueType, index: u32) -> Self95     pub fn new(queue: QueueType, index: u32) -> Self {
96         QBuffer {
97             index,
98             queue,
99             flags: Default::default(),
100             field: Default::default(),
101             sequence: Default::default(),
102             timestamp: TimeVal::zero(),
103             planes: Vec::new(),
104             request: None,
105             _h: std::marker::PhantomData,
106         }
107     }
108 }
109 
110 impl<H: PlaneHandle> QBuffer<H> {
set_timestamp(mut self, sec: time_t, usec: suseconds_t) -> Self111     pub fn set_timestamp(mut self, sec: time_t, usec: suseconds_t) -> Self {
112         self.timestamp = TimeVal::new(sec, usec);
113         self
114     }
115 
set_request(mut self, fd: RawFd) -> Self116     pub fn set_request(mut self, fd: RawFd) -> Self {
117         self.request = Some(fd);
118         self.flags |= BufferFlags::REQUEST_FD;
119         self
120     }
121 }
122 
123 impl<H: PlaneHandle> From<QBuffer<H>> for UncheckedV4l2Buffer {
from(qbuf: QBuffer<H>) -> Self124     fn from(qbuf: QBuffer<H>) -> Self {
125         let mut v4l2_buf = UncheckedV4l2Buffer::new_for_querybuf(qbuf.queue, Some(qbuf.index));
126         v4l2_buf.0.index = qbuf.index;
127         v4l2_buf.0.type_ = qbuf.queue as u32;
128         v4l2_buf.0.memory = H::Memory::MEMORY_TYPE as u32;
129         v4l2_buf.0.flags = qbuf.flags.bits();
130         v4l2_buf.0.field = qbuf.field;
131         v4l2_buf.0.sequence = qbuf.sequence;
132         v4l2_buf.0.timestamp.tv_sec = qbuf.timestamp.tv_sec();
133         v4l2_buf.0.timestamp.tv_usec = qbuf.timestamp.tv_usec();
134         if let Some(request) = &qbuf.request {
135             v4l2_buf.0.__bindgen_anon_1.request_fd = *request;
136         }
137         if let Some(planes) = &mut v4l2_buf.1 {
138             for (dst_plane, src_plane) in planes.iter_mut().zip(qbuf.planes.into_iter()) {
139                 *dst_plane = src_plane.0;
140             }
141         } else {
142             let plane = &qbuf.planes[0];
143 
144             v4l2_buf.0.length = plane.0.length;
145             v4l2_buf.0.bytesused = plane.0.bytesused;
146             v4l2_buf.0.m = (&plane.0.m, H::Memory::MEMORY_TYPE).into();
147         }
148 
149         v4l2_buf
150     }
151 }
152 
153 #[doc(hidden)]
154 mod ioctl {
155     use crate::bindings::v4l2_buffer;
156     nix::ioctl_readwrite!(vidioc_querybuf, b'V', 9, v4l2_buffer);
157     nix::ioctl_readwrite!(vidioc_qbuf, b'V', 15, v4l2_buffer);
158     nix::ioctl_readwrite!(vidioc_dqbuf, b'V', 17, v4l2_buffer);
159     nix::ioctl_readwrite!(vidioc_prepare_buf, b'V', 93, v4l2_buffer);
160 }
161 
162 pub type QBufError<CE> = IoctlConvertError<QBufIoctlError, CE>;
163 pub type QBufResult<O, CE> = IoctlConvertResult<O, QBufIoctlError, CE>;
164 
165 /// Safe wrapper around the `VIDIOC_QBUF` ioctl.
166 ///
167 /// TODO: `qbuf` should be unsafe! The following invariants need to be guaranteed
168 /// by the caller:
169 ///
170 /// For MMAP buffers, any mapping must not be accessed by the caller (or any
171 /// mapping must be unmapped before queueing?). Also if the buffer has been
172 /// DMABUF-exported, its consumers must likewise not access it.
173 ///
174 /// For DMABUF buffers, the FD must not be duplicated and accessed anywhere else.
175 ///
176 /// For USERPTR buffers, things are most tricky. Not only must the data not be
177 /// accessed by anyone else, the caller also needs to guarantee that the backing
178 /// memory won't be freed until the corresponding buffer is returned by either
179 /// `dqbuf` or `streamoff`.
qbuf<I, O>(fd: &impl AsRawFd, buffer: I) -> QBufResult<O, O::Error> where I: Into<UncheckedV4l2Buffer>, O: TryFrom<UncheckedV4l2Buffer>, O::Error: std::fmt::Debug,180 pub fn qbuf<I, O>(fd: &impl AsRawFd, buffer: I) -> QBufResult<O, O::Error>
181 where
182     I: Into<UncheckedV4l2Buffer>,
183     O: TryFrom<UncheckedV4l2Buffer>,
184     O::Error: std::fmt::Debug,
185 {
186     let mut v4l2_buf: UncheckedV4l2Buffer = buffer.into();
187 
188     ioctl_and_convert(
189         unsafe { ioctl::vidioc_qbuf(fd.as_raw_fd(), v4l2_buf.as_mut()) }
190             .map(|_| v4l2_buf)
191             .map_err(Into::into),
192     )
193 }
194 
195 /// Safe wrapper around the `VIDIOC_PREPARE_BUF` ioctl.
prepare_buf<I, O>(fd: &impl AsRawFd, buffer: I) -> QBufResult<O, O::Error> where I: Into<UncheckedV4l2Buffer>, O: TryFrom<UncheckedV4l2Buffer>, O::Error: std::fmt::Debug,196 pub fn prepare_buf<I, O>(fd: &impl AsRawFd, buffer: I) -> QBufResult<O, O::Error>
197 where
198     I: Into<UncheckedV4l2Buffer>,
199     O: TryFrom<UncheckedV4l2Buffer>,
200     O::Error: std::fmt::Debug,
201 {
202     let mut v4l2_buf: UncheckedV4l2Buffer = buffer.into();
203 
204     ioctl_and_convert(
205         unsafe { ioctl::vidioc_prepare_buf(fd.as_raw_fd(), v4l2_buf.as_mut()) }
206             .map(|_| v4l2_buf)
207             .map_err(Into::into),
208     )
209 }
210