Skip to main content

virtio_media/devices/
v4l2_device_proxy.rs

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//! This module uses `v4l2r` to proxy a host V4L2 device into the guest.
6
7use std::collections::BTreeMap;
8use std::io::Result as IoResult;
9use std::os::fd::AsFd;
10use std::os::fd::BorrowedFd;
11use std::os::fd::OwnedFd;
12use std::path::PathBuf;
13use std::sync::Arc;
14use std::time::Duration;
15
16use log::error;
17use log::warn;
18use v4l2r::bindings::v4l2_audio;
19use v4l2r::bindings::v4l2_audioout;
20use v4l2r::bindings::v4l2_control;
21use v4l2r::bindings::v4l2_create_buffers;
22use v4l2r::bindings::v4l2_decoder_cmd;
23use v4l2r::bindings::v4l2_dv_timings;
24use v4l2r::bindings::v4l2_dv_timings_cap;
25use v4l2r::bindings::v4l2_enc_idx;
26use v4l2r::bindings::v4l2_encoder_cmd;
27use v4l2r::bindings::v4l2_event;
28use v4l2r::bindings::v4l2_event_subscription;
29use v4l2r::bindings::v4l2_ext_control;
30use v4l2r::bindings::v4l2_ext_controls;
31use v4l2r::bindings::v4l2_fmtdesc;
32use v4l2r::bindings::v4l2_format;
33use v4l2r::bindings::v4l2_frequency;
34use v4l2r::bindings::v4l2_frequency_band;
35use v4l2r::bindings::v4l2_frmivalenum;
36use v4l2r::bindings::v4l2_frmsizeenum;
37use v4l2r::bindings::v4l2_input;
38use v4l2r::bindings::v4l2_modulator;
39use v4l2r::bindings::v4l2_output;
40use v4l2r::bindings::v4l2_query_ext_ctrl;
41use v4l2r::bindings::v4l2_queryctrl;
42use v4l2r::bindings::v4l2_querymenu;
43use v4l2r::bindings::v4l2_rect;
44use v4l2r::bindings::v4l2_requestbuffers;
45use v4l2r::bindings::v4l2_standard;
46use v4l2r::bindings::v4l2_std_id;
47use v4l2r::bindings::v4l2_streamparm;
48use v4l2r::bindings::v4l2_tuner;
49use v4l2r::device::poller::DeviceEvent;
50use v4l2r::device::poller::PollEvent;
51use v4l2r::device::poller::Poller;
52pub use v4l2r::device::Device as V4l2Device;
53use v4l2r::device::DeviceConfig;
54use v4l2r::device::DeviceOpenError;
55use v4l2r::ioctl::AudioMode;
56use v4l2r::ioctl::BufferFlags;
57use v4l2r::ioctl::CtrlId;
58use v4l2r::ioctl::CtrlWhich;
59use v4l2r::ioctl::DqBufError;
60use v4l2r::ioctl::DqBufIoctlError;
61use v4l2r::ioctl::DqEventError;
62use v4l2r::ioctl::EventType as V4l2EventType;
63use v4l2r::ioctl::ExpbufFlags;
64use v4l2r::ioctl::ExtControlError;
65use v4l2r::ioctl::IntoErrno;
66use v4l2r::ioctl::MemoryConsistency;
67use v4l2r::ioctl::QueryCapError;
68use v4l2r::ioctl::QueryCtrlFlags;
69use v4l2r::ioctl::SelectionFlags;
70use v4l2r::ioctl::SelectionTarget;
71use v4l2r::ioctl::SelectionType;
72use v4l2r::ioctl::SubscribeEventFlags;
73use v4l2r::ioctl::TunerMode;
74use v4l2r::ioctl::TunerTransmissionFlags;
75use v4l2r::ioctl::TunerType;
76use v4l2r::ioctl::V4l2Buffer;
77use v4l2r::ioctl::V4l2PlanesWithBacking;
78use v4l2r::ioctl::V4l2PlanesWithBackingMut;
79use v4l2r::memory::Memory;
80use v4l2r::memory::MemoryType;
81use v4l2r::memory::UserPtr;
82use v4l2r::QueueDirection;
83use v4l2r::QueueType;
84
85use crate::ioctl::virtio_media_dispatch_ioctl;
86use crate::ioctl::IoctlResult;
87use crate::ioctl::VirtioMediaIoctlHandler;
88use crate::mmap::MmapMappingManager;
89use crate::protocol::DequeueBufferEvent;
90use crate::protocol::SessionEvent;
91use crate::protocol::SgEntry;
92use crate::protocol::V4l2Event;
93use crate::protocol::V4l2Ioctl;
94use crate::protocol::VIRTIO_MEDIA_MMAP_FLAG_RW;
95use crate::GuestMemoryRange;
96use crate::ReadFromDescriptorChain;
97use crate::VirtioMediaDevice;
98use crate::VirtioMediaDeviceSession;
99use crate::VirtioMediaEventQueue;
100use crate::VirtioMediaGuestMemoryMapper;
101use crate::VirtioMediaHostMemoryMapper;
102use crate::WriteToDescriptorChain;
103
104type GuestAddrType = <UserPtr as Memory>::RawBacking;
105
106fn guest_v4l2_buffer_to_host<M: VirtioMediaGuestMemoryMapper>(
107    guest_buffer: &V4l2Buffer,
108    guest_regions: Vec<Vec<SgEntry>>,
109    m: &M,
110) -> anyhow::Result<(V4l2Buffer, Vec<M::GuestMemoryMapping>)> {
111    let mut resources = vec![];
112    // The host buffer is a copy of the guest's with its plane resources updated.
113    let mut host_buffer = guest_buffer.clone();
114    let writable = host_buffer.queue().direction() == QueueDirection::Capture;
115
116    if let V4l2PlanesWithBackingMut::UserPtr(host_planes) =
117        host_buffer.planes_with_backing_iter_mut()
118    {
119        for (mut host_plane, mem_regions) in
120            host_planes.filter(|p| *p.length > 0).zip(guest_regions)
121        {
122            let mut mapping = m.new_mapping(mem_regions)?;
123
124            host_plane.set_userptr(if writable {
125                mapping.as_mut_ptr()
126            } else {
127                mapping.as_ptr()
128            } as GuestAddrType);
129            resources.push(mapping);
130        }
131    };
132
133    Ok((host_buffer, resources))
134}
135
136/// Restore the user pointers of `host_buffer` using the values in `initial_guest_buffer`, if the buffer's
137/// memory type is `USERPTR`. This allows a buffer processed on the host to be passed back to the
138/// guest with the correct values.
139fn host_v4l2_buffer_to_guest<R>(
140    host_buffer: &V4l2Buffer,
141    userptr_buffers: &BTreeMap<GuestAddrType, V4l2UserPlaneInfo<R>>,
142) -> anyhow::Result<V4l2Buffer> {
143    // The guest buffer is a copy of the host's with its plane resources updated.
144    let mut guest_buffer = host_buffer.clone();
145
146    if let V4l2PlanesWithBackingMut::UserPtr(host_planes) =
147        guest_buffer.planes_with_backing_iter_mut()
148    {
149        for mut plane in host_planes.filter(|p| p.userptr() != 0) {
150            let host_userptr = plane.userptr();
151            let guest_userptr = userptr_buffers
152                .get(&(host_userptr as GuestAddrType))
153                .map(|p| p.guest_addr)
154                .ok_or_else(|| {
155                    anyhow::anyhow!("host buffer address 0x{:x} not registered!", host_userptr)
156                })?;
157            plane.set_userptr(guest_userptr as GuestAddrType);
158        }
159    }
160
161    Ok(guest_buffer)
162}
163
164#[derive(Clone, Copy, Debug)]
165enum ExtCtrlIoctl {
166    Get,
167    Set,
168    Try,
169}
170
171fn perform_ext_ctrls_ioctl<M: VirtioMediaGuestMemoryMapper>(
172    ioctl: ExtCtrlIoctl,
173    device: &V4l2Device,
174    mem: &M,
175    which: CtrlWhich,
176    ctrls: (
177        &mut v4l2_ext_controls,
178        &mut Vec<v4l2_ext_control>,
179        Vec<Vec<SgEntry>>,
180    ),
181) -> Result<(), ExtControlError> {
182    let (ctrls, ctrl_array, mem_regions) = ctrls;
183    // TODO only backup the addresses of controls which size of > 0 for efficiency? Also keep track
184    // of the control index so we don't make a mistake if the host changes the control size.
185    let ctrl_array_backup = ctrl_array.clone();
186
187    // Read the payloads for all the controls with one.
188    let mut payloads = ctrl_array
189        .iter()
190        .filter(|ctrl| ctrl.size > 0)
191        .zip(mem_regions)
192        .map(|(_, sgs)| mem.new_mapping(sgs))
193        // TODO remove unwrap
194        .collect::<anyhow::Result<Vec<_>>>()
195        .unwrap();
196
197    // Patch the pointers to the payloads.
198    for (ctrl, payload) in ctrl_array
199        .iter_mut()
200        .filter(|ctrl| ctrl.size > 0)
201        .zip(payloads.iter_mut())
202    {
203        ctrl.__bindgen_anon_1.ptr = payload.as_mut_ptr() as *mut libc::c_void;
204    }
205
206    let res = match ioctl {
207        ExtCtrlIoctl::Get => v4l2r::ioctl::g_ext_ctrls(device, which, ctrl_array.as_mut_slice()),
208        ExtCtrlIoctl::Set => v4l2r::ioctl::s_ext_ctrls(device, which, ctrl_array.as_mut_slice()),
209        ExtCtrlIoctl::Try => v4l2r::ioctl::try_ext_ctrls(device, which, ctrl_array.as_mut_slice()),
210    };
211
212    // Restore guest addresses in the controls array.
213    for (ctrl, ctrl_backup) in ctrl_array
214        .iter_mut()
215        .zip(ctrl_array_backup.iter())
216        .filter(|(_, ctrl)| ctrl.size > 0)
217    {
218        ctrl.__bindgen_anon_1.ptr = unsafe { ctrl_backup.__bindgen_anon_1.ptr };
219    }
220
221    if let Err(e) = &res {
222        ctrls.error_idx = e.error_idx;
223    }
224
225    res
226}
227
228/// Information about a given USERPTR memory plane.
229struct V4l2UserPlaneInfo<R> {
230    /// Queue the buffer belongs to.
231    queue: QueueType,
232    /// Buffer index.
233    index: u8,
234
235    guest_addr: GuestAddrType,
236    _guest_resource: R,
237}
238
239pub struct V4l2Session<M: VirtioMediaGuestMemoryMapper> {
240    id: u32,
241    device: Arc<V4l2Device>,
242    /// Proxy epoll for polling `device`. We need to use a proxy here because V4L2 events are
243    /// signaled using `EPOLLPRI`, and we sometimes need to stop listening to the `CAPTURE` queue.
244    /// `poller`'s FD is what is actually added to the client's session poller.
245    poller: Poller,
246
247    /// Type of the capture queue, if one has been set up.
248    capture_queue_type: Option<QueueType>,
249    /// Type of the output queue, if one has been set up.
250    output_queue_type: Option<QueueType>,
251
252    capture_streaming: bool,
253    capture_num_queued: usize,
254
255    output_streaming: bool,
256    output_num_queued: usize,
257
258    /// Map of host USERPTR addresses to guest USERPTR addresses. Only used for queues which memory
259    /// type is USERPTR.
260    ///
261    /// TODO this is not properly cleared. We should probably record the session ID and queue in
262    /// order to remove the records upon REQBUFS or session deletion?
263    userptr_buffers: BTreeMap<GuestAddrType, V4l2UserPlaneInfo<M::GuestMemoryMapping>>,
264}
265
266impl<M: VirtioMediaGuestMemoryMapper> VirtioMediaDeviceSession for V4l2Session<M> {
267    fn poll_fd(&self) -> Option<BorrowedFd<'_>> {
268        Some(self.poller.as_fd())
269    }
270}
271
272impl<M> V4l2Session<M>
273where
274    M: VirtioMediaGuestMemoryMapper,
275{
276    fn new(id: u32, device: Arc<V4l2Device>) -> Self {
277        // Only listen to V4L2 events for now.
278        let mut poller = Poller::new(Arc::clone(&device)).unwrap();
279        poller.enable_event(DeviceEvent::V4L2Event).unwrap();
280
281        Self {
282            id,
283            device,
284            poller,
285            capture_queue_type: None,
286            output_queue_type: None,
287            capture_streaming: false,
288            capture_num_queued: 0,
289            output_streaming: false,
290            output_num_queued: 0,
291            userptr_buffers: Default::default(),
292        }
293    }
294
295    /// Returns whether this session should be polling for CAPTURE buffers in its current state.
296    fn should_poll_capture(&self) -> bool {
297        self.capture_streaming && self.capture_num_queued > 0
298    }
299
300    fn register_userptr_addresses(
301        &mut self,
302        host_buffer: &V4l2Buffer,
303        guest_buffer: &V4l2Buffer,
304        guest_resources: Vec<M::GuestMemoryMapping>,
305    ) {
306        if let V4l2PlanesWithBacking::UserPtr(host_planes) = host_buffer.planes_with_backing_iter()
307        {
308            if let V4l2PlanesWithBacking::UserPtr(guest_planes) =
309                guest_buffer.planes_with_backing_iter()
310            {
311                for ((host_userptr, guest_plane), guest_resource) in host_planes
312                    .map(|p| p.userptr())
313                    .zip(guest_planes)
314                    .filter(|(h, _)| *h != 0)
315                    .zip(guest_resources.into_iter())
316                {
317                    let plane_info = {
318                        V4l2UserPlaneInfo {
319                            queue: guest_buffer.queue(),
320                            index: guest_buffer.index() as u8,
321                            guest_addr: guest_plane.userptr(),
322                            _guest_resource: guest_resource,
323                        }
324                    };
325                    self.userptr_buffers.insert(host_userptr, plane_info);
326                }
327            }
328        }
329    }
330}
331
332/// Information about a given MMAP memory plane.
333///
334/// We keep these around indexed by the memory offset in order to service MMAP commands. Only used
335/// if the memory type of the queue is MMAP.
336struct V4l2MmapPlaneInfo {
337    /// ID of the session the buffer belongs to.
338    session_id: u32,
339    /// Queue the buffer belongs to.
340    queue: QueueType,
341    /// Buffer index.
342    index: u8,
343    /// Plane index.
344    plane: u8,
345    /// Guest address at which the buffer has been mapped.
346    map_address: u64,
347    /// Whether the buffer is still active from the device's point of view.
348    active: bool,
349}
350
351/// A host V4L2 device that can be proxied into a virtio-media guest.
352pub struct V4l2ProxyDevice<
353    Q: VirtioMediaEventQueue,
354    M: VirtioMediaGuestMemoryMapper,
355    HM: VirtioMediaHostMemoryMapper,
356> {
357    /// `/dev/videoX` host device path.
358    device_path: PathBuf,
359
360    mem: M,
361    evt_queue: Q,
362
363    /// Map of memory offsets to detailed buffer information. Only used for queues which memory
364    /// type is MMAP.
365    mmap_buffers: BTreeMap<u32, V4l2MmapPlaneInfo>,
366
367    mmap_manager: MmapMappingManager<HM>,
368}
369
370#[derive(Debug)]
371pub struct DequeueEventError(pub i32);
372#[derive(Debug)]
373pub struct DequeueBufferError(pub i32);
374
375impl<Q, M, HM> V4l2ProxyDevice<Q, M, HM>
376where
377    Q: VirtioMediaEventQueue,
378    M: VirtioMediaGuestMemoryMapper,
379    HM: VirtioMediaHostMemoryMapper,
380{
381    pub fn new(device_path: PathBuf, evt_queue: Q, mem: M, mapper: HM) -> Self {
382        Self {
383            mem,
384            evt_queue,
385            device_path,
386            mmap_buffers: Default::default(),
387            mmap_manager: MmapMappingManager::from(mapper),
388        }
389    }
390
391    fn delete_session(&mut self, session: &V4l2Session<M>) {
392        // Mark all buffers from this session as being inactive.
393        for (&offset, buffer) in self.mmap_buffers.iter_mut() {
394            if buffer.session_id == session.id {
395                self.mmap_manager.unregister_buffer(offset);
396                buffer.active = false;
397            }
398        }
399        // Garbage-collect buffers that can be deleted.
400        self.mmap_buffers.retain(|_, b| b.active);
401    }
402
403    /// Clear all the previous buffer information for this queue, and insert new information if the
404    /// memory type is MMAP.
405    fn update_mmap_offsets(
406        &mut self,
407        session: &mut V4l2Session<M>,
408        queue: QueueType,
409        range: std::ops::Range<u32>,
410    ) {
411        // Remove buffers that have been deallocated.
412        self.mmap_buffers
413            .iter_mut()
414            .filter(|(_, b)| b.session_id == session.id && b.queue == queue)
415            .filter(|(_, b)| range.is_empty() || b.index as u32 >= range.start)
416            .for_each(|(&offset, b)| {
417                self.mmap_manager.unregister_buffer(offset);
418                b.active = false;
419            });
420        // Garbage-collect buffers that can be deleted.
421        self.mmap_buffers.retain(|_, b| b.active);
422
423        for i in range {
424            let buffer =
425                match v4l2r::ioctl::querybuf::<V4l2Buffer>(&session.device, queue, i as usize) {
426                    Ok(buffer) => buffer,
427                    Err(e) => {
428                        warn!("failed to query newly allocated buffer: {:#}", e);
429                        continue;
430                    }
431                };
432
433            if let V4l2PlanesWithBacking::Mmap(planes) = buffer.planes_with_backing_iter() {
434                for (j, plane) in planes.enumerate() {
435                    let offset = plane.mem_offset();
436
437                    self.mmap_manager
438                        .register_buffer(Some(offset), *plane.length)
439                        .unwrap();
440
441                    self.mmap_buffers.insert(
442                        offset,
443                        V4l2MmapPlaneInfo {
444                            session_id: session.id,
445                            queue,
446                            index: buffer.index() as u8,
447                            plane: j as u8,
448                            map_address: 0,
449                            active: true,
450                        },
451                    );
452                }
453            };
454        }
455
456        // If we allocated on the capture or output queue successfully, remember its type.
457        // TODO this should be somewhere else?
458        match queue {
459            QueueType::VideoCapture | QueueType::VideoCaptureMplane => {
460                session.capture_queue_type = Some(queue);
461            }
462            QueueType::VideoOutput | QueueType::VideoOutputMplane => {
463                session.output_queue_type = Some(queue);
464            }
465            _ => (),
466        }
467    }
468
469    /// Dequeue all pending events for `session` and send them to the guest.
470    ///
471    /// In case of error, the session should be considered invalid and destroyed.
472    fn dequeue_events(&mut self, session: &mut V4l2Session<M>) -> Result<(), DequeueEventError> {
473        loop {
474            match v4l2r::ioctl::dqevent::<v4l2_event>(&session.device) {
475                Ok(event) => self
476                    .evt_queue
477                    .send_event(V4l2Event::Event(SessionEvent::new(session.id, event))),
478                Err(DqEventError::NotReady) => return Ok(()),
479                Err(e) => {
480                    let err = e.into_errno();
481                    self.evt_queue.send_error(session.id, err);
482                    return Err(DequeueEventError(err));
483                }
484            }
485        }
486    }
487
488    /// Attempt to dequeue all processed OUTPUT buffers and send the corresponding events to
489    /// `evt_queue`.
490    ///
491    /// In case of error, the session should be considered invalid and destroyed.
492    fn dequeue_output_buffers(
493        &mut self,
494        session: &mut V4l2Session<M>,
495    ) -> Result<(), DequeueBufferError> {
496        let output_queue_type = match session.output_queue_type {
497            Some(queue_type) => queue_type,
498            None => return Ok(()),
499        };
500
501        if !session.output_streaming || session.output_num_queued == 0 {
502            return Ok(());
503        }
504
505        loop {
506            match v4l2r::ioctl::dqbuf::<V4l2Buffer>(&session.device, output_queue_type) {
507                Ok(buffer) => {
508                    // Drop buffer information. This also syncs the buffer content if it has been shadowed.
509                    session.userptr_buffers.retain(|_, v| {
510                        Some(v.queue) != session.output_queue_type
511                            || v.index != buffer.index() as u8
512                    });
513                    self.evt_queue
514                        .send_event(V4l2Event::DequeueBuffer(DequeueBufferEvent::new(
515                            session.id, buffer,
516                        )))
517                }
518                Err(DqBufError::IoctlError(DqBufIoctlError::Eos))
519                | Err(DqBufError::IoctlError(DqBufIoctlError::NotReady)) => return Ok(()),
520                Err(e) => {
521                    let err = e.into_errno();
522                    self.evt_queue.send_error(session.id, err);
523                    return Err(DequeueBufferError(err));
524                }
525            };
526        }
527    }
528
529    /// Attempt to dequeue a single CAPTURE buffer and send the corresponding event to `evt_queue`.
530    ///
531    /// In case of error, the session should be considered invalid and destroyed.
532    fn dequeue_capture_buffer(
533        &mut self,
534        session: &mut V4l2Session<M>,
535    ) -> Result<(), DequeueBufferError> {
536        let capture_queue_type = match session.capture_queue_type {
537            Some(queue_type) => queue_type,
538            None => return Ok(()),
539        };
540
541        let v4l2_buffer =
542            match v4l2r::ioctl::dqbuf::<V4l2Buffer>(&session.device, capture_queue_type) {
543                Ok(buffer) => buffer,
544                Err(DqBufError::IoctlError(DqBufIoctlError::Eos)) => return Ok(()),
545                Err(DqBufError::IoctlError(DqBufIoctlError::NotReady)) => return Ok(()),
546                Err(e) => {
547                    let err = e.into_errno();
548                    self.evt_queue.send_error(session.id, err);
549                    return Err(DequeueBufferError(err));
550                }
551            };
552
553        // Drop buffer information. This also syncs the buffer content if it has been shadowed.
554        session.userptr_buffers.retain(|_, v| {
555            Some(v.queue) != session.capture_queue_type || v.index != v4l2_buffer.index() as u8
556        });
557
558        let capture_polling_active = session.should_poll_capture();
559        session.capture_num_queued -= 1;
560        if (capture_polling_active && session.capture_num_queued == 0) ||
561            // This may or may not be needed...
562            v4l2_buffer.flags().contains(BufferFlags::LAST)
563        {
564            if let Err(e) = session.poller.disable_event(DeviceEvent::CaptureReady) {
565                error!("cannot disable CAPTURE polling after last buffer: {}", e);
566            }
567        }
568
569        self.evt_queue
570            .send_event(V4l2Event::DequeueBuffer(DequeueBufferEvent::new(
571                session.id,
572                v4l2_buffer,
573            )));
574
575        Ok(())
576    }
577}
578
579impl<Q, M, HM> VirtioMediaIoctlHandler for V4l2ProxyDevice<Q, M, HM>
580where
581    Q: VirtioMediaEventQueue,
582    M: VirtioMediaGuestMemoryMapper,
583    HM: VirtioMediaHostMemoryMapper,
584{
585    type Session = V4l2Session<M>;
586
587    fn enum_fmt(
588        &mut self,
589        session: &Self::Session,
590        queue: QueueType,
591        index: u32,
592    ) -> IoctlResult<v4l2_fmtdesc> {
593        v4l2r::ioctl::enum_fmt(&session.device, queue, index).map_err(IntoErrno::into_errno)
594    }
595
596    fn g_fmt(&mut self, session: &Self::Session, queue: QueueType) -> IoctlResult<v4l2_format> {
597        v4l2r::ioctl::g_fmt(&session.device, queue).map_err(IntoErrno::into_errno)
598    }
599
600    fn s_fmt(
601        &mut self,
602        session: &mut Self::Session,
603        _queue: QueueType,
604        format: v4l2_format,
605    ) -> IoctlResult<v4l2_format> {
606        v4l2r::ioctl::s_fmt(&mut session.device, format).map_err(IntoErrno::into_errno)
607    }
608
609    fn reqbufs(
610        &mut self,
611        session: &mut Self::Session,
612        queue: QueueType,
613        memory: MemoryType,
614        count: u32,
615        flags: MemoryConsistency,
616    ) -> IoctlResult<v4l2_requestbuffers> {
617        let mut reqbufs: v4l2_requestbuffers =
618            v4l2r::ioctl::reqbufs(&session.device, queue, memory, count, flags)
619                .map_err(IntoErrno::into_errno)?;
620
621        // We do not support requests at the moment, so do not advertize them.
622        reqbufs.capabilities &= !v4l2r::bindings::V4L2_BUF_CAP_SUPPORTS_REQUESTS;
623
624        self.update_mmap_offsets(session, queue, 0..reqbufs.count);
625
626        match queue {
627            QueueType::VideoCapture | QueueType::VideoCaptureMplane => {
628                // REQBUFS(0) is an implicit STREAMOFF.
629                if reqbufs.count == 0 {
630                    let was_polling_capture = session.should_poll_capture();
631                    session.capture_streaming = false;
632                    session.capture_num_queued = 0;
633                    if was_polling_capture {
634                        if let Err(e) = session.poller.disable_event(DeviceEvent::CaptureReady) {
635                            error!(
636                                "cannot disable CAPTURE polling after REQBUFS(0) ioctl: {}",
637                                e
638                            );
639                        }
640                    }
641                }
642            }
643            QueueType::VideoOutput | QueueType::VideoOutputMplane => {
644                // REQBUFS(0) is an implicit STREAMOFF.
645                if reqbufs.count == 0 {
646                    session.output_streaming = false;
647                    session.output_num_queued = 0;
648                }
649            }
650            _ => (),
651        }
652
653        Ok(reqbufs)
654    }
655
656    fn querybuf(
657        &mut self,
658        session: &Self::Session,
659        queue: QueueType,
660        index: u32,
661    ) -> IoctlResult<V4l2Buffer> {
662        v4l2r::ioctl::querybuf(&session.device, queue, index as usize)
663            .map_err(IntoErrno::into_errno)
664            .and_then(|host_buffer| {
665                host_v4l2_buffer_to_guest(&host_buffer, &session.userptr_buffers).map_err(|e| {
666                    error!("{:#}", e.context("while performing QUERYBUF ioctl"));
667                    libc::EINVAL
668                })
669            })
670    }
671
672    fn streamon(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
673        v4l2r::ioctl::streamon(&session.device, queue).map_err(IntoErrno::into_errno)?;
674
675        match queue {
676            QueueType::VideoCapture | QueueType::VideoCaptureMplane
677                if !session.capture_streaming =>
678            {
679                session.capture_streaming = true;
680                if session.should_poll_capture() {
681                    if let Err(e) = session.poller.enable_event(DeviceEvent::CaptureReady) {
682                        error!("cannot enable CAPTURE polling after STREAMON ioctl: {}", e);
683                    }
684                }
685            }
686            QueueType::VideoOutput | QueueType::VideoOutputMplane if !session.output_streaming => {
687                session.output_streaming = true;
688            }
689            _ => (),
690        }
691
692        Ok(())
693    }
694
695    fn streamoff(&mut self, session: &mut Self::Session, queue: QueueType) -> IoctlResult<()> {
696        v4l2r::ioctl::streamoff(&session.device, queue).map_err(IntoErrno::into_errno)?;
697
698        match queue {
699            QueueType::VideoCapture | QueueType::VideoCaptureMplane => {
700                let was_polling_capture = session.should_poll_capture();
701                session.capture_streaming = false;
702                session.capture_num_queued = 0;
703                if was_polling_capture {
704                    if let Err(e) = session.poller.disable_event(DeviceEvent::CaptureReady) {
705                        error!(
706                            "cannot disable CAPTURE polling after STREAMOFF ioctl: {}",
707                            e
708                        );
709                    }
710                }
711            }
712            QueueType::VideoOutput | QueueType::VideoOutputMplane => {
713                session.output_streaming = false;
714                session.output_num_queued = 0;
715            }
716            _ => (),
717        }
718
719        Ok(())
720    }
721
722    fn qbuf(
723        &mut self,
724        session: &mut Self::Session,
725        guest_buffer: V4l2Buffer,
726        guest_regions: Vec<Vec<SgEntry>>,
727    ) -> IoctlResult<V4l2Buffer> {
728        // Proactively dequeue output buffers we are done with. Errors can be ignored in
729        // this context.
730        let _ = self.dequeue_output_buffers(session);
731
732        let (host_buffer, guest_resources) =
733            guest_v4l2_buffer_to_host(&guest_buffer, guest_regions, &self.mem)
734                .map_err(|_| libc::EINVAL)?;
735        session.register_userptr_addresses(&host_buffer, &guest_buffer, guest_resources);
736        let queue = host_buffer.queue();
737        let out_buffer = v4l2r::ioctl::qbuf(&session.device, host_buffer)
738            .map_err(|e| e.into_errno())
739            .and_then(|host_out_buffer| {
740                // TODO if we had a PREPARE_BUF before, do we need to patch the addresses
741                // from the buffer given at that time?
742                host_v4l2_buffer_to_guest(&host_out_buffer, &session.userptr_buffers).map_err(|e| {
743                    error!("{:#}", e.context("while processing QBUF"));
744                    libc::EINVAL
745                })
746            })?;
747
748        match queue {
749            QueueType::VideoCapture | QueueType::VideoCaptureMplane => {
750                let was_polling_capture = session.should_poll_capture();
751                session.capture_num_queued += 1;
752                if !was_polling_capture && session.should_poll_capture() {
753                    if let Err(e) = session.poller.enable_event(DeviceEvent::CaptureReady) {
754                        error!("cannot enable CAPTURE polling after QBUF ioctl: {}", e);
755                    }
756                }
757            }
758            QueueType::VideoOutput | QueueType::VideoOutputMplane => {
759                session.output_num_queued += 1;
760            }
761            _ => (),
762        }
763
764        Ok(out_buffer)
765    }
766
767    fn g_parm(
768        &mut self,
769        session: &Self::Session,
770        queue: QueueType,
771    ) -> IoctlResult<v4l2_streamparm> {
772        v4l2r::ioctl::g_parm(&session.device, queue).map_err(|e| e.into_errno())
773    }
774
775    fn s_parm(
776        &mut self,
777        session: &mut Self::Session,
778        parm: v4l2_streamparm,
779    ) -> IoctlResult<v4l2_streamparm> {
780        v4l2r::ioctl::s_parm(&session.device, parm).map_err(|e| e.into_errno())
781    }
782
783    fn g_std(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id> {
784        v4l2r::ioctl::g_std(&session.device).map_err(|e| e.into_errno())
785    }
786
787    fn s_std(&mut self, session: &mut Self::Session, std: v4l2_std_id) -> IoctlResult<()> {
788        v4l2r::ioctl::s_std(&session.device, std).map_err(|e| e.into_errno())
789    }
790
791    fn enumstd(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_standard> {
792        v4l2r::ioctl::enumstd(&session.device, index).map_err(|e| e.into_errno())
793    }
794
795    fn enuminput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_input> {
796        v4l2r::ioctl::enuminput(&session.device, index as usize).map_err(|e| e.into_errno())
797    }
798
799    fn g_ctrl(&mut self, session: &Self::Session, id: u32) -> IoctlResult<v4l2_control> {
800        v4l2r::ioctl::g_ctrl(&session.device, id)
801            .map(|value| v4l2_control { id, value })
802            .map_err(|e| e.into_errno())
803    }
804
805    fn s_ctrl(
806        &mut self,
807        session: &mut Self::Session,
808        id: u32,
809        value: i32,
810    ) -> IoctlResult<v4l2_control> {
811        v4l2r::ioctl::s_ctrl(&session.device, id, value)
812            .map(|value| v4l2_control { id, value })
813            .map_err(|e| e.into_errno())
814    }
815
816    fn g_tuner(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_tuner> {
817        v4l2r::ioctl::g_tuner(&session.device, index).map_err(|e| e.into_errno())
818    }
819
820    fn s_tuner(
821        &mut self,
822        session: &mut Self::Session,
823        index: u32,
824        mode: TunerMode,
825    ) -> IoctlResult<()> {
826        v4l2r::ioctl::s_tuner(&session.device, index, mode).map_err(|e| e.into_errno())
827    }
828
829    fn g_audio(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audio> {
830        v4l2r::ioctl::g_audio(&session.device).map_err(|e| e.into_errno())
831    }
832
833    fn s_audio(
834        &mut self,
835        session: &mut Self::Session,
836        index: u32,
837        mode: Option<AudioMode>,
838    ) -> IoctlResult<()> {
839        v4l2r::ioctl::s_audio(&session.device, index, mode).map_err(|e| e.into_errno())
840    }
841
842    fn queryctrl(
843        &mut self,
844        session: &Self::Session,
845        id: v4l2r::ioctl::CtrlId,
846        flags: v4l2r::ioctl::QueryCtrlFlags,
847    ) -> IoctlResult<v4l2_queryctrl> {
848        v4l2r::ioctl::queryctrl(&session.device, id, flags).map_err(|e| e.into_errno())
849    }
850
851    fn querymenu(
852        &mut self,
853        session: &Self::Session,
854        id: u32,
855        index: u32,
856    ) -> IoctlResult<v4l2_querymenu> {
857        v4l2r::ioctl::querymenu(&session.device, id, index).map_err(|e| e.into_errno())
858    }
859
860    fn g_input(&mut self, session: &Self::Session) -> IoctlResult<i32> {
861        v4l2r::ioctl::g_input(&session.device)
862            .map(|i| i as i32)
863            .map_err(|e| e.into_errno())
864    }
865
866    fn s_input(&mut self, session: &mut Self::Session, input: i32) -> IoctlResult<i32> {
867        v4l2r::ioctl::s_input(&session.device, input as usize)
868            .map(|i| i as i32)
869            .map_err(|e| e.into_errno())
870    }
871
872    fn g_output(&mut self, session: &Self::Session) -> IoctlResult<i32> {
873        v4l2r::ioctl::g_output(&session.device)
874            .map(|o| o as i32)
875            .map_err(|e| e.into_errno())
876    }
877
878    fn s_output(&mut self, session: &mut Self::Session, output: i32) -> IoctlResult<i32> {
879        v4l2r::ioctl::s_output(&session.device, output as usize)
880            .map(|()| output)
881            .map_err(|e| e.into_errno())
882    }
883
884    fn enumoutput(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_output> {
885        v4l2r::ioctl::enumoutput(&session.device, index as usize).map_err(|e| e.into_errno())
886    }
887
888    fn g_audout(&mut self, session: &Self::Session) -> IoctlResult<v4l2_audioout> {
889        v4l2r::ioctl::g_audout(&session.device).map_err(|e| e.into_errno())
890    }
891
892    fn s_audout(&mut self, session: &mut Self::Session, index: u32) -> IoctlResult<()> {
893        v4l2r::ioctl::s_audout(&session.device, index).map_err(|e| e.into_errno())
894    }
895
896    fn g_modulator(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_modulator> {
897        v4l2r::ioctl::g_modulator(&session.device, index).map_err(|e| e.into_errno())
898    }
899
900    fn s_modulator(
901        &mut self,
902        session: &mut Self::Session,
903        index: u32,
904        flags: TunerTransmissionFlags,
905    ) -> IoctlResult<()> {
906        v4l2r::ioctl::s_modulator(&session.device, index, flags).map_err(|e| e.into_errno())
907    }
908
909    fn g_frequency(&mut self, session: &Self::Session, tuner: u32) -> IoctlResult<v4l2_frequency> {
910        v4l2r::ioctl::g_frequency(&session.device, tuner).map_err(|e| e.into_errno())
911    }
912
913    fn s_frequency(
914        &mut self,
915        session: &mut Self::Session,
916        tuner: u32,
917        type_: TunerType,
918        frequency: u32,
919    ) -> IoctlResult<()> {
920        v4l2r::ioctl::s_frequency(&session.device, tuner, type_, frequency)
921            .map_err(|e| e.into_errno())
922    }
923
924    fn querystd(&mut self, session: &Self::Session) -> IoctlResult<v4l2_std_id> {
925        v4l2r::ioctl::querystd::<v4l2_std_id>(&session.device).map_err(|e| e.into_errno())
926    }
927
928    fn try_fmt(
929        &mut self,
930        session: &Self::Session,
931        _queue: QueueType,
932        format: v4l2_format,
933    ) -> IoctlResult<v4l2_format> {
934        v4l2r::ioctl::try_fmt::<_, v4l2_format>(&session.device, format).map_err(|e| e.into_errno())
935    }
936
937    fn enumaudio(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audio> {
938        v4l2r::ioctl::enumaudio::<v4l2_audio>(&session.device, index).map_err(|e| e.into_errno())
939    }
940
941    fn enumaudout(&mut self, session: &Self::Session, index: u32) -> IoctlResult<v4l2_audioout> {
942        v4l2r::ioctl::enumaudout::<v4l2_audioout>(&session.device, index)
943            .map_err(|e| e.into_errno())
944    }
945
946    fn g_ext_ctrls(
947        &mut self,
948        session: &Self::Session,
949        which: CtrlWhich,
950        ctrls: &mut v4l2_ext_controls,
951        ctrl_array: &mut Vec<v4l2_ext_control>,
952        user_regions: Vec<Vec<SgEntry>>,
953    ) -> IoctlResult<()> {
954        perform_ext_ctrls_ioctl(
955            ExtCtrlIoctl::Get,
956            &session.device,
957            &self.mem,
958            which,
959            (ctrls, ctrl_array, user_regions),
960        )
961        .map_err(|e| e.into_errno())
962    }
963
964    fn s_ext_ctrls(
965        &mut self,
966        session: &mut Self::Session,
967        which: CtrlWhich,
968        ctrls: &mut v4l2_ext_controls,
969        ctrl_array: &mut Vec<v4l2_ext_control>,
970        user_regions: Vec<Vec<SgEntry>>,
971    ) -> IoctlResult<()> {
972        perform_ext_ctrls_ioctl(
973            ExtCtrlIoctl::Set,
974            &session.device,
975            &self.mem,
976            which,
977            (ctrls, ctrl_array, user_regions),
978        )
979        .map_err(|e| e.into_errno())
980    }
981
982    fn try_ext_ctrls(
983        &mut self,
984        session: &Self::Session,
985        which: CtrlWhich,
986        ctrls: &mut v4l2_ext_controls,
987        ctrl_array: &mut Vec<v4l2_ext_control>,
988        user_regions: Vec<Vec<SgEntry>>,
989    ) -> IoctlResult<()> {
990        perform_ext_ctrls_ioctl(
991            ExtCtrlIoctl::Try,
992            &session.device,
993            &self.mem,
994            which,
995            (ctrls, ctrl_array, user_regions),
996        )
997        .map_err(|e| e.into_errno())
998    }
999
1000    fn enum_framesizes(
1001        &mut self,
1002        session: &Self::Session,
1003        index: u32,
1004        pixel_format: u32,
1005    ) -> IoctlResult<v4l2_frmsizeenum> {
1006        v4l2r::ioctl::enum_frame_sizes(&session.device, index, pixel_format.into())
1007            .map_err(|e| e.into_errno())
1008    }
1009
1010    fn enum_frameintervals(
1011        &mut self,
1012        session: &Self::Session,
1013        index: u32,
1014        pixel_format: u32,
1015        width: u32,
1016        height: u32,
1017    ) -> IoctlResult<v4l2_frmivalenum> {
1018        v4l2r::ioctl::enum_frame_intervals(
1019            &session.device,
1020            index,
1021            pixel_format.into(),
1022            width,
1023            height,
1024        )
1025        .map_err(|e| e.into_errno())
1026    }
1027
1028    fn g_enc_index(&mut self, session: &Self::Session) -> IoctlResult<v4l2_enc_idx> {
1029        v4l2r::ioctl::g_enc_index(&session.device).map_err(|e| e.into_errno())
1030    }
1031
1032    fn encoder_cmd(
1033        &mut self,
1034        session: &mut Self::Session,
1035        cmd: v4l2_encoder_cmd,
1036    ) -> IoctlResult<v4l2_encoder_cmd> {
1037        v4l2r::ioctl::encoder_cmd(&session.device, cmd).map_err(|e| e.into_errno())
1038    }
1039
1040    fn try_encoder_cmd(
1041        &mut self,
1042        session: &Self::Session,
1043        cmd: v4l2_encoder_cmd,
1044    ) -> IoctlResult<v4l2_encoder_cmd> {
1045        v4l2r::ioctl::try_encoder_cmd(&session.device, cmd).map_err(|e| e.into_errno())
1046    }
1047
1048    fn s_dv_timings(
1049        &mut self,
1050        session: &mut Self::Session,
1051        timings: v4l2_dv_timings,
1052    ) -> IoctlResult<v4l2_dv_timings> {
1053        v4l2r::ioctl::s_dv_timings(&session.device, timings).map_err(|e| e.into_errno())
1054    }
1055
1056    fn g_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings> {
1057        v4l2r::ioctl::g_dv_timings(&session.device).map_err(|e| e.into_errno())
1058    }
1059
1060    fn subscribe_event(
1061        &mut self,
1062        session: &mut Self::Session,
1063        event: V4l2EventType,
1064        flags: SubscribeEventFlags,
1065    ) -> IoctlResult<()> {
1066        v4l2r::ioctl::subscribe_event(&session.device, event, flags).map_err(|e| e.into_errno())?;
1067
1068        // Make sure the initial event it put into the eventq before we return.
1069        if flags.contains(SubscribeEventFlags::SEND_INITIAL) {
1070            // This sends the potential events before the command response,
1071            // ensuring the guest will be able to see them alongside the response.
1072            let _ = self.dequeue_events(session).err();
1073        }
1074
1075        Ok(())
1076    }
1077
1078    fn unsubscribe_event(
1079        &mut self,
1080        session: &mut Self::Session,
1081        event: v4l2_event_subscription,
1082    ) -> IoctlResult<()> {
1083        if event.type_ == v4l2r::bindings::V4L2_EVENT_ALL {
1084            v4l2r::ioctl::unsubscribe_all_events(&session.device)
1085        } else {
1086            let event = V4l2EventType::try_from(&event).map_err(|_| libc::EINVAL)?;
1087
1088            v4l2r::ioctl::unsubscribe_event(&session.device, event)
1089        }
1090        .map_err(|e| e.into_errno())
1091    }
1092
1093    fn create_bufs(
1094        &mut self,
1095        session: &mut Self::Session,
1096        count: u32,
1097        queue: QueueType,
1098        memory: MemoryType,
1099        format: v4l2_format,
1100    ) -> IoctlResult<v4l2_create_buffers> {
1101        let create_bufs = v4l2r::ioctl::create_bufs::<_, v4l2_create_buffers>(
1102            &session.device,
1103            count,
1104            memory,
1105            format,
1106        )
1107        .map_err(|e| e.into_errno())?;
1108
1109        if count > 0 {
1110            let bufs_range = create_bufs.index..(create_bufs.index + create_bufs.count);
1111            self.update_mmap_offsets(session, queue, bufs_range);
1112        }
1113
1114        Ok(create_bufs)
1115    }
1116
1117    fn prepare_buf(
1118        &mut self,
1119        session: &mut Self::Session,
1120        guest_buffer: V4l2Buffer,
1121        guest_regions: Vec<Vec<SgEntry>>,
1122    ) -> IoctlResult<V4l2Buffer> {
1123        let (host_buffer, guest_resources) =
1124            guest_v4l2_buffer_to_host(&guest_buffer, guest_regions, &self.mem)
1125                .map_err(|_| libc::EINVAL)?;
1126        session.register_userptr_addresses(&host_buffer, &guest_buffer, guest_resources);
1127        v4l2r::ioctl::prepare_buf(&session.device, host_buffer)
1128            .map_err(|e| e.into_errno())
1129            .and_then(|host_out_buffer| {
1130                host_v4l2_buffer_to_guest(&host_out_buffer, &session.userptr_buffers).map_err(|e| {
1131                    error!("{:#}", e.context("while processing PREPARE_BUF"));
1132                    libc::EINVAL
1133                })
1134            })
1135    }
1136
1137    fn g_selection(
1138        &mut self,
1139        session: &Self::Session,
1140        sel_type: SelectionType,
1141        sel_target: SelectionTarget,
1142    ) -> IoctlResult<v4l2_rect> {
1143        v4l2r::ioctl::g_selection(&session.device, sel_type, sel_target).map_err(|e| e.into_errno())
1144    }
1145
1146    fn s_selection(
1147        &mut self,
1148        session: &mut Self::Session,
1149        sel_type: SelectionType,
1150        sel_target: SelectionTarget,
1151        sel_rect: v4l2_rect,
1152        sel_flags: SelectionFlags,
1153    ) -> IoctlResult<v4l2_rect> {
1154        v4l2r::ioctl::s_selection(&session.device, sel_type, sel_target, sel_rect, sel_flags)
1155            .map_err(|e| e.into_errno())
1156    }
1157
1158    fn decoder_cmd(
1159        &mut self,
1160        session: &mut Self::Session,
1161        cmd: v4l2_decoder_cmd,
1162    ) -> IoctlResult<v4l2_decoder_cmd> {
1163        v4l2r::ioctl::decoder_cmd(&session.device, cmd).map_err(|e| e.into_errno())
1164    }
1165
1166    fn try_decoder_cmd(
1167        &mut self,
1168        session: &Self::Session,
1169        cmd: v4l2_decoder_cmd,
1170    ) -> IoctlResult<v4l2_decoder_cmd> {
1171        v4l2r::ioctl::try_decoder_cmd(&session.device, cmd).map_err(|e| e.into_errno())
1172    }
1173
1174    fn enum_dv_timings(
1175        &mut self,
1176        session: &Self::Session,
1177        index: u32,
1178    ) -> IoctlResult<v4l2_dv_timings> {
1179        v4l2r::ioctl::enum_dv_timings(&session.device, index).map_err(|e| e.into_errno())
1180    }
1181
1182    fn query_dv_timings(&mut self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings> {
1183        v4l2r::ioctl::query_dv_timings(&session.device).map_err(|e| e.into_errno())
1184    }
1185
1186    fn dv_timings_cap(&self, session: &Self::Session) -> IoctlResult<v4l2_dv_timings_cap> {
1187        v4l2r::ioctl::dv_timings_cap(&session.device).map_err(|e| e.into_errno())
1188    }
1189
1190    fn enum_freq_bands(
1191        &self,
1192        session: &Self::Session,
1193        tuner: u32,
1194        type_: TunerType,
1195        index: u32,
1196    ) -> IoctlResult<v4l2_frequency_band> {
1197        v4l2r::ioctl::enum_freq_bands(&session.device, tuner, type_, index)
1198            .map_err(|e| e.into_errno())
1199    }
1200
1201    fn query_ext_ctrl(
1202        &mut self,
1203        session: &Self::Session,
1204        id: CtrlId,
1205        flags: QueryCtrlFlags,
1206    ) -> IoctlResult<v4l2_query_ext_ctrl> {
1207        v4l2r::ioctl::query_ext_ctrl::<v4l2_query_ext_ctrl>(&session.device, id, flags)
1208            .map_err(|e| e.into_errno())
1209    }
1210}
1211
1212impl<Q, M, HM, Reader, Writer> VirtioMediaDevice<Reader, Writer> for V4l2ProxyDevice<Q, M, HM>
1213where
1214    Q: VirtioMediaEventQueue,
1215    M: VirtioMediaGuestMemoryMapper,
1216    HM: VirtioMediaHostMemoryMapper,
1217    Reader: ReadFromDescriptorChain,
1218    Writer: WriteToDescriptorChain,
1219{
1220    type Session = V4l2Session<M>;
1221
1222    fn new_session(&mut self, session_id: u32) -> Result<Self::Session, i32> {
1223        match V4l2Device::open(&self.device_path, DeviceConfig::new().non_blocking_dqbuf()) {
1224            Ok(device) => Ok(V4l2Session::new(session_id, Arc::new(device))),
1225            Err(DeviceOpenError::OpenError(e)) => Err(e as i32),
1226            Err(DeviceOpenError::QueryCapError(QueryCapError::IoctlError(e))) => Err(e as i32),
1227        }
1228    }
1229
1230    fn close_session(&mut self, session: Self::Session) {
1231        self.delete_session(&session)
1232    }
1233
1234    fn do_mmap(
1235        &mut self,
1236        session: &mut Self::Session,
1237        flags: u32,
1238        offset: u32,
1239    ) -> Result<(u64, u64), i32> {
1240        let rw = (flags & VIRTIO_MEDIA_MMAP_FLAG_RW) != 0;
1241
1242        let plane_info = self.mmap_buffers.get_mut(&offset).ok_or(libc::EINVAL)?;
1243
1244        // Export the FD for the plane and cache it if needed.
1245        //
1246        // We must NOT cache this result to reuse in case of multiple MMAP requests. If we do, then
1247        // there is the risk that a session requests a buffer belonging to another one. The call
1248        // the `expbuf` also serves as a permission check that the requesting session indeed has
1249        // access to the buffer.
1250        let exported_fd = v4l2r::ioctl::expbuf::<OwnedFd>(
1251            &session.device,
1252            plane_info.queue,
1253            plane_info.index as usize,
1254            plane_info.plane as usize,
1255            if rw {
1256                ExpbufFlags::RDWR
1257            } else {
1258                ExpbufFlags::RDONLY
1259            },
1260        )
1261        .map_err(|e| e.into_errno())?;
1262
1263        let (mapping_addr, mapping_size) = self
1264            .mmap_manager
1265            .create_mapping(offset, exported_fd.as_fd(), rw)
1266            // TODO: better error mapping?
1267            .map_err(|_| libc::EINVAL)?;
1268
1269        plane_info.map_address = mapping_addr;
1270        Ok((mapping_addr, mapping_size))
1271    }
1272
1273    fn do_munmap(&mut self, guest_addr: u64) -> Result<(), i32> {
1274        self.mmap_manager
1275            .remove_mapping(guest_addr)
1276            .map(|_| ())
1277            .map_err(|_| libc::EINVAL)
1278    }
1279
1280    fn do_ioctl(
1281        &mut self,
1282        session: &mut Self::Session,
1283        ioctl: V4l2Ioctl,
1284        reader: &mut Reader,
1285        writer: &mut Writer,
1286    ) -> IoResult<()> {
1287        virtio_media_dispatch_ioctl(self, session, ioctl, reader, writer)
1288    }
1289
1290    fn process_events(&mut self, session: &mut Self::Session) -> Result<(), i32> {
1291        let events = session
1292            .poller
1293            .poll(Some(Duration::ZERO))
1294            .map_err(|_| libc::EIO)?;
1295
1296        let mut has_event = false;
1297
1298        for event in events {
1299            has_event = true;
1300
1301            match event {
1302                PollEvent::Device(DeviceEvent::CaptureReady) => {
1303                    self.dequeue_capture_buffer(session).map_err(|e| e.0)?;
1304                    // Try to release OUTPUT buffers while we are at it.
1305                    self.dequeue_output_buffers(session).map_err(|e| e.0)?;
1306                }
1307                PollEvent::Device(DeviceEvent::V4L2Event) => {
1308                    self.dequeue_events(session).map_err(|e| e.0)?
1309                }
1310                _ => panic!(),
1311            }
1312        }
1313
1314        if !has_event {
1315            log::warn!("process_events called but no event was pending");
1316        }
1317
1318        Ok(())
1319    }
1320}