xref: /aosp_15_r20/external/virtio-media/device/src/devices/simple_device.rs (revision 1b4853f54772485c5dd4001ae33a7a958bcc97a1)
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 //! Simple example virtio-media CAPTURE device with no dependency.
6 //!
7 //! This module illustrates how to write a device for virtio-media. It exposes a capture device
8 //! that generates a RGB pattern on the buffers queued by the guest.
9 
10 use std::collections::VecDeque;
11 use std::io::BufWriter;
12 use std::io::Result as IoResult;
13 use std::io::Seek;
14 use std::io::SeekFrom;
15 use std::io::Write;
16 use std::os::fd::AsFd;
17 use std::os::fd::BorrowedFd;
18 
19 use v4l2r::bindings;
20 use v4l2r::bindings::v4l2_fmtdesc;
21 use v4l2r::bindings::v4l2_format;
22 use v4l2r::bindings::v4l2_pix_format;
23 use v4l2r::bindings::v4l2_requestbuffers;
24 use v4l2r::ioctl::BufferCapabilities;
25 use v4l2r::ioctl::BufferField;
26 use v4l2r::ioctl::BufferFlags;
27 use v4l2r::ioctl::V4l2Buffer;
28 use v4l2r::ioctl::V4l2PlanesWithBackingMut;
29 use v4l2r::memory::MemoryType;
30 use v4l2r::PixelFormat;
31 use v4l2r::QueueType;
32 
33 use crate::ioctl::virtio_media_dispatch_ioctl;
34 use crate::ioctl::IoctlResult;
35 use crate::ioctl::VirtioMediaIoctlHandler;
36 use crate::memfd::MemFdBuffer;
37 use crate::mmap::MmapMappingManager;
38 use crate::protocol::DequeueBufferEvent;
39 use crate::protocol::SgEntry;
40 use crate::protocol::V4l2Event;
41 use crate::protocol::V4l2Ioctl;
42 use crate::protocol::VIRTIO_MEDIA_MMAP_FLAG_RW;
43 use crate::VirtioMediaDevice;
44 use crate::VirtioMediaDeviceSession;
45 use crate::VirtioMediaEventQueue;
46 use crate::VirtioMediaHostMemoryMapper;
47 
48 /// Current status of a buffer.
49 #[derive(Debug, PartialEq, Eq)]
50 enum BufferState {
51     /// Buffer has just been created (or streamed off) and not been used yet.
52     New,
53     /// Buffer has been QBUF'd by the driver but not yet processed.
54     Incoming,
55     /// Buffer has been processed and is ready for dequeue.
56     Outgoing {
57         /// Sequence of the generated frame.
58         sequence: u32,
59     },
60 }
61 
62 /// Information about a single buffer.
63 struct Buffer {
64     /// Current state of the buffer.
65     state: BufferState,
66     /// V4L2 representation of this buffer to be sent to the guest when requested.
67     v4l2_buffer: V4l2Buffer,
68     /// Backing storage for the buffer.
69     fd: MemFdBuffer,
70     /// Offset that can be used to map the buffer.
71     ///
72     /// Cached from `v4l2_buffer` to avoid doing a match.
73     offset: u32,
74 }
75 
76 impl Buffer {
77     /// Update the state of the buffer as well as its V4L2 representation.
set_state(&mut self, state: BufferState)78     fn set_state(&mut self, state: BufferState) {
79         let mut flags = self.v4l2_buffer.flags();
80         match state {
81             BufferState::New => {
82                 *self.v4l2_buffer.get_first_plane_mut().bytesused = 0;
83                 flags -= BufferFlags::QUEUED;
84             }
85             BufferState::Incoming => {
86                 *self.v4l2_buffer.get_first_plane_mut().bytesused = 0;
87                 flags |= BufferFlags::QUEUED;
88             }
89             BufferState::Outgoing { sequence } => {
90                 *self.v4l2_buffer.get_first_plane_mut().bytesused = BUFFER_SIZE;
91                 self.v4l2_buffer.set_sequence(sequence);
92                 self.v4l2_buffer.set_timestamp(bindings::timeval {
93                     tv_sec: (sequence + 1) as bindings::time_t / 1000,
94                     tv_usec: (sequence + 1) as bindings::time_t % 1000,
95                 });
96                 flags -= BufferFlags::QUEUED;
97             }
98         }
99 
100         self.v4l2_buffer.set_flags(flags);
101         self.state = state;
102     }
103 }
104 
105 /// Session data of [`SimpleCaptureDevice`].
106 pub struct SimpleCaptureDeviceSession {
107     /// Id of the session.
108     id: u32,
109     /// Current iteration of the pattern generation cycle.
110     iteration: u64,
111     /// Buffers currently allocated for this session.
112     buffers: Vec<Buffer>,
113     /// FIFO of queued buffers awaiting processing.
114     queued_buffers: VecDeque<usize>,
115     /// Is the session currently streaming?
116     streaming: bool,
117 }
118 
119 impl VirtioMediaDeviceSession for SimpleCaptureDeviceSession {
poll_fd(&self) -> Option<BorrowedFd>120     fn poll_fd(&self) -> Option<BorrowedFd> {
121         None
122     }
123 }
124 
125 impl SimpleCaptureDeviceSession {
126     /// Generate the data pattern on all queued buffers and send the corresponding
127     /// [`DequeueBufferEvent`] to the driver.
process_queued_buffers<Q: VirtioMediaEventQueue>( &mut self, evt_queue: &mut Q, ) -> IoctlResult<()>128     fn process_queued_buffers<Q: VirtioMediaEventQueue>(
129         &mut self,
130         evt_queue: &mut Q,
131     ) -> IoctlResult<()> {
132         while let Some(buf_id) = self.queued_buffers.pop_front() {
133             let buffer = self.buffers.get_mut(buf_id).ok_or(libc::EIO)?;
134             let sequence = self.iteration as u32;
135 
136             buffer
137                 .fd
138                 .as_file()
139                 .seek(SeekFrom::Start(0))
140                 .map_err(|_| libc::EIO)?;
141             let mut writer = BufWriter::new(buffer.fd.as_file());
142             let color = [
143                 0xffu8 * (sequence as u8 % 2),
144                 0x55u8 * (sequence as u8 % 3),
145                 0x10u8 * (sequence as u8 % 16),
146             ];
147             for _ in 0..(WIDTH * HEIGHT) {
148                 let _ = writer.write(&color).map_err(|_| libc::EIO)?;
149             }
150             drop(writer);
151 
152             *buffer.v4l2_buffer.get_first_plane_mut().bytesused = BUFFER_SIZE;
153             buffer.set_state(BufferState::Outgoing { sequence });
154             // TODO: should we set the DONE flag here?
155             self.iteration += 1;
156 
157             let v4l2_buffer = buffer.v4l2_buffer.clone();
158 
159             evt_queue.send_event(V4l2Event::DequeueBuffer(DequeueBufferEvent::new(
160                 self.id,
161                 v4l2_buffer,
162             )));
163         }
164 
165         Ok(())
166     }
167 }
168 
169 /// A simplistic video capture device, used to demonstrate how device code can be written, or for
170 /// testing VMMs and guests without dedicated hardware support.
171 ///
172 /// This device supports a single pixel format (`RGB3`) and a single resolution, and generates
173 /// frames of varying uniform color. The only buffer type supported is `MMAP`
174 pub struct SimpleCaptureDevice<Q: VirtioMediaEventQueue, HM: VirtioMediaHostMemoryMapper> {
175     /// Queue used to send events to the guest.
176     evt_queue: Q,
177     /// Host MMAP mapping manager.
178     mmap_manager: MmapMappingManager<HM>,
179     /// ID of the session with allocated buffers, if any.
180     ///
181     /// v4l2-compliance checks that only a single session can have allocated buffers at a given
182     /// time, since that's how actual hardware works - no two sessions can access a camera at the
183     /// same time. It will fails if we allow simultaneous sessions to be active, so we need this
184     /// artificial limitation to make it pass fully.
185     active_session: Option<u32>,
186 }
187 
188 impl<Q, HM> SimpleCaptureDevice<Q, HM>
189 where
190     Q: VirtioMediaEventQueue,
191     HM: VirtioMediaHostMemoryMapper,
192 {
new(evt_queue: Q, mapper: HM) -> Self193     pub fn new(evt_queue: Q, mapper: HM) -> Self {
194         Self {
195             evt_queue,
196             mmap_manager: MmapMappingManager::from(mapper),
197             active_session: None,
198         }
199     }
200 }
201 
202 impl<Q, HM, Reader, Writer> VirtioMediaDevice<Reader, Writer> for SimpleCaptureDevice<Q, HM>
203 where
204     Q: VirtioMediaEventQueue,
205     HM: VirtioMediaHostMemoryMapper,
206     Reader: std::io::Read,
207     Writer: std::io::Write,
208 {
209     type Session = SimpleCaptureDeviceSession;
210 
new_session(&mut self, session_id: u32) -> Result<Self::Session, i32>211     fn new_session(&mut self, session_id: u32) -> Result<Self::Session, i32> {
212         Ok(SimpleCaptureDeviceSession {
213             id: session_id,
214             iteration: 0,
215             buffers: Default::default(),
216             queued_buffers: Default::default(),
217             streaming: false,
218         })
219     }
220 
close_session(&mut self, session: Self::Session)221     fn close_session(&mut self, session: Self::Session) {
222         if self.active_session == Some(session.id) {
223             self.active_session = None;
224         }
225 
226         for buffer in &session.buffers {
227             self.mmap_manager.unregister_buffer(buffer.offset);
228         }
229     }
230 
do_ioctl( &mut self, session: &mut Self::Session, ioctl: V4l2Ioctl, reader: &mut Reader, writer: &mut Writer, ) -> IoResult<()>231     fn do_ioctl(
232         &mut self,
233         session: &mut Self::Session,
234         ioctl: V4l2Ioctl,
235         reader: &mut Reader,
236         writer: &mut Writer,
237     ) -> IoResult<()> {
238         virtio_media_dispatch_ioctl(self, session, ioctl, reader, writer)
239     }
240 
do_mmap( &mut self, session: &mut Self::Session, flags: u32, offset: u32, ) -> Result<(u64, u64), i32>241     fn do_mmap(
242         &mut self,
243         session: &mut Self::Session,
244         flags: u32,
245         offset: u32,
246     ) -> Result<(u64, u64), i32> {
247         let buffer = session
248             .buffers
249             .iter_mut()
250             .find(|b| b.offset == offset)
251             .ok_or(libc::EINVAL)?;
252         let rw = (flags & VIRTIO_MEDIA_MMAP_FLAG_RW) != 0;
253         let fd = buffer.fd.as_file().as_fd();
254         let (guest_addr, size) = self
255             .mmap_manager
256             .create_mapping(offset, fd, rw)
257             .map_err(|_| libc::EINVAL)?;
258 
259         // TODO: would be nice to enable this, but how do we find the buffer again during munmap?
260         //
261         // Maybe keep a guest_addr -> session map in the device...
262         // buffer.v4l2_buffer.set_flags(buffer.v4l2_buffer.flags() | BufferFlags::MAPPED);
263 
264         Ok((guest_addr, size))
265     }
266 
do_munmap(&mut self, guest_addr: u64) -> Result<(), i32>267     fn do_munmap(&mut self, guest_addr: u64) -> Result<(), i32> {
268         self.mmap_manager
269             .remove_mapping(guest_addr)
270             .map(|_| ())
271             .map_err(|_| libc::EINVAL)
272     }
273 }
274 
275 const PIXELFORMAT: u32 = PixelFormat::from_fourcc(b"RGB3").to_u32();
276 const WIDTH: u32 = 640;
277 const HEIGHT: u32 = 480;
278 const BYTES_PER_LINE: u32 = WIDTH * 3;
279 const BUFFER_SIZE: u32 = BYTES_PER_LINE * HEIGHT;
280 
281 const INPUTS: [bindings::v4l2_input; 1] = [bindings::v4l2_input {
282     index: 0,
283     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",
284     type_: bindings::V4L2_INPUT_TYPE_CAMERA,
285     ..unsafe { std::mem::zeroed() }
286 }];
287 
default_fmtdesc(queue: QueueType) -> v4l2_fmtdesc288 fn default_fmtdesc(queue: QueueType) -> v4l2_fmtdesc {
289     v4l2_fmtdesc {
290         index: 0,
291         type_: queue as u32,
292         pixelformat: PIXELFORMAT,
293         ..Default::default()
294     }
295 }
296 
default_fmt(queue: QueueType) -> v4l2_format297 fn default_fmt(queue: QueueType) -> v4l2_format {
298     let pix = v4l2_pix_format {
299         width: WIDTH,
300         height: HEIGHT,
301         pixelformat: PIXELFORMAT,
302         field: bindings::v4l2_field_V4L2_FIELD_NONE,
303         bytesperline: BYTES_PER_LINE,
304         sizeimage: BUFFER_SIZE,
305         colorspace: bindings::v4l2_colorspace_V4L2_COLORSPACE_SRGB,
306         ..Default::default()
307     };
308 
309     v4l2_format {
310         type_: queue as u32,
311         fmt: bindings::v4l2_format__bindgen_ty_1 { pix },
312     }
313 }
314 
315 /// Implementations of the ioctls required by a CAPTURE device.
316 impl<Q, HM> VirtioMediaIoctlHandler for SimpleCaptureDevice<Q, HM>
317 where
318     Q: VirtioMediaEventQueue,
319     HM: VirtioMediaHostMemoryMapper,
320 {
321     type Session = SimpleCaptureDeviceSession;
322 
enum_fmt( &mut self, _session: &Self::Session, queue: QueueType, index: u32, ) -> IoctlResult<v4l2_fmtdesc>323     fn enum_fmt(
324         &mut self,
325         _session: &Self::Session,
326         queue: QueueType,
327         index: u32,
328     ) -> IoctlResult<v4l2_fmtdesc> {
329         if queue != QueueType::VideoCapture {
330             return Err(libc::EINVAL);
331         }
332         if index > 0 {
333             return Err(libc::EINVAL);
334         }
335 
336         Ok(default_fmtdesc(queue))
337     }
338 
g_fmt(&mut self, _session: &Self::Session, queue: QueueType) -> IoctlResult<v4l2_format>339     fn g_fmt(&mut self, _session: &Self::Session, queue: QueueType) -> IoctlResult<v4l2_format> {
340         if queue != QueueType::VideoCapture {
341             return Err(libc::EINVAL);
342         }
343 
344         Ok(default_fmt(queue))
345     }
346 
s_fmt( &mut self, _session: &mut Self::Session, queue: QueueType, _format: v4l2_format, ) -> IoctlResult<v4l2_format>347     fn s_fmt(
348         &mut self,
349         _session: &mut Self::Session,
350         queue: QueueType,
351         _format: v4l2_format,
352     ) -> IoctlResult<v4l2_format> {
353         if queue != QueueType::VideoCapture {
354             return Err(libc::EINVAL);
355         }
356 
357         Ok(default_fmt(queue))
358     }
359 
try_fmt( &mut self, _session: &Self::Session, queue: QueueType, _format: v4l2_format, ) -> IoctlResult<v4l2_format>360     fn try_fmt(
361         &mut self,
362         _session: &Self::Session,
363         queue: QueueType,
364         _format: v4l2_format,
365     ) -> IoctlResult<v4l2_format> {
366         if queue != QueueType::VideoCapture {
367             return Err(libc::EINVAL);
368         }
369 
370         Ok(default_fmt(queue))
371     }
372 
reqbufs( &mut self, session: &mut Self::Session, queue: QueueType, memory: MemoryType, count: u32, ) -> IoctlResult<v4l2_requestbuffers>373     fn reqbufs(
374         &mut self,
375         session: &mut Self::Session,
376         queue: QueueType,
377         memory: MemoryType,
378         count: u32,
379     ) -> IoctlResult<v4l2_requestbuffers> {
380         if queue != QueueType::VideoCapture {
381             return Err(libc::EINVAL);
382         }
383         if memory != MemoryType::Mmap {
384             return Err(libc::EINVAL);
385         }
386         if session.streaming {
387             return Err(libc::EBUSY);
388         }
389         // Buffers cannot be requested on a session if there is already another session with
390         // allocated buffers.
391         match self.active_session {
392             Some(id) if id != session.id => return Err(libc::EBUSY),
393             _ => (),
394         }
395 
396         // Reqbufs(0) is an implicit streamoff.
397         if count == 0 {
398             self.active_session = None;
399             self.streamoff(session, queue)?;
400         } else {
401             // TODO factorize with streamoff.
402             session.queued_buffers.clear();
403             for buffer in session.buffers.iter_mut() {
404                 buffer.set_state(BufferState::New);
405             }
406             self.active_session = Some(session.id);
407         }
408 
409         let count = std::cmp::min(count, 32);
410 
411         for buffer in &session.buffers {
412             self.mmap_manager.unregister_buffer(buffer.offset);
413         }
414 
415         session.buffers = (0..count)
416             .map(|i| {
417                 MemFdBuffer::new(BUFFER_SIZE as u64)
418                     .map_err(|e| {
419                         log::error!("failed to allocate MMAP buffers: {:#}", e);
420                         libc::ENOMEM
421                     })
422                     .and_then(|fd| {
423                         let offset = self
424                             .mmap_manager
425                             .register_buffer(None, BUFFER_SIZE)
426                             .map_err(|_| libc::EINVAL)?;
427 
428                         let mut v4l2_buffer =
429                             V4l2Buffer::new(QueueType::VideoCapture, i, MemoryType::Mmap);
430                         if let V4l2PlanesWithBackingMut::Mmap(mut planes) =
431                             v4l2_buffer.planes_with_backing_iter_mut()
432                         {
433                             // SAFETY: every buffer has at least one plane.
434                             let mut plane = planes.next().unwrap();
435                             plane.set_mem_offset(offset);
436                             *plane.length = BUFFER_SIZE;
437                         } else {
438                             // SAFETY: we have just set the buffer type to MMAP. Reaching this point means a bug in
439                             // the code.
440                             panic!()
441                         }
442                         v4l2_buffer.set_field(BufferField::None);
443                         v4l2_buffer.set_flags(BufferFlags::TIMESTAMP_MONOTONIC);
444 
445                         Ok(Buffer {
446                             state: BufferState::New,
447                             v4l2_buffer,
448                             fd,
449                             offset,
450                         })
451                     })
452             })
453             .collect::<Result<_, _>>()?;
454 
455         Ok(v4l2_requestbuffers {
456             count,
457             type_: queue as u32,
458             memory: memory as u32,
459             capabilities: (BufferCapabilities::SUPPORTS_MMAP
460                 | BufferCapabilities::SUPPORTS_ORPHANED_BUFS)
461                 .bits(),
462             ..Default::default()
463         })
464     }
465 
querybuf( &mut self, session: &Self::Session, queue: QueueType, index: u32, ) -> IoctlResult<v4l2r::ioctl::V4l2Buffer>466     fn querybuf(
467         &mut self,
468         session: &Self::Session,
469         queue: QueueType,
470         index: u32,
471     ) -> IoctlResult<v4l2r::ioctl::V4l2Buffer> {
472         if queue != QueueType::VideoCapture {
473             return Err(libc::EINVAL);
474         }
475         let buffer = session.buffers.get(index as usize).ok_or(libc::EINVAL)?;
476 
477         Ok(buffer.v4l2_buffer.clone())
478     }
479 
qbuf( &mut self, session: &mut Self::Session, buffer: v4l2r::ioctl::V4l2Buffer, _guest_regions: Vec<Vec<SgEntry>>, ) -> IoctlResult<v4l2r::ioctl::V4l2Buffer>480     fn qbuf(
481         &mut self,
482         session: &mut Self::Session,
483         buffer: v4l2r::ioctl::V4l2Buffer,
484         _guest_regions: Vec<Vec<SgEntry>>,
485     ) -> IoctlResult<v4l2r::ioctl::V4l2Buffer> {
486         let host_buffer = session
487             .buffers
488             .get_mut(buffer.index() as usize)
489             .ok_or(libc::EINVAL)?;
490         // Attempt to queue already queued buffer.
491         if matches!(host_buffer.state, BufferState::Incoming) {
492             return Err(libc::EINVAL);
493         }
494 
495         host_buffer.set_state(BufferState::Incoming);
496         session.queued_buffers.push_back(buffer.index() as usize);
497 
498         let buffer = host_buffer.v4l2_buffer.clone();
499 
500         if session.streaming {
501             session.process_queued_buffers(&mut self.evt_queue)?;
502         }
503 
504         Ok(buffer)
505     }
506 
streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()>507     fn streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
508         if queue != QueueType::VideoCapture || session.buffers.is_empty() {
509             return Err(libc::EINVAL);
510         }
511         session.streaming = true;
512 
513         session.process_queued_buffers(&mut self.evt_queue)?;
514 
515         Ok(())
516     }
517 
streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()>518     fn streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
519         if queue != QueueType::VideoCapture {
520             return Err(libc::EINVAL);
521         }
522         session.streaming = false;
523         session.queued_buffers.clear();
524         for buffer in session.buffers.iter_mut() {
525             buffer.set_state(BufferState::New);
526         }
527 
528         Ok(())
529     }
530 
g_input(&mut self, _session: &Self::Session) -> IoctlResult<i32>531     fn g_input(&mut self, _session: &Self::Session) -> IoctlResult<i32> {
532         Ok(0)
533     }
534 
s_input(&mut self, _session: &mut Self::Session, input: i32) -> IoctlResult<i32>535     fn s_input(&mut self, _session: &mut Self::Session, input: i32) -> IoctlResult<i32> {
536         if input != 0 {
537             Err(libc::EINVAL)
538         } else {
539             Ok(0)
540         }
541     }
542 
enuminput( &mut self, _session: &Self::Session, index: u32, ) -> IoctlResult<bindings::v4l2_input>543     fn enuminput(
544         &mut self,
545         _session: &Self::Session,
546         index: u32,
547     ) -> IoctlResult<bindings::v4l2_input> {
548         match INPUTS.get(index as usize) {
549             Some(&input) => Ok(input),
550             None => Err(libc::EINVAL),
551         }
552     }
553 }
554