use bitflags::bitflags;
use std::fmt;
use std::fs::File;
use std::os::unix::prelude::{AsRawFd, FromRawFd};
pub use super::common::*;
#[repr(u8)]
enum Ioctl {
GetLineInfo = 5,
WatchLineInfo = 6,
GetLine = 7,
SetLineConfig = 0xD,
GetLineValues = 0xE,
SetLineValues = 0xF,
}
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct LineFlags: u64 {
const USED = 1;
const ACTIVE_LOW = 2;
const INPUT = 4;
const OUTPUT = 8;
const EDGE_RISING = 16;
const EDGE_FALLING = 32;
const OPEN_DRAIN = 64;
const OPEN_SOURCE = 128;
const BIAS_PULL_UP = 256;
const BIAS_PULL_DOWN = 512;
const BIAS_DISABLED = 1024;
const EVENT_CLOCK_REALTIME = 2048;
const EVENT_CLOCK_HTE = 4096;
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct LineValues {
pub bits: u64,
pub mask: u64,
}
impl LineValues {
pub fn from_slice(s: &[bool]) -> Self {
let mut lv: LineValues = Default::default();
for (idx, val) in s.iter().enumerate() {
lv.set(idx, *val);
}
lv
}
pub fn copy_from_slice(&mut self, s: &[bool]) {
let extent = std::cmp::min(64usize, s.len());
for (i, v) in s.iter().enumerate().take(extent) {
self.set(i, *v);
}
}
#[inline]
pub fn get(&self, idx: usize) -> Option<bool> {
debug_assert!(idx < 64);
let mask = 0x01 << idx;
if self.mask & mask == 0 {
return None;
}
Some(self.bits & mask != 0)
}
#[inline]
pub fn set(&mut self, idx: usize, active: bool) {
debug_assert!(idx < 64);
let mask = 0x01 << idx;
self.mask |= mask;
if active {
self.bits |= mask;
} else {
self.bits &= !mask;
}
}
#[inline]
pub fn unset_mask(&mut self, idx: usize) {
debug_assert!(idx < 64);
let mask = 0x01 << idx;
self.mask &= !mask;
}
}
#[inline]
pub fn get_line_values(lf: &File, lv: &mut LineValues) -> Result<()> {
match unsafe { libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::GetLineValues, LineValues), lv) } {
0 => Ok(()),
_ => Err(Error::from_errno()),
}
}
#[inline]
pub fn set_line_values(lf: &File, lv: &LineValues) -> Result<()> {
match unsafe { libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::SetLineValues, LineValues), lv) } {
0 => Ok(()),
_ => Err(Error::from_errno()),
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum LineAttributeKind {
#[default]
Unused = 0,
Flags = 1,
Values = 2,
Debounce = 3,
}
impl TryFrom<u32> for LineAttributeKind {
type Error = ();
fn try_from(v: u32) -> std::result::Result<Self, Self::Error> {
use LineAttributeKind::*;
Ok(match v {
0 => Unused,
1 => Flags,
2 => Values,
3 => Debounce,
_ => return Err(()),
})
}
}
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct LineAttribute {
pub kind: u32,
#[doc(hidden)]
pub padding: Padding<1>,
pub value: LineAttributeValueUnion,
}
impl LineAttribute {
pub fn set_debounce_period_us(&mut self, debounce_period_us: u32) {
self.kind = LineAttributeKind::Debounce as u32;
self.value.debounce_period_us = debounce_period_us;
}
pub fn set_flags(&mut self, flags: LineFlags) {
self.kind = LineAttributeKind::Flags as u32;
self.value.flags = flags;
}
pub fn set_values(&mut self, values: u64) {
self.kind = LineAttributeKind::Values as u32;
self.value.values = values;
}
pub fn to_value(&self) -> Option<LineAttributeValue> {
unsafe {
Some(match self.kind {
1 => LineAttributeValue::Flags(self.value.flags),
2 => LineAttributeValue::Values(self.value.values),
3 => LineAttributeValue::DebouncePeriod(self.value.debounce_period_us),
_ => return None,
})
}
}
}
impl fmt::Debug for LineAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
unsafe {
match self.kind {
0 => write!(f, "unused"),
1 => write!(f, "flags: {:?}", self.value.flags),
2 => {
write!(f, "values: {:08x}", self.value.values)
}
3 => {
write!(f, "debounce_period_us: {}", self.value.debounce_period_us)
}
_ => write!(f, "unknown attr kind {}", self.kind),
}
}
}
}
impl PartialEq for LineAttribute {
fn eq(&self, other: &Self) -> bool {
if self.kind != other.kind {
return false;
}
unsafe {
match self.kind {
0 => true,
1 => self.value.flags == other.value.flags,
2 => self.value.values == other.value.values,
3 => self.value.debounce_period_us == other.value.debounce_period_us,
_ => false,
}
}
}
}
impl Eq for LineAttribute {}
#[repr(C)]
#[derive(Clone, Copy)]
pub union LineAttributeValueUnion {
pub flags: LineFlags,
pub values: u64,
pub debounce_period_us: u32,
}
impl Default for LineAttributeValueUnion {
fn default() -> Self {
LineAttributeValueUnion {
flags: Default::default(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LineAttributeValue {
DebouncePeriod(u32),
Flags(LineFlags),
Values(u64),
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct LineConfigAttribute {
pub attr: LineAttribute,
pub mask: u64,
}
#[repr(C)]
#[derive(Clone, Debug, Default)]
pub struct LineConfigAttributes(pub [LineConfigAttribute; NUM_ATTRS_MAX]);
#[repr(C)]
#[derive(Clone, Debug, Default)]
pub struct LineConfig {
pub flags: LineFlags,
pub num_attrs: u32,
#[doc(hidden)]
pub padding: Padding<5>,
pub attrs: LineConfigAttributes,
}
impl LineConfig {
#[inline]
pub fn attr(&self, idx: usize) -> &LineConfigAttribute {
&self.attrs.0[idx]
}
#[inline]
pub fn attr_mut(&mut self, idx: usize) -> &mut LineConfigAttribute {
&mut self.attrs.0[idx]
}
pub fn add_debounce(&mut self, period: u32, mask: u64) {
let lca = &mut self.attrs.0[self.num_attrs as usize];
lca.mask = mask;
lca.attr.set_debounce_period_us(period);
self.num_attrs += 1;
}
pub fn add_flags(&mut self, lf: LineFlags, mask: u64) {
let lca = &mut self.attrs.0[self.num_attrs as usize];
lca.mask = mask;
lca.attr.set_flags(lf);
self.num_attrs += 1;
}
pub fn add_values(&mut self, values: &LineValues) {
let lca = &mut self.attrs.0[self.num_attrs as usize];
lca.mask = values.mask;
lca.attr.set_values(values.bits);
self.num_attrs += 1;
}
}
#[inline]
pub fn set_line_config(lf: &File, lc: LineConfig) -> Result<()> {
unsafe {
match libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::SetLineConfig, LineConfig), &lc) {
0 => Ok(()),
_ => Err(Error::from_errno()),
}
}
}
#[repr(C)]
#[derive(Clone, Debug, Default)]
pub struct LineRequest {
pub offsets: Offsets,
pub consumer: Name,
pub config: LineConfig,
pub num_lines: u32,
pub event_buffer_size: u32,
#[doc(hidden)]
pub padding: Padding<5>,
#[doc(hidden)]
pub fd: i32,
}
#[inline]
pub fn get_line(cf: &File, mut lr: LineRequest) -> Result<File> {
unsafe {
match libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::GetLine, LineRequest), &mut lr) {
0 => Ok(File::from_raw_fd(lr.fd)),
_ => Err(Error::from_errno()),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct LineAttributes(pub [LineAttribute; NUM_ATTRS_MAX]);
pub const NUM_ATTRS_MAX: usize = 10;
#[repr(C)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct LineInfo {
pub name: Name,
pub consumer: Name,
pub offset: Offset,
pub num_attrs: u32,
pub flags: LineFlags,
pub attrs: LineAttributes,
#[doc(hidden)]
pub padding: Padding<4>,
}
impl LineInfo {
#[inline]
pub fn attr(&self, idx: usize) -> &LineAttribute {
&self.attrs.0[idx]
}
#[inline]
pub fn attr_mut(&mut self, idx: usize) -> &mut LineAttribute {
&mut self.attrs.0[idx]
}
}
#[inline]
pub fn get_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
let mut li = LineInfo {
offset,
..Default::default()
};
match unsafe { libc::ioctl(cf.as_raw_fd(), iorw!(Ioctl::GetLineInfo, LineInfo), &mut li) } {
0 => Ok(li),
_ => Err(Error::from_errno()),
}
}
#[inline]
pub fn watch_line_info(cf: &File, offset: Offset) -> Result<LineInfo> {
let mut li = LineInfo {
offset,
..Default::default()
};
match unsafe {
libc::ioctl(
cf.as_raw_fd(),
iorw!(Ioctl::WatchLineInfo, LineInfo),
&mut li,
)
} {
0 => Ok(li),
_ => Err(Error::from_errno()),
}
}
#[repr(C)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LineInfoChangeEvent {
pub info: LineInfo,
pub timestamp_ns: u64,
pub kind: u32,
#[doc(hidden)]
pub padding: Padding<5>,
}
impl LineInfoChangeEvent {
pub fn from_slice(d: &[u64]) -> Result<&LineInfoChangeEvent> {
debug_assert!(std::mem::size_of::<LineInfoChangeEvent>() % 8 == 0);
let len = d.len() * 8;
if len < std::mem::size_of::<LineInfoChangeEvent>() {
return Err(Error::from(UnderReadError::new(
"LineInfoChangeEvent",
std::mem::size_of::<LineInfoChangeEvent>(),
len,
)));
}
let ice = unsafe { &*(d as *const [u64] as *const LineInfoChangeEvent) };
Ok(ice)
}
pub fn u64_size() -> usize {
std::mem::size_of::<LineInfoChangeEvent>() / 8
}
}
#[repr(C)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LineEdgeEvent {
pub timestamp_ns: u64,
pub kind: u32,
pub offset: Offset,
pub seqno: u32,
pub line_seqno: u32,
#[doc(hidden)]
pub padding: Padding<6>,
}
impl LineEdgeEvent {
#[inline]
pub fn from_slice(d: &[u64]) -> Result<&LineEdgeEvent> {
debug_assert!(std::mem::size_of::<LineEdgeEvent>() % 8 == 0);
let len = d.len() * 8;
if len < std::mem::size_of::<LineEdgeEvent>() {
return Err(Error::from(UnderReadError::new(
"LineEdgeEvent",
std::mem::size_of::<LineEdgeEvent>(),
len,
)));
}
let le = unsafe { &*(d as *const [u64] as *const LineEdgeEvent) };
Ok(le)
}
pub fn u64_size() -> usize {
std::mem::size_of::<LineEdgeEvent>() / 8
}
}
#[cfg(test)]
mod tests {
use super::*;
mod line_attribute {
use super::LineAttribute;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineAttribute>(),
16usize,
concat!("Size of: ", stringify!(LineAttribute))
);
}
}
mod line_attribute_value_union {
use super::LineAttributeValueUnion;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineAttributeValueUnion>(),
8usize,
concat!("Size of: ", stringify!(LineAttributeValueUnion))
);
}
}
mod line_config_attribute {
use super::LineConfigAttribute;
#[test]
fn line_config_attribute() {
assert_eq!(
std::mem::size_of::<LineConfigAttribute>(),
24usize,
concat!("Size of: ", stringify!(LineConfigAttribute))
);
}
}
mod line_config {
use super::LineConfig;
#[test]
fn line_config() {
assert_eq!(
std::mem::size_of::<LineConfig>(),
272usize,
concat!("Size of: ", stringify!(LineConfig))
);
}
}
mod line_request {
use super::LineRequest;
#[test]
fn line_request() {
assert_eq!(
std::mem::size_of::<LineRequest>(),
592usize,
concat!("Size of: ", stringify!(LineRequest))
);
}
}
mod line_values {
use super::LineValues;
#[test]
fn get() {
let mut a = LineValues::default();
for idx in [0, 2] {
let mask = 0x1 << idx;
assert_eq!(a.bits & mask, 0, "idx: {idx}");
assert!(a.get(idx).is_none(), "idx: {idx}");
a.mask |= mask;
assert!(!a.get(idx).unwrap(), "idx: {idx}");
a.bits |= mask;
assert!(a.get(idx).unwrap(), "idx: {idx}");
}
}
#[test]
fn set() {
let mut a = LineValues::default();
for idx in [0, 2] {
let mask = 0x1 << idx;
a.set(idx, false);
assert_eq!(a.mask & mask, mask, "idx: {idx}");
assert_eq!(a.bits & mask, 0, "idx: {idx}");
a.set(idx, true);
assert_eq!(a.mask & mask, mask, "idx: {idx}");
assert_eq!(a.bits & mask, mask, "idx: {idx}");
}
}
#[test]
fn unset_mask() {
let mut a = LineValues {
mask: 0x7f,
..Default::default()
};
assert_eq!(a.mask & 0x01, 0x01);
a.unset_mask(0);
assert_eq!(a.mask & 0x01, 0);
assert_eq!(a.mask & 0x08, 0x08);
a.unset_mask(3);
assert_eq!(a.mask & 0x08, 0);
assert_eq!(a.mask & 0x20, 0x20);
a.unset_mask(5);
assert_eq!(a.mask & 0x20, 0);
}
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineValues>(),
16usize,
concat!("Size of: ", stringify!(LineValues))
);
}
}
mod line_info {
use super::LineInfo;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineInfo>(),
256usize,
concat!("Size of: ", stringify!(LineInfo))
);
}
}
mod line_info_changed {
use super::LineInfoChangeEvent;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineInfoChangeEvent>(),
288usize,
concat!("Size of: ", stringify!(LineInfoChangeEvent))
);
}
}
mod line_event {
use super::LineEdgeEvent;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineEdgeEvent>(),
48usize,
concat!("Size of: ", stringify!(LineEdgeEvent))
);
}
}
}