1use std::io::Result as IoResult;
6
7use v4l2r::bindings::v4l2_audio;
8use v4l2r::bindings::v4l2_audioout;
9use v4l2r::bindings::v4l2_buffer;
10use v4l2r::bindings::v4l2_control;
11use v4l2r::bindings::v4l2_create_buffers;
12use v4l2r::bindings::v4l2_decoder_cmd;
13use v4l2r::bindings::v4l2_dv_timings;
14use v4l2r::bindings::v4l2_dv_timings_cap;
15use v4l2r::bindings::v4l2_enc_idx;
16use v4l2r::bindings::v4l2_encoder_cmd;
17use v4l2r::bindings::v4l2_enum_dv_timings;
18use v4l2r::bindings::v4l2_event_subscription;
19use v4l2r::bindings::v4l2_ext_control;
20use v4l2r::bindings::v4l2_ext_controls;
21use v4l2r::bindings::v4l2_fmtdesc;
22use v4l2r::bindings::v4l2_format;
23use v4l2r::bindings::v4l2_frequency;
24use v4l2r::bindings::v4l2_frequency_band;
25use v4l2r::bindings::v4l2_frmivalenum;
26use v4l2r::bindings::v4l2_frmsizeenum;
27use v4l2r::bindings::v4l2_input;
28use v4l2r::bindings::v4l2_modulator;
29use v4l2r::bindings::v4l2_output;
30use v4l2r::bindings::v4l2_plane;
31use v4l2r::bindings::v4l2_query_ext_ctrl;
32use v4l2r::bindings::v4l2_queryctrl;
33use v4l2r::bindings::v4l2_querymenu;
34use v4l2r::bindings::v4l2_rect;
35use v4l2r::bindings::v4l2_requestbuffers;
36use v4l2r::bindings::v4l2_selection;
37use v4l2r::bindings::v4l2_standard;
38use v4l2r::bindings::v4l2_std_id;
39use v4l2r::bindings::v4l2_streamparm;
40use v4l2r::bindings::v4l2_tuner;
41use v4l2r::ioctl::AudioMode;
42use v4l2r::ioctl::CtrlId;
43use v4l2r::ioctl::CtrlWhich;
44use v4l2r::ioctl::EventType as V4l2EventType;
45use v4l2r::ioctl::MemoryConsistency;
46use v4l2r::ioctl::QueryCtrlFlags;
47use v4l2r::ioctl::SelectionFlags;
48use v4l2r::ioctl::SelectionTarget;
49use v4l2r::ioctl::SelectionType;
50use v4l2r::ioctl::SubscribeEventFlags;
51use v4l2r::ioctl::TunerMode;
52use v4l2r::ioctl::TunerTransmissionFlags;
53use v4l2r::ioctl::TunerType;
54use v4l2r::ioctl::UncheckedV4l2Buffer;
55use v4l2r::ioctl::V4l2Buffer;
56use v4l2r::ioctl::V4l2PlanesWithBacking;
57use v4l2r::memory::MemoryType;
58use v4l2r::QueueDirection;
59use v4l2r::QueueType;
60
61use crate::io::ReadFromDescriptorChain;
62use crate::io::VmediaType;
63use crate::io::WriteToDescriptorChain;
64use crate::protocol::RespHeader;
65use crate::protocol::SgEntry;
66use crate::protocol::V4l2Ioctl;
67
68fn get_userptr_regions<R: ReadFromDescriptorChain>(
70 r: &mut R,
71 size: usize,
72) -> anyhow::Result<Vec<SgEntry>> {
73 let mut bytes_taken = 0;
74 let mut res = Vec::new();
75
76 while bytes_taken < size {
77 let sg_entry = r.read_obj::<SgEntry>()?;
78 bytes_taken += sg_entry.len as usize;
79 res.push(sg_entry);
80 }
81
82 Ok(res)
83}
84
85trait FromDescriptorChain {
88 fn read_from_chain<R: ReadFromDescriptorChain>(reader: &mut R) -> std::io::Result<Self>
89 where
90 Self: Sized;
91}
92
93impl<T> FromDescriptorChain for T
96where
97 T: VmediaType,
98{
99 fn read_from_chain<R: ReadFromDescriptorChain>(reader: &mut R) -> std::io::Result<Self> {
100 reader.read_obj()
101 }
102}
103
104impl FromDescriptorChain for (V4l2Buffer, Vec<Vec<SgEntry>>) {
107 fn read_from_chain<R: ReadFromDescriptorChain>(reader: &mut R) -> IoResult<Self>
108 where
109 Self: Sized,
110 {
111 let v4l2_buffer = reader.read_obj::<v4l2_buffer>()?;
112 let queue = match QueueType::n(v4l2_buffer.type_) {
113 Some(queue) => queue,
114 None => return Err(std::io::ErrorKind::InvalidData.into()),
115 };
116
117 let v4l2_planes = if queue.is_multiplanar() && v4l2_buffer.length > 0 {
118 if v4l2_buffer.length > v4l2r::bindings::VIDEO_MAX_PLANES {
119 return Err(std::io::ErrorKind::InvalidData.into());
120 }
121
122 let planes: [v4l2r::bindings::v4l2_plane; v4l2r::bindings::VIDEO_MAX_PLANES as usize] =
123 (0..v4l2_buffer.length as usize)
124 .map(|_| reader.read_obj::<v4l2_plane>())
125 .collect::<IoResult<Vec<_>>>()?
126 .into_iter()
127 .chain(std::iter::repeat(Default::default()))
128 .take(v4l2r::bindings::VIDEO_MAX_PLANES as usize)
129 .collect::<Vec<_>>()
130 .try_into()
131 .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;
132 Some(planes)
133 } else {
134 None
135 };
136
137 let v4l2_buffer = V4l2Buffer::try_from(UncheckedV4l2Buffer(v4l2_buffer, v4l2_planes))
138 .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))?;
139
140 let guest_regions = if let V4l2PlanesWithBacking::UserPtr(planes) =
142 v4l2_buffer.planes_with_backing_iter()
143 {
144 planes
145 .filter(|p| *p.length > 0)
146 .map(|p| {
147 get_userptr_regions(reader, *p.length as usize)
148 .map_err(|_| std::io::ErrorKind::InvalidData.into())
149 })
150 .collect::<IoResult<Vec<_>>>()?
151 } else {
152 vec![]
153 };
154
155 Ok((v4l2_buffer, guest_regions))
156 }
157}
158
159impl FromDescriptorChain for (v4l2_ext_controls, Vec<v4l2_ext_control>, Vec<Vec<SgEntry>>) {
162 fn read_from_chain<R: ReadFromDescriptorChain>(reader: &mut R) -> std::io::Result<Self>
163 where
164 Self: Sized,
165 {
166 let ctrls = reader.read_obj::<v4l2_ext_controls>()?;
167
168 let ctrl_array = (0..ctrls.count)
169 .map(|_| reader.read_obj::<v4l2_ext_control>())
170 .collect::<IoResult<Vec<_>>>()?;
171
172 let mem_regions = ctrl_array
174 .iter()
175 .filter(|ctrl| ctrl.size > 0)
176 .map(|ctrl| {
177 get_userptr_regions(reader, ctrl.size as usize)
178 .map_err(|_| std::io::Error::from(std::io::ErrorKind::InvalidData))
179 })
180 .collect::<IoResult<Vec<_>>>()?;
181
182 Ok((ctrls, ctrl_array, mem_regions))
183 }
184}
185
186trait ToDescriptorChain {
189 fn write_to_chain<W: WriteToDescriptorChain>(self, writer: &mut W) -> std::io::Result<()>;
190}
191
192impl<T> ToDescriptorChain for T
195where
196 T: VmediaType,
197{
198 fn write_to_chain<W: WriteToDescriptorChain>(self, writer: &mut W) -> std::io::Result<()> {
199 writer.write_obj(self)
200 }
201}
202
203impl ToDescriptorChain for (V4l2Buffer, usize) {
207 fn write_to_chain<W: WriteToDescriptorChain>(self, writer: &mut W) -> std::io::Result<()> {
208 let mut v4l2_buffer = *self.0.as_v4l2_buffer();
209 if self.0.queue().is_multiplanar() {
212 v4l2_buffer.m.planes = std::ptr::null_mut();
213 }
214 writer.write_obj(v4l2_buffer)?;
215
216 for plane in self.0.as_v4l2_planes().iter().take(self.1) {
219 writer.write_obj(*plane)?;
220 }
221
222 Ok(())
223 }
224}
225
226impl ToDescriptorChain for (v4l2_ext_controls, Vec<v4l2_ext_control>) {
229 fn write_to_chain<W: WriteToDescriptorChain>(self, writer: &mut W) -> std::io::Result<()> {
230 let (ctrls, ctrl_array) = self;
231 let mut ctrls = ctrls;
232
233 ctrls.controls = std::ptr::null_mut();
235 writer.write_obj(ctrls)?;
236
237 for ctrl in ctrl_array {
238 writer.write_obj(ctrl)?;
239 }
240
241 Ok(())
242 }
243}
244
245macro_rules! unhandled_ioctl {
247 () => {
248 Err(libc::ENOTTY)
249 };
250}
251
252pub type IoctlResult<T> = Result<T, i32>;
253
254#[allow(unused_variables)]
261pub trait VirtioMediaIoctlHandler {
262 type Session;
263
264 fn enum_fmt(
265 &mut self,
266 session: &Self::Session,
267 queue: QueueType,
268 index: u32,
269 ) -> IoctlResult<v4l2_fmtdesc> {
270 unhandled_ioctl!()
271 }
272 fn g_fmt(&mut self, session: &Self::Session, queue: QueueType) -> IoctlResult<v4l2_format> {
273 unhandled_ioctl!()
274 }
275 fn s_fmt(
279 &mut self,
280 session: &mut Self::Session,
281 queue: QueueType,
282 format: v4l2_format,
283 ) -> IoctlResult<v4l2_format> {
284 unhandled_ioctl!()
285 }
286 fn reqbufs(
287 &mut self,
288 session: &mut Self::Session,
289 queue: QueueType,
290 memory: MemoryType,
291 count: u32,
292 flags: MemoryConsistency,
293 ) -> IoctlResult<v4l2_requestbuffers> {
294 unhandled_ioctl!()
295 }
296 fn querybuf(
297 &mut self,
298 session: &Self::Session,
299 queue: QueueType,
300 index: u32,
301 ) -> IoctlResult<V4l2Buffer> {
302 unhandled_ioctl!()
303 }
304
305 fn qbuf(
307 &mut self,
308 session: &mut Self::Session,
309 buffer: V4l2Buffer,
310 guest_regions: Vec<Vec<SgEntry>>,
311 ) -> IoctlResult<V4l2Buffer> {
312 unhandled_ioctl!()
313 }
314
315 fn streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
318 unhandled_ioctl!()
319 }
320 fn streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
321 unhandled_ioctl!()
322 }
323
324 fn g_parm(
325 &mut self,
326 session: &Self::Session,
327 queue: QueueType,
328 ) -> IoctlResult<v4l2_streamparm> {
329 unhandled_ioctl!()
330 }
331 fn s_parm(
332 &mut self,
333 session: &mut Self::Session,
334 parm: v4l2_streamparm,
335 ) -> IoctlResult<v4l2_streamparm> {
336 unhandled_ioctl!()
337 }
338
339 fn g_std(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id> {
340 unhandled_ioctl!()
341 }
342
343 fn s_std(&mut self, session: &mut Self::Session, std: v4l2_std_id) -> IoctlResult<()> {
344 unhandled_ioctl!()
345 }
346
347 fn enumstd(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_standard> {
348 unhandled_ioctl!()
349 }
350
351 fn enuminput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_input> {
352 unhandled_ioctl!()
353 }
354
355 fn g_ctrl(&mut self, session: &Self::Session, id: u32) -> IoctlResult<v4l2_control> {
356 unhandled_ioctl!()
357 }
358
359 fn s_ctrl(
360 &mut self,
361 session: &mut Self::Session,
362 id: u32,
363 value: i32,
364 ) -> IoctlResult<v4l2_control> {
365 unhandled_ioctl!()
366 }
367
368 fn g_tuner(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_tuner> {
369 unhandled_ioctl!()
370 }
371
372 fn s_tuner(
373 &mut self,
374 session: &mut Self::Session,
375 index: u32,
376 mode: TunerMode,
377 ) -> IoctlResult<()> {
378 unhandled_ioctl!()
379 }
380
381 fn g_audio(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audio> {
382 unhandled_ioctl!()
383 }
384
385 fn s_audio(
386 &mut self,
387 session: &mut Self::Session,
388 index: u32,
389 mode: Option<AudioMode>,
390 ) -> IoctlResult<()> {
391 unhandled_ioctl!()
392 }
393
394 fn queryctrl(
395 &mut self,
396 session: &Self::Session,
397 id: CtrlId,
398 flags: QueryCtrlFlags,
399 ) -> IoctlResult<v4l2_queryctrl> {
400 unhandled_ioctl!()
401 }
402
403 fn querymenu(
404 &mut self,
405 session: &Self::Session,
406 id: u32,
407 index: u32,
408 ) -> IoctlResult<v4l2_querymenu> {
409 unhandled_ioctl!()
410 }
411
412 fn g_input(&mut self, session: &Self::Session) -> IoctlResult<i32> {
413 unhandled_ioctl!()
414 }
415
416 fn s_input(&mut self, session: &mut Self::Session, input: i32) -> IoctlResult<i32> {
417 unhandled_ioctl!()
418 }
419
420 fn g_output(&mut self, session: &Self::Session) -> IoctlResult<i32> {
421 unhandled_ioctl!()
422 }
423
424 fn s_output(&mut self, session: &mut Self::Session, output: i32) -> IoctlResult<i32> {
425 unhandled_ioctl!()
426 }
427
428 fn enumoutput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_output> {
429 unhandled_ioctl!()
430 }
431
432 fn g_audout(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audioout> {
433 unhandled_ioctl!()
434 }
435
436 fn s_audout(&mut self, session: &mut Self::Session, index: u32) -> IoctlResult<()> {
437 unhandled_ioctl!()
438 }
439
440 fn g_modulator(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_modulator> {
441 unhandled_ioctl!()
442 }
443
444 fn s_modulator(
445 &mut self,
446 session: &mut Self::Session,
447 index: u32,
448 flags: TunerTransmissionFlags,
449 ) -> IoctlResult<()> {
450 unhandled_ioctl!()
451 }
452
453 fn g_frequency(&mut self, session: &Self::Session, tuner: u32) -> IoctlResult<v4l2_frequency> {
454 unhandled_ioctl!()
455 }
456
457 fn s_frequency(
458 &mut self,
459 session: &mut Self::Session,
460 tuner: u32,
461 type_: TunerType,
462 frequency: u32,
463 ) -> IoctlResult<()> {
464 unhandled_ioctl!()
465 }
466
467 fn querystd(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id> {
468 unhandled_ioctl!()
469 }
470
471 fn try_fmt(
475 &mut self,
476 session: &Self::Session,
477 queue: QueueType,
478 format: v4l2_format,
479 ) -> IoctlResult<v4l2_format> {
480 unhandled_ioctl!()
481 }
482
483 fn enumaudio(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audio> {
484 unhandled_ioctl!()
485 }
486
487 fn enumaudout(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audioout> {
488 unhandled_ioctl!()
489 }
490
491 fn g_ext_ctrls(
493 &mut self,
494 session: &Self::Session,
495 which: CtrlWhich,
496 ctrls: &mut v4l2_ext_controls,
497 ctrl_array: &mut Vec<v4l2_ext_control>,
498 user_regions: Vec<Vec<SgEntry>>,
499 ) -> IoctlResult<()> {
500 unhandled_ioctl!()
501 }
502 fn s_ext_ctrls(
504 &mut self,
505 session: &mut Self::Session,
506 which: CtrlWhich,
507 ctrls: &mut v4l2_ext_controls,
508 ctrl_array: &mut Vec<v4l2_ext_control>,
509 user_regions: Vec<Vec<SgEntry>>,
510 ) -> IoctlResult<()> {
511 unhandled_ioctl!()
512 }
513 fn try_ext_ctrls(
515 &mut self,
516 session: &Self::Session,
517 which: CtrlWhich,
518 ctrls: &mut v4l2_ext_controls,
519 ctrl_array: &mut Vec<v4l2_ext_control>,
520 user_regions: Vec<Vec<SgEntry>>,
521 ) -> IoctlResult<()> {
522 unhandled_ioctl!()
523 }
524
525 fn enum_framesizes(
526 &mut self,
527 session: &Self::Session,
528 index: u32,
529 pixel_format: u32,
530 ) -> IoctlResult<v4l2_frmsizeenum> {
531 unhandled_ioctl!()
532 }
533
534 fn enum_frameintervals(
535 &mut self,
536 session: &Self::Session,
537 index: u32,
538 pixel_format: u32,
539 width: u32,
540 height: u32,
541 ) -> IoctlResult<v4l2_frmivalenum> {
542 unhandled_ioctl!()
543 }
544
545 fn g_enc_index(&mut self, session: &Self::Session) -> IoctlResult<v4l2_enc_idx> {
546 unhandled_ioctl!()
547 }
548
549 fn encoder_cmd(
550 &mut self,
551 session: &mut Self::Session,
552 cmd: v4l2_encoder_cmd,
553 ) -> IoctlResult<v4l2_encoder_cmd> {
554 unhandled_ioctl!()
555 }
556
557 fn try_encoder_cmd(
558 &mut self,
559 session: &Self::Session,
560 cmd: v4l2_encoder_cmd,
561 ) -> IoctlResult<v4l2_encoder_cmd> {
562 unhandled_ioctl!()
563 }
564
565 fn s_dv_timings(
566 &mut self,
567 session: &mut Self::Session,
568 timings: v4l2_dv_timings,
569 ) -> IoctlResult<v4l2_dv_timings> {
570 unhandled_ioctl!()
571 }
572
573 fn g_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings> {
574 unhandled_ioctl!()
575 }
576
577 fn subscribe_event(
578 &mut self,
579 session: &mut Self::Session,
580 event: V4l2EventType,
581 flags: SubscribeEventFlags,
582 ) -> IoctlResult<()> {
583 unhandled_ioctl!()
584 }
585
586 fn unsubscribe_event(
587 &mut self,
588 session: &mut Self::Session,
589 event: v4l2_event_subscription,
590 ) -> IoctlResult<()> {
591 unhandled_ioctl!()
592 }
593
594 fn create_bufs(
598 &mut self,
599 session: &mut Self::Session,
600 count: u32,
601 queue: QueueType,
602 memory: MemoryType,
603 format: v4l2_format,
604 ) -> IoctlResult<v4l2_create_buffers> {
605 unhandled_ioctl!()
606 }
607
608 fn prepare_buf(
611 &mut self,
612 session: &mut Self::Session,
613 buffer: V4l2Buffer,
614 guest_regions: Vec<Vec<SgEntry>>,
615 ) -> IoctlResult<V4l2Buffer> {
616 unhandled_ioctl!()
617 }
618
619 fn g_selection(
620 &mut self,
621 session: &Self::Session,
622 sel_type: SelectionType,
623 sel_target: SelectionTarget,
624 ) -> IoctlResult<v4l2_rect> {
625 unhandled_ioctl!()
626 }
627
628 fn s_selection(
629 &mut self,
630 session: &mut Self::Session,
631 sel_type: SelectionType,
632 sel_target: SelectionTarget,
633 sel_rect: v4l2_rect,
634 sel_flags: SelectionFlags,
635 ) -> IoctlResult<v4l2_rect> {
636 unhandled_ioctl!()
637 }
638
639 fn decoder_cmd(
640 &mut self,
641 session: &mut Self::Session,
642 cmd: v4l2_decoder_cmd,
643 ) -> IoctlResult<v4l2_decoder_cmd> {
644 unhandled_ioctl!()
645 }
646
647 fn try_decoder_cmd(
648 &mut self,
649 session: &Self::Session,
650 cmd: v4l2_decoder_cmd,
651 ) -> IoctlResult<v4l2_decoder_cmd> {
652 unhandled_ioctl!()
653 }
654
655 fn enum_dv_timings(
656 &mut self,
657 session: &Self::Session,
658 index: u32,
659 ) -> IoctlResult<v4l2_dv_timings> {
660 unhandled_ioctl!()
661 }
662
663 fn query_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings> {
664 unhandled_ioctl!()
665 }
666
667 fn dv_timings_cap(&self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings_cap> {
668 unhandled_ioctl!()
669 }
670
671 fn enum_freq_bands(
672 &self,
673 session: &Self::Session,
674 tuner: u32,
675 type_: TunerType,
676 index: u32,
677 ) -> IoctlResult<v4l2_frequency_band> {
678 unhandled_ioctl!()
679 }
680
681 fn query_ext_ctrl(
682 &mut self,
683 session: &Self::Session,
684 id: CtrlId,
685 flags: QueryCtrlFlags,
686 ) -> IoctlResult<v4l2_query_ext_ctrl> {
687 unhandled_ioctl!()
688 }
689}
690
691fn invalid_ioctl<W: WriteToDescriptorChain>(code: V4l2Ioctl, writer: &mut W) -> IoResult<()> {
694 writer.write_err_response(libc::ENOTTY).map_err(|e| {
695 log::error!(
696 "failed to write error response for invalid ioctl {:?}: {:#}",
697 code,
698 e
699 );
700 e
701 })
702}
703
704fn wr_ioctl_with_err_payload<Reader, Writer, I, O, X>(
714 ioctl: V4l2Ioctl,
715 reader: &mut Reader,
716 writer: &mut Writer,
717 process: X,
718) -> IoResult<()>
719where
720 Reader: ReadFromDescriptorChain,
721 Writer: WriteToDescriptorChain,
722 I: FromDescriptorChain,
723 O: ToDescriptorChain,
724 X: FnOnce(I) -> Result<O, (i32, Option<O>)>,
725{
726 let input = match I::read_from_chain(reader) {
727 Ok(input) => input,
728 Err(e) => {
729 log::error!("error while reading input for {:?} ioctl: {:#}", ioctl, e);
730 return writer.write_err_response(libc::EINVAL);
731 }
732 };
733
734 let (resp_header, output) = match process(input) {
735 Ok(output) => (RespHeader::ok(), Some(output)),
736 Err((errno, output)) => (RespHeader::err(errno), output),
737 };
738
739 writer.write_response(resp_header)?;
740 if let Some(output) = output {
741 output.write_to_chain(writer)?;
742 }
743
744 Ok(())
745}
746
747fn wr_ioctl<Reader, Writer, I, O, X>(
756 ioctl: V4l2Ioctl,
757 reader: &mut Reader,
758 writer: &mut Writer,
759 process: X,
760) -> IoResult<()>
761where
762 Reader: ReadFromDescriptorChain,
763 Writer: WriteToDescriptorChain,
764 I: FromDescriptorChain,
765 O: ToDescriptorChain,
766 X: FnOnce(I) -> Result<O, i32>,
767{
768 wr_ioctl_with_err_payload(ioctl, reader, writer, |input| {
769 process(input).map_err(|err| (err, None))
770 })
771}
772
773fn w_ioctl<Reader, Writer, I, X>(
780 ioctl: V4l2Ioctl,
781 reader: &mut Reader,
782 writer: &mut Writer,
783 process: X,
784) -> IoResult<()>
785where
786 I: FromDescriptorChain,
787 Reader: ReadFromDescriptorChain,
788 Writer: WriteToDescriptorChain,
789 X: FnOnce(I) -> Result<(), i32>,
790{
791 wr_ioctl(ioctl, reader, writer, process)
792}
793
794fn r_ioctl<Writer, O, X>(ioctl: V4l2Ioctl, writer: &mut Writer, process: X) -> IoResult<()>
801where
802 Writer: WriteToDescriptorChain,
803 O: ToDescriptorChain,
804 X: FnOnce() -> Result<O, i32>,
805{
806 wr_ioctl(ioctl, &mut std::io::empty(), writer, |()| process())
807}
808
809fn patch_streamparm(mut parm: v4l2_streamparm) -> v4l2_streamparm {
812 match QueueType::n(parm.type_)
813 .unwrap_or(QueueType::VideoCapture)
814 .direction()
815 {
816 QueueDirection::Output => parm.parm.output.writebuffers = 0,
817 QueueDirection::Capture => parm.parm.capture.readbuffers = 0,
818 }
819
820 parm
821}
822
823pub fn virtio_media_dispatch_ioctl<S, H, Reader, Writer>(
831 handler: &mut H,
832 session: &mut S,
833 ioctl: V4l2Ioctl,
834 reader: &mut Reader,
835 writer: &mut Writer,
836) -> IoResult<()>
837where
838 H: VirtioMediaIoctlHandler<Session = S>,
839 Reader: ReadFromDescriptorChain,
840 Writer: WriteToDescriptorChain,
841{
842 use V4l2Ioctl::*;
843
844 match ioctl {
845 VIDIOC_QUERYCAP => invalid_ioctl(ioctl, writer),
846 VIDIOC_ENUM_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_fmtdesc| {
847 let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
848 handler.enum_fmt(session, queue, format.index)
849 }),
850 VIDIOC_G_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_format| {
851 let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
852 handler.g_fmt(session, queue)
853 }),
854 VIDIOC_S_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_format| {
855 let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
856 handler.s_fmt(session, queue, format)
857 }),
858 VIDIOC_REQBUFS => wr_ioctl(ioctl, reader, writer, |reqbufs: v4l2_requestbuffers| {
859 let queue = QueueType::n(reqbufs.type_).ok_or(libc::EINVAL)?;
860 let memory = MemoryType::n(reqbufs.memory).ok_or(libc::EINVAL)?;
861 let flags = MemoryConsistency::from_bits(reqbufs.flags).ok_or(libc::EINVAL)?;
862
863 match memory {
864 MemoryType::Mmap | MemoryType::UserPtr => (),
865 t => {
866 log::error!(
867 "VIDIOC_REQBUFS: memory type {:?} is currently unsupported",
868 t
869 );
870 return Err(libc::EINVAL);
871 }
872 }
873
874 handler.reqbufs(session, queue, memory, reqbufs.count, flags)
875 }),
876 VIDIOC_QUERYBUF => {
877 wr_ioctl(ioctl, reader, writer, |buffer: v4l2_buffer| {
878 let queue = QueueType::n(buffer.type_).ok_or(libc::EINVAL)?;
879 let num_planes = if queue.is_multiplanar() {
881 buffer.length as usize
882 } else {
883 0
884 };
885
886 handler
887 .querybuf(session, queue, buffer.index)
888 .map(|guest_buffer| (guest_buffer, num_planes))
889 })
890 }
891 VIDIOC_G_FBUF => invalid_ioctl(ioctl, writer),
892 VIDIOC_S_FBUF => invalid_ioctl(ioctl, writer),
893 VIDIOC_OVERLAY => invalid_ioctl(ioctl, writer),
894 VIDIOC_QBUF => wr_ioctl(ioctl, reader, writer, |(guest_buffer, guest_regions)| {
895 let num_planes = guest_buffer.num_planes();
896
897 handler
898 .qbuf(session, guest_buffer, guest_regions)
899 .map(|guest_buffer| (guest_buffer, num_planes))
900 }),
901 VIDIOC_EXPBUF => invalid_ioctl(ioctl, writer),
903 VIDIOC_DQBUF => invalid_ioctl(ioctl, writer),
904 VIDIOC_STREAMON => w_ioctl(ioctl, reader, writer, |input: u32| {
905 let queue = QueueType::n(input).ok_or(libc::EINVAL)?;
906
907 handler.streamon(session, queue)
908 }),
909 VIDIOC_STREAMOFF => w_ioctl(ioctl, reader, writer, |input: u32| {
910 let queue = QueueType::n(input).ok_or(libc::EINVAL)?;
911
912 handler.streamoff(session, queue)
913 }),
914 VIDIOC_G_PARM => wr_ioctl(ioctl, reader, writer, |parm: v4l2_streamparm| {
915 let queue = QueueType::n(parm.type_).ok_or(libc::EINVAL)?;
916
917 handler.g_parm(session, queue).map(patch_streamparm)
918 }),
919 VIDIOC_S_PARM => wr_ioctl(ioctl, reader, writer, |parm: v4l2_streamparm| {
920 handler
921 .s_parm(session, patch_streamparm(parm))
922 .map(patch_streamparm)
923 }),
924 VIDIOC_G_STD => r_ioctl(ioctl, writer, || handler.g_std(session)),
925 VIDIOC_S_STD => w_ioctl(ioctl, reader, writer, |id: v4l2_std_id| {
926 handler.s_std(session, id)
927 }),
928 VIDIOC_ENUMSTD => wr_ioctl(ioctl, reader, writer, |std: v4l2_standard| {
929 handler.enumstd(session, std.index)
930 }),
931 VIDIOC_ENUMINPUT => wr_ioctl(ioctl, reader, writer, |input: v4l2_input| {
932 handler.enuminput(session, input.index)
933 }),
934 VIDIOC_G_CTRL => wr_ioctl(ioctl, reader, writer, |ctrl: v4l2_control| {
935 handler.g_ctrl(session, ctrl.id)
936 }),
937 VIDIOC_S_CTRL => wr_ioctl(ioctl, reader, writer, |ctrl: v4l2_control| {
938 handler.s_ctrl(session, ctrl.id, ctrl.value)
939 }),
940 VIDIOC_G_TUNER => wr_ioctl(ioctl, reader, writer, |tuner: v4l2_tuner| {
941 handler.g_tuner(session, tuner.index)
942 }),
943 VIDIOC_S_TUNER => w_ioctl(ioctl, reader, writer, |tuner: v4l2_tuner| {
944 let mode = TunerMode::n(tuner.audmode).ok_or(libc::EINVAL)?;
945 handler.s_tuner(session, tuner.index, mode)
946 }),
947 VIDIOC_G_AUDIO => r_ioctl(ioctl, writer, || handler.g_audio(session)),
948 VIDIOC_S_AUDIO => w_ioctl(ioctl, reader, writer, |input: v4l2_audio| {
949 handler.s_audio(session, input.index, AudioMode::n(input.mode))
950 }),
951 VIDIOC_QUERYCTRL => wr_ioctl(ioctl, reader, writer, |input: v4l2_queryctrl| {
952 let (id, flags) = v4l2r::ioctl::parse_ctrl_id_and_flags(input.id);
953
954 handler.queryctrl(session, id, flags)
955 }),
956 VIDIOC_QUERYMENU => wr_ioctl(ioctl, reader, writer, |input: v4l2_querymenu| {
957 handler.querymenu(session, input.id, input.index)
958 }),
959 VIDIOC_G_INPUT => r_ioctl(ioctl, writer, || handler.g_input(session)),
960 VIDIOC_S_INPUT => wr_ioctl(ioctl, reader, writer, |input: i32| {
961 handler.s_input(session, input)
962 }),
963 VIDIOC_G_EDID => invalid_ioctl(ioctl, writer),
964 VIDIOC_S_EDID => invalid_ioctl(ioctl, writer),
965 VIDIOC_G_OUTPUT => r_ioctl(ioctl, writer, || handler.g_output(session)),
966 VIDIOC_S_OUTPUT => wr_ioctl(ioctl, reader, writer, |output: i32| {
967 handler.s_output(session, output)
968 }),
969 VIDIOC_ENUMOUTPUT => wr_ioctl(ioctl, reader, writer, |output: v4l2_output| {
970 handler.enumoutput(session, output.index)
971 }),
972 VIDIOC_G_AUDOUT => r_ioctl(ioctl, writer, || handler.g_audout(session)),
973 VIDIOC_S_AUDOUT => w_ioctl(ioctl, reader, writer, |audout: v4l2_audioout| {
974 handler.s_audout(session, audout.index)
975 }),
976 VIDIOC_G_MODULATOR => wr_ioctl(ioctl, reader, writer, |modulator: v4l2_modulator| {
977 handler.g_modulator(session, modulator.index)
978 }),
979 VIDIOC_S_MODULATOR => w_ioctl(ioctl, reader, writer, |modulator: v4l2_modulator| {
980 let flags =
981 TunerTransmissionFlags::from_bits(modulator.txsubchans).ok_or(libc::EINVAL)?;
982 handler.s_modulator(session, modulator.index, flags)
983 }),
984 VIDIOC_G_FREQUENCY => wr_ioctl(ioctl, reader, writer, |freq: v4l2_frequency| {
985 handler.g_frequency(session, freq.tuner)
986 }),
987 VIDIOC_S_FREQUENCY => w_ioctl(ioctl, reader, writer, |freq: v4l2_frequency| {
988 let type_ = TunerType::n(freq.type_).ok_or(libc::EINVAL)?;
989
990 handler.s_frequency(session, freq.tuner, type_, freq.frequency)
991 }),
992 VIDIOC_CROPCAP => invalid_ioctl(ioctl, writer),
994 VIDIOC_G_CROP => invalid_ioctl(ioctl, writer),
995 VIDIOC_S_CROP => invalid_ioctl(ioctl, writer),
996 VIDIOC_G_JPEGCOMP => invalid_ioctl(ioctl, writer),
998 VIDIOC_S_JPEGCOMP => invalid_ioctl(ioctl, writer),
1000 VIDIOC_QUERYSTD => r_ioctl(ioctl, writer, || handler.querystd(session)),
1001 VIDIOC_TRY_FMT => wr_ioctl(ioctl, reader, writer, |format: v4l2_format| {
1002 let queue = QueueType::n(format.type_).ok_or(libc::EINVAL)?;
1003 handler.try_fmt(session, queue, format)
1004 }),
1005 VIDIOC_ENUMAUDIO => wr_ioctl(ioctl, reader, writer, |audio: v4l2_audio| {
1006 handler.enumaudio(session, audio.index)
1007 }),
1008 VIDIOC_ENUMAUDOUT => wr_ioctl(ioctl, reader, writer, |audio: v4l2_audioout| {
1009 handler.enumaudout(session, audio.index)
1010 }),
1011 VIDIOC_G_PRIORITY => invalid_ioctl(ioctl, writer),
1012 VIDIOC_S_PRIORITY => invalid_ioctl(ioctl, writer),
1013 VIDIOC_G_SLICED_VBI_CAP => invalid_ioctl(ioctl, writer),
1015 VIDIOC_LOG_STATUS => invalid_ioctl(ioctl, writer),
1017 VIDIOC_G_EXT_CTRLS => wr_ioctl_with_err_payload(
1018 ioctl,
1019 reader,
1020 writer,
1021 |(mut ctrls, mut ctrl_array, user_regions)| {
1022 let which = CtrlWhich::try_from(&ctrls).map_err(|()| (libc::EINVAL, None))?;
1023
1024 match handler.g_ext_ctrls(session, which, &mut ctrls, &mut ctrl_array, user_regions)
1025 {
1026 Ok(()) => Ok((ctrls, ctrl_array)),
1027 Err(e) => Err((e, Some((ctrls, ctrl_array)))),
1030 }
1031 },
1032 ),
1033 VIDIOC_S_EXT_CTRLS => wr_ioctl_with_err_payload(
1034 ioctl,
1035 reader,
1036 writer,
1037 |(mut ctrls, mut ctrl_array, user_regions)| {
1038 let which = CtrlWhich::try_from(&ctrls).map_err(|()| (libc::EINVAL, None))?;
1039
1040 match handler.s_ext_ctrls(session, which, &mut ctrls, &mut ctrl_array, user_regions)
1041 {
1042 Ok(()) => Ok((ctrls, ctrl_array)),
1043 Err(e) => Err((e, Some((ctrls, ctrl_array)))),
1046 }
1047 },
1048 ),
1049 VIDIOC_TRY_EXT_CTRLS => wr_ioctl_with_err_payload(
1050 ioctl,
1051 reader,
1052 writer,
1053 |(mut ctrls, mut ctrl_array, user_regions)| {
1054 let which = CtrlWhich::try_from(&ctrls).map_err(|()| (libc::EINVAL, None))?;
1055
1056 match handler.try_ext_ctrls(
1057 session,
1058 which,
1059 &mut ctrls,
1060 &mut ctrl_array,
1061 user_regions,
1062 ) {
1063 Ok(()) => Ok((ctrls, ctrl_array)),
1064 Err(e) => Err((e, Some((ctrls, ctrl_array)))),
1067 }
1068 },
1069 ),
1070 VIDIOC_ENUM_FRAMESIZES => {
1071 wr_ioctl(ioctl, reader, writer, |frmsizeenum: v4l2_frmsizeenum| {
1072 handler.enum_framesizes(session, frmsizeenum.index, frmsizeenum.pixel_format)
1073 })
1074 }
1075 VIDIOC_ENUM_FRAMEINTERVALS => {
1076 wr_ioctl(ioctl, reader, writer, |frmivalenum: v4l2_frmivalenum| {
1077 handler.enum_frameintervals(
1078 session,
1079 frmivalenum.index,
1080 frmivalenum.pixel_format,
1081 frmivalenum.width,
1082 frmivalenum.height,
1083 )
1084 })
1085 }
1086 VIDIOC_G_ENC_INDEX => r_ioctl(ioctl, writer, || handler.g_enc_index(session)),
1087 VIDIOC_ENCODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_encoder_cmd| {
1088 handler.encoder_cmd(session, cmd)
1089 }),
1090 VIDIOC_TRY_ENCODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_encoder_cmd| {
1091 handler.try_encoder_cmd(session, cmd)
1092 }),
1093 VIDIOC_DBG_G_REGISTER => invalid_ioctl(ioctl, writer),
1095 VIDIOC_DBG_S_REGISTER => invalid_ioctl(ioctl, writer),
1097 VIDIOC_S_HW_FREQ_SEEK => invalid_ioctl(ioctl, writer),
1098 VIDIOC_S_DV_TIMINGS => wr_ioctl(ioctl, reader, writer, |timings: v4l2_dv_timings| {
1099 handler.s_dv_timings(session, timings)
1100 }),
1101 VIDIOC_G_DV_TIMINGS => wr_ioctl(
1102 ioctl,
1103 reader,
1104 writer,
1105 |_: v4l2_dv_timings| handler.g_dv_timings(session),
1107 ),
1108 VIDIOC_DQEVENT => invalid_ioctl(ioctl, writer),
1110 VIDIOC_SUBSCRIBE_EVENT => {
1111 w_ioctl(ioctl, reader, writer, |input: v4l2_event_subscription| {
1112 let event = V4l2EventType::try_from(&input).unwrap();
1113 let flags = SubscribeEventFlags::from_bits(input.flags).unwrap();
1114
1115 handler.subscribe_event(session, event, flags)
1116 })?;
1117
1118 Ok(())
1119 }
1120 VIDIOC_UNSUBSCRIBE_EVENT => {
1121 w_ioctl(ioctl, reader, writer, |event: v4l2_event_subscription| {
1122 handler.unsubscribe_event(session, event)
1123 })
1124 }
1125 VIDIOC_CREATE_BUFS => wr_ioctl(ioctl, reader, writer, |input: v4l2_create_buffers| {
1126 let queue = QueueType::n(input.format.type_).ok_or(libc::EINVAL)?;
1127 let memory = MemoryType::n(input.memory).ok_or(libc::EINVAL)?;
1128
1129 handler.create_bufs(session, input.count, queue, memory, input.format)
1130 }),
1131 VIDIOC_PREPARE_BUF => wr_ioctl(ioctl, reader, writer, |(guest_buffer, guest_regions)| {
1132 let num_planes = guest_buffer.num_planes();
1133
1134 handler
1135 .prepare_buf(session, guest_buffer, guest_regions)
1136 .map(|out_buffer| (out_buffer, num_planes))
1137 }),
1138 VIDIOC_G_SELECTION => wr_ioctl(ioctl, reader, writer, |mut selection: v4l2_selection| {
1139 let sel_type = SelectionType::n(selection.type_).ok_or(libc::EINVAL)?;
1140 let sel_target = SelectionTarget::n(selection.target).ok_or(libc::EINVAL)?;
1141
1142 handler
1143 .g_selection(session, sel_type, sel_target)
1144 .map(|rect| {
1145 selection.r = rect;
1146 selection
1147 })
1148 }),
1149 VIDIOC_S_SELECTION => wr_ioctl(ioctl, reader, writer, |mut selection: v4l2_selection| {
1150 let sel_type = SelectionType::n(selection.type_).ok_or(libc::EINVAL)?;
1151 let sel_target = SelectionTarget::n(selection.target).ok_or(libc::EINVAL)?;
1152 let sel_flags = SelectionFlags::from_bits(selection.flags).ok_or(libc::EINVAL)?;
1153
1154 handler
1155 .s_selection(session, sel_type, sel_target, selection.r, sel_flags)
1156 .map(|rect| {
1157 selection.r = rect;
1158 selection
1159 })
1160 }),
1161 VIDIOC_DECODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_decoder_cmd| {
1162 handler.decoder_cmd(session, cmd)
1163 }),
1164 VIDIOC_TRY_DECODER_CMD => wr_ioctl(ioctl, reader, writer, |cmd: v4l2_decoder_cmd| {
1165 handler.try_decoder_cmd(session, cmd)
1166 }),
1167 VIDIOC_ENUM_DV_TIMINGS => wr_ioctl(
1168 ioctl,
1169 reader,
1170 writer,
1171 |mut enum_timings: v4l2_enum_dv_timings| {
1172 handler
1173 .enum_dv_timings(session, enum_timings.index)
1174 .map(|timings| {
1175 enum_timings.timings = timings;
1176 enum_timings
1177 })
1178 },
1179 ),
1180 VIDIOC_QUERY_DV_TIMINGS => r_ioctl(ioctl, writer, || handler.query_dv_timings(session)),
1181 VIDIOC_DV_TIMINGS_CAP => wr_ioctl(ioctl, reader, writer, |_: v4l2_dv_timings_cap| {
1182 handler.dv_timings_cap(session)
1183 }),
1184 VIDIOC_ENUM_FREQ_BANDS => {
1185 wr_ioctl(ioctl, reader, writer, |freq_band: v4l2_frequency_band| {
1186 let type_ = TunerType::n(freq_band.type_).ok_or(libc::EINVAL)?;
1187
1188 handler.enum_freq_bands(session, freq_band.tuner, type_, freq_band.index)
1189 })
1190 }
1191 VIDIOC_DBG_G_CHIP_INFO => invalid_ioctl(ioctl, writer),
1193 VIDIOC_QUERY_EXT_CTRL => wr_ioctl(ioctl, reader, writer, |ctrl: v4l2_query_ext_ctrl| {
1194 let (id, flags) = v4l2r::ioctl::parse_ctrl_id_and_flags(ctrl.id);
1195 handler.query_ext_ctrl(session, id, flags)
1196 }),
1197 }
1198}