1 //! Interface to the V4L2 primitives that is both safe and higher-level than
2 //! `ioctl`, while staying low-level enough to allow the implementation of any
3 //! kind of V4L2 program on top of it.
4 //!
5 //! The `Device` struct lets the user open a V4L2 device and querying its
6 //! capabilities, from which `Queue` objects can be created.
7 //!
8 //! A `Queue` object can be assigned a format and allocated buffers, from which
9 //! point it can be streamed on and off, and buffers queued to it.
10 //!
11 //! The emphasis of this interface is to limit the actions and data that are
12 //! accessible at a given point in time to those that make sense. For instance,
13 //! the compiler wil reject any code that tries to stream a queue on before it
14 //! has buffers allocated. Similarly, if a `Queue` uses `UserPtr` buffers, then
15 //! queuing a buffer requires to provide a valid memory area to back it up.
16 //!
17 //! Using this interface, the user does not have to worry about which fields of
18 //! a V4L2 structure make sense - if it is relevant, then it will be visible,
19 //! and if it is required, then the code won't compile unless it is provided.
20 use super::ioctl;
21 use super::ioctl::Capability;
22 use super::QueueType;
23 use std::collections::BTreeSet;
24 use std::fs::File;
25 use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
26 use std::{path::Path, sync::Mutex};
27 use thiserror::Error;
28 
29 pub mod poller;
30 pub mod queue;
31 mod traits;
32 
33 pub use traits::*;
34 
35 /// Options that can be specified when creating a `Device`.
36 #[derive(Default)]
37 pub struct DeviceConfig {
38     non_blocking_dqbuf: bool,
39 }
40 
41 impl DeviceConfig {
new() -> Self42     pub fn new() -> Self {
43         Default::default()
44     }
45 
non_blocking_dqbuf(self) -> Self46     pub fn non_blocking_dqbuf(self) -> Self {
47         DeviceConfig {
48             non_blocking_dqbuf: true,
49         }
50     }
51 }
52 
53 /// An opened V4L2 device. `Queue` objects can be instantiated from it.
54 pub struct Device {
55     capability: Capability,
56     fd: File,
57     used_queues: Mutex<BTreeSet<QueueType>>,
58 }
59 
60 #[derive(Debug, Error)]
61 pub enum DeviceOpenError {
62     #[error("error while opening device")]
63     OpenError(#[from] nix::Error),
64     #[error("error while querying capabilities")]
65     QueryCapError(#[from] ioctl::QueryCapError),
66 }
67 
68 impl Device {
new(fd: File) -> Result<Self, ioctl::QueryCapError>69     fn new(fd: File) -> Result<Self, ioctl::QueryCapError> {
70         Ok(Device {
71             capability: ioctl::querycap(&fd)?,
72             fd,
73             used_queues: Mutex::new(BTreeSet::new()),
74         })
75     }
76 
open(path: &Path, config: DeviceConfig) -> Result<Self, DeviceOpenError>77     pub fn open(path: &Path, config: DeviceConfig) -> Result<Self, DeviceOpenError> {
78         use nix::fcntl::{open, OFlag};
79         use nix::sys::stat::Mode;
80 
81         let flags = OFlag::O_RDWR
82             | OFlag::O_CLOEXEC
83             | if config.non_blocking_dqbuf {
84                 OFlag::O_NONBLOCK
85             } else {
86                 OFlag::empty()
87             };
88 
89         let fd = open(path, flags, Mode::empty())?;
90 
91         // Safe because we are constructing a file from Fd we just opened.
92         Ok(Device::new(unsafe { File::from_raw_fd(fd) })?)
93     }
94 
95     /// Returns the capabilities of the device, i.e. the result of QUERYCAPS.
caps(&self) -> &Capability96     pub fn caps(&self) -> &Capability {
97         &self.capability
98     }
99 }
100 
101 impl AsFd for Device {
as_fd(&self) -> BorrowedFd102     fn as_fd(&self) -> BorrowedFd {
103         self.fd.as_fd()
104     }
105 }
106 
107 impl AsRawFd for Device {
as_raw_fd(&self) -> RawFd108     fn as_raw_fd(&self) -> RawFd {
109         self.fd.as_raw_fd()
110     }
111 }
112