libdvb_rs/fe/
sys.rs

1use anyhow::Context;
2use std::fmt::Debug;
3use std::str::FromStr;
4use std::{fmt, mem};
5
6pub use {
7    fe_code_rate::*, fe_delivery_system::*, fe_guard_interval::*, fe_hierarchy::*,
8    fe_interleaving::*, fe_modulation::*, fe_pilot::*, fe_rolloff::*, fe_sec_mini_cmd::*,
9    fe_sec_tone_mode::*, fe_sec_voltage::*, fe_spectral_inversion::*, fe_transmit_mode::*,
10    fe_type::*, DtvProperty::*, DtvStat::*,
11};
12
13use bitflags::bitflags;
14use strum::{Display, EnumString, FromRepr};
15
16bitflags! {
17    /// Frontend capabilities
18    #[repr(C)]
19    pub struct fe_caps : u32 {
20        /// There's something wrong at the frontend, and it can't report its capabilities
21        const FE_IS_STUPID = 0x0;
22        /// Can auto-detect frequency spectral band inversion
23        const FE_CAN_INVERSION_AUTO = 0x1;
24        /// Supports FEC 1/2
25        const FE_CAN_FEC_1_2 = 0x2;
26        /// Supports FEC 2/3
27        const FE_CAN_FEC_2_3 = 0x4;
28        /// Supports FEC 3/4
29        const FE_CAN_FEC_3_4 = 0x8;
30        /// Supports FEC 4/5
31        const FE_CAN_FEC_4_5 = 0x10;
32        /// Supports FEC 5/6
33        const FE_CAN_FEC_5_6 = 0x20;
34        /// Supports FEC 6/7
35        const FE_CAN_FEC_6_7 = 0x40;
36        /// Supports FEC 7/8
37        const FE_CAN_FEC_7_8 = 0x80;
38        /// Supports FEC 8/9
39        const FE_CAN_FEC_8_9 = 0x100;
40        /// Can auto-detect FEC
41        const FE_CAN_FEC_AUTO = 0x200;
42        /// Supports QPSK modulation
43        const FE_CAN_QPSK = 0x400;
44        /// Supports 16-QAM modulation
45        const FE_CAN_QAM_16 = 0x800;
46        /// Supports 32-QAM modulation
47        const FE_CAN_QAM_32 = 0x1000;
48        /// Supports 64-QAM modulation
49        const FE_CAN_QAM_64 = 0x2000;
50        /// Supports 128-QAM modulation
51        const FE_CAN_QAM_128 = 0x4000;
52        /// Supports 256-QAM modulation
53        const FE_CAN_QAM_256 = 0x8000;
54        /// Can auto-detect QAM modulation
55        const FE_CAN_QAM_AUTO = 0x10000;
56        /// Can auto-detect transmission mode
57        const FE_CAN_TRANSMISSION_MODE_AUTO = 0x20000;
58        /// Can auto-detect bandwidth
59        const FE_CAN_BANDWIDTH_AUTO = 0x40000;
60        /// Can auto-detect guard interval
61        const FE_CAN_GUARD_INTERVAL_AUTO = 0x80000;
62        /// Can auto-detect hierarchy
63        const FE_CAN_HIERARCHY_AUTO = 0x100000;
64        /// Supports 8-VSB modulation
65        const FE_CAN_8VSB = 0x200000;
66        /// Supports 16-VSB modulation
67        const FE_CAN_16VSB = 0x400000;
68        /// Unused
69        const FE_HAS_EXTENDED_CAPS = 0x800000;
70        /// Supports multistream filtering
71        const FE_CAN_MULTISTREAM = 0x4000000;
72        /// Supports "turbo FEC" modulation
73        const FE_CAN_TURBO_FEC = 0x8000000;
74        /// Supports "2nd generation" modulation, e. g. DVB-S2, DVB-T2, DVB-C2
75        const FE_CAN_2G_MODULATION = 0x10000000;
76        /// Unused
77        const FE_NEEDS_BENDING = 0x20000000;
78        /// Can recover from a cable unplug automatically
79        const FE_CAN_RECOVER = 0x40000000;
80        /// Can stop spurious TS data output
81        const FE_CAN_MUTE_TS = 0x80000000;
82    }
83}
84
85/// DEPRECATED: Should be kept just due to backward compatibility
86#[repr(u32)]
87#[allow(non_camel_case_types)]
88#[derive(Debug, PartialEq, Eq, FromRepr)]
89pub enum fe_type {
90    FE_QPSK = 0,
91    FE_QAM = 1,
92    FE_OFDM = 2,
93    FE_ATSC = 3,
94}
95
96/// Frontend properties and capabilities
97/// The frequencies are specified in Hz for Terrestrial and Cable systems.
98/// The frequencies are specified in kHz for Satellite systems.
99#[repr(C)]
100#[derive(Debug)]
101pub struct FeInfo {
102    /// Name of the frontend
103    pub name: [std::os::raw::c_char; 128],
104    /// DEPRECATED: frontend delivery system
105    pub fe_type: fe_type,
106    /// Minimal frequency supported by the frontend
107    pub frequency_min: u32,
108    /// Maximal frequency supported by the frontend
109    pub frequency_max: u32,
110    /// All frequencies are multiple of this value
111    pub frequency_stepsize: u32,
112    /// Frequency tolerance
113    pub frequency_tolerance: u32,
114    /// Minimal symbol rate, in bauds (for Cable/Satellite systems)
115    pub symbol_rate_min: u32,
116    /// Maximal symbol rate, in bauds (for Cable/Satellite systems)
117    pub symbol_rate_max: u32,
118    /// Maximal symbol rate tolerance, in ppm (for Cable/Satellite systems)
119    pub symbol_rate_tolerance: u32,
120    /// DEPRECATED
121    pub notifier_delay: u32,
122    /// Capabilities supported by the frontend
123    pub caps: fe_caps,
124}
125
126impl Default for FeInfo {
127    #[inline]
128    fn default() -> Self {
129        unsafe { mem::zeroed::<Self>() }
130    }
131}
132
133impl FeInfo {
134    #[inline]
135    pub fn as_mut_ptr(&mut self) -> *mut FeInfo {
136        self as *mut _
137    }
138}
139
140/// DiSEqC master command
141/// Check out the DiSEqC bus spec available on http://www.eutelsat.org/ for
142/// the possible messages that can be used.
143#[repr(C)]
144#[derive(Debug)]
145pub struct DiseqcMasterCmd {
146    /// DiSEqC message to be sent. It contains a 3 bytes header with:
147    /// framing + address + command, and an optional argument
148    /// of up to 3 bytes of data.
149    pub msg: [u8; 6],
150    /// Length of the DiSEqC message. Valid values are 3 to 6.
151    pub len: u8,
152}
153
154impl Default for DiseqcMasterCmd {
155    #[inline]
156    fn default() -> Self {
157        unsafe { mem::zeroed::<Self>() }
158    }
159}
160
161/// DiSEqC received data
162#[repr(C)]
163#[derive(Debug)]
164pub struct DiseqcSlaveReply {
165    /// DiSEqC message buffer to store a message received via DiSEqC.
166    /// It contains one byte header with: framing and
167    /// an optional argument of up to 3 bytes of data.
168    pub msg: [u8; 4],
169    /// Length of the DiSEqC message. Valid values are 0 to 4,
170    /// where 0 means no message.
171    pub len: u8,
172    /// Return from ioctl after timeout ms with errorcode when
173    /// no message was received.
174    pub timeout: u32,
175}
176
177impl Default for DiseqcSlaveReply {
178    #[inline]
179    fn default() -> Self {
180        unsafe { mem::zeroed::<Self>() }
181    }
182}
183
184/// DC Voltage used to feed the LNBf
185#[repr(u32)]
186#[allow(non_camel_case_types)]
187#[derive(EnumString, Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
188pub enum fe_sec_voltage {
189    /// Output 13V to the LNB. Vertical linear. Right circular.
190    SEC_VOLTAGE_13 = 0,
191    /// Output 18V to the LNB. Horizontal linear. Left circular.
192    SEC_VOLTAGE_18 = 1,
193    /// Don't feed the LNB with a DC voltage
194    SEC_VOLTAGE_OFF = 2,
195}
196
197#[repr(u32)]
198#[allow(non_camel_case_types)]
199#[derive(EnumString, Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
200pub enum fe_sec_tone_mode {
201    /// Sends a 22kHz tone burst to the antenna
202    SEC_TONE_ON = 0,
203    /// Don't send a 22kHz tone to the antenna (except if the FE_DISEQC_* ioctl are called)
204    SEC_TONE_OFF = 1,
205}
206
207/// Type of mini burst to be sent
208#[repr(u32)]
209#[allow(non_camel_case_types)]
210#[derive(Debug, Copy, Clone, PartialEq, Eq, FromRepr)]
211pub enum fe_sec_mini_cmd {
212    /// Sends a mini-DiSEqC 22kHz '0' Tone Burst to select satellite-A
213    SEC_MINI_A = 0,
214    /// Sends a mini-DiSEqC 22kHz '1' Data Burst to select satellite-B
215    SEC_MINI_B = 1,
216}
217
218bitflags! {
219    /// Enumerates the possible frontend status
220    #[repr(C)]
221    pub struct fe_status : u32 {
222        /// The frontend doesn't have any kind of lock. That's the initial frontend status
223        const FE_NONE = 0x00;
224        /// Has found something above the noise level
225        const FE_HAS_SIGNAL = 0x01;
226        /// Has found a signal
227        const FE_HAS_CARRIER = 0x02;
228        /// FEC inner coding (Viterbi, LDPC or other inner code) is stable.
229        const FE_HAS_VITERBI = 0x04;
230        /// Synchronization bytes was found
231        const FE_HAS_SYNC = 0x08;
232        /// Digital TV were locked and everything is working
233        const FE_HAS_LOCK = 0x10;
234        /// Fo lock within the last about 2 seconds
235        const FE_TIMEDOUT = 0x20;
236        /// Frontend was reinitialized, application is recommended
237        /// to reset DiSEqC, tone and parameters
238        const FE_REINIT = 0x40;
239    }
240}
241
242/// Spectral band inversion
243#[repr(u32)]
244#[allow(non_camel_case_types)]
245#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Clone, Copy)]
246pub enum fe_spectral_inversion {
247    #[strum(serialize = "OFF")]
248    INVERSION_OFF = 0,
249    #[strum(serialize = "ON")]
250    INVERSION_ON = 1,
251    #[strum(serialize = "AUTO")]
252    INVERSION_AUTO = 2,
253}
254
255#[repr(u32)]
256#[allow(non_camel_case_types)]
257#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Clone, Copy)]
258#[strum(ascii_case_insensitive)]
259pub enum fe_code_rate {
260    #[strum(serialize = "NONE")]
261    FEC_NONE = 0,
262    #[strum(serialize = "1/2")]
263    FEC_1_2 = 1,
264    #[strum(serialize = "2/3")]
265    FEC_2_3 = 2,
266    #[strum(serialize = "3/4")]
267    FEC_3_4 = 3,
268    #[strum(serialize = "4/5")]
269    FEC_4_5 = 4,
270    #[strum(serialize = "5/6")]
271    FEC_5_6 = 5,
272    #[strum(serialize = "6/7")]
273    FEC_6_7 = 6,
274    #[strum(serialize = "7/8")]
275    FEC_7_8 = 7,
276    #[strum(serialize = "8/9")]
277    FEC_8_9 = 8,
278    #[strum(serialize = "AUTO")]
279    FEC_AUTO = 9,
280    #[strum(serialize = "3/5")]
281    FEC_3_5 = 10,
282    #[strum(serialize = "9/10")]
283    FEC_9_10 = 11,
284    #[strum(serialize = "2/5")]
285    FEC_2_5 = 12,
286    #[strum(serialize = "1/4")]
287    FEC_1_4 = 13,
288    #[strum(serialize = "1/3")]
289    FEC_1_3 = 14,
290}
291
292/// Type of modulation/constellation
293#[repr(u32)]
294#[allow(non_camel_case_types)]
295#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
296pub enum fe_modulation {
297    QPSK = 0,
298    #[strum(serialize = "QAM/16")]
299    QAM_16 = 1,
300    #[strum(serialize = "QAM/32")]
301    QAM_32 = 2,
302    #[strum(serialize = "QAM/64")]
303    QAM_64 = 3,
304    #[strum(serialize = "QAM/128")]
305    QAM_128 = 4,
306    #[strum(serialize = "QAM/256")]
307    QAM_256 = 5,
308    #[strum(serialize = "QAM/AUTO")]
309    QAM_AUTO = 6,
310    #[strum(serialize = "VSB/8")]
311    VSB_8 = 7,
312    #[strum(serialize = "VSB/16")]
313    VSB_16 = 8,
314    #[strum(serialize = "PSK/8")]
315    PSK_8 = 9,
316    #[strum(serialize = "APSK/16")]
317    APSK_16 = 10,
318    #[strum(serialize = "APSK/32")]
319    APSK_32 = 11,
320    #[strum(serialize = "DQPSK")]
321    DQPSK = 12,
322    #[strum(serialize = "QAM/4/NR")]
323    QAM_4_NR = 13,
324    #[strum(serialize = "APSK/64")]
325    APSK_64 = 14,
326    #[strum(serialize = "APSK/128")]
327    APSK_128 = 15,
328    #[strum(serialize = "APSK/256")]
329    APSK_256 = 16,
330}
331
332#[repr(u32)]
333#[allow(non_camel_case_types)]
334#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
335pub enum fe_transmit_mode {
336    #[strum(serialize = "2K")]
337    TRANSMISSION_MODE_2K = 0,
338    #[strum(serialize = "8K")]
339    TRANSMISSION_MODE_8K = 1,
340    #[strum(serialize = "AUTO")]
341    TRANSMISSION_MODE_AUTO = 2,
342    #[strum(serialize = "4K")]
343    TRANSMISSION_MODE_4K = 3,
344    #[strum(serialize = "1K")]
345    TRANSMISSION_MODE_1K = 4,
346    #[strum(serialize = "16K")]
347    TRANSMISSION_MODE_16K = 5,
348    #[strum(serialize = "32K")]
349    TRANSMISSION_MODE_32K = 6,
350    #[strum(serialize = "C1")]
351    TRANSMISSION_MODE_C1 = 7,
352    #[strum(serialize = "C3780")]
353    TRANSMISSION_MODE_C3780 = 8,
354}
355
356#[repr(u32)]
357#[allow(non_camel_case_types)]
358#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
359pub enum fe_guard_interval {
360    #[strum(serialize = "1/32")]
361    GUARD_INTERVAL_1_32 = 0,
362    #[strum(serialize = "1/16")]
363    GUARD_INTERVAL_1_16 = 1,
364    #[strum(serialize = "1/8")]
365    GUARD_INTERVAL_1_8 = 2,
366    #[strum(serialize = "1/4")]
367    GUARD_INTERVAL_1_4 = 3,
368    #[strum(serialize = "AUTO")]
369    GUARD_INTERVAL_AUTO = 4,
370    #[strum(serialize = "1/128")]
371    GUARD_INTERVAL_1_128 = 5,
372    #[strum(serialize = "19/128")]
373    GUARD_INTERVAL_19_128 = 6,
374    #[strum(serialize = "19/256")]
375    GUARD_INTERVAL_19_256 = 7,
376    #[strum(serialize = "PN420")]
377    GUARD_INTERVAL_PN420 = 8,
378    #[strum(serialize = "PN595")]
379    GUARD_INTERVAL_PN595 = 9,
380    #[strum(serialize = "PN945")]
381    GUARD_INTERVAL_PN945 = 10,
382}
383
384#[repr(u32)]
385#[allow(non_camel_case_types)]
386#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
387pub enum fe_hierarchy {
388    #[strum(serialize = "NONE")]
389    HIERARCHY_NONE = 0,
390    #[strum(serialize = "1")]
391    HIERARCHY_1 = 1,
392    #[strum(serialize = "2")]
393    HIERARCHY_2 = 2,
394    #[strum(serialize = "4")]
395    HIERARCHY_4 = 3,
396    #[strum(serialize = "AUTO")]
397    HIERARCHY_AUTO = 4,
398}
399
400#[repr(u32)]
401#[allow(non_camel_case_types)]
402#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
403pub enum fe_interleaving {
404    #[strum(serialize = "NONE")]
405    INTERLEAVING_NONE = 0,
406    #[strum(serialize = "AUTO")]
407    INTERLEAVING_AUTO = 1,
408    #[strum(serialize = "240")]
409    INTERLEAVING_240 = 2,
410    #[strum(serialize = "720")]
411    INTERLEAVING_720 = 3,
412}
413
414#[repr(u32)]
415#[allow(non_camel_case_types)]
416#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
417pub enum fe_pilot {
418    PILOT_ON = 0,
419    PILOT_OFF = 1,
420    PILOT_AUTO = 2,
421}
422
423#[repr(u32)]
424#[allow(non_camel_case_types)]
425#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
426pub enum fe_rolloff {
427    ROLLOFF_35 = 0,
428    ROLLOFF_20 = 1,
429    ROLLOFF_25 = 2,
430    ROLLOFF_AUTO = 3,
431    ROLLOFF_15 = 4,
432    ROLLOFF_10 = 5,
433    ROLLOFF_5 = 6,
434}
435
436#[derive(EnumString, Display, FromRepr, Debug, Copy, Clone)]
437#[repr(u32)]
438#[allow(non_camel_case_types)]
439#[strum(ascii_case_insensitive)]
440pub enum fe_delivery_system {
441    #[strum(to_string = "none")]
442    SYS_UNDEFINED = 0,
443    #[strum(to_string = "dbvc/annex_a")]
444    SYS_DVBC_ANNEX_A = 1,
445    #[strum(to_string = "dvbc/annex_b")]
446    SYS_DVBC_ANNEX_B = 2,
447    #[strum(to_string = "dvbt")]
448    SYS_DVBT = 3,
449    #[strum(to_string = "dss")]
450    SYS_DSS = 4,
451    #[strum(to_string = "dvbs")]
452    SYS_DVBS = 5,
453    #[strum(to_string = "dvbs2")]
454    SYS_DVBS2 = 6,
455    #[strum(to_string = "dvbh")]
456    SYS_DVBH = 7,
457    #[strum(to_string = "isdbt")]
458    SYS_ISDBT = 8,
459    #[strum(to_string = "isdbs")]
460    SYS_ISDBS = 9,
461    #[strum(to_string = "isdbc")]
462    SYS_ISDBC = 10,
463    #[strum(to_string = "atsc")]
464    SYS_ATSC = 11,
465    #[strum(to_string = "atsc-m/h")]
466    SYS_ATSCMH = 12,
467    #[strum(to_string = "dtmb")]
468    SYS_DTMB = 13,
469    #[strum(to_string = "cmmb")]
470    SYS_CMMB = 14,
471    #[strum(to_string = "dab")]
472    SYS_DAB = 15,
473    #[strum(to_string = "dvbt2", serialize = "dvbt22")]
474    SYS_DVBT2 = 16,
475    #[strum(to_string = "dvbs/turbo")]
476    SYS_TURBO = 17,
477    #[strum(to_string = "dvbc/annex_c")]
478    SYS_DVBC_ANNEX_C = 18,
479    #[strum(to_string = "dvbc2")]
480    SYS_DVBC2 = 19,
481}
482
483#[repr(u32)]
484#[allow(non_camel_case_types)]
485#[derive(EnumString, Debug, PartialEq, Eq, FromRepr, Copy, Clone)]
486pub enum fe_lna {
487    LNA_OFF = 0,
488    LNA_ON = 1,
489    LNA_AUTO = 0xFFFFFFFF,
490}
491
492// From here on, structures passed to Linux
493pub trait WrappedSlice<T> {
494    fn slice(&self) -> &[T];
495}
496
497pub trait WrappedResult<T> {
498    fn get(&self) -> anyhow::Result<T>;
499}
500
501pub trait DtvStatType {
502    fn get_decibel(&self) -> Option<i64>;
503    fn get_relative(&self) -> Option<u16>;
504    fn get_counter(&self) -> Option<u64>;
505    fn get_decibel_float(&self) -> Option<f64> {
506        Some((self.get_decibel()? as f64) / 1000.0)
507    }
508    fn get_relative_percentage(&self) -> Option<u8> {
509        Some((((self.get_relative()? as u32) * 100) / 65535) as u8)
510    }
511}
512
513#[repr(C, packed)]
514#[derive(Debug, Copy, Clone)]
515pub struct NoScale {
516    __reserved: [u8; 8],
517}
518
519#[repr(C, packed)]
520#[derive(Debug, Copy, Clone)]
521pub struct ScaleDecibel {
522    pub scale: i64,
523}
524
525#[repr(C, packed)]
526#[derive(Debug, Copy, Clone)]
527pub struct ScaleRelative {
528    pub scale: u16,
529    __reserved: [u8; 6],
530}
531
532#[repr(C, packed)]
533#[derive(Debug, Copy, Clone)]
534pub struct ScaleCounter {
535    pub scale: u64,
536}
537
538/// Used for reading a DTV status property
539#[repr(u8)]
540#[allow(non_camel_case_types)]
541#[derive(Debug, Copy, Clone)]
542pub enum DtvStat {
543    /// That QoS measure is not available. That could indicate
544    /// a temporary or a permanent condition.
545    FE_SCALE_NOT_AVAILABLE(NoScale),
546    /// The scale is measured in 0.001 dB steps, typically used on signal measures.
547    FE_SCALE_DECIBEL(ScaleDecibel),
548    /// The scale is a relative percentual measure,
549    /// ranging from 0 (0%) to 0xffff (100%).
550    FE_SCALE_RELATIVE(ScaleRelative),
551    /// The scale counts the occurrence of an event, like
552    /// bit error, block error, lapsed time.
553    FE_SCALE_COUNTER(ScaleCounter),
554}
555
556impl DtvStatType for DtvStat {
557    fn get_decibel(&self) -> Option<i64> {
558        match self {
559            FE_SCALE_DECIBEL(s) => Some(s.scale),
560            _ => None,
561        }
562    }
563    fn get_relative(&self) -> Option<u16> {
564        match self {
565            FE_SCALE_RELATIVE(s) => Some(s.scale),
566            _ => None,
567        }
568    }
569    fn get_counter(&self) -> Option<u64> {
570        match self {
571            FE_SCALE_COUNTER(s) => Some(s.scale),
572            _ => None,
573        }
574    }
575}
576
577pub const MAX_DTV_STATS: usize = 4;
578
579/// Store Digital TV frontend statistics
580#[repr(C, packed)]
581#[derive(Copy, Clone)]
582pub struct DtvFrontendStats {
583    len: u8,
584    stat: [DtvStat; MAX_DTV_STATS],
585}
586
587impl WrappedSlice<DtvStat> for DtvFrontendStats {
588    fn slice(&self) -> &[DtvStat] {
589        let len = ::std::cmp::min(self.len as usize, self.stat.len());
590        &self.stat[0..len]
591    }
592}
593
594impl Debug for DtvFrontendStats {
595    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
596        f.debug_list().entries(self.slice().into_iter()).finish()
597    }
598}
599
600impl DtvStatType for DtvFrontendStats {
601    fn get_decibel(&self) -> Option<i64> {
602        for stat in self.slice() {
603            if let Some(v) = stat.get_decibel() {
604                return Some(v);
605            }
606        }
607        None
608    }
609    fn get_relative(&self) -> Option<u16> {
610        for stat in self.slice() {
611            if let Some(v) = stat.get_relative() {
612                return Some(v);
613            }
614        }
615        None
616    }
617    fn get_counter(&self) -> Option<u64> {
618        for stat in self.slice() {
619            if let Some(v) = stat.get_counter() {
620                return Some(v);
621            }
622        }
623        None
624    }
625}
626
627#[repr(C, packed)]
628#[derive(Copy, Clone)]
629pub struct DtvPropertyBuffer {
630    data: [u8; 32],
631    len: u32,
632}
633
634impl WrappedSlice<u8> for DtvPropertyBuffer {
635    fn slice(&self) -> &[u8] {
636        let len = ::std::cmp::min(self.len as usize, self.data.len());
637        &self.data[0..len]
638    }
639}
640
641impl Debug for DtvPropertyBuffer {
642    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
643        f.debug_list().entries(self.slice().into_iter()).finish()
644    }
645}
646
647const DATA_SIZE: usize = 56;
648
649#[repr(C, packed)]
650pub struct DtvPropertyRequest<T, const N: usize> {
651    __reserved: [u32; 3],
652    data: T,
653    padding: [u8; N],
654    result: i32, // Unused
655}
656
657impl<T, const N: usize> DtvPropertyRequest<T, N> {
658    #[inline]
659    pub fn new(data: T) -> Self {
660        Self {
661            __reserved: [0; 3],
662            data,
663            padding: [0; N],
664            result: 0,
665        }
666    }
667}
668
669impl<T, const N: usize> Default for DtvPropertyRequest<T, N> {
670    #[inline]
671    fn default() -> Self {
672        unsafe { mem::zeroed::<Self>() }
673    }
674}
675
676pub type DtvPropertyRequestVoid = DtvPropertyRequest<(), DATA_SIZE>;
677
678impl WrappedResult<()> for DtvPropertyRequestVoid {
679    #[inline]
680    fn get(&self) -> anyhow::Result<()> {
681        Ok(())
682    }
683}
684
685impl Debug for DtvPropertyRequestVoid {
686    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
687        f.write_str("()")
688    }
689}
690
691pub type DtvPropertyRequestInt<T> = DtvPropertyRequest<T, { DATA_SIZE - 4 }>;
692
693impl<T: Copy + Debug> WrappedResult<T> for DtvPropertyRequestInt<T> {
694    #[inline]
695    fn get(&self) -> anyhow::Result<T> {
696        Ok(self.data)
697    }
698}
699
700impl<T: Copy + Debug> Debug for DtvPropertyRequestInt<T> {
701    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702        self.get().unwrap().fmt(f)
703    }
704}
705
706pub type DtvPropertyRequestFrontendStats = DtvPropertyRequest<DtvFrontendStats, { DATA_SIZE - 37 }>;
707
708impl WrappedResult<DtvFrontendStats> for DtvPropertyRequestFrontendStats {
709    #[inline]
710    fn get(&self) -> anyhow::Result<DtvFrontendStats> {
711        Ok(self.data)
712    }
713}
714
715impl Debug for DtvPropertyRequestFrontendStats {
716    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
717        self.data.fmt(f)
718    }
719}
720
721pub type DtvPropertyRequestDeliverySystems =
722    DtvPropertyRequest<DtvPropertyBuffer, { DATA_SIZE - 4 - 32 }>;
723
724impl WrappedResult<Vec<fe_delivery_system>> for DtvPropertyRequestDeliverySystems {
725    #[inline]
726    fn get(&self) -> Result<Vec<fe_delivery_system>, anyhow::Error> {
727        self.data
728            .slice()
729            .into_iter()
730            .map(|&x| fe_delivery_system::from_repr(x as u32).context("Invalid delivery system"))
731            .try_collect()
732    }
733}
734
735impl Debug for DtvPropertyRequestDeliverySystems {
736    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
737        f.debug_list().entries(self.get().unwrap().iter()).finish()
738    }
739}
740
741#[repr(C, packed)]
742pub struct DtvPropertyNotImplementedLinux {
743    __reserved: [u8; DATA_SIZE],
744}
745
746impl Debug for DtvPropertyNotImplementedLinux {
747    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
748        f.write_str("Not implemented")
749    }
750}
751
752#[deprecated(
753    note = "Not implemented, please fork libdvb and provide a correct implementation for this property."
754)]
755type DtvPropertyNotImplemented = DtvPropertyNotImplementedLinux;
756#[deprecated]
757type DtvPropertyDeprecated = DtvPropertyNotImplementedLinux;
758
759/// DVBv5 property Commands
760#[repr(u32, C)]
761#[allow(non_camel_case_types)]
762#[allow(deprecated)]
763#[derive(Debug)]
764pub enum DtvProperty {
765    DTV_UNDEFINED(DtvPropertyNotImplementedLinux),
766    DTV_TUNE(DtvPropertyRequestVoid),
767    DTV_CLEAR(DtvPropertyRequestVoid),
768    DTV_FREQUENCY(DtvPropertyRequestInt<u32>),
769    DTV_MODULATION(DtvPropertyRequestInt<fe_modulation>),
770    DTV_BANDWIDTH_HZ(DtvPropertyRequestInt<u32>),
771    DTV_INVERSION(DtvPropertyRequestInt<fe_spectral_inversion>),
772    DTV_DISEQC_MASTER(DtvPropertyNotImplementedLinux),
773    DTV_SYMBOL_RATE(DtvPropertyRequestInt<u32>),
774    DTV_INNER_FEC(DtvPropertyRequestInt<fe_code_rate>),
775    DTV_VOLTAGE(DtvPropertyRequestInt<fe_sec_voltage>),
776    DTV_TONE(DtvPropertyRequestInt<fe_sec_tone_mode>),
777    DTV_PILOT(DtvPropertyRequestInt<fe_pilot>),
778    DTV_ROLLOFF(DtvPropertyRequestInt<fe_rolloff>),
779    DTV_DISEQC_SLAVE_REPLY(DtvPropertyNotImplementedLinux),
780
781    /* Basic enumeration set for querying unlimited capabilities */
782    DTV_FE_CAPABILITY_COUNT(DtvPropertyNotImplementedLinux),
783    DTV_FE_CAPABILITY(DtvPropertyNotImplementedLinux),
784    DTV_DELIVERY_SYSTEM(DtvPropertyRequestInt<fe_delivery_system>),
785
786    /* ISDB-T and ISDB-Tsb */
787    // Please fork
788    DTV_ISDBT_PARTIAL_RECEPTION(DtvPropertyRequestInt<i32>),
789    DTV_ISDBT_SOUND_BROADCASTING(DtvPropertyRequestInt<i32>),
790
791    DTV_ISDBT_SB_SUBCHANNEL_ID(DtvPropertyRequestInt<i32>),
792    DTV_ISDBT_SB_SEGMENT_IDX(DtvPropertyRequestInt<i32>),
793    DTV_ISDBT_SB_SEGMENT_COUNT(DtvPropertyRequestInt<u32>),
794
795    DTV_ISDBT_LAYERA_FEC(DtvPropertyRequestInt<fe_code_rate>),
796    DTV_ISDBT_LAYERA_MODULATION(DtvPropertyRequestInt<fe_modulation>),
797    DTV_ISDBT_LAYERA_SEGMENT_COUNT(DtvPropertyRequestInt<i32>),
798    DTV_ISDBT_LAYERA_TIME_INTERLEAVING(DtvPropertyRequestInt<i32>),
799
800    DTV_ISDBT_LAYERB_FEC(DtvPropertyRequestInt<fe_code_rate>),
801    DTV_ISDBT_LAYERB_MODULATION(DtvPropertyRequestInt<fe_modulation>),
802    DTV_ISDBT_LAYERB_SEGMENT_COUNT(DtvPropertyRequestInt<i32>),
803    DTV_ISDBT_LAYERB_TIME_INTERLEAVING(DtvPropertyRequestInt<i32>),
804
805    DTV_ISDBT_LAYERC_FEC(DtvPropertyRequestInt<fe_code_rate>),
806    DTV_ISDBT_LAYERC_MODULATION(DtvPropertyRequestInt<fe_modulation>),
807    DTV_ISDBT_LAYERC_SEGMENT_COUNT(DtvPropertyRequestInt<i32>),
808    DTV_ISDBT_LAYERC_TIME_INTERLEAVING(DtvPropertyRequestInt<i32>),
809
810    DTV_API_VERSION(DtvPropertyRequestInt<u32>),
811
812    /* DVB-T/T2 */
813    DTV_CODE_RATE_HP(DtvPropertyRequestInt<fe_code_rate>),
814    DTV_CODE_RATE_LP(DtvPropertyRequestInt<fe_code_rate>),
815    DTV_GUARD_INTERVAL(DtvPropertyRequestInt<fe_guard_interval>),
816    DTV_TRANSMISSION_MODE(DtvPropertyRequestInt<fe_transmit_mode>),
817    DTV_HIERARCHY(DtvPropertyRequestInt<fe_hierarchy>),
818
819    DTV_ISDBT_LAYER_ENABLED(DtvPropertyRequestInt<u32>),
820
821    DTV_STREAM_ID(DtvPropertyRequestInt<u32>),
822    #[deprecated(note = "Obsolete, replaced with DTV_STREAM_ID.")]
823    DTV_DVBT2_PLP_ID_LEGACY(DtvPropertyDeprecated),
824
825    DTV_ENUM_DELSYS(DtvPropertyRequestDeliverySystems),
826
827    /* ATSC-MH */
828    DTV_ATSCMH_FIC_VER(DtvPropertyRequestInt<u32>),
829    DTV_ATSCMH_PARADE_ID(DtvPropertyRequestInt<u32>),
830    DTV_ATSCMH_NOG(DtvPropertyRequestInt<u32>),
831    DTV_ATSCMH_TNOG(DtvPropertyRequestInt<u32>),
832    DTV_ATSCMH_SGN(DtvPropertyRequestInt<u32>),
833    DTV_ATSCMH_PRC(DtvPropertyRequestInt<u32>),
834    DTV_ATSCMH_RS_FRAME_MODE(DtvPropertyNotImplemented),
835    DTV_ATSCMH_RS_FRAME_ENSEMBLE(DtvPropertyNotImplemented),
836    DTV_ATSCMH_RS_CODE_MODE_PRI(DtvPropertyNotImplemented),
837    DTV_ATSCMH_RS_CODE_MODE_SEC(DtvPropertyNotImplemented),
838    DTV_ATSCMH_SCCC_BLOCK_MODE(DtvPropertyNotImplemented),
839    DTV_ATSCMH_SCCC_CODE_MODE_A(DtvPropertyNotImplemented),
840    DTV_ATSCMH_SCCC_CODE_MODE_B(DtvPropertyNotImplemented),
841    DTV_ATSCMH_SCCC_CODE_MODE_C(DtvPropertyNotImplemented),
842    DTV_ATSCMH_SCCC_CODE_MODE_D(DtvPropertyNotImplemented),
843
844    DTV_INTERLEAVING(DtvPropertyRequestInt<fe_interleaving>),
845    DTV_LNA(DtvPropertyRequestInt<fe_lna>),
846
847    /* Quality parameters */
848    DTV_STAT_SIGNAL_STRENGTH(DtvPropertyRequestFrontendStats),
849    DTV_STAT_CNR(DtvPropertyRequestFrontendStats),
850    DTV_STAT_PRE_ERROR_BIT_COUNT(DtvPropertyRequestFrontendStats),
851    DTV_STAT_PRE_TOTAL_BIT_COUNT(DtvPropertyRequestFrontendStats),
852    DTV_STAT_POST_ERROR_BIT_COUNT(DtvPropertyRequestFrontendStats),
853    DTV_STAT_POST_TOTAL_BIT_COUNT(DtvPropertyRequestFrontendStats),
854    DTV_STAT_ERROR_BLOCK_COUNT(DtvPropertyRequestFrontendStats),
855    DTV_STAT_TOTAL_BLOCK_COUNT(DtvPropertyRequestFrontendStats),
856
857    /* Physical layer scrambling */
858    DTV_SCRAMBLING_SEQUENCE_INDEX(DtvPropertyRequestInt<u32>),
859}
860
861#[macro_export]
862macro_rules! dtv_property {
863    ( $property:ident ) => {
864        $property($crate::fe::sys::DtvPropertyRequest::new(()))
865    };
866    ( $property:ident($data:expr) ) => {
867        $property($crate::fe::sys::DtvPropertyRequest::new($data))
868    };
869    ( $property:ident, $data:expr ) => {
870        $property($crate::fe::sys::DtvPropertyRequest::new($data))
871    };
872}
873
874#[macro_export]
875macro_rules! dtv_property_parse {
876    ( $property:ident($data:expr)) => {
877        $property($crate::fe::sys::DtvPropertyRequest::new($data.parse().with_context(||format!("Invalid {}: {}", stringify!($property), $data))?))
878    };
879}
880
881impl FromStr for DtvProperty {
882    type Err = anyhow::Error;
883    fn from_str(s: &str) -> Result<Self, Self::Err> {
884        let (k, v) = s.split_once('=').context("Invalid line")?;
885        let v = v.trim();
886        Ok(match k.trim() {
887            "FREQUENCY" => dtv_property_parse!(DTV_FREQUENCY(v)),
888            "MODULATION" => dtv_property_parse!(DTV_MODULATION(v)),
889            "BANDWIDTH_HZ" => dtv_property_parse!(DTV_BANDWIDTH_HZ(v)),
890            "INVERSION" => dtv_property_parse!(DTV_INVERSION(v)),
891            "SYMBOL_RATE" => dtv_property_parse!(DTV_SYMBOL_RATE(v)),
892            "INNER_FEC" => dtv_property_parse!(DTV_INNER_FEC(v)),
893            "VOLTAGE" => dtv_property_parse!(DTV_VOLTAGE(v)),
894            "TONE" => dtv_property_parse!(DTV_TONE(v)),
895            "PILOT" => dtv_property_parse!(DTV_PILOT(v)),
896            "ROLLOFF" => dtv_property_parse!(DTV_ROLLOFF(v)),
897
898            /* Basic enumeration set for querying unlimited capabilities */
899            "DELIVERY_SYSTEM" => dtv_property_parse!(DTV_DELIVERY_SYSTEM(v)),
900
901            /* ISDB-T and ISDB-Tsb */
902            "ISDBT_PARTIAL_RECEPTION" => dtv_property_parse!(DTV_ISDBT_PARTIAL_RECEPTION(v)),
903            "ISDBT_SOUND_BROADCASTING" => dtv_property_parse!(DTV_ISDBT_SOUND_BROADCASTING(v)),
904
905            "ISDBT_SB_SUBCHANNEL_ID" => dtv_property_parse!(DTV_ISDBT_SB_SUBCHANNEL_ID(v)),
906            "ISDBT_SB_SEGMENT_IDX" => dtv_property_parse!(DTV_ISDBT_SB_SEGMENT_IDX(v)),
907            "ISDBT_SB_SEGMENT_COUNT" => dtv_property_parse!(DTV_ISDBT_SB_SEGMENT_COUNT(v)),
908
909            "ISDBT_LAYERA_FEC" => dtv_property_parse!(DTV_ISDBT_LAYERA_FEC(v)),
910            "ISDBT_LAYERA_MODULATION" => dtv_property_parse!(DTV_ISDBT_LAYERA_MODULATION(v)),
911            "ISDBT_LAYERA_SEGMENT_COUNT" => dtv_property_parse!(DTV_ISDBT_LAYERA_SEGMENT_COUNT(v)),
912            "ISDBT_LAYERA_TIME_INTERLEAVING" => {
913                dtv_property_parse!(DTV_ISDBT_LAYERA_TIME_INTERLEAVING(v))
914            }
915
916            "ISDBT_LAYERB_FEC" => dtv_property_parse!(DTV_ISDBT_LAYERB_FEC(v)),
917            "ISDBT_LAYERB_MODULATION" => dtv_property_parse!(DTV_ISDBT_LAYERB_MODULATION(v)),
918            "ISDBT_LAYERB_SEGMENT_COUNT" => dtv_property_parse!(DTV_ISDBT_LAYERB_SEGMENT_COUNT(v)),
919            "ISDBT_LAYERB_TIME_INTERLEAVING" => {
920                dtv_property_parse!(DTV_ISDBT_LAYERB_TIME_INTERLEAVING(v))
921            }
922
923            "ISDBT_LAYERC_FEC" => dtv_property_parse!(DTV_ISDBT_LAYERC_FEC(v)),
924            "ISDBT_LAYERC_MODULATION" => dtv_property_parse!(DTV_ISDBT_LAYERC_MODULATION(v)),
925            "ISDBT_LAYERC_SEGMENT_COUNT" => dtv_property_parse!(DTV_ISDBT_LAYERC_SEGMENT_COUNT(v)),
926            "ISDBT_LAYERC_TIME_INTERLEAVING" => {
927                dtv_property_parse!(DTV_ISDBT_LAYERC_TIME_INTERLEAVING(v))
928            }
929
930            /* DVB-T/T2 */
931            "CODE_RATE_HP" => dtv_property_parse!(DTV_CODE_RATE_HP(v)),
932            "CODE_RATE_LP" => dtv_property_parse!(DTV_CODE_RATE_LP(v)),
933            "GUARD_INTERVAL" => dtv_property_parse!(DTV_GUARD_INTERVAL(v)),
934            "TRANSMISSION_MODE" => dtv_property_parse!(DTV_TRANSMISSION_MODE(v)),
935            "HIERARCHY" => dtv_property_parse!(DTV_HIERARCHY(v)),
936
937            "ISDBT_LAYER_ENABLED" => dtv_property_parse!(DTV_ISDBT_LAYER_ENABLED(v)),
938
939            "STREAM_ID" => dtv_property_parse!(DTV_STREAM_ID(v)),
940
941            /* ATSC-MH */
942            "ATSCMH_FIC_VER" => dtv_property_parse!(DTV_ATSCMH_FIC_VER(v)),
943            "ATSCMH_PARADE_ID" => dtv_property_parse!(DTV_ATSCMH_PARADE_ID(v)),
944            "ATSCMH_NOG" => dtv_property_parse!(DTV_ATSCMH_NOG(v)),
945            "ATSCMH_TNOG" => dtv_property_parse!(DTV_ATSCMH_TNOG(v)),
946            "ATSCMH_SGN" => dtv_property_parse!(DTV_ATSCMH_SGN(v)),
947            "ATSCMH_PRC" => dtv_property_parse!(DTV_ATSCMH_PRC(v)),
948
949            "INTERLEAVING" => dtv_property_parse!(DTV_INTERLEAVING(v)),
950            "LNA" => dtv_property_parse!(DTV_LNA(v)),
951            &_ => bail!("Invalid key {}", k),
952        })
953    }
954}
955
956/// num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl
957pub const DTV_IOCTL_MAX_MSGS: usize = 64;
958
959#[repr(C)]
960#[derive(Debug)]
961pub struct FeParameters {
962    /// (absolute) frequency in Hz for DVB-C/DVB-T/ATSC
963    /// intermediate frequency in kHz for DVB-S
964    pub frequency: u32,
965    pub inversion: u32,
966    /// unimplemented frontend parameters data
967    __reserved_1: [u8; 28],
968}
969
970pub const FE_MAX_EVENT: usize = 8;
971
972#[repr(C)]
973#[derive(Debug)]
974pub struct FeEvent {
975    pub status: u32,
976    pub parameters: FeParameters,
977}
978
979impl Default for FeEvent {
980    #[inline]
981    fn default() -> Self {
982        unsafe { mem::zeroed::<Self>() }
983    }
984}
985
986impl FeEvent {
987    #[inline]
988    pub fn as_mut_ptr(&mut self) -> *mut FeEvent {
989        self as *mut _
990    }
991}