blob: 9b2624e26d1337e8cb5f3a9014e62fa58ce6a4b4 [file] [log] [blame]
use std::os::unix::io::AsRawFd;
use bitflags::bitflags;
use enumn::N;
use nix::errno::Errno;
use thiserror::Error;
use crate::bindings;
use crate::bindings::v4l2_audio;
use crate::bindings::v4l2_audioout;
use crate::bindings::v4l2_frequency;
use crate::bindings::v4l2_frequency_band;
use crate::bindings::v4l2_modulator;
use crate::bindings::v4l2_tuner;
bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct AudioCapability: u32 {
const STEREO = bindings::V4L2_AUDCAP_STEREO;
const AVL = bindings::V4L2_AUDCAP_AVL;
}
}
#[derive(Debug, N)]
#[repr(u32)]
pub enum AudioMode {
Avl = bindings::V4L2_AUDMODE_AVL,
}
#[derive(Clone, Copy, Debug, N)]
#[repr(u32)]
pub enum TunerType {
Radio = bindings::v4l2_tuner_type_V4L2_TUNER_RADIO,
AnalogTv = bindings::v4l2_tuner_type_V4L2_TUNER_ANALOG_TV,
DigitalTv = bindings::v4l2_tuner_type_V4L2_TUNER_DIGITAL_TV,
Sdr = bindings::v4l2_tuner_type_V4L2_TUNER_SDR,
Rf = bindings::v4l2_tuner_type_V4L2_TUNER_RF,
}
bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct TunerCapFlags: u32 {
const LOW = bindings::V4L2_TUNER_CAP_LOW;
const NORM = bindings::V4L2_TUNER_CAP_NORM;
const HWSEEK_BOUNDED = bindings::V4L2_TUNER_CAP_HWSEEK_BOUNDED;
const HWSEEK_WRAP = bindings::V4L2_TUNER_CAP_HWSEEK_WRAP;
const STEREO = bindings::V4L2_TUNER_CAP_STEREO;
const LANG1 = bindings::V4L2_TUNER_CAP_LANG1;
const LANG2 = bindings::V4L2_TUNER_CAP_LANG2;
const SAP = bindings::V4L2_TUNER_CAP_SAP;
const RDS = bindings::V4L2_TUNER_CAP_RDS;
const RDS_BLOCK_IO = bindings::V4L2_TUNER_CAP_RDS_BLOCK_IO;
const RDS_CONTROLS = bindings::V4L2_TUNER_CAP_RDS_CONTROLS;
const FREQ_BANDS = bindings::V4L2_TUNER_CAP_FREQ_BANDS;
const HWSEEK_PROG_LIM = bindings::V4L2_TUNER_CAP_HWSEEK_PROG_LIM;
const ONE_HZ = bindings::V4L2_TUNER_CAP_1HZ;
}
#[derive(Clone, Copy, Debug)]
pub struct TunerTransmissionFlags: u32 {
const MONO = bindings::V4L2_TUNER_SUB_MONO;
const STEREO = bindings::V4L2_TUNER_SUB_STEREO;
const LANG1 = bindings::V4L2_TUNER_SUB_LANG1;
const LANG2 = bindings::V4L2_TUNER_SUB_LANG2;
const RDS = bindings::V4L2_TUNER_SUB_RDS;
}
}
#[derive(Debug, N)]
#[repr(u32)]
pub enum TunerMode {
Mono = bindings::V4L2_TUNER_MODE_MONO,
Stereo = bindings::V4L2_TUNER_MODE_STEREO,
Lang1 = bindings::V4L2_TUNER_MODE_LANG1,
Lang2 = bindings::V4L2_TUNER_MODE_LANG2,
Lang1Lang2 = bindings::V4L2_TUNER_MODE_LANG1_LANG2,
}
#[doc(hidden)]
mod ioctl {
use crate::bindings::v4l2_audio;
use crate::bindings::v4l2_audioout;
use crate::bindings::v4l2_frequency;
use crate::bindings::v4l2_frequency_band;
use crate::bindings::v4l2_modulator;
use crate::bindings::v4l2_tuner;
nix::ioctl_readwrite!(vidioc_g_tuner, b'V', 29, v4l2_tuner);
nix::ioctl_write_ptr!(vidioc_s_tuner, b'V', 30, v4l2_tuner);
nix::ioctl_read!(vidioc_g_audio, b'V', 33, v4l2_audio);
nix::ioctl_write_ptr!(vidioc_s_audio, b'V', 34, v4l2_audio);
nix::ioctl_read!(vidioc_g_audout, b'V', 49, v4l2_audioout);
nix::ioctl_write_ptr!(vidioc_s_audout, b'V', 50, v4l2_audioout);
nix::ioctl_readwrite!(vidioc_g_modulator, b'V', 54, v4l2_modulator);
nix::ioctl_write_ptr!(vidioc_s_modulator, b'V', 55, v4l2_modulator);
nix::ioctl_readwrite!(vidioc_g_frequency, b'V', 56, v4l2_frequency);
nix::ioctl_write_ptr!(vidioc_s_frequency, b'V', 57, v4l2_frequency);
nix::ioctl_readwrite!(vidioc_enumaudio, b'V', 65, v4l2_audio);
nix::ioctl_readwrite!(vidioc_enumaudout, b'V', 66, v4l2_audioout);
nix::ioctl_readwrite!(vidioc_enum_freq_bands, b'V', 101, v4l2_frequency_band);
}
#[derive(Debug, Error)]
pub enum GAudioError {
#[error("invalid input index")]
Invalid,
#[error("ioctl error: {0}")]
IoctlError(Errno),
}
impl From<GAudioError> for Errno {
fn from(err: GAudioError) -> Self {
match err {
GAudioError::Invalid => Errno::EINVAL,
GAudioError::IoctlError(e) => e,
}
}
}
/// Safe wrapper around the `VIDIOC_G_TUNER` ioctl.
pub fn g_tuner<O: From<v4l2_tuner>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError> {
let mut tuner = v4l2_tuner {
index,
..Default::default()
};
match unsafe { ioctl::vidioc_g_tuner(fd.as_raw_fd(), &mut tuner) } {
Ok(_) => Ok(O::from(tuner)),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_S_TUNER` ioctl.
pub fn s_tuner(fd: &impl AsRawFd, index: u32, mode: TunerMode) -> Result<(), GAudioError> {
let tuner = v4l2_tuner {
index,
audmode: mode as u32,
..Default::default()
};
match unsafe { ioctl::vidioc_s_tuner(fd.as_raw_fd(), &tuner) } {
Ok(_) => Ok(()),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_G_AUDIO` ioctl.
pub fn g_audio<O: From<v4l2_audio>>(fd: &impl AsRawFd) -> Result<O, GAudioError> {
let mut audio = v4l2_audio {
..Default::default()
};
match unsafe { ioctl::vidioc_g_audio(fd.as_raw_fd(), &mut audio) } {
Ok(_) => Ok(O::from(audio)),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_S_AUDIO` ioctl.
pub fn s_audio(fd: &impl AsRawFd, index: u32, mode: Option<AudioMode>) -> Result<(), GAudioError> {
let audio = v4l2_audio {
index,
mode: mode.map(|m| m as u32).unwrap_or(0),
..Default::default()
};
match unsafe { ioctl::vidioc_s_audio(fd.as_raw_fd(), &audio) } {
Ok(_) => Ok(()),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_G_AUDOUT` ioctl.
pub fn g_audout<O: From<v4l2_audioout>>(fd: &impl AsRawFd) -> Result<O, GAudioError> {
let mut audio = v4l2_audioout {
..Default::default()
};
match unsafe { ioctl::vidioc_g_audout(fd.as_raw_fd(), &mut audio) } {
Ok(_) => Ok(O::from(audio)),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_S_AUDIO` ioctl.
pub fn s_audout(fd: &impl AsRawFd, index: u32) -> Result<(), GAudioError> {
let audio = v4l2_audioout {
index,
..Default::default()
};
match unsafe { ioctl::vidioc_s_audout(fd.as_raw_fd(), &audio) } {
Ok(_) => Ok(()),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_G_MODULATOR` ioctl.
pub fn g_modulator<O: From<v4l2_modulator>>(
fd: &impl AsRawFd,
index: u32,
) -> Result<O, GAudioError> {
let mut modulator = v4l2_modulator {
index,
..Default::default()
};
match unsafe { ioctl::vidioc_g_modulator(fd.as_raw_fd(), &mut modulator) } {
Ok(_) => Ok(O::from(modulator)),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_S_MODULATOR` ioctl.
pub fn s_modulator(
fd: &impl AsRawFd,
index: u32,
txsubchans: TunerTransmissionFlags,
) -> Result<(), GAudioError> {
let modulator = v4l2_modulator {
index,
txsubchans: txsubchans.bits(),
..Default::default()
};
match unsafe { ioctl::vidioc_s_modulator(fd.as_raw_fd(), &modulator) } {
Ok(_) => Ok(()),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_G_FREQUENCY` ioctl.
pub fn g_frequency<O: From<v4l2_frequency>>(
fd: &impl AsRawFd,
tuner: u32,
) -> Result<O, GAudioError> {
let mut frequency = v4l2_frequency {
tuner,
..Default::default()
};
match unsafe { ioctl::vidioc_g_frequency(fd.as_raw_fd(), &mut frequency) } {
Ok(_) => Ok(O::from(frequency)),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_S_FREQUENCY` ioctl.
pub fn s_frequency(
fd: &impl AsRawFd,
tuner: u32,
type_: TunerType,
frequency: u32,
) -> Result<(), GAudioError> {
let frequency = v4l2_frequency {
tuner,
type_: type_ as u32,
frequency,
..Default::default()
};
match unsafe { ioctl::vidioc_s_frequency(fd.as_raw_fd(), &frequency) } {
Ok(_) => Ok(()),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_ENUMAUDIO` ioctl.
pub fn enumaudio<O: From<v4l2_audio>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError> {
let mut audio = v4l2_audio {
index,
..Default::default()
};
match unsafe { ioctl::vidioc_enumaudio(fd.as_raw_fd(), &mut audio) } {
Ok(_) => Ok(O::from(audio)),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
/// Safe wrapper around the `VIDIOC_ENUMAUDOUT` ioctl.
pub fn enumaudout<O: From<v4l2_audioout>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError> {
let mut audio = v4l2_audioout {
index,
..Default::default()
};
match unsafe { ioctl::vidioc_enumaudout(fd.as_raw_fd(), &mut audio) } {
Ok(_) => Ok(O::from(audio)),
Err(Errno::EINVAL) => Err(GAudioError::Invalid),
Err(e) => Err(GAudioError::IoctlError(e)),
}
}
#[derive(Debug, Error)]
pub enum EnumFreqBandsError {
#[error("invalid tuner, index, or type")]
Invalid,
#[error("ioctl error: {0}")]
IoctlError(Errno),
}
impl From<EnumFreqBandsError> for Errno {
fn from(err: EnumFreqBandsError) -> Self {
match err {
EnumFreqBandsError::Invalid => Errno::EINVAL,
EnumFreqBandsError::IoctlError(e) => e,
}
}
}
/// Safe wrapper around the `VIDIOC_ENUM_FREQ_BANDS` ioctl.
pub fn enum_freq_bands<O: From<v4l2_frequency_band>>(
fd: &impl AsRawFd,
tuner: u32,
type_: TunerType,
index: u32,
) -> Result<O, EnumFreqBandsError> {
let mut freq_band = v4l2_frequency_band {
tuner,
type_: type_ as u32,
index,
..Default::default()
};
match unsafe { ioctl::vidioc_enum_freq_bands(fd.as_raw_fd(), &mut freq_band) } {
Ok(_) => Ok(O::from(freq_band)),
Err(Errno::EINVAL) => Err(EnumFreqBandsError::Invalid),
Err(e) => Err(EnumFreqBandsError::IoctlError(e)),
}
}