1use std::collections::VecDeque;
11use std::io::BufWriter;
12use std::io::Result as IoResult;
13use std::io::Seek;
14use std::io::SeekFrom;
15use std::io::Write;
16use std::os::fd::AsFd;
17use std::os::fd::BorrowedFd;
18
19use v4l2r::bindings;
20use v4l2r::bindings::v4l2_fmtdesc;
21use v4l2r::bindings::v4l2_format;
22use v4l2r::bindings::v4l2_requestbuffers;
23use v4l2r::ioctl::BufferCapabilities;
24use v4l2r::ioctl::BufferField;
25use v4l2r::ioctl::BufferFlags;
26use v4l2r::ioctl::MemoryConsistency;
27use v4l2r::ioctl::V4l2Buffer;
28use v4l2r::ioctl::V4l2PlanesWithBackingMut;
29use v4l2r::memory::MemoryType;
30use v4l2r::PixelFormat;
31use v4l2r::QueueType;
32
33use crate::ioctl::virtio_media_dispatch_ioctl;
34use crate::ioctl::IoctlResult;
35use crate::ioctl::VirtioMediaIoctlHandler;
36use crate::memfd::MemFdBuffer;
37use crate::mmap::MmapMappingManager;
38use crate::protocol::DequeueBufferEvent;
39use crate::protocol::SgEntry;
40use crate::protocol::V4l2Event;
41use crate::protocol::V4l2Ioctl;
42use crate::protocol::VIRTIO_MEDIA_MMAP_FLAG_RW;
43use crate::ReadFromDescriptorChain;
44use crate::VirtioMediaDevice;
45use crate::VirtioMediaDeviceSession;
46use crate::VirtioMediaEventQueue;
47use crate::VirtioMediaHostMemoryMapper;
48use crate::WriteToDescriptorChain;
49
50#[derive(Debug, PartialEq, Eq)]
52enum BufferState {
53 New,
55 Incoming,
57 Outgoing {
59 sequence: u32,
61 },
62}
63
64struct Buffer {
66 state: BufferState,
68 v4l2_buffer: V4l2Buffer,
70 fd: MemFdBuffer,
72 offset: u32,
76}
77
78impl Buffer {
79 fn set_state(&mut self, state: BufferState) {
81 let mut flags = self.v4l2_buffer.flags();
82 match state {
83 BufferState::New => {
84 *self.v4l2_buffer.get_first_plane_mut().bytesused = 0;
85 flags -= BufferFlags::QUEUED;
86 }
87 BufferState::Incoming => {
88 *self.v4l2_buffer.get_first_plane_mut().bytesused = 0;
89 flags |= BufferFlags::QUEUED;
90 }
91 BufferState::Outgoing { sequence } => {
92 *self.v4l2_buffer.get_first_plane_mut().bytesused = BUFFER_SIZE;
93 self.v4l2_buffer.set_sequence(sequence);
94 self.v4l2_buffer.set_timestamp(bindings::timeval {
95 tv_sec: (sequence + 1) as bindings::__time_t / 1000,
96 tv_usec: (sequence + 1) as bindings::__time_t % 1000,
97 });
98 flags -= BufferFlags::QUEUED;
99 }
100 }
101
102 self.v4l2_buffer.set_flags(flags);
103 self.state = state;
104 }
105}
106
107pub struct SimpleCaptureDeviceSession {
109 id: u32,
111 iteration: u64,
113 buffers: Vec<Buffer>,
115 queued_buffers: VecDeque<usize>,
117 streaming: bool,
119}
120
121impl VirtioMediaDeviceSession for SimpleCaptureDeviceSession {
122 fn poll_fd(&self) -> Option<BorrowedFd<'_>> {
123 None
124 }
125}
126
127impl SimpleCaptureDeviceSession {
128 fn process_queued_buffers<Q: VirtioMediaEventQueue>(
131 &mut self,
132 evt_queue: &mut Q,
133 ) -> IoctlResult<()> {
134 while let Some(buf_id) = self.queued_buffers.pop_front() {
135 let buffer = self.buffers.get_mut(buf_id).ok_or(libc::EIO)?;
136 let sequence = self.iteration as u32;
137
138 buffer
139 .fd
140 .as_file()
141 .seek(SeekFrom::Start(0))
142 .map_err(|_| libc::EIO)?;
143 let mut writer = BufWriter::new(buffer.fd.as_file());
144 let y = (sequence % 256) as u8;
145 let u = ((sequence + 64) % 256) as u8;
146 let v = ((sequence + 128) % 256) as u8;
147
148 for _ in 0..(WIDTH * HEIGHT) {
149 writer.write_all(&[y]).map_err(|_| libc::EIO)?;
150 }
151 for _ in 0..(WIDTH * HEIGHT / 4) {
152 writer.write_all(&[u]).map_err(|_| libc::EIO)?;
153 }
154 for _ in 0..(WIDTH * HEIGHT / 4) {
155 writer.write_all(&[v]).map_err(|_| libc::EIO)?;
156 }
157 drop(writer);
158
159 *buffer.v4l2_buffer.get_first_plane_mut().bytesused = BUFFER_SIZE;
160 buffer.set_state(BufferState::Outgoing { sequence });
161 self.iteration += 1;
163
164 let v4l2_buffer = buffer.v4l2_buffer.clone();
165
166 evt_queue.send_event(V4l2Event::DequeueBuffer(DequeueBufferEvent::new(
167 self.id,
168 v4l2_buffer,
169 )));
170 }
171
172 Ok(())
173 }
174}
175
176pub struct SimpleCaptureDevice<Q: VirtioMediaEventQueue, HM: VirtioMediaHostMemoryMapper> {
182 evt_queue: Q,
184 mmap_manager: MmapMappingManager<HM>,
186 active_session: Option<u32>,
193}
194
195impl<Q, HM> SimpleCaptureDevice<Q, HM>
196where
197 Q: VirtioMediaEventQueue,
198 HM: VirtioMediaHostMemoryMapper,
199{
200 pub fn new(evt_queue: Q, mapper: HM) -> Self {
201 Self {
202 evt_queue,
203 mmap_manager: MmapMappingManager::from(mapper),
204 active_session: None,
205 }
206 }
207}
208
209impl<Q, HM, Reader, Writer> VirtioMediaDevice<Reader, Writer> for SimpleCaptureDevice<Q, HM>
210where
211 Q: VirtioMediaEventQueue,
212 HM: VirtioMediaHostMemoryMapper,
213 Reader: ReadFromDescriptorChain,
214 Writer: WriteToDescriptorChain,
215{
216 type Session = SimpleCaptureDeviceSession;
217
218 fn new_session(&mut self, session_id: u32) -> Result<Self::Session, i32> {
219 Ok(SimpleCaptureDeviceSession {
220 id: session_id,
221 iteration: 0,
222 buffers: Default::default(),
223 queued_buffers: Default::default(),
224 streaming: false,
225 })
226 }
227
228 fn close_session(&mut self, session: Self::Session) {
229 if self.active_session == Some(session.id) {
230 self.active_session = None;
231 }
232
233 for buffer in &session.buffers {
234 self.mmap_manager.unregister_buffer(buffer.offset);
235 }
236 }
237
238 fn do_ioctl(
239 &mut self,
240 session: &mut Self::Session,
241 ioctl: V4l2Ioctl,
242 reader: &mut Reader,
243 writer: &mut Writer,
244 ) -> IoResult<()> {
245 virtio_media_dispatch_ioctl(self, session, ioctl, reader, writer)
246 }
247
248 fn do_mmap(
249 &mut self,
250 session: &mut Self::Session,
251 flags: u32,
252 offset: u32,
253 ) -> Result<(u64, u64), i32> {
254 let buffer = session
255 .buffers
256 .iter_mut()
257 .find(|b| b.offset == offset)
258 .ok_or(libc::EINVAL)?;
259 let rw = (flags & VIRTIO_MEDIA_MMAP_FLAG_RW) != 0;
260 let fd = buffer.fd.as_file().as_fd();
261 let (guest_addr, size) = self
262 .mmap_manager
263 .create_mapping(offset, fd, rw)
264 .map_err(|_| libc::EINVAL)?;
265
266 Ok((guest_addr, size))
272 }
273
274 fn do_munmap(&mut self, guest_addr: u64) -> Result<(), i32> {
275 self.mmap_manager
276 .remove_mapping(guest_addr)
277 .map(|_| ())
278 .map_err(|_| libc::EINVAL)
279 }
280}
281
282const PIXELFORMAT: u32 = PixelFormat::from_fourcc(b"YU12").to_u32();
283const WIDTH: u32 = 640;
284const HEIGHT: u32 = 480;
285const FRAME_RATE: u32 = 30;
286const BUFFER_SIZE: u32 = WIDTH * HEIGHT * 3 / 2;
287
288const INPUTS: [bindings::v4l2_input; 1] = [bindings::v4l2_input {
289 index: 0,
290 name: *b"Default\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",
291 type_: bindings::V4L2_INPUT_TYPE_CAMERA,
292 ..unsafe { std::mem::zeroed() }
293}];
294
295fn default_fmtdesc(queue: QueueType) -> v4l2_fmtdesc {
296 v4l2_fmtdesc {
297 index: 0,
298 type_: queue as u32,
299 pixelformat: PIXELFORMAT,
300 ..Default::default()
301 }
302}
303
304fn default_fmt(queue: QueueType) -> v4l2_format {
305 let pix_mp = bindings::v4l2_pix_format_mplane {
306 width: WIDTH,
307 height: HEIGHT,
308 pixelformat: PIXELFORMAT,
309 field: bindings::v4l2_field_V4L2_FIELD_NONE,
310 colorspace: bindings::v4l2_colorspace_V4L2_COLORSPACE_SRGB,
311 num_planes: 3,
312 plane_fmt: [
313 bindings::v4l2_plane_pix_format {
314 sizeimage: WIDTH * HEIGHT,
315 bytesperline: WIDTH,
316 ..Default::default()
317 },
318 bindings::v4l2_plane_pix_format {
319 sizeimage: WIDTH * HEIGHT / 4,
320 bytesperline: WIDTH / 2,
321 ..Default::default()
322 },
323 bindings::v4l2_plane_pix_format {
324 sizeimage: WIDTH * HEIGHT / 4,
325 bytesperline: WIDTH / 2,
326 ..Default::default()
327 },
328 Default::default(),
329 Default::default(),
330 Default::default(),
331 Default::default(),
332 Default::default(),
333 ],
334 ..Default::default()
335 };
336
337 v4l2_format {
338 type_: queue as u32,
339 fmt: bindings::v4l2_format__bindgen_ty_1 { pix_mp },
340 }
341}
342
343impl<Q, HM> VirtioMediaIoctlHandler for SimpleCaptureDevice<Q, HM>
345where
346 Q: VirtioMediaEventQueue,
347 HM: VirtioMediaHostMemoryMapper,
348{
349 type Session = SimpleCaptureDeviceSession;
350
351 fn enum_fmt(
352 &mut self,
353 _session: &Self::Session,
354 queue: QueueType,
355 index: u32,
356 ) -> IoctlResult<v4l2_fmtdesc> {
357 if queue != QueueType::VideoCaptureMplane {
358 return Err(libc::EINVAL);
359 }
360 if index > 0 {
361 return Err(libc::EINVAL);
362 }
363
364 Ok(default_fmtdesc(queue))
365 }
366
367 fn g_fmt(&mut self, _session: &Self::Session, queue: QueueType) -> IoctlResult<v4l2_format> {
368 if queue != QueueType::VideoCaptureMplane {
369 return Err(libc::EINVAL);
370 }
371 Ok(default_fmt(queue))
372 }
373
374 fn s_fmt(
375 &mut self,
376 _session: &mut Self::Session,
377 queue: QueueType,
378 _format: v4l2_format,
379 ) -> IoctlResult<v4l2_format> {
380 if queue != QueueType::VideoCaptureMplane {
381 return Err(libc::EINVAL);
382 }
383 Ok(default_fmt(queue))
384 }
385
386 fn try_fmt(
387 &mut self,
388 _session: &Self::Session,
389 queue: QueueType,
390 _format: v4l2_format,
391 ) -> IoctlResult<v4l2_format> {
392 if queue != QueueType::VideoCaptureMplane {
393 return Err(libc::EINVAL);
394 }
395 Ok(default_fmt(queue))
396 }
397
398 fn g_parm(
399 &mut self,
400 _session: &Self::Session,
401 queue: QueueType,
402 ) -> IoctlResult<bindings::v4l2_streamparm> {
403 if queue != QueueType::VideoCaptureMplane {
404 return Err(libc::EINVAL);
405 }
406
407 let mut parm = bindings::v4l2_streamparm {
408 type_: queue as u32,
409 ..Default::default()
410 };
411
412 let capture = unsafe { &mut parm.parm.capture };
414 capture.capability = bindings::V4L2_CAP_TIMEPERFRAME;
415 capture.timeperframe = bindings::v4l2_fract {
416 numerator: 1,
417 denominator: FRAME_RATE,
418 };
419
420 Ok(parm)
421 }
422
423 fn s_parm(
424 &mut self,
425 _session: &mut Self::Session,
426 mut parm: bindings::v4l2_streamparm,
427 ) -> IoctlResult<bindings::v4l2_streamparm> {
428 if parm.type_ != QueueType::VideoCaptureMplane as u32 {
429 return Err(libc::EINVAL);
430 }
431
432 let capture = unsafe { &mut parm.parm.capture };
435 capture.capability = bindings::V4L2_CAP_TIMEPERFRAME;
436 capture.timeperframe = bindings::v4l2_fract {
437 numerator: 1,
438 denominator: FRAME_RATE,
439 };
440
441 Ok(parm)
442 }
443
444 fn reqbufs(
445 &mut self,
446 session: &mut Self::Session,
447 queue: QueueType,
448 memory: MemoryType,
449 count: u32,
450 _flags: MemoryConsistency,
451 ) -> IoctlResult<v4l2_requestbuffers> {
452 if queue != QueueType::VideoCaptureMplane {
453 return Err(libc::EINVAL);
454 }
455 if memory != MemoryType::Mmap {
456 return Err(libc::EINVAL);
457 }
458 if session.streaming {
459 return Err(libc::EBUSY);
460 }
461 match self.active_session {
464 Some(id) if id != session.id => return Err(libc::EBUSY),
465 _ => (),
466 }
467
468 if count == 0 {
470 self.active_session = None;
471 self.streamoff(session, queue)?;
472 } else {
473 session.queued_buffers.clear();
475 for buffer in session.buffers.iter_mut() {
476 buffer.set_state(BufferState::New);
477 }
478 self.active_session = Some(session.id);
479 }
480
481 let count = std::cmp::min(count, 32);
482
483 for buffer in &session.buffers {
484 self.mmap_manager.unregister_buffer(buffer.offset);
485 }
486
487 session.buffers = (0..count)
488 .map(|i| {
489 MemFdBuffer::new(BUFFER_SIZE as u64)
490 .map_err(|e| {
491 log::error!("failed to allocate MMAP buffers: {:#}", e);
492 libc::ENOMEM
493 })
494 .and_then(|fd| {
495 let offset = self
496 .mmap_manager
497 .register_buffer(None, BUFFER_SIZE)
498 .map_err(|_| libc::EINVAL)?;
499
500 let mut v4l2_buffer = V4l2Buffer::new(queue, i, MemoryType::Mmap);
501 if let V4l2PlanesWithBackingMut::Mmap(mut planes) =
502 v4l2_buffer.planes_with_backing_iter_mut()
503 {
504 let mut plane = planes.next().unwrap();
506 plane.set_mem_offset(offset);
507 *plane.length = BUFFER_SIZE;
508 } else {
509 panic!()
512 }
513 v4l2_buffer.set_field(BufferField::None);
514 v4l2_buffer.set_flags(BufferFlags::TIMESTAMP_MONOTONIC);
515
516 Ok(Buffer {
517 state: BufferState::New,
518 v4l2_buffer,
519 fd,
520 offset,
521 })
522 })
523 })
524 .collect::<Result<_, _>>()?;
525
526 Ok(v4l2_requestbuffers {
527 count,
528 type_: queue as u32,
529 memory: memory as u32,
530 capabilities: (BufferCapabilities::SUPPORTS_MMAP
531 | BufferCapabilities::SUPPORTS_ORPHANED_BUFS)
532 .bits(),
533 flags: 0,
536 ..Default::default()
537 })
538 }
539
540 fn querybuf(
541 &mut self,
542 session: &Self::Session,
543 queue: QueueType,
544 index: u32,
545 ) -> IoctlResult<v4l2r::ioctl::V4l2Buffer> {
546 if queue != QueueType::VideoCaptureMplane {
547 return Err(libc::EINVAL);
548 }
549 let buffer = session.buffers.get(index as usize).ok_or(libc::EINVAL)?;
550
551 Ok(buffer.v4l2_buffer.clone())
552 }
553
554 fn qbuf(
555 &mut self,
556 session: &mut Self::Session,
557 buffer: v4l2r::ioctl::V4l2Buffer,
558 _guest_regions: Vec<Vec<SgEntry>>,
559 ) -> IoctlResult<v4l2r::ioctl::V4l2Buffer> {
560 let host_buffer = session
561 .buffers
562 .get_mut(buffer.index() as usize)
563 .ok_or(libc::EINVAL)?;
564 if matches!(host_buffer.state, BufferState::Incoming) {
566 return Err(libc::EINVAL);
567 }
568
569 host_buffer.set_state(BufferState::Incoming);
570 session.queued_buffers.push_back(buffer.index() as usize);
571
572 let buffer = host_buffer.v4l2_buffer.clone();
573
574 if session.streaming {
575 session.process_queued_buffers(&mut self.evt_queue)?;
576 }
577
578 Ok(buffer)
579 }
580
581 fn streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
582 if queue != QueueType::VideoCaptureMplane || session.buffers.is_empty() {
583 return Err(libc::EINVAL);
584 }
585 session.streaming = true;
586
587 session.process_queued_buffers(&mut self.evt_queue)?;
588
589 Ok(())
590 }
591
592 fn streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
593 if queue != QueueType::VideoCaptureMplane {
594 return Err(libc::EINVAL);
595 }
596 session.streaming = false;
597 session.queued_buffers.clear();
598 for buffer in session.buffers.iter_mut() {
599 buffer.set_state(BufferState::New);
600 }
601
602 Ok(())
603 }
604
605 fn g_input(&mut self, _session: &Self::Session) -> IoctlResult<i32> {
606 Ok(0)
607 }
608
609 fn s_input(&mut self, _session: &mut Self::Session, input: i32) -> IoctlResult<i32> {
610 if input != 0 {
611 Err(libc::EINVAL)
612 } else {
613 Ok(0)
614 }
615 }
616
617 fn enuminput(
618 &mut self,
619 _session: &Self::Session,
620 index: u32,
621 ) -> IoctlResult<bindings::v4l2_input> {
622 match INPUTS.get(index as usize) {
623 Some(&input) => Ok(input),
624 None => Err(libc::EINVAL),
625 }
626 }
627
628 fn enum_framesizes(
629 &mut self,
630 _session: &Self::Session,
631 index: u32,
632 pixel_format: u32,
633 ) -> IoctlResult<bindings::v4l2_frmsizeenum> {
634 if pixel_format != PIXELFORMAT {
635 return Err(libc::EINVAL);
636 }
637 if index > 0 {
638 return Err(libc::EINVAL);
639 }
640
641 Ok(bindings::v4l2_frmsizeenum {
642 index,
643 pixel_format,
644 type_: bindings::v4l2_frmsizetypes_V4L2_FRMSIZE_TYPE_DISCRETE,
645 __bindgen_anon_1: bindings::v4l2_frmsizeenum__bindgen_ty_1 {
646 discrete: bindings::v4l2_frmsize_discrete {
647 width: WIDTH,
648 height: HEIGHT,
649 },
650 },
651 ..Default::default()
652 })
653 }
654
655 fn enum_frameintervals(
656 &mut self,
657 _session: &Self::Session,
658 index: u32,
659 pixel_format: u32,
660 width: u32,
661 height: u32,
662 ) -> IoctlResult<bindings::v4l2_frmivalenum> {
663 if pixel_format != PIXELFORMAT {
664 return Err(libc::EINVAL);
665 }
666 if width != WIDTH || height != HEIGHT {
667 return Err(libc::EINVAL);
668 }
669 if index > 0 {
670 return Err(libc::EINVAL);
671 }
672
673 Ok(bindings::v4l2_frmivalenum {
674 index,
675 pixel_format,
676 width,
677 height,
678 type_: bindings::v4l2_frmivaltypes_V4L2_FRMIVAL_TYPE_DISCRETE,
679 __bindgen_anon_1: bindings::v4l2_frmivalenum__bindgen_ty_1 {
680 discrete: bindings::v4l2_fract {
681 numerator: 1,
682 denominator: FRAME_RATE,
683 },
684 },
685 ..Default::default()
686 })
687 }
688}