1 use std::os::unix::io::AsRawFd;
2 
3 use bitflags::bitflags;
4 use enumn::N;
5 use nix::errno::Errno;
6 use thiserror::Error;
7 
8 use crate::bindings;
9 use crate::bindings::v4l2_audio;
10 use crate::bindings::v4l2_audioout;
11 use crate::bindings::v4l2_frequency;
12 use crate::bindings::v4l2_frequency_band;
13 use crate::bindings::v4l2_modulator;
14 use crate::bindings::v4l2_tuner;
15 
16 bitflags! {
17     #[derive(Clone, Copy, Debug)]
18     pub struct AudioCapability: u32 {
19         const STEREO = bindings::V4L2_AUDCAP_STEREO;
20         const AVL = bindings::V4L2_AUDCAP_AVL;
21     }
22 }
23 
24 #[derive(Debug, N)]
25 #[repr(u32)]
26 pub enum AudioMode {
27     Avl = bindings::V4L2_AUDMODE_AVL,
28 }
29 
30 #[derive(Clone, Copy, Debug, N)]
31 #[repr(u32)]
32 pub enum TunerType {
33     Radio = bindings::v4l2_tuner_type_V4L2_TUNER_RADIO,
34     AnalogTv = bindings::v4l2_tuner_type_V4L2_TUNER_ANALOG_TV,
35     DigitalTv = bindings::v4l2_tuner_type_V4L2_TUNER_DIGITAL_TV,
36     Sdr = bindings::v4l2_tuner_type_V4L2_TUNER_SDR,
37     Rf = bindings::v4l2_tuner_type_V4L2_TUNER_RF,
38 }
39 
40 bitflags! {
41     #[derive(Clone, Copy, Debug)]
42     pub struct TunerCapFlags: u32 {
43         const LOW = bindings::V4L2_TUNER_CAP_LOW;
44         const NORM = bindings::V4L2_TUNER_CAP_NORM;
45         const HWSEEK_BOUNDED = bindings::V4L2_TUNER_CAP_HWSEEK_BOUNDED;
46         const HWSEEK_WRAP = bindings::V4L2_TUNER_CAP_HWSEEK_WRAP;
47         const STEREO = bindings::V4L2_TUNER_CAP_STEREO;
48         const LANG1 = bindings::V4L2_TUNER_CAP_LANG1;
49         const LANG2 = bindings::V4L2_TUNER_CAP_LANG2;
50         const SAP = bindings::V4L2_TUNER_CAP_SAP;
51         const RDS = bindings::V4L2_TUNER_CAP_RDS;
52         const RDS_BLOCK_IO = bindings::V4L2_TUNER_CAP_RDS_BLOCK_IO;
53         const RDS_CONTROLS = bindings::V4L2_TUNER_CAP_RDS_CONTROLS;
54         const FREQ_BANDS = bindings::V4L2_TUNER_CAP_FREQ_BANDS;
55         const HWSEEK_PROG_LIM = bindings::V4L2_TUNER_CAP_HWSEEK_PROG_LIM;
56         const ONE_HZ = bindings::V4L2_TUNER_CAP_1HZ;
57     }
58 
59     #[derive(Clone, Copy, Debug)]
60     pub struct TunerTransmissionFlags: u32 {
61         const MONO = bindings::V4L2_TUNER_SUB_MONO;
62         const STEREO = bindings::V4L2_TUNER_SUB_STEREO;
63         const LANG1 = bindings::V4L2_TUNER_SUB_LANG1;
64         const LANG2 = bindings::V4L2_TUNER_SUB_LANG2;
65         const RDS = bindings::V4L2_TUNER_SUB_RDS;
66     }
67 }
68 
69 #[derive(Debug, N)]
70 #[repr(u32)]
71 pub enum TunerMode {
72     Mono = bindings::V4L2_TUNER_MODE_MONO,
73     Stereo = bindings::V4L2_TUNER_MODE_STEREO,
74     Lang1 = bindings::V4L2_TUNER_MODE_LANG1,
75     Lang2 = bindings::V4L2_TUNER_MODE_LANG2,
76     Lang1Lang2 = bindings::V4L2_TUNER_MODE_LANG1_LANG2,
77 }
78 
79 #[doc(hidden)]
80 mod ioctl {
81     use crate::bindings::v4l2_audio;
82     use crate::bindings::v4l2_audioout;
83     use crate::bindings::v4l2_frequency;
84     use crate::bindings::v4l2_frequency_band;
85     use crate::bindings::v4l2_modulator;
86     use crate::bindings::v4l2_tuner;
87 
88     nix::ioctl_readwrite!(vidioc_g_tuner, b'V', 29, v4l2_tuner);
89     nix::ioctl_write_ptr!(vidioc_s_tuner, b'V', 30, v4l2_tuner);
90 
91     nix::ioctl_read!(vidioc_g_audio, b'V', 33, v4l2_audio);
92     nix::ioctl_write_ptr!(vidioc_s_audio, b'V', 34, v4l2_audio);
93 
94     nix::ioctl_read!(vidioc_g_audout, b'V', 49, v4l2_audioout);
95     nix::ioctl_write_ptr!(vidioc_s_audout, b'V', 50, v4l2_audioout);
96 
97     nix::ioctl_readwrite!(vidioc_g_modulator, b'V', 54, v4l2_modulator);
98     nix::ioctl_write_ptr!(vidioc_s_modulator, b'V', 55, v4l2_modulator);
99 
100     nix::ioctl_readwrite!(vidioc_g_frequency, b'V', 56, v4l2_frequency);
101     nix::ioctl_write_ptr!(vidioc_s_frequency, b'V', 57, v4l2_frequency);
102 
103     nix::ioctl_readwrite!(vidioc_enumaudio, b'V', 65, v4l2_audio);
104     nix::ioctl_readwrite!(vidioc_enumaudout, b'V', 66, v4l2_audioout);
105 
106     nix::ioctl_readwrite!(vidioc_enum_freq_bands, b'V', 101, v4l2_frequency_band);
107 }
108 
109 #[derive(Debug, Error)]
110 pub enum GAudioError {
111     #[error("invalid input index")]
112     Invalid,
113     #[error("ioctl error: {0}")]
114     IoctlError(Errno),
115 }
116 
117 impl From<GAudioError> for Errno {
from(err: GAudioError) -> Self118     fn from(err: GAudioError) -> Self {
119         match err {
120             GAudioError::Invalid => Errno::EINVAL,
121             GAudioError::IoctlError(e) => e,
122         }
123     }
124 }
125 
126 /// Safe wrapper around the `VIDIOC_G_TUNER` ioctl.
g_tuner<O: From<v4l2_tuner>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError>127 pub fn g_tuner<O: From<v4l2_tuner>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError> {
128     let mut tuner = v4l2_tuner {
129         index,
130         ..Default::default()
131     };
132 
133     match unsafe { ioctl::vidioc_g_tuner(fd.as_raw_fd(), &mut tuner) } {
134         Ok(_) => Ok(O::from(tuner)),
135         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
136         Err(e) => Err(GAudioError::IoctlError(e)),
137     }
138 }
139 
140 /// Safe wrapper around the `VIDIOC_S_TUNER` ioctl.
s_tuner(fd: &impl AsRawFd, index: u32, mode: TunerMode) -> Result<(), GAudioError>141 pub fn s_tuner(fd: &impl AsRawFd, index: u32, mode: TunerMode) -> Result<(), GAudioError> {
142     let tuner = v4l2_tuner {
143         index,
144         audmode: mode as u32,
145         ..Default::default()
146     };
147 
148     match unsafe { ioctl::vidioc_s_tuner(fd.as_raw_fd(), &tuner) } {
149         Ok(_) => Ok(()),
150         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
151         Err(e) => Err(GAudioError::IoctlError(e)),
152     }
153 }
154 
155 /// Safe wrapper around the `VIDIOC_G_AUDIO` ioctl.
g_audio<O: From<v4l2_audio>>(fd: &impl AsRawFd) -> Result<O, GAudioError>156 pub fn g_audio<O: From<v4l2_audio>>(fd: &impl AsRawFd) -> Result<O, GAudioError> {
157     let mut audio = v4l2_audio {
158         ..Default::default()
159     };
160 
161     match unsafe { ioctl::vidioc_g_audio(fd.as_raw_fd(), &mut audio) } {
162         Ok(_) => Ok(O::from(audio)),
163         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
164         Err(e) => Err(GAudioError::IoctlError(e)),
165     }
166 }
167 
168 /// Safe wrapper around the `VIDIOC_S_AUDIO` ioctl.
s_audio(fd: &impl AsRawFd, index: u32, mode: Option<AudioMode>) -> Result<(), GAudioError>169 pub fn s_audio(fd: &impl AsRawFd, index: u32, mode: Option<AudioMode>) -> Result<(), GAudioError> {
170     let audio = v4l2_audio {
171         index,
172         mode: mode.map(|m| m as u32).unwrap_or(0),
173         ..Default::default()
174     };
175 
176     match unsafe { ioctl::vidioc_s_audio(fd.as_raw_fd(), &audio) } {
177         Ok(_) => Ok(()),
178         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
179         Err(e) => Err(GAudioError::IoctlError(e)),
180     }
181 }
182 
183 /// Safe wrapper around the `VIDIOC_G_AUDOUT` ioctl.
g_audout<O: From<v4l2_audioout>>(fd: &impl AsRawFd) -> Result<O, GAudioError>184 pub fn g_audout<O: From<v4l2_audioout>>(fd: &impl AsRawFd) -> Result<O, GAudioError> {
185     let mut audio = v4l2_audioout {
186         ..Default::default()
187     };
188 
189     match unsafe { ioctl::vidioc_g_audout(fd.as_raw_fd(), &mut audio) } {
190         Ok(_) => Ok(O::from(audio)),
191         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
192         Err(e) => Err(GAudioError::IoctlError(e)),
193     }
194 }
195 
196 /// Safe wrapper around the `VIDIOC_S_AUDIO` ioctl.
s_audout(fd: &impl AsRawFd, index: u32) -> Result<(), GAudioError>197 pub fn s_audout(fd: &impl AsRawFd, index: u32) -> Result<(), GAudioError> {
198     let audio = v4l2_audioout {
199         index,
200         ..Default::default()
201     };
202 
203     match unsafe { ioctl::vidioc_s_audout(fd.as_raw_fd(), &audio) } {
204         Ok(_) => Ok(()),
205         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
206         Err(e) => Err(GAudioError::IoctlError(e)),
207     }
208 }
209 
210 /// Safe wrapper around the `VIDIOC_G_MODULATOR` ioctl.
g_modulator<O: From<v4l2_modulator>>( fd: &impl AsRawFd, index: u32, ) -> Result<O, GAudioError>211 pub fn g_modulator<O: From<v4l2_modulator>>(
212     fd: &impl AsRawFd,
213     index: u32,
214 ) -> Result<O, GAudioError> {
215     let mut modulator = v4l2_modulator {
216         index,
217         ..Default::default()
218     };
219 
220     match unsafe { ioctl::vidioc_g_modulator(fd.as_raw_fd(), &mut modulator) } {
221         Ok(_) => Ok(O::from(modulator)),
222         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
223         Err(e) => Err(GAudioError::IoctlError(e)),
224     }
225 }
226 
227 /// Safe wrapper around the `VIDIOC_S_MODULATOR` ioctl.
s_modulator( fd: &impl AsRawFd, index: u32, txsubchans: TunerTransmissionFlags, ) -> Result<(), GAudioError>228 pub fn s_modulator(
229     fd: &impl AsRawFd,
230     index: u32,
231     txsubchans: TunerTransmissionFlags,
232 ) -> Result<(), GAudioError> {
233     let modulator = v4l2_modulator {
234         index,
235         txsubchans: txsubchans.bits(),
236         ..Default::default()
237     };
238 
239     match unsafe { ioctl::vidioc_s_modulator(fd.as_raw_fd(), &modulator) } {
240         Ok(_) => Ok(()),
241         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
242         Err(e) => Err(GAudioError::IoctlError(e)),
243     }
244 }
245 
246 /// Safe wrapper around the `VIDIOC_G_FREQUENCY` ioctl.
g_frequency<O: From<v4l2_frequency>>( fd: &impl AsRawFd, tuner: u32, ) -> Result<O, GAudioError>247 pub fn g_frequency<O: From<v4l2_frequency>>(
248     fd: &impl AsRawFd,
249     tuner: u32,
250 ) -> Result<O, GAudioError> {
251     let mut frequency = v4l2_frequency {
252         tuner,
253         ..Default::default()
254     };
255 
256     match unsafe { ioctl::vidioc_g_frequency(fd.as_raw_fd(), &mut frequency) } {
257         Ok(_) => Ok(O::from(frequency)),
258         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
259         Err(e) => Err(GAudioError::IoctlError(e)),
260     }
261 }
262 
263 /// Safe wrapper around the `VIDIOC_S_FREQUENCY` ioctl.
s_frequency( fd: &impl AsRawFd, tuner: u32, type_: TunerType, frequency: u32, ) -> Result<(), GAudioError>264 pub fn s_frequency(
265     fd: &impl AsRawFd,
266     tuner: u32,
267     type_: TunerType,
268     frequency: u32,
269 ) -> Result<(), GAudioError> {
270     let frequency = v4l2_frequency {
271         tuner,
272         type_: type_ as u32,
273         frequency,
274         ..Default::default()
275     };
276 
277     match unsafe { ioctl::vidioc_s_frequency(fd.as_raw_fd(), &frequency) } {
278         Ok(_) => Ok(()),
279         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
280         Err(e) => Err(GAudioError::IoctlError(e)),
281     }
282 }
283 
284 /// Safe wrapper around the `VIDIOC_ENUMAUDIO` ioctl.
enumaudio<O: From<v4l2_audio>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError>285 pub fn enumaudio<O: From<v4l2_audio>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError> {
286     let mut audio = v4l2_audio {
287         index,
288         ..Default::default()
289     };
290 
291     match unsafe { ioctl::vidioc_enumaudio(fd.as_raw_fd(), &mut audio) } {
292         Ok(_) => Ok(O::from(audio)),
293         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
294         Err(e) => Err(GAudioError::IoctlError(e)),
295     }
296 }
297 
298 /// Safe wrapper around the `VIDIOC_ENUMAUDOUT` ioctl.
enumaudout<O: From<v4l2_audioout>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError>299 pub fn enumaudout<O: From<v4l2_audioout>>(fd: &impl AsRawFd, index: u32) -> Result<O, GAudioError> {
300     let mut audio = v4l2_audioout {
301         index,
302         ..Default::default()
303     };
304 
305     match unsafe { ioctl::vidioc_enumaudout(fd.as_raw_fd(), &mut audio) } {
306         Ok(_) => Ok(O::from(audio)),
307         Err(Errno::EINVAL) => Err(GAudioError::Invalid),
308         Err(e) => Err(GAudioError::IoctlError(e)),
309     }
310 }
311 
312 #[derive(Debug, Error)]
313 pub enum EnumFreqBandsError {
314     #[error("invalid tuner, index, or type")]
315     Invalid,
316     #[error("ioctl error: {0}")]
317     IoctlError(Errno),
318 }
319 
320 impl From<EnumFreqBandsError> for Errno {
from(err: EnumFreqBandsError) -> Self321     fn from(err: EnumFreqBandsError) -> Self {
322         match err {
323             EnumFreqBandsError::Invalid => Errno::EINVAL,
324             EnumFreqBandsError::IoctlError(e) => e,
325         }
326     }
327 }
328 /// Safe wrapper around the `VIDIOC_ENUM_FREQ_BANDS` ioctl.
enum_freq_bands<O: From<v4l2_frequency_band>>( fd: &impl AsRawFd, tuner: u32, type_: TunerType, index: u32, ) -> Result<O, EnumFreqBandsError>329 pub fn enum_freq_bands<O: From<v4l2_frequency_band>>(
330     fd: &impl AsRawFd,
331     tuner: u32,
332     type_: TunerType,
333     index: u32,
334 ) -> Result<O, EnumFreqBandsError> {
335     let mut freq_band = v4l2_frequency_band {
336         tuner,
337         type_: type_ as u32,
338         index,
339         ..Default::default()
340     };
341 
342     match unsafe { ioctl::vidioc_enum_freq_bands(fd.as_raw_fd(), &mut freq_band) } {
343         Ok(_) => Ok(O::from(freq_band)),
344         Err(Errno::EINVAL) => Err(EnumFreqBandsError::Invalid),
345         Err(e) => Err(EnumFreqBandsError::IoctlError(e)),
346     }
347 }
348