gpiocdev_uapi/
v2.rs

1// SPDX-FileCopyrightText: 2021 Kent Gibson <warthog618@gmail.com>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5use bitflags::bitflags;
6use std::fmt;
7use std::fs::File;
8use std::os::unix::prelude::{AsRawFd, FromRawFd};
9
10// common to ABI v1 and v2.
11pub use super::common::*;
12
13#[repr(u8)]
14enum Ioctl {
15    GetLineInfo = 5,
16    WatchLineInfo = 6,
17    GetLine = 7,
18    SetLineConfig = 0xD,
19    GetLineValues = 0xE,
20    SetLineValues = 0xF,
21}
22
23bitflags! {
24    /// Flags indicating the configuration of a line.
25    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
26    pub struct LineFlags: u64 {
27        /// The line is in use and is not available for request.
28        const USED = 1;
29
30        /// The line active state corresponds to a physical low.
31        const ACTIVE_LOW = 2;
32
33        /// The line is an input.
34        const INPUT = 4;
35
36        /// The line is an output.
37        const OUTPUT = 8;
38
39        /// The line detects rising (*inactive* to *active*) edges.
40        const EDGE_RISING = 16;
41
42        /// The line detects falling (*active* to *inactive*) edges.
43        const EDGE_FALLING = 32;
44
45        /// The line is an open drain output.
46        const OPEN_DRAIN = 64;
47
48        /// The line is an open source output.
49        const OPEN_SOURCE = 128;
50
51        /// The line has pull-up bias enabled.
52        const BIAS_PULL_UP = 256;
53
54        /// The line has pull-down bias enabled.
55        const BIAS_PULL_DOWN = 512;
56
57        /// The line has bias disabled.
58        const BIAS_DISABLED = 1024;
59
60        /// The line events contain **CLOCK_REALTIME** timestamps.
61        const EVENT_CLOCK_REALTIME = 2048;
62
63        /// The line events contain **HTE** timestamps.
64        const EVENT_CLOCK_HTE = 4096;
65    }
66}
67
68/// Values of GPIO lines.
69///
70/// Bits in the bitmaps correspond to the index into [`LineRequest.offsets`].
71/// The first requested line, `offsets[0]`, is bit 0.
72///
73/// [`LineRequest.offsets`]: struct@LineRequest
74#[repr(C)]
75#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
76pub struct LineValues {
77    /// The value of the lines, set to 1 for *active* and 0 for *inactive*.
78    pub bits: u64,
79
80    /// The lines in a request to access, set to 1 to access and 0 to ignore.
81    pub mask: u64,
82}
83
84impl LineValues {
85    /// Create values from a slice.
86    ///
87    /// The values are in the same order as [`LineRequest.offsets`].
88    ///
89    /// [`LineRequest.offsets`]: struct@LineRequest
90    pub fn from_slice(s: &[bool]) -> Self {
91        let mut lv: LineValues = Default::default();
92        for (idx, val) in s.iter().enumerate() {
93            lv.set(idx, *val);
94        }
95        lv
96    }
97
98    /// Copy values from an iterable list - in order of requested offsets.
99    pub fn copy_from_slice(&mut self, s: &[bool]) {
100        let extent = std::cmp::min(64usize, s.len());
101        for (i, v) in s.iter().enumerate().take(extent) {
102            self.set(i, *v);
103        }
104    }
105
106    /// Return the value of a line.
107    ///
108    /// Note that the [`LineValues`] need to be populated via a call to [`get_line_values`]
109    /// to get values from the underlying hardware.
110    ///
111    /// Fails if the line of interest is not set in the mask.
112    ///
113    /// * `idx` - The index into the [`LineRequest.offsets`] for the line of interest.
114    ///
115    /// [`LineRequest.offsets`]: struct@LineRequest
116    #[inline]
117    pub fn get(&self, idx: usize) -> Option<bool> {
118        debug_assert!(idx < 64);
119        let mask = 0x01 << idx;
120        if self.mask & mask == 0 {
121            return None;
122        }
123        Some(self.bits & mask != 0)
124    }
125
126    /// Set the value of a line.
127    ///
128    /// Note that the values are not applied to hardware until passed to [`set_line_values`].
129    ///
130    /// * `idx` - The index into the [`LineRequest.offsets`] for the line of interest.
131    /// * `active` - The logical state of the line to be set.
132    ///
133    /// [`LineRequest.offsets`]: struct@LineRequest
134    #[inline]
135    pub fn set(&mut self, idx: usize, active: bool) {
136        debug_assert!(idx < 64);
137        let mask = 0x01 << idx;
138        self.mask |= mask;
139        if active {
140            self.bits |= mask;
141        } else {
142            self.bits &= !mask;
143        }
144    }
145
146    /// Clear the mask bit for a line.
147    ///
148    /// The line will be ignored in subsequent calls to [`get_line_values`] and
149    /// [`set_line_values`].
150    ///
151    /// * `idx` - The index into the [`LineRequest.offsets`] for the line of interest.
152    ///
153    /// [`LineRequest.offsets`]: struct@LineRequest
154    #[inline]
155    pub fn unset_mask(&mut self, idx: usize) {
156        debug_assert!(idx < 64);
157        let mask = 0x01 << idx;
158        self.mask &= !mask;
159    }
160}
161
162/// Read values of requested lines.
163///
164/// * `lf` - The request file returned by [`get_line`].
165/// * `lv` - The line values to be populated.
166#[inline]
167pub fn get_line_values(lf: &File, lv: &mut LineValues) -> Result<()> {
168    // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
169    match unsafe { libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::GetLineValues, LineValues), lv) } {
170        0 => Ok(()),
171        _ => Err(Error::from_errno()),
172    }
173}
174
175/// Set values of requested output lines.
176///
177/// Note that requesting a set on an input line is an error.
178///
179/// * `lf` - The request file returned by [`get_line`].
180/// * `lv` - The line values to be set.
181#[inline]
182pub fn set_line_values(lf: &File, lv: &LineValues) -> Result<()> {
183    // SAFETY: lv is not modified.
184    match unsafe { libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::SetLineValues, LineValues), lv) } {
185        0 => Ok(()),
186        _ => Err(Error::from_errno()),
187    }
188}
189
190/// An identifier for which field of the [`LineAttributeValueUnion`] is in use.
191#[repr(u32)]
192#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
193pub enum LineAttributeKind {
194    /// The attribute is *inactive* - no fields are in use.
195    #[default]
196    Unused = 0,
197
198    /// The flags field is in use.
199    Flags = 1,
200
201    /// The values field is in use.
202    Values = 2,
203
204    /// The debounce_period_us field is in use.
205    Debounce = 3,
206}
207
208impl TryFrom<u32> for LineAttributeKind {
209    type Error = ();
210
211    fn try_from(v: u32) -> std::result::Result<Self, Self::Error> {
212        use LineAttributeKind::*;
213        Ok(match v {
214            0 => Unused,
215            1 => Flags,
216            2 => Values,
217            3 => Debounce,
218            _ => return Err(()),
219        })
220    }
221}
222
223/// A configurable attribute of a line.
224#[repr(C)]
225#[derive(Clone, Copy, Default)]
226pub struct LineAttribute {
227    /// The type of attribute stored in `value` (LineAttributeKind).
228    pub kind: u32,
229
230    /// Reserved for future use and must be zero filled.
231    #[doc(hidden)]
232    pub padding: Padding<1>,
233
234    /// The attribute value.
235    pub value: LineAttributeValueUnion,
236}
237
238impl LineAttribute {
239    /// Set the attribute as debounce period.
240    pub fn set_debounce_period_us(&mut self, debounce_period_us: u32) {
241        self.kind = LineAttributeKind::Debounce as u32;
242        self.value.debounce_period_us = debounce_period_us;
243    }
244
245    /// Set the attribute as flags.
246    pub fn set_flags(&mut self, flags: LineFlags) {
247        self.kind = LineAttributeKind::Flags as u32;
248        self.value.flags = flags;
249    }
250
251    /// Set the attribute as output values.
252    pub fn set_values(&mut self, values: u64) {
253        self.kind = LineAttributeKind::Values as u32;
254        self.value.values = values;
255    }
256
257    /// Get the contained value.
258    ///
259    /// Converts the unsafe kind/union into a safe enum.
260    pub fn to_value(&self) -> Option<LineAttributeValue> {
261        // SAFETY: checks kind before accessing union
262        unsafe {
263            Some(match self.kind {
264                1 => LineAttributeValue::Flags(self.value.flags),
265                2 => LineAttributeValue::Values(self.value.values),
266                3 => LineAttributeValue::DebouncePeriod(self.value.debounce_period_us),
267                _ => return None,
268            })
269        }
270    }
271}
272
273impl fmt::Debug for LineAttribute {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        // SAFETY: checks kind before accessing union
276        unsafe {
277            match self.kind {
278                0 => write!(f, "unused"),
279                1 => write!(f, "flags: {:?}", self.value.flags),
280                2 => {
281                    write!(f, "values: {:08x}", self.value.values)
282                }
283                3 => {
284                    write!(f, "debounce_period_us: {}", self.value.debounce_period_us)
285                }
286                _ => write!(f, "unknown attr kind {}", self.kind),
287            }
288        }
289    }
290}
291
292impl PartialEq for LineAttribute {
293    fn eq(&self, other: &Self) -> bool {
294        if self.kind != other.kind {
295            return false;
296        }
297        // SAFETY: checks kind before accessing union
298        unsafe {
299            match self.kind {
300                0 => true,
301                1 => self.value.flags == other.value.flags,
302                2 => self.value.values == other.value.values,
303                3 => self.value.debounce_period_us == other.value.debounce_period_us,
304                _ => false,
305            }
306        }
307    }
308}
309impl Eq for LineAttribute {}
310
311/// The value of a particular line attribute.
312#[repr(C)]
313#[derive(Clone, Copy)]
314pub union LineAttributeValueUnion {
315    /// The line configuration flags.
316    pub flags: LineFlags,
317
318    /// The values to which the lines will be set, with each bit number
319    ///  corresponding to the index into [`LineRequest.offsets`].
320    ///
321    /// [`LineRequest.offsets`]: struct@LineRequest
322    pub values: u64,
323
324    /// The debounce period, in microseconds.
325    pub debounce_period_us: u32,
326}
327
328impl Default for LineAttributeValueUnion {
329    fn default() -> Self {
330        LineAttributeValueUnion {
331            flags: Default::default(),
332        }
333    }
334}
335
336/// The attribute value contained within a [`LineAttribute`].
337#[derive(Clone, Copy, Debug, Eq, PartialEq)]
338pub enum LineAttributeValue {
339    /// The debounce period in microseconds.
340    DebouncePeriod(u32),
341
342    /// The configuration flags.
343    Flags(LineFlags),
344
345    /// The line values.
346    Values(u64),
347}
348
349/// A configuration attribute associated with one or more of the requested lines.
350#[repr(C)]
351#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
352pub struct LineConfigAttribute {
353    /// The configurable attribute.
354    pub attr: LineAttribute,
355
356    /// The lines to which the attribute applies, with each bit number corresponding
357    /// to the index into [`LineRequest.offsets`].
358    ///
359    /// [`LineRequest.offsets`]: struct@LineRequest
360    pub mask: u64,
361}
362
363/// The set of additional configuration attributes for a line request.
364///
365/// [`LineConfig.num_attrs`] specifies the number of entries in use.
366///
367/// Any attribute should only be associated with a particular line once.
368/// If an attribute is associated with a line multiple times then the
369/// first occurrence (i.e. lowest index) has precedence.
370///
371/// [`LineConfig.num_attrs`]: struct@LineConfig
372#[repr(C)]
373#[derive(Clone, Debug, Default)]
374pub struct LineConfigAttributes(pub [LineConfigAttribute; NUM_ATTRS_MAX]);
375
376/// Configuration for a set of requested lines.
377#[repr(C)]
378#[derive(Clone, Debug, Default)]
379pub struct LineConfig {
380    /// Flags for the GPIO lines.  This is the default for all requested lines but
381    /// may be overridden for particular lines using `attrs`.
382    pub flags: LineFlags,
383
384    /// The number of attributes active in `attrs`.
385    pub num_attrs: u32,
386
387    /// Reserved for future use and must be zero filled.
388    #[doc(hidden)]
389    pub padding: Padding<5>,
390
391    /// The configuration attributes associated with the requested lines.
392    ///
393    /// The number of active attributes in the array is specified by `num_attrs`.
394    pub attrs: LineConfigAttributes,
395}
396
397impl LineConfig {
398    /// The nth attribute in the attrs
399    #[inline]
400    pub fn attr(&self, idx: usize) -> &LineConfigAttribute {
401        &self.attrs.0[idx]
402    }
403
404    /// The nth attribute in the attrs
405    #[inline]
406    pub fn attr_mut(&mut self, idx: usize) -> &mut LineConfigAttribute {
407        &mut self.attrs.0[idx]
408    }
409
410    /// Add a debounce attribute to the config.
411    pub fn add_debounce(&mut self, period: u32, mask: u64) {
412        let lca = &mut self.attrs.0[self.num_attrs as usize];
413        lca.mask = mask;
414        lca.attr.set_debounce_period_us(period);
415        self.num_attrs += 1;
416    }
417
418    /// Add a flags attribute to the config.
419    pub fn add_flags(&mut self, lf: LineFlags, mask: u64) {
420        let lca = &mut self.attrs.0[self.num_attrs as usize];
421        lca.mask = mask;
422        lca.attr.set_flags(lf);
423        self.num_attrs += 1;
424    }
425
426    /// Add a line values attribute to the config.
427    pub fn add_values(&mut self, values: &LineValues) {
428        let lca = &mut self.attrs.0[self.num_attrs as usize];
429        lca.mask = values.mask;
430        lca.attr.set_values(values.bits);
431        self.num_attrs += 1;
432    }
433}
434
435/// Update the configuration of an existing line request.
436///
437/// * `lf` - The request file returned by [`get_line`].
438/// * `lc` - The configuration to be applied.
439#[inline]
440pub fn set_line_config(lf: &File, lc: LineConfig) -> Result<()> {
441    // SAFETY: lc is consumed.
442    unsafe {
443        match libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::SetLineConfig, LineConfig), &lc) {
444            0 => Ok(()),
445            _ => Err(Error::from_errno()),
446        }
447    }
448}
449
450/// Information about a request for GPIO lines.
451#[repr(C)]
452#[derive(Clone, Debug, Default)]
453pub struct LineRequest {
454    /// An array of requested lines, identified by offset on the associated GPIO chip.
455    pub offsets: Offsets,
456
457    /// The requested consumer label for the selected GPIO lines such as
458    /// "*my-bitbanged-relay*".
459    pub consumer: Name,
460
461    /// The requested configuration for the lines.
462    pub config: LineConfig,
463
464    /// The number of lines requested in this request.
465    /// i.e. the number of valid elements in `offsets`.
466    ///
467    /// Set to 1 to request a single line.
468    pub num_lines: u32,
469
470    /// A suggested minimum number of line events that the kernel should buffer.
471    ///
472    /// This is only relevant if edge detection is enabled in the configuration.
473    ///
474    /// Note that this is only a suggested value and the kernel may allocate a
475    /// larger buffer or cap the size of the buffer.
476    /// If this field is zero then the buffer size defaults to a minimum of `num_lines*16`.
477    pub event_buffer_size: u32,
478
479    /// Reserved for future use and must be zero filled.
480    #[doc(hidden)]
481    pub padding: Padding<5>,
482
483    /// This field is only present for the underlying ioctl call and is only used internally.
484    #[doc(hidden)]
485    pub fd: i32,
486}
487
488/// Request a line or set of lines for exclusive access.
489///
490/// * `cf` - The open gpiochip device file.
491/// * `lr` - The line request.
492#[inline]
493pub fn get_line(cf: &File, mut lr: LineRequest) -> Result<File> {
494    // SAFETY: lr is consumed and the returned file is drawn from the returned fd.
495    unsafe {
496        match libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::GetLine, LineRequest), &mut lr) {
497            0 => Ok(File::from_raw_fd(lr.fd)),
498            _ => Err(Error::from_errno()),
499        }
500    }
501}
502
503/// The set of potential configuration attributes for a line.
504///
505/// [`LineInfo.num_attrs`] specifies the number of entries in use.
506///
507/// [`LineInfo.num_attrs`]: struct@LineInfo
508#[repr(C)]
509#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
510pub struct LineAttributes(pub [LineAttribute; NUM_ATTRS_MAX]);
511
512/// The capacity of [`LineAttributes`] and [`LineConfigAttributes`] arrays.
513pub const NUM_ATTRS_MAX: usize = 10;
514
515/// Information about a certain GPIO line.
516#[repr(C)]
517#[derive(Clone, Debug, Default, Eq, PartialEq)]
518pub struct LineInfo {
519    /// The name of this GPIO line, such as the output pin of the line on the chip,
520    /// a rail or a pin header name on a board, as specified by the GPIO chip.
521    ///
522    /// May be empty.
523    pub name: Name,
524
525    /// A functional name for the consumer of this GPIO line as set by whatever is using it.
526    ///
527    /// Will be empty if there is no current user.
528    /// May also be empty if the consumer requests doesn't set this up.
529    pub consumer: Name,
530
531    /// The local offset on this GPIO chip.
532    pub offset: Offset,
533
534    /// The number of attributes active in `attrs`.
535    pub num_attrs: u32,
536
537    /// The configuration flags for this GPIO line.
538    pub flags: LineFlags,
539
540    /// Additional configuration attributes associated with the line.
541    ///
542    /// The number of active attributes in the array is specified by `num_attrs`.
543    pub attrs: LineAttributes,
544
545    /// Reserved for future use.
546    #[doc(hidden)]
547    pub padding: Padding<4>,
548}
549
550impl LineInfo {
551    /// The nth attribute in the attrs
552    #[inline]
553    pub fn attr(&self, idx: usize) -> &LineAttribute {
554        &self.attrs.0[idx]
555    }
556    /// The nth attribute in the attrs
557    #[inline]
558    pub fn attr_mut(&mut self, idx: usize) -> &mut LineAttribute {
559        &mut self.attrs.0[idx]
560    }
561}
562
563/// Get the publicly available information for a line.
564///
565/// This does not include the line value.
566/// The line must be requested to access the value.
567///
568/// * `cf` - The open gpiochip device file.
569/// * `offset` - The offset of the line.
570#[inline]
571pub fn get_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
572    let mut li = LineInfo {
573        offset,
574        ..Default::default()
575    };
576    // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
577    match unsafe { libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::GetLineInfo, LineInfo), &mut li) } {
578        0 => Ok(li),
579        _ => Err(Error::from_errno()),
580    }
581}
582
583/// Add a watch on changes to the [`LineInfo`] for a line.
584///
585/// Returns the current state of that information.
586///
587/// This does not include the line value.
588/// The line must be requested to access the value.
589///
590/// * `cf` - The open gpiochip device file.
591/// * `offset` - The offset of the line to watch.
592#[inline]
593pub fn watch_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
594    let mut li = LineInfo {
595        offset,
596        ..Default::default()
597    };
598    // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
599    match unsafe {
600        libc::ioctl(
601            cf.as_raw_fd(),
602            iorw!(Ioctl::WatchLineInfo, LineInfo),
603            &mut li,
604        )
605    } {
606        0 => Ok(li),
607        _ => Err(Error::from_errno()),
608    }
609}
610
611/// An event indicating a change to the info for a line.
612#[repr(C)]
613#[derive(Clone, Debug, Eq, PartialEq)]
614pub struct LineInfoChangeEvent {
615    /// The new line info.
616    pub info: LineInfo,
617
618    /// The best estimate of time of event occurrence, in nanoseconds.
619    pub timestamp_ns: u64,
620
621    /// The trigger for the change (LineInfoChangeKind)
622    pub kind: u32,
623
624    /// Reserved for future use.
625    #[doc(hidden)]
626    pub padding: Padding<5>,
627}
628
629impl LineInfoChangeEvent {
630    /// Read an info change event from a buffer.
631    ///
632    /// The buffer is assumed to have been populated by a read of the chip File,
633    /// so the content is initialised.
634    pub fn from_slice(d: &[u64]) -> Result<&LineInfoChangeEvent> {
635        debug_assert!(std::mem::size_of::<LineInfoChangeEvent>() % 8 == 0);
636        let len = d.len() * 8;
637        if len < std::mem::size_of::<LineInfoChangeEvent>() {
638            return Err(Error::from(UnderReadError::new(
639                "LineInfoChangeEvent",
640                std::mem::size_of::<LineInfoChangeEvent>(),
641                len,
642            )));
643        }
644        // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
645        let ice = unsafe { &*(d as *const [u64] as *const LineInfoChangeEvent) };
646        Ok(ice)
647    }
648
649    /// The number of u64 words required to store a LineInfoChangeEvent.
650    pub fn u64_size() -> usize {
651        std::mem::size_of::<LineInfoChangeEvent>() / 8
652    }
653}
654
655/// Information about an edge event on a requested line.
656#[repr(C)]
657#[derive(Clone, Debug, Eq, PartialEq)]
658pub struct LineEdgeEvent {
659    /// The best estimate of time of event occurrence, in nanoseconds.
660    ///
661    /// By default the timestamp is read from **CLOCK_MONOTONIC** and is
662    /// intended to allow the accurate measurement of the time between events.
663    /// It does not provide the wall-clock time.
664    ///
665    /// If the [`LineFlags::EVENT_CLOCK_REALTIME`] flag is set then the
666    /// timestamp is read from **CLOCK_REALTIME**.
667    pub timestamp_ns: u64,
668
669    /// The event trigger identifier (LineEdgeEventKind)
670    pub kind: u32,
671
672    /// The offset of the line that triggered the event.
673    pub offset: Offset,
674
675    /// The sequence number for this event in the sequence of events for all
676    /// the lines in this line request.
677    pub seqno: u32,
678
679    /// The sequence number for this event in the sequence of events on this
680    /// particular line.
681    pub line_seqno: u32,
682
683    /// Reserved for future use.
684    #[doc(hidden)]
685    pub padding: Padding<6>,
686}
687
688impl LineEdgeEvent {
689    /// Read an edge event from a buffer.
690    ///
691    /// The buffer is assumed to have been populated by a read of the line request File,
692    /// so the content is initialised.
693    #[inline]
694    pub fn from_slice(d: &[u64]) -> Result<&LineEdgeEvent> {
695        debug_assert!(std::mem::size_of::<LineEdgeEvent>() % 8 == 0);
696        let len = d.len() * 8;
697        if len < std::mem::size_of::<LineEdgeEvent>() {
698            return Err(Error::from(UnderReadError::new(
699                "LineEdgeEvent",
700                std::mem::size_of::<LineEdgeEvent>(),
701                len,
702            )));
703        }
704        // SAFETY: returned struct contains raw byte arrays and bitfields that are safe to decode.
705        let le = unsafe { &*(d as *const [u64] as *const LineEdgeEvent) };
706        Ok(le)
707    }
708
709    /// The number of u64 words required to store a LineEdgeEvent.
710    pub fn u64_size() -> usize {
711        std::mem::size_of::<LineEdgeEvent>() / 8
712    }
713}
714
715#[cfg(test)]
716mod tests {
717    use super::*;
718
719    mod line_attribute {
720        use super::LineAttribute;
721
722        #[test]
723        fn size() {
724            assert_eq!(
725                std::mem::size_of::<LineAttribute>(),
726                16usize,
727                concat!("Size of: ", stringify!(LineAttribute))
728            );
729        }
730    }
731
732    mod line_attribute_value_union {
733        use super::LineAttributeValueUnion;
734
735        #[test]
736        fn size() {
737            assert_eq!(
738                std::mem::size_of::<LineAttributeValueUnion>(),
739                8usize,
740                concat!("Size of: ", stringify!(LineAttributeValueUnion))
741            );
742        }
743    }
744
745    mod line_config_attribute {
746        use super::LineConfigAttribute;
747
748        #[test]
749        fn line_config_attribute() {
750            assert_eq!(
751                std::mem::size_of::<LineConfigAttribute>(),
752                24usize,
753                concat!("Size of: ", stringify!(LineConfigAttribute))
754            );
755        }
756    }
757
758    mod line_config {
759        use super::LineConfig;
760
761        #[test]
762        fn line_config() {
763            assert_eq!(
764                std::mem::size_of::<LineConfig>(),
765                272usize,
766                concat!("Size of: ", stringify!(LineConfig))
767            );
768        }
769    }
770
771    mod line_request {
772        use super::LineRequest;
773
774        #[test]
775        fn line_request() {
776            assert_eq!(
777                std::mem::size_of::<LineRequest>(),
778                592usize,
779                concat!("Size of: ", stringify!(LineRequest))
780            );
781        }
782    }
783
784    mod line_values {
785        use super::LineValues;
786
787        #[test]
788        fn get() {
789            let mut a = LineValues::default();
790            for idx in [0, 2] {
791                let mask = 0x1 << idx;
792                assert_eq!(a.bits & mask, 0, "idx: {idx}");
793                assert!(a.get(idx).is_none(), "idx: {idx}");
794
795                a.mask |= mask;
796                assert!(!a.get(idx).unwrap(), "idx: {idx}");
797
798                a.bits |= mask;
799                assert!(a.get(idx).unwrap(), "idx: {idx}");
800            }
801        }
802
803        #[test]
804        fn set() {
805            let mut a = LineValues::default();
806            for idx in [0, 2] {
807                let mask = 0x1 << idx;
808                a.set(idx, false);
809                assert_eq!(a.mask & mask, mask, "idx: {idx}");
810                assert_eq!(a.bits & mask, 0, "idx: {idx}");
811
812                a.set(idx, true);
813                assert_eq!(a.mask & mask, mask, "idx: {idx}");
814                assert_eq!(a.bits & mask, mask, "idx: {idx}");
815            }
816        }
817
818        #[test]
819        fn unset_mask() {
820            let mut a = LineValues {
821                mask: 0x7f,
822                ..Default::default()
823            };
824            assert_eq!(a.mask & 0x01, 0x01);
825            a.unset_mask(0);
826            assert_eq!(a.mask & 0x01, 0);
827
828            assert_eq!(a.mask & 0x08, 0x08);
829            a.unset_mask(3);
830            assert_eq!(a.mask & 0x08, 0);
831
832            assert_eq!(a.mask & 0x20, 0x20);
833            a.unset_mask(5);
834            assert_eq!(a.mask & 0x20, 0);
835        }
836
837        #[test]
838        fn size() {
839            assert_eq!(
840                std::mem::size_of::<LineValues>(),
841                16usize,
842                concat!("Size of: ", stringify!(LineValues))
843            );
844        }
845    }
846
847    mod line_info {
848        use super::LineInfo;
849
850        #[test]
851        fn size() {
852            assert_eq!(
853                std::mem::size_of::<LineInfo>(),
854                256usize,
855                concat!("Size of: ", stringify!(LineInfo))
856            );
857        }
858    }
859
860    mod line_info_changed {
861        use super::LineInfoChangeEvent;
862
863        #[test]
864        fn size() {
865            assert_eq!(
866                std::mem::size_of::<LineInfoChangeEvent>(),
867                288usize,
868                concat!("Size of: ", stringify!(LineInfoChangeEvent))
869            );
870        }
871    }
872
873    mod line_event {
874        use super::LineEdgeEvent;
875
876        #[test]
877        fn size() {
878            assert_eq!(
879                std::mem::size_of::<LineEdgeEvent>(),
880                48usize,
881                concat!("Size of: ", stringify!(LineEdgeEvent))
882            );
883        }
884    }
885}