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