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