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