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 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>(fd: &impl AsRawFd, index: u32) -> Result { 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>(fd: &impl AsRawFd) -> Result { 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) -> 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>(fd: &impl AsRawFd) -> Result { 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>( fd: &impl AsRawFd, index: u32, ) -> Result { 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>( fd: &impl AsRawFd, tuner: u32, ) -> Result { 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>(fd: &impl AsRawFd, index: u32) -> Result { 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>(fd: &impl AsRawFd, index: u32) -> Result { 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 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>( fd: &impl AsRawFd, tuner: u32, type_: TunerType, index: u32, ) -> Result { 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)), } }