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