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