blob: 9baa12882b282055cb27cb5fdb63dfa3978a511e [file] [log] [blame] [edit]
//! Interface to the V4L2 primitives that is both safe and higher-level than
//! `ioctl`, while staying low-level enough to allow the implementation of any
//! kind of V4L2 program on top of it.
//!
//! The `Device` struct lets the user open a V4L2 device and querying its
//! capabilities, from which `Queue` objects can be created.
//!
//! A `Queue` object can be assigned a format and allocated buffers, from which
//! point it can be streamed on and off, and buffers queued to it.
//!
//! The emphasis of this interface is to limit the actions and data that are
//! accessible at a given point in time to those that make sense. For instance,
//! the compiler wil reject any code that tries to stream a queue on before it
//! has buffers allocated. Similarly, if a `Queue` uses `UserPtr` buffers, then
//! queuing a buffer requires to provide a valid memory area to back it up.
//!
//! Using this interface, the user does not have to worry about which fields of
//! a V4L2 structure make sense - if it is relevant, then it will be visible,
//! and if it is required, then the code won't compile unless it is provided.
use super::ioctl;
use super::ioctl::Capability;
use super::QueueType;
use std::collections::BTreeSet;
use std::fs::File;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
use std::{path::Path, sync::Mutex};
use thiserror::Error;
pub mod poller;
pub mod queue;
mod traits;
pub use traits::*;
/// Options that can be specified when creating a `Device`.
#[derive(Default)]
pub struct DeviceConfig {
non_blocking_dqbuf: bool,
}
impl DeviceConfig {
pub fn new() -> Self {
Default::default()
}
pub fn non_blocking_dqbuf(self) -> Self {
DeviceConfig {
non_blocking_dqbuf: true,
}
}
}
/// An opened V4L2 device. `Queue` objects can be instantiated from it.
pub struct Device {
capability: Capability,
fd: File,
used_queues: Mutex<BTreeSet<QueueType>>,
}
#[derive(Debug, Error)]
pub enum DeviceOpenError {
#[error("error while opening device")]
OpenError(#[from] nix::Error),
#[error("error while querying capabilities")]
QueryCapError(#[from] ioctl::QueryCapError),
}
impl Device {
fn new(fd: File) -> Result<Self, ioctl::QueryCapError> {
Ok(Device {
capability: ioctl::querycap(&fd)?,
fd,
used_queues: Mutex::new(BTreeSet::new()),
})
}
pub fn open(path: &Path, config: DeviceConfig) -> Result<Self, DeviceOpenError> {
use nix::fcntl::{open, OFlag};
use nix::sys::stat::Mode;
let flags = OFlag::O_RDWR
| OFlag::O_CLOEXEC
| if config.non_blocking_dqbuf {
OFlag::O_NONBLOCK
} else {
OFlag::empty()
};
let fd = open(path, flags, Mode::empty())?;
// Safe because we are constructing a file from Fd we just opened.
Ok(Device::new(unsafe { File::from_raw_fd(fd) })?)
}
/// Returns the capabilities of the device, i.e. the result of QUERYCAPS.
pub fn caps(&self) -> &Capability {
&self.capability
}
}
impl AsFd for Device {
fn as_fd(&self) -> BorrowedFd {
self.fd.as_fd()
}
}
impl AsRawFd for Device {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}