1#![allow(dead_code)]
2mod status;
3pub mod sys;
4
5use {
6 anyhow::{Context, Result},
7 nix::{ioctl_read, ioctl_write_int_bad, ioctl_write_ptr, request_code_none},
8 std::{
9 ffi::CStr,
10 fmt,
11 fs::{File, OpenOptions},
12 ops::Range,
13 os::unix::{
14 fs::{FileTypeExt, OpenOptionsExt},
15 io::{AsRawFd, RawFd},
16 },
17 },
18 sys::*,
19};
20
21pub use status::FeStatus;
22
23#[derive(Debug)]
25pub struct FeDevice {
26 adapter: u32,
27 device: u32,
28
29 file: File,
30
31 api_version: u16,
32
33 name: String,
34 delivery_system_list: Vec<fe_delivery_system>,
35 frequency_range: Range<u32>,
36 symbolrate_range: Range<u32>,
37 caps: fe_caps,
38}
39
40impl fmt::Display for FeDevice {
41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
42 writeln!(
43 f,
44 "DVB API: {}.{}",
45 self.api_version >> 8,
46 self.api_version & 0xFF
47 )?;
48 writeln!(f, "Frontend: {}", self.name)?;
49
50 write!(f, "Delivery system:")?;
51 for v in &self.delivery_system_list {
52 write!(f, " {}", v)?;
53 }
54 writeln!(f, "")?;
55
56 writeln!(
57 f,
58 "Frequency range: {} .. {}",
59 self.frequency_range.start / 1000,
60 self.frequency_range.end / 1000
61 )?;
62
63 writeln!(
64 f,
65 "Symbolrate range: {} .. {}",
66 self.symbolrate_range.start / 1000,
67 self.symbolrate_range.end / 1000
68 )?;
69
70 write!(f, "Frontend capabilities: 0x{:08x}", self.caps)?;
71
72 Ok(())
73 }
74}
75
76impl AsRawFd for FeDevice {
77 #[inline]
78 fn as_raw_fd(&self) -> RawFd {
79 self.file.as_raw_fd()
80 }
81}
82
83#[macro_export]
84macro_rules! get_dtv_properties {
85 ( $device:expr, $( $property:ident ),+ ) => { (|| -> ::anyhow::Result<_> {
86 let mut input = [ $( $property($crate::fe::sys::DtvPropertyRequest::default()), )* ];
87 ::anyhow::Context::context($device.get_properties(&mut input), "Error fetching properties")?;
88 let mut iterator = input.iter();
89 Ok((
90 $(
91 ::anyhow::Context::with_context(match iterator.next() {
92 Some($property(d)) => d.get(),
93 _ => ::anyhow::Result::Err(::anyhow::anyhow!("Missing value")),
94 }, || format!("Error unpacking {}", stringify!($property)))?,
95 )*
96 ))
97 })()}
98}
99
100#[macro_export]
101macro_rules! set_dtv_properties {
102 ( $device:expr, $( $property:ident($data:expr) ),+ ) => {
103 $device.set_properties(&[
104 $( $property($crate::fe::sys::DtvPropertyRequest::new($data)), )*
105 ])
106 };
107}
108
109impl FeDevice {
110 pub fn clear(&self) -> Result<()> {
112 set_dtv_properties!(
113 self,
114 DTV_VOLTAGE(SEC_VOLTAGE_OFF),
115 DTV_TONE(SEC_TONE_OFF),
116 DTV_CLEAR(())
117 )
118 .context("FE: clear")?;
119
120 let mut event = FeEvent::default();
121
122 for _ in 0..FE_MAX_EVENT {
123 if self.get_event(&mut event).is_err() {
124 break;
125 }
126 }
127
128 Ok(())
129 }
130
131 fn get_info(&mut self) -> Result<()> {
132 let mut feinfo = FeInfo::default();
133
134 ioctl_read!(
136 #[inline]
137 ioctl_call,
138 b'o',
139 61,
140 FeInfo
141 );
142 unsafe { ioctl_call(self.as_raw_fd(), &mut feinfo as *mut _) }.context("FE: get info")?;
143
144 if let Some(len) = feinfo.name.iter().position(|&b| b == 0) {
145 let name = unsafe { CStr::from_ptr(feinfo.name[..len + 1].as_ptr()) };
146 if let Ok(name) = name.to_str() {
147 self.name = name.to_owned();
148 }
149 }
150
151 self.frequency_range = feinfo.frequency_min..feinfo.frequency_max;
152 self.symbolrate_range = feinfo.symbol_rate_min..feinfo.symbol_rate_max;
153
154 self.caps = feinfo.caps;
155
156 let (api_version, enum_delsys) =
158 get_dtv_properties!(self, DTV_API_VERSION, DTV_ENUM_DELSYS)
159 .context("FE: get api version (deprecated driver)")?;
160
161 self.api_version = api_version as u16;
163
164 self.delivery_system_list = enum_delsys;
166
167 let metadata = self.file.metadata().context("FE: get device metadata")?;
170
171 ensure!(
172 metadata.file_type().is_char_device(),
173 "FE: path is not to char device"
174 );
175
176 Ok(())
177 }
178
179 fn open(adapter: u32, device: u32, is_write: bool) -> Result<FeDevice> {
180 let path = format!("/dev/dvb/adapter{}/frontend{}", adapter, device);
181 let file = OpenOptions::new()
182 .read(true)
183 .write(is_write)
184 .custom_flags(::nix::libc::O_NONBLOCK)
185 .open(&path)
186 .with_context(|| format!("FE: failed to open device {}", &path))?;
187
188 let mut fe = FeDevice {
189 adapter,
190 device,
191
192 file,
193
194 api_version: 0,
195
196 name: String::default(),
197 delivery_system_list: Vec::default(),
198 frequency_range: 0..0,
199 symbolrate_range: 0..0,
200 caps: fe_caps::FE_IS_STUPID,
201 };
202
203 fe.get_info()?;
204
205 Ok(fe)
206 }
207
208 #[inline]
210 pub fn open_ro(adapter: u32, device: u32) -> Result<FeDevice> {
211 Self::open(adapter, device, false)
212 }
213
214 #[inline]
216 pub fn open_rw(adapter: u32, device: u32) -> Result<FeDevice> {
217 Self::open(adapter, device, true)
218 }
219
220 fn check_properties(&self, cmdseq: &[DtvProperty]) -> Result<()> {
221 for p in cmdseq {
222 match p {
223 DTV_FREQUENCY(d) => {
224 ensure!(
225 self.frequency_range.contains(&d.get()?),
226 "FE: frequency out of range"
227 );
228 }
229 DTV_SYMBOL_RATE(d) => {
230 ensure!(
231 self.symbolrate_range.contains(&d.get()?),
232 "FE: symbolrate out of range"
233 );
234 }
235 DTV_INVERSION(d) => {
236 if d.get()? == INVERSION_AUTO {
237 ensure!(
238 self.caps.contains(fe_caps::FE_CAN_INVERSION_AUTO),
239 "FE: auto inversion is not available"
240 );
241 }
242 }
243 DTV_TRANSMISSION_MODE(d) => {
244 if d.get()? == TRANSMISSION_MODE_AUTO {
245 ensure!(
246 self.caps.contains(fe_caps::FE_CAN_TRANSMISSION_MODE_AUTO),
247 "FE: no auto transmission mode"
248 );
249 }
250 }
251 DTV_GUARD_INTERVAL(d) => {
252 if d.get()? == GUARD_INTERVAL_AUTO {
253 ensure!(
254 self.caps.contains(fe_caps::FE_CAN_GUARD_INTERVAL_AUTO),
255 "FE: no auto guard interval"
256 );
257 }
258 }
259 DTV_HIERARCHY(d) => {
260 if d.get()? == HIERARCHY_AUTO {
261 ensure!(
262 self.caps.contains(fe_caps::FE_CAN_HIERARCHY_AUTO),
263 "FE: no auto hierarchy"
264 );
265 }
266 }
267 DTV_STREAM_ID(..) => {
268 ensure!(
269 self.caps.contains(fe_caps::FE_CAN_MULTISTREAM),
270 "FE: no multistream"
271 );
272 }
273 _ => {}
274 }
275 }
276
277 Ok(())
278 }
279
280 pub fn set_properties(&self, cmdseq: &[DtvProperty]) -> Result<()> {
282 self.check_properties(cmdseq)?;
283
284 #[repr(C)]
285 pub struct DtvProperties {
286 num: u32,
287 props: *const DtvProperty,
288 }
289
290 let cmd = DtvProperties {
291 num: cmdseq.len() as u32,
292 props: cmdseq.as_ptr(),
293 };
294
295 ioctl_write_ptr!(
297 #[inline]
298 ioctl_call,
299 b'o',
300 82,
301 DtvProperties
302 );
303 unsafe { ioctl_call(self.as_raw_fd(), &cmd as *const _) }.context("FE: set properties")?;
304
305 Ok(())
306 }
307
308 pub fn get_properties(&self, cmdseq: &mut [DtvProperty]) -> Result<()> {
310 #[repr(C)]
311 pub struct DtvProperties {
312 num: u32,
313 props: *mut DtvProperty,
314 }
315
316 let mut cmd = DtvProperties {
317 num: cmdseq.len() as u32,
318 props: cmdseq.as_mut_ptr(),
319 };
320
321 ioctl_read!(
323 #[inline]
324 ioctl_call,
325 b'o',
326 83,
327 DtvProperties
328 );
329 unsafe { ioctl_call(self.as_raw_fd(), &mut cmd as *mut _) }
330 .context("FE: get properties")?;
331
332 Ok(())
333 }
334
335 pub fn get_event(&self, event: &mut FeEvent) -> Result<()> {
337 ioctl_read!(
339 #[inline]
340 ioctl_call,
341 b'o',
342 78,
343 FeEvent
344 );
345 unsafe { ioctl_call(self.as_raw_fd(), event as *mut _) }.context("FE: get event")?;
346
347 Ok(())
348 }
349
350 pub fn read_status(&self) -> Result<fe_status> {
360 let mut result: u32 = 0;
361
362 ioctl_read!(
364 #[inline]
365 ioctl_call,
366 b'o',
367 69,
368 u32
369 );
370 unsafe { ioctl_call(self.as_raw_fd(), &mut result as *mut _) }
371 .context("FE: read status")?;
372
373 Ok(fe_status::from_bits(result).context("Invalid status")?)
374 }
375
376 pub fn read_signal_strength(&self) -> Result<u16> {
378 let mut result: u16 = 0;
379
380 ioctl_read!(
382 #[inline]
383 ioctl_call,
384 b'o',
385 71,
386 u16
387 );
388 unsafe { ioctl_call(self.as_raw_fd(), &mut result as *mut _) }
389 .context("FE: read signal strength")?;
390
391 Ok(result)
392 }
393
394 pub fn read_snr(&self) -> Result<u16> {
396 let mut result: u16 = 0;
397
398 ioctl_read!(
400 #[inline]
401 ioctl_call,
402 b'o',
403 72,
404 u16
405 );
406 unsafe { ioctl_call(self.as_raw_fd(), &mut result as *mut _) }.context("FE: read snr")?;
407
408 Ok(result)
409 }
410
411 pub fn read_ber(&self) -> Result<u64> {
413 let mut result: u32 = 0;
414
415 ioctl_read!(
417 #[inline]
418 ioctl_call,
419 b'o',
420 70,
421 u32
422 );
423 unsafe { ioctl_call(self.as_raw_fd(), &mut result as *mut _) }.context("FE: read ber")?;
424
425 Ok(result as u64)
426 }
427
428 pub fn read_unc(&self) -> Result<u64> {
430 let mut result: u32 = 0;
431
432 ioctl_read!(
434 #[inline]
435 ioctl_call,
436 b'o',
437 73,
438 u32
439 );
440 unsafe { ioctl_call(self.as_raw_fd(), &mut result as *mut _) }
441 .context("FE: read uncorrected blocks")?;
442
443 Ok(result as u64)
444 }
445
446 pub fn set_tone(&self, value: u32) -> Result<()> {
453 ioctl_write_int_bad!(
455 #[inline]
456 ioctl_call,
457 request_code_none!(b'o', 66)
458 );
459
460 unsafe { ioctl_call(self.as_raw_fd(), value as _) }.context("FE: set tone")?;
461
462 Ok(())
463 }
464
465 pub fn set_voltage(&self, value: u32) -> Result<()> {
484 ioctl_write_int_bad!(
486 #[inline]
487 ioctl_call,
488 request_code_none!(b'o', 67)
489 );
490
491 unsafe { ioctl_call(self.as_raw_fd(), value as _) }.context("FE: set voltage")?;
492
493 Ok(())
494 }
495
496 pub fn diseqc_master_cmd(&self, msg: &[u8]) -> Result<()> {
515 let mut cmd = DiseqcMasterCmd::default();
516 debug_assert!(msg.len() <= cmd.msg.len());
517
518 cmd.msg[0..msg.len()].copy_from_slice(msg);
519 cmd.len = msg.len() as u8;
520
521 ioctl_write_ptr!(ioctl_call, b'o', 63, DiseqcMasterCmd);
523 unsafe { ioctl_call(self.as_raw_fd(), &cmd as *const _) }
524 .context("FE: diseqc master cmd")?;
525
526 Ok(())
527 }
528
529 #[inline]
533 pub fn get_api_version(&self) -> u16 {
534 self.api_version
535 }
536}