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