xref: /aosp_15_r20/external/virtio-media/device/src/ioctl.rs (revision 1b4853f54772485c5dd4001ae33a7a958bcc97a1)
1 // Copyright 2024 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::io::Result as IoResult;
6 
7 use v4l2r::bindings::v4l2_audio;
8 use v4l2r::bindings::v4l2_audioout;
9 use v4l2r::bindings::v4l2_buffer;
10 use v4l2r::bindings::v4l2_control;
11 use v4l2r::bindings::v4l2_create_buffers;
12 use v4l2r::bindings::v4l2_decoder_cmd;
13 use v4l2r::bindings::v4l2_dv_timings;
14 use v4l2r::bindings::v4l2_dv_timings_cap;
15 use v4l2r::bindings::v4l2_enc_idx;
16 use v4l2r::bindings::v4l2_encoder_cmd;
17 use v4l2r::bindings::v4l2_enum_dv_timings;
18 use v4l2r::bindings::v4l2_event_subscription;
19 use v4l2r::bindings::v4l2_ext_control;
20 use v4l2r::bindings::v4l2_ext_controls;
21 use v4l2r::bindings::v4l2_fmtdesc;
22 use v4l2r::bindings::v4l2_format;
23 use v4l2r::bindings::v4l2_frequency;
24 use v4l2r::bindings::v4l2_frequency_band;
25 use v4l2r::bindings::v4l2_frmivalenum;
26 use v4l2r::bindings::v4l2_frmsizeenum;
27 use v4l2r::bindings::v4l2_input;
28 use v4l2r::bindings::v4l2_modulator;
29 use v4l2r::bindings::v4l2_output;
30 use v4l2r::bindings::v4l2_plane;
31 use v4l2r::bindings::v4l2_query_ext_ctrl;
32 use v4l2r::bindings::v4l2_queryctrl;
33 use v4l2r::bindings::v4l2_querymenu;
34 use v4l2r::bindings::v4l2_rect;
35 use v4l2r::bindings::v4l2_requestbuffers;
36 use v4l2r::bindings::v4l2_selection;
37 use v4l2r::bindings::v4l2_standard;
38 use v4l2r::bindings::v4l2_std_id;
39 use v4l2r::bindings::v4l2_streamparm;
40 use v4l2r::bindings::v4l2_tuner;
41 use v4l2r::ioctl::AudioMode;
42 use v4l2r::ioctl::CtrlId;
43 use v4l2r::ioctl::CtrlWhich;
44 use v4l2r::ioctl::EventType as V4l2EventType;
45 use v4l2r::ioctl::QueryCtrlFlags;
46 use v4l2r::ioctl::SelectionFlags;
47 use v4l2r::ioctl::SelectionTarget;
48 use v4l2r::ioctl::SelectionType;
49 use v4l2r::ioctl::SubscribeEventFlags;
50 use v4l2r::ioctl::TunerMode;
51 use v4l2r::ioctl::TunerTransmissionFlags;
52 use v4l2r::ioctl::TunerType;
53 use v4l2r::ioctl::UncheckedV4l2Buffer;
54 use v4l2r::ioctl::V4l2Buffer;
55 use v4l2r::ioctl::V4l2PlanesWithBacking;
56 use v4l2r::memory::MemoryType;
57 use v4l2r::QueueDirection;
58 use v4l2r::QueueType;
59 
60 use crate::protocol::RespHeader;
61 use crate::protocol::SgEntry;
62 use crate::protocol::V4l2Ioctl;
63 use crate::FromDescriptorChain;
64 use crate::ReadFromDescriptorChain;
65 use crate::ToDescriptorChain;
66 use crate::WriteToDescriptorChain;
67 
68 /// Module allowing select V4L2 structures from implementing zerocopy and implementations of
69 /// [`FromDescriptorChain`] and [`ToDescriptorChain`] for them.
70 mod v4l2_zerocopy {
71     use v4l2r::bindings;
72     use zerocopy::AsBytes;
73     use zerocopy::FromBytes;
74     use zerocopy::FromZeroes;
75 
76     use crate::FromDescriptorChain;
77     use crate::ReadFromDescriptorChain;
78     use crate::ToDescriptorChain;
79 
80     /// Wrapper allowing any structure to be read/written using zerocopy. This obviously should be
81     /// used with caution and thus is private.
82     #[repr(transparent)]
83     struct ForceZeroCopyWrapper<T: Sized>(T);
84 
85     unsafe impl<T: Sized> FromZeroes for ForceZeroCopyWrapper<T> {
only_derive_is_allowed_to_implement_this_trait()86         fn only_derive_is_allowed_to_implement_this_trait() {}
87     }
88 
89     unsafe impl<T: Sized> FromBytes for ForceZeroCopyWrapper<T> {
only_derive_is_allowed_to_implement_this_trait()90         fn only_derive_is_allowed_to_implement_this_trait() {}
91     }
92 
93     unsafe impl<T: Sized> AsBytes for ForceZeroCopyWrapper<T> {
only_derive_is_allowed_to_implement_this_trait() where Self: Sized,94         fn only_derive_is_allowed_to_implement_this_trait()
95         where
96             Self: Sized,
97         {
98         }
99     }
100 
101     impl<T> FromDescriptorChain for ForceZeroCopyWrapper<T> {
read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self>102         fn read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
103             reader.read_obj()
104         }
105     }
106 
107     impl<T> ToDescriptorChain for ForceZeroCopyWrapper<T> {
write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()>108         fn write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
109             writer.write_all(self.as_bytes())
110         }
111     }
112 
113     /// Trait granting implementations of [`FromDescriptorChain`] and [`ToDescriptorChain`] to
114     /// implementors.
115     ///
116     /// # Safety
117     ///
118     /// Only types that can be read from an arbitrary stream of data should implement this. This
119     /// covers all V4L2 types used in ioctls.
120     unsafe trait ForceZeroCopy {}
121 
122     impl<T: ForceZeroCopy> FromDescriptorChain for T {
read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self>123         fn read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
124             ForceZeroCopyWrapper::<T>::read_from_chain(reader).map(|r| r.0)
125         }
126     }
127 
128     impl<T: ForceZeroCopy> ToDescriptorChain for T {
write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()>129         fn write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
130             unsafe { std::mem::transmute::<&T, &ForceZeroCopyWrapper<T>>(self) }
131                 .write_to_chain(writer)
132         }
133     }
134 
135     // Allows V4L2 types to be read from/written to a descriptor chain.
136 
137     unsafe impl ForceZeroCopy for () {}
138     unsafe impl ForceZeroCopy for u32 {}
139     unsafe impl ForceZeroCopy for i32 {}
140     unsafe impl ForceZeroCopy for bindings::v4l2_buffer {}
141     unsafe impl ForceZeroCopy for bindings::v4l2_standard {}
142     unsafe impl ForceZeroCopy for bindings::v4l2_input {}
143     unsafe impl ForceZeroCopy for bindings::v4l2_control {}
144     unsafe impl ForceZeroCopy for bindings::v4l2_std_id {}
145     unsafe impl ForceZeroCopy for bindings::v4l2_tuner {}
146     unsafe impl ForceZeroCopy for bindings::v4l2_audio {}
147     unsafe impl ForceZeroCopy for bindings::v4l2_plane {}
148     unsafe impl ForceZeroCopy for bindings::v4l2_format {}
149     unsafe impl ForceZeroCopy for bindings::v4l2_enc_idx {}
150     unsafe impl ForceZeroCopy for bindings::v4l2_output {}
151     unsafe impl ForceZeroCopy for bindings::v4l2_audioout {}
152     unsafe impl ForceZeroCopy for bindings::v4l2_modulator {}
153     unsafe impl ForceZeroCopy for bindings::v4l2_frequency {}
154     unsafe impl ForceZeroCopy for bindings::v4l2_frmsizeenum {}
155     unsafe impl ForceZeroCopy for bindings::v4l2_frmivalenum {}
156     unsafe impl ForceZeroCopy for bindings::v4l2_encoder_cmd {}
157     unsafe impl ForceZeroCopy for bindings::v4l2_decoder_cmd {}
158     unsafe impl ForceZeroCopy for bindings::v4l2_dv_timings {}
159     unsafe impl ForceZeroCopy for bindings::v4l2_event_subscription {}
160     unsafe impl ForceZeroCopy for bindings::v4l2_create_buffers {}
161     unsafe impl ForceZeroCopy for bindings::v4l2_selection {}
162     unsafe impl ForceZeroCopy for bindings::v4l2_enum_dv_timings {}
163     unsafe impl ForceZeroCopy for bindings::v4l2_dv_timings_cap {}
164     unsafe impl ForceZeroCopy for bindings::v4l2_frequency_band {}
165     unsafe impl ForceZeroCopy for bindings::v4l2_query_ext_ctrl {}
166     unsafe impl ForceZeroCopy for bindings::v4l2_queryctrl {}
167     unsafe impl ForceZeroCopy for bindings::v4l2_querymenu {}
168     unsafe impl ForceZeroCopy for bindings::v4l2_ext_control {}
169     unsafe impl ForceZeroCopy for bindings::v4l2_ext_controls {}
170     unsafe impl ForceZeroCopy for bindings::v4l2_fmtdesc {}
171     unsafe impl ForceZeroCopy for bindings::v4l2_requestbuffers {}
172     unsafe impl ForceZeroCopy for bindings::v4l2_streamparm {}
173 
174     unsafe impl ForceZeroCopy for crate::protocol::DequeueBufferEvent {}
175     unsafe impl ForceZeroCopy for crate::protocol::SessionEvent {}
176 }
177 
178 /// Returns `ENOTTY` to signal that an ioctl is not handled by this device.
179 macro_rules! unhandled_ioctl {
180     () => {
181         Err(libc::ENOTTY)
182     };
183 }
184 
185 pub type IoctlResult<T> = Result<T, i32>;
186 
187 /// Trait for implementing ioctls supported by a device.
188 ///
189 /// It provides a default implementation for all ioctls that returns the error code for an
190 /// unsupported ioctl (`ENOTTY`) to the driver. This means that a device just needs to implement
191 /// this trait and override the ioctls it supports in order to provide the expected behavior. All
192 /// parsing and input validation is done by the companion function [`virtio_media_dispatch_ioctl`].
193 #[allow(unused_variables)]
194 pub trait VirtioMediaIoctlHandler {
195     type Session;
196 
enum_fmt( &mut self, session: &Self::Session, queue: QueueType, index: u32, ) -> IoctlResult<v4l2_fmtdesc>197     fn enum_fmt(
198         &mut self,
199         session: &Self::Session,
200         queue: QueueType,
201         index: u32,
202     ) -> IoctlResult<v4l2_fmtdesc> {
203         unhandled_ioctl!()
204     }
g_fmt(&mut self, session: &Self::Session, queue: QueueType) -> IoctlResult<v4l2_format>205     fn g_fmt(&mut self, session: &Self::Session, queue: QueueType) -> IoctlResult<v4l2_format> {
206         unhandled_ioctl!()
207     }
208     /// Hook for the `VIDIOC_S_FMT` ioctl.
209     ///
210     /// `queue` is guaranteed to match `format.type_`.
s_fmt( &mut self, session: &mut Self::Session, queue: QueueType, format: v4l2_format, ) -> IoctlResult<v4l2_format>211     fn s_fmt(
212         &mut self,
213         session: &mut Self::Session,
214         queue: QueueType,
215         format: v4l2_format,
216     ) -> IoctlResult<v4l2_format> {
217         unhandled_ioctl!()
218     }
reqbufs( &mut self, session: &mut Self::Session, queue: QueueType, memory: MemoryType, count: u32, ) -> IoctlResult<v4l2_requestbuffers>219     fn reqbufs(
220         &mut self,
221         session: &mut Self::Session,
222         queue: QueueType,
223         memory: MemoryType,
224         count: u32,
225     ) -> IoctlResult<v4l2_requestbuffers> {
226         unhandled_ioctl!()
227     }
querybuf( &mut self, session: &Self::Session, queue: QueueType, index: u32, ) -> IoctlResult<V4l2Buffer>228     fn querybuf(
229         &mut self,
230         session: &Self::Session,
231         queue: QueueType,
232         index: u32,
233     ) -> IoctlResult<V4l2Buffer> {
234         unhandled_ioctl!()
235     }
236 
237     // TODO qbuf needs a better structure to represent a buffer and its potential guest buffers.
qbuf( &mut self, session: &mut Self::Session, buffer: V4l2Buffer, guest_regions: Vec<Vec<SgEntry>>, ) -> IoctlResult<V4l2Buffer>238     fn qbuf(
239         &mut self,
240         session: &mut Self::Session,
241         buffer: V4l2Buffer,
242         guest_regions: Vec<Vec<SgEntry>>,
243     ) -> IoctlResult<V4l2Buffer> {
244         unhandled_ioctl!()
245     }
246 
247     // TODO expbuf
248 
streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()>249     fn streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
250         unhandled_ioctl!()
251     }
streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()>252     fn streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
253         unhandled_ioctl!()
254     }
255 
g_parm( &mut self, session: &Self::Session, queue: QueueType, ) -> IoctlResult<v4l2_streamparm>256     fn g_parm(
257         &mut self,
258         session: &Self::Session,
259         queue: QueueType,
260     ) -> IoctlResult<v4l2_streamparm> {
261         unhandled_ioctl!()
262     }
s_parm( &mut self, session: &mut Self::Session, parm: v4l2_streamparm, ) -> IoctlResult<v4l2_streamparm>263     fn s_parm(
264         &mut self,
265         session: &mut Self::Session,
266         parm: v4l2_streamparm,
267     ) -> IoctlResult<v4l2_streamparm> {
268         unhandled_ioctl!()
269     }
270 
g_std(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id>271     fn g_std(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id> {
272         unhandled_ioctl!()
273     }
274 
s_std(&mut self, session: &mut Self::Session, std: v4l2_std_id) -> IoctlResult<()>275     fn s_std(&mut self, session: &mut Self::Session, std: v4l2_std_id) -> IoctlResult<()> {
276         unhandled_ioctl!()
277     }
278 
enumstd(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_standard>279     fn enumstd(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_standard> {
280         unhandled_ioctl!()
281     }
282 
enuminput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_input>283     fn enuminput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_input> {
284         unhandled_ioctl!()
285     }
286 
g_ctrl(&mut self, session: &Self::Session, id: u32) -> IoctlResult<v4l2_control>287     fn g_ctrl(&mut self, session: &Self::Session, id: u32) -> IoctlResult<v4l2_control> {
288         unhandled_ioctl!()
289     }
290 
s_ctrl( &mut self, session: &mut Self::Session, id: u32, value: i32, ) -> IoctlResult<v4l2_control>291     fn s_ctrl(
292         &mut self,
293         session: &mut Self::Session,
294         id: u32,
295         value: i32,
296     ) -> IoctlResult<v4l2_control> {
297         unhandled_ioctl!()
298     }
299 
g_tuner(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_tuner>300     fn g_tuner(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_tuner> {
301         unhandled_ioctl!()
302     }
303 
s_tuner( &mut self, session: &mut Self::Session, index: u32, mode: TunerMode, ) -> IoctlResult<()>304     fn s_tuner(
305         &mut self,
306         session: &mut Self::Session,
307         index: u32,
308         mode: TunerMode,
309     ) -> IoctlResult<()> {
310         unhandled_ioctl!()
311     }
312 
g_audio(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audio>313     fn g_audio(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audio> {
314         unhandled_ioctl!()
315     }
316 
s_audio( &mut self, session: &mut Self::Session, index: u32, mode: Option<AudioMode>, ) -> IoctlResult<()>317     fn s_audio(
318         &mut self,
319         session: &mut Self::Session,
320         index: u32,
321         mode: Option<AudioMode>,
322     ) -> IoctlResult<()> {
323         unhandled_ioctl!()
324     }
325 
queryctrl( &mut self, session: &Self::Session, id: CtrlId, flags: QueryCtrlFlags, ) -> IoctlResult<v4l2_queryctrl>326     fn queryctrl(
327         &mut self,
328         session: &Self::Session,
329         id: CtrlId,
330         flags: QueryCtrlFlags,
331     ) -> IoctlResult<v4l2_queryctrl> {
332         unhandled_ioctl!()
333     }
334 
querymenu( &mut self, session: &Self::Session, id: u32, index: u32, ) -> IoctlResult<v4l2_querymenu>335     fn querymenu(
336         &mut self,
337         session: &Self::Session,
338         id: u32,
339         index: u32,
340     ) -> IoctlResult<v4l2_querymenu> {
341         unhandled_ioctl!()
342     }
343 
g_input(&mut self, session: &Self::Session) -> IoctlResult<i32>344     fn g_input(&mut self, session: &Self::Session) -> IoctlResult<i32> {
345         unhandled_ioctl!()
346     }
347 
s_input(&mut self, session: &mut Self::Session, input: i32) -> IoctlResult<i32>348     fn s_input(&mut self, session: &mut Self::Session, input: i32) -> IoctlResult<i32> {
349         unhandled_ioctl!()
350     }
351 
g_output(&mut self, session: &Self::Session) -> IoctlResult<i32>352     fn g_output(&mut self, session: &Self::Session) -> IoctlResult<i32> {
353         unhandled_ioctl!()
354     }
355 
s_output(&mut self, session: &mut Self::Session, output: i32) -> IoctlResult<i32>356     fn s_output(&mut self, session: &mut Self::Session, output: i32) -> IoctlResult<i32> {
357         unhandled_ioctl!()
358     }
359 
enumoutput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_output>360     fn enumoutput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_output> {
361         unhandled_ioctl!()
362     }
363 
g_audout(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audioout>364     fn g_audout(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audioout> {
365         unhandled_ioctl!()
366     }
367 
s_audout(&mut self, session: &mut Self::Session, index: u32) -> IoctlResult<()>368     fn s_audout(&mut self, session: &mut Self::Session, index: u32) -> IoctlResult<()> {
369         unhandled_ioctl!()
370     }
371 
g_modulator(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_modulator>372     fn g_modulator(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_modulator> {
373         unhandled_ioctl!()
374     }
375 
s_modulator( &mut self, session: &mut Self::Session, index: u32, flags: TunerTransmissionFlags, ) -> IoctlResult<()>376     fn s_modulator(
377         &mut self,
378         session: &mut Self::Session,
379         index: u32,
380         flags: TunerTransmissionFlags,
381     ) -> IoctlResult<()> {
382         unhandled_ioctl!()
383     }
384 
g_frequency(&mut self, session: &Self::Session, tuner: u32) -> IoctlResult<v4l2_frequency>385     fn g_frequency(&mut self, session: &Self::Session, tuner: u32) -> IoctlResult<v4l2_frequency> {
386         unhandled_ioctl!()
387     }
388 
s_frequency( &mut self, session: &mut Self::Session, tuner: u32, type_: TunerType, frequency: u32, ) -> IoctlResult<()>389     fn s_frequency(
390         &mut self,
391         session: &mut Self::Session,
392         tuner: u32,
393         type_: TunerType,
394         frequency: u32,
395     ) -> IoctlResult<()> {
396         unhandled_ioctl!()
397     }
398 
querystd(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id>399     fn querystd(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id> {
400         unhandled_ioctl!()
401     }
402 
403     /// Hook for the `VIDIOC_TRY_FMT` ioctl.
404     ///
405     /// `queue` is guaranteed to match `format.type_`.
try_fmt( &mut self, session: &Self::Session, queue: QueueType, format: v4l2_format, ) -> IoctlResult<v4l2_format>406     fn try_fmt(
407         &mut self,
408         session: &Self::Session,
409         queue: QueueType,
410         format: v4l2_format,
411     ) -> IoctlResult<v4l2_format> {
412         unhandled_ioctl!()
413     }
414 
enumaudio(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audio>415     fn enumaudio(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audio> {
416         unhandled_ioctl!()
417     }
418 
enumaudout(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audioout>419     fn enumaudout(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audioout> {
420         unhandled_ioctl!()
421     }
422 
423     /// Ext control ioctls modify `ctrls` and `ctrl_array` in place instead of returning them.
g_ext_ctrls( &mut self, session: &Self::Session, which: CtrlWhich, ctrls: &mut v4l2_ext_controls, ctrl_array: &mut Vec<v4l2_ext_control>, user_regions: Vec<Vec<SgEntry>>, ) -> IoctlResult<()>424     fn g_ext_ctrls(
425         &mut self,
426         session: &Self::Session,
427         which: CtrlWhich,
428         ctrls: &mut v4l2_ext_controls,
429         ctrl_array: &mut Vec<v4l2_ext_control>,
430         user_regions: Vec<Vec<SgEntry>>,
431     ) -> IoctlResult<()> {
432         unhandled_ioctl!()
433     }
434     /// Ext control ioctls modify `ctrls` and `ctrl_array` in place instead of returning them.
s_ext_ctrls( &mut self, session: &mut Self::Session, which: CtrlWhich, ctrls: &mut v4l2_ext_controls, ctrl_array: &mut Vec<v4l2_ext_control>, user_regions: Vec<Vec<SgEntry>>, ) -> IoctlResult<()>435     fn s_ext_ctrls(
436         &mut self,
437         session: &mut Self::Session,
438         which: CtrlWhich,
439         ctrls: &mut v4l2_ext_controls,
440         ctrl_array: &mut Vec<v4l2_ext_control>,
441         user_regions: Vec<Vec<SgEntry>>,
442     ) -> IoctlResult<()> {
443         unhandled_ioctl!()
444     }
445     /// Ext control ioctls modify `ctrls` and `ctrl_array` in place instead of returning them.
try_ext_ctrls( &mut self, session: &Self::Session, which: CtrlWhich, ctrls: &mut v4l2_ext_controls, ctrl_array: &mut Vec<v4l2_ext_control>, user_regions: Vec<Vec<SgEntry>>, ) -> IoctlResult<()>446     fn try_ext_ctrls(
447         &mut self,
448         session: &Self::Session,
449         which: CtrlWhich,
450         ctrls: &mut v4l2_ext_controls,
451         ctrl_array: &mut Vec<v4l2_ext_control>,
452         user_regions: Vec<Vec<SgEntry>>,
453     ) -> IoctlResult<()> {
454         unhandled_ioctl!()
455     }
456 
enum_framesizes( &mut self, session: &Self::Session, index: u32, pixel_format: u32, ) -> IoctlResult<v4l2_frmsizeenum>457     fn enum_framesizes(
458         &mut self,
459         session: &Self::Session,
460         index: u32,
461         pixel_format: u32,
462     ) -> IoctlResult<v4l2_frmsizeenum> {
463         unhandled_ioctl!()
464     }
465 
enum_frameintervals( &mut self, session: &Self::Session, index: u32, pixel_format: u32, width: u32, height: u32, ) -> IoctlResult<v4l2_frmivalenum>466     fn enum_frameintervals(
467         &mut self,
468         session: &Self::Session,
469         index: u32,
470         pixel_format: u32,
471         width: u32,
472         height: u32,
473     ) -> IoctlResult<v4l2_frmivalenum> {
474         unhandled_ioctl!()
475     }
476 
g_enc_index(&mut self, session: &Self::Session) -> IoctlResult<v4l2_enc_idx>477     fn g_enc_index(&mut self, session: &Self::Session) -> IoctlResult<v4l2_enc_idx> {
478         unhandled_ioctl!()
479     }
480 
encoder_cmd( &mut self, session: &mut Self::Session, cmd: v4l2_encoder_cmd, ) -> IoctlResult<v4l2_encoder_cmd>481     fn encoder_cmd(
482         &mut self,
483         session: &mut Self::Session,
484         cmd: v4l2_encoder_cmd,
485     ) -> IoctlResult<v4l2_encoder_cmd> {
486         unhandled_ioctl!()
487     }
488 
try_encoder_cmd( &mut self, session: &Self::Session, cmd: v4l2_encoder_cmd, ) -> IoctlResult<v4l2_encoder_cmd>489     fn try_encoder_cmd(
490         &mut self,
491         session: &Self::Session,
492         cmd: v4l2_encoder_cmd,
493     ) -> IoctlResult<v4l2_encoder_cmd> {
494         unhandled_ioctl!()
495     }
496 
s_dv_timings( &mut self, session: &mut Self::Session, timings: v4l2_dv_timings, ) -> IoctlResult<v4l2_dv_timings>497     fn s_dv_timings(
498         &mut self,
499         session: &mut Self::Session,
500         timings: v4l2_dv_timings,
501     ) -> IoctlResult<v4l2_dv_timings> {
502         unhandled_ioctl!()
503     }
504 
g_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings>505     fn g_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings> {
506         unhandled_ioctl!()
507     }
508 
subscribe_event( &mut self, session: &mut Self::Session, event: V4l2EventType, flags: SubscribeEventFlags, ) -> IoctlResult<()>509     fn subscribe_event(
510         &mut self,
511         session: &mut Self::Session,
512         event: V4l2EventType,
513         flags: SubscribeEventFlags,
514     ) -> IoctlResult<()> {
515         unhandled_ioctl!()
516     }
517 
unsubscribe_event( &mut self, session: &mut Self::Session, event: v4l2_event_subscription, ) -> IoctlResult<()>518     fn unsubscribe_event(
519         &mut self,
520         session: &mut Self::Session,
521         event: v4l2_event_subscription,
522     ) -> IoctlResult<()> {
523         unhandled_ioctl!()
524     }
525 
526     /// `queue` and `memory` are validated versions of the information in `create_buffers`.
527     ///
528     /// `create_buffers` is modified in place and returned to the guest event in case of error.
create_bufs( &mut self, session: &mut Self::Session, count: u32, queue: QueueType, memory: MemoryType, format: v4l2_format, ) -> IoctlResult<v4l2_create_buffers>529     fn create_bufs(
530         &mut self,
531         session: &mut Self::Session,
532         count: u32,
533         queue: QueueType,
534         memory: MemoryType,
535         format: v4l2_format,
536     ) -> IoctlResult<v4l2_create_buffers> {
537         unhandled_ioctl!()
538     }
539 
540     // TODO like qbuf, this needs a better structure to represent a buffer and its potential guest
541     // buffers.
prepare_buf( &mut self, session: &mut Self::Session, buffer: V4l2Buffer, guest_regions: Vec<Vec<SgEntry>>, ) -> IoctlResult<V4l2Buffer>542     fn prepare_buf(
543         &mut self,
544         session: &mut Self::Session,
545         buffer: V4l2Buffer,
546         guest_regions: Vec<Vec<SgEntry>>,
547     ) -> IoctlResult<V4l2Buffer> {
548         unhandled_ioctl!()
549     }
550 
g_selection( &mut self, session: &Self::Session, sel_type: SelectionType, sel_target: SelectionTarget, ) -> IoctlResult<v4l2_rect>551     fn g_selection(
552         &mut self,
553         session: &Self::Session,
554         sel_type: SelectionType,
555         sel_target: SelectionTarget,
556     ) -> IoctlResult<v4l2_rect> {
557         unhandled_ioctl!()
558     }
559 
s_selection( &mut self, session: &mut Self::Session, sel_type: SelectionType, sel_target: SelectionTarget, sel_rect: v4l2_rect, sel_flags: SelectionFlags, ) -> IoctlResult<v4l2_rect>560     fn s_selection(
561         &mut self,
562         session: &mut Self::Session,
563         sel_type: SelectionType,
564         sel_target: SelectionTarget,
565         sel_rect: v4l2_rect,
566         sel_flags: SelectionFlags,
567     ) -> IoctlResult<v4l2_rect> {
568         unhandled_ioctl!()
569     }
570 
decoder_cmd( &mut self, session: &mut Self::Session, cmd: v4l2_decoder_cmd, ) -> IoctlResult<v4l2_decoder_cmd>571     fn decoder_cmd(
572         &mut self,
573         session: &mut Self::Session,
574         cmd: v4l2_decoder_cmd,
575     ) -> IoctlResult<v4l2_decoder_cmd> {
576         unhandled_ioctl!()
577     }
578 
try_decoder_cmd( &mut self, session: &Self::Session, cmd: v4l2_decoder_cmd, ) -> IoctlResult<v4l2_decoder_cmd>579     fn try_decoder_cmd(
580         &mut self,
581         session: &Self::Session,
582         cmd: v4l2_decoder_cmd,
583     ) -> IoctlResult<v4l2_decoder_cmd> {
584         unhandled_ioctl!()
585     }
586 
enum_dv_timings( &mut self, session: &Self::Session, index: u32, ) -> IoctlResult<v4l2_dv_timings>587     fn enum_dv_timings(
588         &mut self,
589         session: &Self::Session,
590         index: u32,
591     ) -> IoctlResult<v4l2_dv_timings> {
592         unhandled_ioctl!()
593     }
594 
query_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings>595     fn query_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings> {
596         unhandled_ioctl!()
597     }
598 
dv_timings_cap(&self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings_cap>599     fn dv_timings_cap(&self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings_cap> {
600         unhandled_ioctl!()
601     }
602 
enum_freq_bands( &self, session: &Self::Session, tuner: u32, type_: TunerType, index: u32, ) -> IoctlResult<v4l2_frequency_band>603     fn enum_freq_bands(
604         &self,
605         session: &Self::Session,
606         tuner: u32,
607         type_: TunerType,
608         index: u32,
609     ) -> IoctlResult<v4l2_frequency_band> {
610         unhandled_ioctl!()
611     }
612 
query_ext_ctrl( &mut self, session: &Self::Session, id: CtrlId, flags: QueryCtrlFlags, ) -> IoctlResult<v4l2_query_ext_ctrl>613     fn query_ext_ctrl(
614         &mut self,
615         session: &Self::Session,
616         id: CtrlId,
617         flags: QueryCtrlFlags,
618     ) -> IoctlResult<v4l2_query_ext_ctrl> {
619         unhandled_ioctl!()
620     }
621 }
622 
623 /// Writes a `ENOTTY` error response into `writer` to signal that an ioctl is not implemented by
624 /// the device.
invalid_ioctl<W: std::io::Write>(code: V4l2Ioctl, writer: &mut W) -> IoResult<()>625 fn invalid_ioctl<W: std::io::Write>(code: V4l2Ioctl, writer: &mut W) -> IoResult<()> {
626     writer.write_err_response(libc::ENOTTY).map_err(|e| {
627         log::error!(
628             "failed to write error response for invalid ioctl {:?}: {:#}",
629             code,
630             e
631         );
632         e
633     })
634 }
635 
636 /// Reads a SG list of guest physical addresses passed from the driver and returns it.
get_userptr_regions<R: std::io::Read>(r: &mut R, size: usize) -> anyhow::Result<Vec<SgEntry>>637 fn get_userptr_regions<R: std::io::Read>(r: &mut R, size: usize) -> anyhow::Result<Vec<SgEntry>> {
638     let mut bytes_taken = 0;
639     let mut res = Vec::new();
640 
641     while bytes_taken < size {
642         let sg_entry = r.read_obj::<SgEntry>()?;
643         bytes_taken += sg_entry.len as usize;
644         res.push(sg_entry);
645     }
646 
647     Ok(res)
648 }
649 
650 /// Allows to easily read a `v4l2_buffer` of `USERPTR` memory type and its associated guest-side
651 /// buffers from a descriptor chain.
652 impl FromDescriptorChain for (V4l2Buffer, Vec<Vec<SgEntry>>) {
read_from_chain<R: std::io::Read>(reader: &mut R) -> IoResult<Self> where Self: Sized,653     fn read_from_chain<R: std::io::Read>(reader: &mut R) -> IoResult<Self>
654     where
655         Self: Sized,
656     {
657         let v4l2_buffer = v4l2_buffer::read_from_chain(reader)?;
658         let queue = match QueueType::n(v4l2_buffer.type_) {
659             Some(queue) => queue,
660             None => return Err(std::io::ErrorKind::InvalidData.into()),
661         };
662 
663         let v4l2_planes = if queue.is_multiplanar() && v4l2_buffer.length > 0 {
664             if v4l2_buffer.length > v4l2r::bindings::VIDEO_MAX_PLANES {
665                 return Err(std::io::ErrorKind::InvalidData.into());
666             }
667 
668             let planes: [v4l2r::bindings::v4l2_plane; v4l2r::bindings::VIDEO_MAX_PLANES as usize] =
669                 (0..v4l2_buffer.length as usize)
670                     .map(|_| v4l2_plane::read_from_chain(reader))
671                     .collect::<IoResult<Vec<_>>>()?
672                     .into_iter()
673                     .chain(std::iter::repeat(Default::default()))
674                     .take(v4l2r::bindings::VIDEO_MAX_PLANES as usize)
675                     .collect::<Vec<_>>()
676                     .try_into()
677                     .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;
678             Some(planes)
679         } else {
680             None
681         };
682 
683         let v4l2_buffer = V4l2Buffer::try_from(UncheckedV4l2Buffer(v4l2_buffer, v4l2_planes))
684             .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;
685 
686         // Read the `MemRegion`s of all planes if the buffer is `USERPTR`.
687         let guest_regions = if let V4l2PlanesWithBacking::UserPtr(planes) =
688             v4l2_buffer.planes_with_backing_iter()
689         {
690             planes
691                 .filter(|p| *p.length > 0)
692                 .map(|p| {
693                     get_userptr_regions(reader, *p.length as usize)
694                         .map_err(|_| std::io::ErrorKind::InvalidData.into())
695                 })
696                 .collect::<IoResult<Vec<_>>>()?
697         } else {
698             vec![]
699         };
700 
701         Ok((v4l2_buffer, guest_regions))
702     }
703 }
704 
705 /// Write a `v4l2_buffer` to a descriptor chain, while ensuring the number of planes written is not
706 /// larger than a limit (i.e. the maximum number of planes that the descriptor chain can receive).
707 impl ToDescriptorChain for (V4l2Buffer, usize) {
write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()>708     fn write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
709         let mut v4l2_buffer = *self.0.as_v4l2_buffer();
710         // If the buffer is multiplanar, nullify the `planes` pointer to avoid leaking host
711         // addresses.
712         if self.0.queue().is_multiplanar() {
713             v4l2_buffer.m.planes = std::ptr::null_mut();
714         }
715         v4l2_buffer.write_to_chain(writer)?;
716 
717         // Write plane information if the buffer is multiplanar. Limit the number of planes to the
718         // upper bound we were given.
719         for plane in self.0.as_v4l2_planes().iter().take(self.1) {
720             plane.write_to_chain(writer)?;
721         }
722 
723         Ok(())
724     }
725 }
726 
727 /// Allows to easily read a `v4l2_ext_controls` struct, its array of controls, and the SG list of
728 /// the buffers pointed to by the controls from a descriptor chain.
729 impl FromDescriptorChain for (v4l2_ext_controls, Vec<v4l2_ext_control>, Vec<Vec<SgEntry>>) {
read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> where Self: Sized,730     fn read_from_chain<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self>
731     where
732         Self: Sized,
733     {
734         let ctrls = v4l2_ext_controls::read_from_chain(reader)?;
735 
736         let ctrl_array = (0..ctrls.count)
737             .map(|_| v4l2_ext_control::read_from_chain(reader))
738             .collect::<IoResult<Vec<_>>>()?;
739 
740         // Read all the payloads.
741         let mem_regions = ctrl_array
742             .iter()
743             .filter(|ctrl| ctrl.size > 0)
744             .map(|ctrl| {
745                 get_userptr_regions(reader, ctrl.size as usize)
746                     .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))
747             })
748             .collect::<IoResult<Vec<_>>>()?;
749 
750         Ok((ctrls, ctrl_array, mem_regions))
751     }
752 }
753 
754 /// Allows to easily write a `v4l2_ext_controls` struct and its array of controls to a descriptor
755 /// chain.
756 impl ToDescriptorChain for (v4l2_ext_controls, Vec<v4l2_ext_control>) {
write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()>757     fn write_to_chain<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
758         let (ctrls, ctrl_array) = self;
759         let mut ctrls = *ctrls;
760 
761         // Nullify the control pointer to avoid leaking host addresses.
762         ctrls.controls = std::ptr::null_mut();
763         ctrls.write_to_chain(writer)?;
764 
765         for ctrl in ctrl_array {
766             ctrl.write_to_chain(writer)?;
767         }
768 
769         Ok(())
770     }
771 }
772 
773 /// Implements a `WR` ioctl for which errors may also carry a payload.
774 ///
775 /// * `Reader` is the reader to the device-readable part of the descriptor chain,
776 /// * `Writer` is the writer to the device-writable part of the descriptor chain,
777 /// * `I` is the data to be read from the descriptor chain,
778 /// * `O` is the type of response to be written to the descriptor chain for both success and
779 ///   failure,
780 /// * `X` processes the input and produces a result. In case of failure, an error code and optional
781 ///   payload to write along with it are returned.
wr_ioctl_with_err_payload<Reader, Writer, I, O, X>( ioctl: V4l2Ioctl, reader: &mut Reader, writer: &mut Writer, process: X, ) -> IoResult<()> where Reader: std::io::Read, Writer: std::io::Write, I: FromDescriptorChain, O: ToDescriptorChain, X: FnOnce(I) -> Result<O, (i32, Option<O>)>,782 fn wr_ioctl_with_err_payload<Reader, Writer, I, O, X>(
783     ioctl: V4l2Ioctl,
784     reader: &mut Reader,
785     writer: &mut Writer,
786     process: X,
787 ) -> IoResult<()>
788 where
789     Reader: std::io::Read,
790     Writer: std::io::Write,
791     I: FromDescriptorChain,
792     O: ToDescriptorChain,
793     X: FnOnce(I) -> Result<O, (i32, Option<O>)>,
794 {
795     let input = match I::read_from_chain(reader) {
796         Ok(input) => input,
797         Err(e) => {
798             log::error!("error while reading input for {:?} ioctl: {:#}", ioctl, e);
799             return writer.write_err_response(libc::EINVAL);
800         }
801     };
802 
803     let (resp_header, output) = match process(input) {
804         Ok(output) => (RespHeader::ok(), Some(output)),
805         Err((errno, output)) => (RespHeader::err(errno), output),
806     };
807 
808     writer.write_response(resp_header)?;
809     if let Some(output) = output {
810         output.write_to_chain(writer)?;
811     }
812 
813     Ok(())
814 }
815 
816 /// Implements a `WR` ioctl for which errors do not carry a payload.
817 ///
818 /// * `Reader` is the reader to the device-readable part of the descriptor chain,
819 /// * `Writer` is the writer to the device-writable part of the descriptor chain,
820 /// * `I` is the data to be read from the descriptor chain,
821 /// * `O` is the type of response to be written to the descriptor chain in case of success,
822 /// * `X` processes the input and produces a result. In case of failure, an error code to transmit
823 ///   to the guest is returned.
wr_ioctl<Reader, Writer, I, O, X>( ioctl: V4l2Ioctl, reader: &mut Reader, writer: &mut Writer, process: X, ) -> IoResult<()> where Reader: std::io::Read, Writer: std::io::Write, I: FromDescriptorChain, O: ToDescriptorChain, X: FnOnce(I) -> Result<O, i32>,824 fn wr_ioctl<Reader, Writer, I, O, X>(
825     ioctl: V4l2Ioctl,
826     reader: &mut Reader,
827     writer: &mut Writer,
828     process: X,
829 ) -> IoResult<()>
830 where
831     Reader: std::io::Read,
832     Writer: std::io::Write,
833     I: FromDescriptorChain,
834     O: ToDescriptorChain,
835     X: FnOnce(I) -> Result<O, i32>,
836 {
837     wr_ioctl_with_err_payload(ioctl, reader, writer, |input| {
838         process(input).map_err(|err| (err, None))
839     })
840 }
841 
842 /// Implements a `W` ioctl.
843 ///
844 /// * `Reader` is the reader to the device-readable part of the descriptor chain,
845 /// * `I` is the data to be read from the descriptor chain,
846 /// * `X` processes the input. In case of failure, an error code to transmit to the guest is
847 ///   returned.
w_ioctl<Reader, Writer, I, X>( ioctl: V4l2Ioctl, reader: &mut Reader, writer: &mut Writer, process: X, ) -> IoResult<()> where I: FromDescriptorChain, Reader: std::io::Read, Writer: std::io::Write, X: FnOnce(I) -> Result<(), i32>,848 fn w_ioctl<Reader, Writer, I, X>(
849     ioctl: V4l2Ioctl,
850     reader: &mut Reader,
851     writer: &mut Writer,
852     process: X,
853 ) -> IoResult<()>
854 where
855     I: FromDescriptorChain,
856     Reader: std::io::Read,
857     Writer: std::io::Write,
858     X: FnOnce(I) -> Result<(), i32>,
859 {
860     wr_ioctl(ioctl, reader, writer, process)
861 }
862 
863 /// Implements a `R` ioctl.
864 ///
865 /// * `Writer` is the writer to the device-writable part of the descriptor chain,
866 /// * `O` is the type of response to be written to the descriptor chain in case of success,
867 /// * `X` runs the ioctl and produces a result. In case of failure, an error code to transmit to
868 ///   the guest is returned.
r_ioctl<Writer, O, X>(ioctl: V4l2Ioctl, writer: &mut Writer, process: X) -> IoResult<()> where Writer: std::io::Write, O: ToDescriptorChain, X: FnOnce() -> Result<O, i32>,869 fn r_ioctl<Writer, O, X>(ioctl: V4l2Ioctl, writer: &mut Writer, process: X) -> IoResult<()>
870 where
871     Writer: std::io::Write,
872     O: ToDescriptorChain,
873     X: FnOnce() -> Result<O, i32>,
874 {
875     wr_ioctl(ioctl, &mut std::io::empty(), writer, |()| process())
876 }
877 
878 /// Ensures that the `readbuffers` and `writebuffers` members of a `v4l2_streamparm` are zero since
879 /// we do not expose the `READWRITE` capability.
patch_streamparm(mut parm: v4l2_streamparm) -> v4l2_streamparm880 fn patch_streamparm(mut parm: v4l2_streamparm) -> v4l2_streamparm {
881     match QueueType::n(parm.type_)
882         .unwrap_or(QueueType::VideoCapture)
883         .direction()
884     {
885         QueueDirection::Output => parm.parm.output.writebuffers = 0,
886         QueueDirection::Capture => parm.parm.capture.readbuffers = 0,
887     }
888 
889     parm
890 }
891 
892 /// IOCTL dispatcher for implementors of [`VirtioMediaIoctlHandler`].
893 ///
894 /// This function takes care of reading and validating IOCTL inputs and writing outputs or errors
895 /// back to the driver, invoking the relevant method of the handler in the middle.
896 ///
897 /// Implementors of [`VirtioMediaIoctlHandler`] can thus just focus on writing the desired behavior
898 /// for their device, and let the more tedious parsing and validation to this function.
virtio_media_dispatch_ioctl<S, H, Reader, Writer>( handler: &mut H, session: &mut S, ioctl: V4l2Ioctl, reader: &mut Reader, writer: &mut Writer, ) -> IoResult<()> where H: VirtioMediaIoctlHandler<Session = S>, Reader: std::io::Read, Writer: std::io::Write,899 pub fn virtio_media_dispatch_ioctl<S, H, Reader, Writer>(
900     handler: &mut H,
901     session: &mut S,
902     ioctl: V4l2Ioctl,
903     reader: &mut Reader,
904     writer: &mut Writer,
905 ) -> IoResult<()>
906 where
907     H: VirtioMediaIoctlHandler<Session = S>,
908     Reader: std::io::Read,
909     Writer: std::io::Write,
910 {
911     use V4l2Ioctl::*;
912 
913     match ioctl {
914         VIDIOC_QUERYCAP => invalid_ioctl(ioctl, writer),
915         VIDIOC_ENUM_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_fmtdesc| {
916             let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
917             handler.enum_fmt(session, queue, format.index)
918         }),
919         VIDIOC_G_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_format| {
920             let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
921             handler.g_fmt(session, queue)
922         }),
923         VIDIOC_S_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_format| {
924             let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
925             handler.s_fmt(session, queue, format)
926         }),
927         VIDIOC_REQBUFS => wr_ioctl(ioctl, reader, writer, |reqbufs: v4l2_requestbuffers| {
928             let queue = QueueType::n(reqbufs.type_).ok_or(libc::EINVAL)?;
929             let memory = MemoryType::n(reqbufs.memory).ok_or(libc::EINVAL)?;
930 
931             match memory {
932                 MemoryType::Mmap | MemoryType::UserPtr => (),
933                 t => {
934                     log::error!(
935                         "VIDIOC_REQBUFS: memory type {:?} is currently unsupported",
936                         t
937                     );
938                     return Err(libc::EINVAL);
939                 }
940             }
941 
942             handler.reqbufs(session, queue, memory, reqbufs.count)
943         }),
944         VIDIOC_QUERYBUF => {
945             wr_ioctl(ioctl, reader, writer, |buffer: v4l2_buffer| {
946                 let queue = QueueType::n(buffer.type_).ok_or(libc::EINVAL)?;
947                 // Maximum number of planes we can write back to the driver.
948                 let num_planes = if queue.is_multiplanar() {
949                     buffer.length as usize
950                 } else {
951                     0
952                 };
953 
954                 handler
955                     .querybuf(session, queue, buffer.index)
956                     .map(|guest_buffer| (guest_buffer, num_planes))
957             })
958         }
959         VIDIOC_G_FBUF => invalid_ioctl(ioctl, writer),
960         VIDIOC_S_FBUF => invalid_ioctl(ioctl, writer),
961         VIDIOC_OVERLAY => invalid_ioctl(ioctl, writer),
962         VIDIOC_QBUF => wr_ioctl(ioctl, reader, writer, |(guest_buffer, guest_regions)| {
963             let num_planes = guest_buffer.num_planes();
964 
965             handler
966                 .qbuf(session, guest_buffer, guest_regions)
967                 .map(|guest_buffer| (guest_buffer, num_planes))
968         }),
969         // TODO implement EXPBUF.
970         VIDIOC_EXPBUF => invalid_ioctl(ioctl, writer),
971         VIDIOC_DQBUF => invalid_ioctl(ioctl, writer),
972         VIDIOC_STREAMON => w_ioctl(ioctl, reader, writer, |input: u32| {
973             let queue = QueueType::n(input).ok_or(libc::EINVAL)?;
974 
975             handler.streamon(session, queue)
976         }),
977         VIDIOC_STREAMOFF => w_ioctl(ioctl, reader, writer, |input: u32| {
978             let queue = QueueType::n(input).ok_or(libc::EINVAL)?;
979 
980             handler.streamoff(session, queue)
981         }),
982         VIDIOC_G_PARM => wr_ioctl(ioctl, reader, writer, |parm: v4l2_streamparm| {
983             let queue = QueueType::n(parm.type_).ok_or(libc::EINVAL)?;
984 
985             handler.g_parm(session, queue).map(patch_streamparm)
986         }),
987         VIDIOC_S_PARM => wr_ioctl(ioctl, reader, writer, |parm: v4l2_streamparm| {
988             handler
989                 .s_parm(session, patch_streamparm(parm))
990                 .map(patch_streamparm)
991         }),
992         VIDIOC_G_STD => r_ioctl(ioctl, writer, || handler.g_std(session)),
993         VIDIOC_S_STD => w_ioctl(ioctl, reader, writer, |id: v4l2_std_id| {
994             handler.s_std(session, id)
995         }),
996         VIDIOC_ENUMSTD => wr_ioctl(ioctl, reader, writer, |std: v4l2_standard| {
997             handler.enumstd(session, std.index)
998         }),
999         VIDIOC_ENUMINPUT => wr_ioctl(ioctl, reader, writer, |input: v4l2_input| {
1000             handler.enuminput(session, input.index)
1001         }),
1002         VIDIOC_G_CTRL => wr_ioctl(ioctl, reader, writer, |ctrl: v4l2_control| {
1003             handler.g_ctrl(session, ctrl.id)
1004         }),
1005         VIDIOC_S_CTRL => wr_ioctl(ioctl, reader, writer, |ctrl: v4l2_control| {
1006             handler.s_ctrl(session, ctrl.id, ctrl.value)
1007         }),
1008         VIDIOC_G_TUNER => wr_ioctl(ioctl, reader, writer, |tuner: v4l2_tuner| {
1009             handler.g_tuner(session, tuner.index)
1010         }),
1011         VIDIOC_S_TUNER => w_ioctl(ioctl, reader, writer, |tuner: v4l2_tuner| {
1012             let mode = TunerMode::n(tuner.audmode).ok_or(libc::EINVAL)?;
1013             handler.s_tuner(session, tuner.index, mode)
1014         }),
1015         VIDIOC_G_AUDIO => r_ioctl(ioctl, writer, || handler.g_audio(session)),
1016         VIDIOC_S_AUDIO => w_ioctl(ioctl, reader, writer, |input: v4l2_audio| {
1017             handler.s_audio(session, input.index, AudioMode::n(input.mode))
1018         }),
1019         VIDIOC_QUERYCTRL => wr_ioctl(ioctl, reader, writer, |input: v4l2_queryctrl| {
1020             let (id, flags) = v4l2r::ioctl::parse_ctrl_id_and_flags(input.id);
1021 
1022             handler.queryctrl(session, id, flags)
1023         }),
1024         VIDIOC_QUERYMENU => wr_ioctl(ioctl, reader, writer, |input: v4l2_querymenu| {
1025             handler.querymenu(session, input.id, input.index)
1026         }),
1027         VIDIOC_G_INPUT => r_ioctl(ioctl, writer, || handler.g_input(session)),
1028         VIDIOC_S_INPUT => wr_ioctl(ioctl, reader, writer, |input: i32| {
1029             handler.s_input(session, input)
1030         }),
1031         VIDIOC_G_EDID => invalid_ioctl(ioctl, writer),
1032         VIDIOC_S_EDID => invalid_ioctl(ioctl, writer),
1033         VIDIOC_G_OUTPUT => r_ioctl(ioctl, writer, || handler.g_output(session)),
1034         VIDIOC_S_OUTPUT => wr_ioctl(ioctl, reader, writer, |output: i32| {
1035             handler.s_output(session, output)
1036         }),
1037         VIDIOC_ENUMOUTPUT => wr_ioctl(ioctl, reader, writer, |output: v4l2_output| {
1038             handler.enumoutput(session, output.index)
1039         }),
1040         VIDIOC_G_AUDOUT => r_ioctl(ioctl, writer, || handler.g_audout(session)),
1041         VIDIOC_S_AUDOUT => w_ioctl(ioctl, reader, writer, |audout: v4l2_audioout| {
1042             handler.s_audout(session, audout.index)
1043         }),
1044         VIDIOC_G_MODULATOR => wr_ioctl(ioctl, reader, writer, |modulator: v4l2_modulator| {
1045             handler.g_modulator(session, modulator.index)
1046         }),
1047         VIDIOC_S_MODULATOR => w_ioctl(ioctl, reader, writer, |modulator: v4l2_modulator| {
1048             let flags =
1049                 TunerTransmissionFlags::from_bits(modulator.txsubchans).ok_or(libc::EINVAL)?;
1050             handler.s_modulator(session, modulator.index, flags)
1051         }),
1052         VIDIOC_G_FREQUENCY => wr_ioctl(ioctl, reader, writer, |freq: v4l2_frequency| {
1053             handler.g_frequency(session, freq.tuner)
1054         }),
1055         VIDIOC_S_FREQUENCY => w_ioctl(ioctl, reader, writer, |freq: v4l2_frequency| {
1056             let type_ = TunerType::n(freq.type_).ok_or(libc::EINVAL)?;
1057 
1058             handler.s_frequency(session, freq.tuner, type_, freq.frequency)
1059         }),
1060         // TODO do these 3 need to be supported?
1061         VIDIOC_CROPCAP => invalid_ioctl(ioctl, writer),
1062         VIDIOC_G_CROP => invalid_ioctl(ioctl, writer),
1063         VIDIOC_S_CROP => invalid_ioctl(ioctl, writer),
1064         // Deprecated in V4L2.
1065         VIDIOC_G_JPEGCOMP => invalid_ioctl(ioctl, writer),
1066         // Deprecated in V4L2.
1067         VIDIOC_S_JPEGCOMP => invalid_ioctl(ioctl, writer),
1068         VIDIOC_QUERYSTD => r_ioctl(ioctl, writer, || handler.querystd(session)),
1069         VIDIOC_TRY_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_format| {
1070             let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
1071             handler.try_fmt(session, queue, format)
1072         }),
1073         VIDIOC_ENUMAUDIO => wr_ioctl(ioctl, reader, writer, |audio: v4l2_audio| {
1074             handler.enumaudio(session, audio.index)
1075         }),
1076         VIDIOC_ENUMAUDOUT => wr_ioctl(ioctl, reader, writer, |audio: v4l2_audioout| {
1077             handler.enumaudout(session, audio.index)
1078         }),
1079         VIDIOC_G_PRIORITY => invalid_ioctl(ioctl, writer),
1080         VIDIOC_S_PRIORITY => invalid_ioctl(ioctl, writer),
1081         // TODO support this, although it's marginal.
1082         VIDIOC_G_SLICED_VBI_CAP => invalid_ioctl(ioctl, writer),
1083         // Doesn't make sense in a virtual context.
1084         VIDIOC_LOG_STATUS => invalid_ioctl(ioctl, writer),
1085         VIDIOC_G_EXT_CTRLS => wr_ioctl_with_err_payload(
1086             ioctl,
1087             reader,
1088             writer,
1089             |(mut ctrls, mut ctrl_array, user_regions)| {
1090                 let which = CtrlWhich::try_from(&ctrls).map_err(|()| (libc::EINVAL, None))?;
1091 
1092                 match handler.g_ext_ctrls(session, which, &mut ctrls, &mut ctrl_array, user_regions)
1093                 {
1094                     Ok(()) => Ok((ctrls, ctrl_array)),
1095                     // It is very important what we write back the updated input in case
1096                     // of error as it contains extra information.
1097                     Err(e) => Err((e, Some((ctrls, ctrl_array)))),
1098                 }
1099             },
1100         ),
1101         VIDIOC_S_EXT_CTRLS => wr_ioctl_with_err_payload(
1102             ioctl,
1103             reader,
1104             writer,
1105             |(mut ctrls, mut ctrl_array, user_regions)| {
1106                 let which = CtrlWhich::try_from(&ctrls).map_err(|()| (libc::EINVAL, None))?;
1107 
1108                 match handler.s_ext_ctrls(session, which, &mut ctrls, &mut ctrl_array, user_regions)
1109                 {
1110                     Ok(()) => Ok((ctrls, ctrl_array)),
1111                     // It is very important what we write back the updated input in case
1112                     // of error as it contains extra information.
1113                     Err(e) => Err((e, Some((ctrls, ctrl_array)))),
1114                 }
1115             },
1116         ),
1117         VIDIOC_TRY_EXT_CTRLS => wr_ioctl_with_err_payload(
1118             ioctl,
1119             reader,
1120             writer,
1121             |(mut ctrls, mut ctrl_array, user_regions)| {
1122                 let which = CtrlWhich::try_from(&ctrls).map_err(|()| (libc::EINVAL, None))?;
1123 
1124                 match handler.try_ext_ctrls(
1125                     session,
1126                     which,
1127                     &mut ctrls,
1128                     &mut ctrl_array,
1129                     user_regions,
1130                 ) {
1131                     Ok(()) => Ok((ctrls, ctrl_array)),
1132                     // It is very important what we write back the updated input in case
1133                     // of error as it contains extra information.
1134                     Err(e) => Err((e, Some((ctrls, ctrl_array)))),
1135                 }
1136             },
1137         ),
1138         VIDIOC_ENUM_FRAMESIZES => {
1139             wr_ioctl(ioctl, reader, writer, |frmsizeenum: v4l2_frmsizeenum| {
1140                 handler.enum_framesizes(session, frmsizeenum.index, frmsizeenum.pixel_format)
1141             })
1142         }
1143         VIDIOC_ENUM_FRAMEINTERVALS => {
1144             wr_ioctl(ioctl, reader, writer, |frmivalenum: v4l2_frmivalenum| {
1145                 handler.enum_frameintervals(
1146                     session,
1147                     frmivalenum.index,
1148                     frmivalenum.pixel_format,
1149                     frmivalenum.width,
1150                     frmivalenum.height,
1151                 )
1152             })
1153         }
1154         VIDIOC_G_ENC_INDEX => r_ioctl(ioctl, writer, || handler.g_enc_index(session)),
1155         VIDIOC_ENCODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_encoder_cmd| {
1156             handler.encoder_cmd(session, cmd)
1157         }),
1158         VIDIOC_TRY_ENCODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_encoder_cmd| {
1159             handler.try_encoder_cmd(session, cmd)
1160         }),
1161         // Doesn't make sense in a virtual context.
1162         VIDIOC_DBG_G_REGISTER => invalid_ioctl(ioctl, writer),
1163         // Doesn't make sense in a virtual context.
1164         VIDIOC_DBG_S_REGISTER => invalid_ioctl(ioctl, writer),
1165         VIDIOC_S_HW_FREQ_SEEK => invalid_ioctl(ioctl, writer),
1166         VIDIOC_S_DV_TIMINGS => wr_ioctl(ioctl, reader, writer, |timings: v4l2_dv_timings| {
1167             handler.s_dv_timings(session, timings)
1168         }),
1169         VIDIOC_G_DV_TIMINGS => wr_ioctl(
1170             ioctl,
1171             reader,
1172             writer,
1173             // We are not using the input - this should probably have been a R ioctl?
1174             |_: v4l2_dv_timings| handler.g_dv_timings(session),
1175         ),
1176         // Supported by an event.
1177         VIDIOC_DQEVENT => invalid_ioctl(ioctl, writer),
1178         VIDIOC_SUBSCRIBE_EVENT => {
1179             w_ioctl(ioctl, reader, writer, |input: v4l2_event_subscription| {
1180                 let event = V4l2EventType::try_from(&input).unwrap();
1181                 let flags = SubscribeEventFlags::from_bits(input.flags).unwrap();
1182 
1183                 handler.subscribe_event(session, event, flags)
1184             })?;
1185 
1186             Ok(())
1187         }
1188         VIDIOC_UNSUBSCRIBE_EVENT => {
1189             w_ioctl(ioctl, reader, writer, |event: v4l2_event_subscription| {
1190                 handler.unsubscribe_event(session, event)
1191             })
1192         }
1193         VIDIOC_CREATE_BUFS => wr_ioctl(ioctl, reader, writer, |input: v4l2_create_buffers| {
1194             let queue = QueueType::n(input.format.type_).ok_or(libc::EINVAL)?;
1195             let memory = MemoryType::n(input.memory).ok_or(libc::EINVAL)?;
1196 
1197             handler.create_bufs(session, input.count, queue, memory, input.format)
1198         }),
1199         VIDIOC_PREPARE_BUF => wr_ioctl(ioctl, reader, writer, |(guest_buffer, guest_regions)| {
1200             let num_planes = guest_buffer.num_planes();
1201 
1202             handler
1203                 .prepare_buf(session, guest_buffer, guest_regions)
1204                 .map(|out_buffer| (out_buffer, num_planes))
1205         }),
1206         VIDIOC_G_SELECTION => wr_ioctl(ioctl, reader, writer, |mut selection: v4l2_selection| {
1207             let sel_type = SelectionType::n(selection.type_).ok_or(libc::EINVAL)?;
1208             let sel_target = SelectionTarget::n(selection.target).ok_or(libc::EINVAL)?;
1209 
1210             handler
1211                 .g_selection(session, sel_type, sel_target)
1212                 .map(|rect| {
1213                     selection.r = rect;
1214                     selection
1215                 })
1216         }),
1217         VIDIOC_S_SELECTION => wr_ioctl(ioctl, reader, writer, |mut selection: v4l2_selection| {
1218             let sel_type = SelectionType::n(selection.type_).ok_or(libc::EINVAL)?;
1219             let sel_target = SelectionTarget::n(selection.target).ok_or(libc::EINVAL)?;
1220             let sel_flags = SelectionFlags::from_bits(selection.flags).ok_or(libc::EINVAL)?;
1221 
1222             handler
1223                 .s_selection(session, sel_type, sel_target, selection.r, sel_flags)
1224                 .map(|rect| {
1225                     selection.r = rect;
1226                     selection
1227                 })
1228         }),
1229         VIDIOC_DECODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_decoder_cmd| {
1230             handler.decoder_cmd(session, cmd)
1231         }),
1232         VIDIOC_TRY_DECODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_decoder_cmd| {
1233             handler.try_decoder_cmd(session, cmd)
1234         }),
1235         VIDIOC_ENUM_DV_TIMINGS => wr_ioctl(
1236             ioctl,
1237             reader,
1238             writer,
1239             |mut enum_timings: v4l2_enum_dv_timings| {
1240                 handler
1241                     .enum_dv_timings(session, enum_timings.index)
1242                     .map(|timings| {
1243                         enum_timings.timings = timings;
1244                         enum_timings
1245                     })
1246             },
1247         ),
1248         VIDIOC_QUERY_DV_TIMINGS => r_ioctl(ioctl, writer, || handler.query_dv_timings(session)),
1249         VIDIOC_DV_TIMINGS_CAP => wr_ioctl(ioctl, reader, writer, |_: v4l2_dv_timings_cap| {
1250             handler.dv_timings_cap(session)
1251         }),
1252         VIDIOC_ENUM_FREQ_BANDS => {
1253             wr_ioctl(ioctl, reader, writer, |freq_band: v4l2_frequency_band| {
1254                 let type_ = TunerType::n(freq_band.type_).ok_or(libc::EINVAL)?;
1255 
1256                 handler.enum_freq_bands(session, freq_band.tuner, type_, freq_band.index)
1257             })
1258         }
1259         // Doesn't make sense in a virtual context.
1260         VIDIOC_DBG_G_CHIP_INFO => invalid_ioctl(ioctl, writer),
1261         VIDIOC_QUERY_EXT_CTRL => wr_ioctl(ioctl, reader, writer, |ctrl: v4l2_query_ext_ctrl| {
1262             let (id, flags) = v4l2r::ioctl::parse_ctrl_id_and_flags(ctrl.id);
1263             handler.query_ext_ctrl(session, id, flags)
1264         }),
1265     }
1266 }
1267