use bitflags::bitflags;
use std::fs::File;
use std::os::unix::prelude::{AsRawFd, FromRawFd};
pub use super::common::*;
#[repr(u8)]
enum Ioctl {
GetLineInfo = 2,
GetLineHandle = 3,
GetLineEvent = 4,
GetLineValues = 8,
SetLineValues = 9,
SetConfig = 0xA,
WatchLineInfo = 0xB,
}
#[repr(C)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct LineInfo {
pub offset: Offset,
pub flags: LineInfoFlags,
pub name: Name,
pub consumer: Name,
}
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct LineInfoFlags: u32 {
const USED = 1;
const OUTPUT = 2;
const ACTIVE_LOW = 4;
const OPEN_DRAIN = 8;
const OPEN_SOURCE = 16;
const BIAS_PULL_UP = 32;
const BIAS_PULL_DOWN = 64;
const BIAS_DISABLED = 128;
}
}
#[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 le = unsafe { &*(d as *const [u64] as *const LineInfoChangeEvent) };
Ok(le)
}
pub fn u64_size() -> usize {
std::mem::size_of::<LineInfoChangeEvent>() / 8
}
}
#[repr(C)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct HandleRequest {
pub offsets: Offsets,
pub flags: HandleRequestFlags,
pub values: LineValues,
pub consumer: Name,
pub num_lines: u32,
#[doc(hidden)]
pub fd: i32,
}
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct HandleRequestFlags: u32 {
const INPUT = 1;
const OUTPUT = 2;
const ACTIVE_LOW = 4;
const OPEN_DRAIN = 8;
const OPEN_SOURCE = 16;
const BIAS_PULL_UP = 32;
const BIAS_PULL_DOWN = 64;
const BIAS_DISABLED = 128;
}
}
#[inline]
pub fn get_line_handle(cf: &File, mut hr: HandleRequest) -> Result<File> {
unsafe {
match libc::ioctl(
cf.as_raw_fd(),
iorw!(Ioctl::GetLineHandle, HandleRequest),
&mut hr,
) {
0 => Ok(File::from_raw_fd(hr.fd)),
_ => Err(Error::from_errno()),
}
}
}
#[repr(C)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct HandleConfig {
pub flags: HandleRequestFlags,
pub values: LineValues,
#[doc(hidden)]
pub padding: Padding<4>,
}
#[inline]
pub fn set_line_config(lf: &File, hc: HandleConfig) -> Result<()> {
unsafe {
match libc::ioctl(lf.as_raw_fd(), iorw!(Ioctl::SetConfig, HandleConfig), &hc) {
0 => Ok(()),
_ => Err(Error::from_errno()),
}
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LineValues(pub [u8; 64usize]);
impl LineValues {
pub fn from_slice(s: &[u8]) -> Self {
let mut n: LineValues = Default::default();
for (src, dst) in s.iter().zip(n.0.iter_mut()) {
*dst = *src;
}
n
}
pub fn copy_from_slice(&mut self, s: &[u8]) {
let extent = std::cmp::min(64usize, s.len());
self.0[0..extent].copy_from_slice(s);
}
#[inline]
pub fn get(&self, idx: usize) -> u8 {
self.0[idx]
}
#[inline]
pub fn set(&mut self, idx: usize, value: u8) {
self.0[idx] = value;
}
}
impl Default for LineValues {
fn default() -> Self {
LineValues([0; 64])
}
}
#[inline]
pub fn get_line_values(lf: &File, vals: &mut LineValues) -> Result<()> {
match unsafe {
libc::ioctl(
lf.as_raw_fd(),
iorw!(Ioctl::GetLineValues, LineValues),
vals.0.as_mut_ptr(),
)
} {
0 => Ok(()),
_ => Err(Error::from_errno()),
}
}
#[inline]
pub fn set_line_values(lf: &File, vals: &LineValues) -> Result<()> {
match unsafe {
libc::ioctl(
lf.as_raw_fd(),
iorw!(Ioctl::SetLineValues, LineValues),
vals.0.as_ptr(),
)
} {
0 => Ok(()),
_ => Err(Error::from_errno()),
}
}
#[repr(C)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct EventRequest {
pub offset: Offset,
pub handleflags: HandleRequestFlags,
pub eventflags: EventRequestFlags,
pub consumer: Name,
#[doc(hidden)]
pub fd: i32,
}
bitflags! {
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct EventRequestFlags: u32 {
const RISING_EDGE = 1;
const FALLING_EDGE = 2;
const BOTH_EDGES = Self::RISING_EDGE.bits() | Self::FALLING_EDGE.bits();
}
}
#[inline]
pub fn get_line_event(cf: &File, mut er: EventRequest) -> Result<File> {
unsafe {
match libc::ioctl(
cf.as_raw_fd(),
iorw!(Ioctl::GetLineEvent, EventRequest),
&mut er,
) {
0 => Ok(File::from_raw_fd(er.fd)),
_ => Err(Error::from_errno()),
}
}
}
#[repr(C)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LineEdgeEvent {
pub timestamp_ns: u64,
pub kind: u32,
}
impl LineEdgeEvent {
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_info {
use super::LineInfo;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineInfo>(),
72usize,
concat!("Size of: ", stringify!(LineInfo))
);
}
}
mod line_info_changed {
use super::LineInfoChangeEvent;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineInfoChangeEvent>(),
104usize,
concat!("Size of: ", stringify!(LineInfoChangeEvent))
);
}
}
mod handle_request {
use super::HandleRequest;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<HandleRequest>(),
364usize,
concat!("Size of: ", stringify!(HandleRequest))
);
}
}
mod handle_config {
use super::HandleConfig;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<HandleConfig>(),
84usize,
concat!("Size of: ", stringify!(HandleConfig))
);
}
}
mod event_request {
use super::EventRequest;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<EventRequest>(),
48usize,
concat!("Size of: ", stringify!(EventRequest))
);
}
}
mod line_event {
use super::LineEdgeEvent;
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineEdgeEvent>(),
16usize,
concat!("Size of: ", stringify!(LineEdgeEvent))
);
}
}
mod line_values {
use super::LineValues;
#[test]
fn get() {
let mut a = LineValues::default();
for idx in [0, 2] {
assert_eq!(a.0[idx], 0, "idx: {idx}");
assert_eq!(a.get(idx), 0, "idx: {idx}");
a.0[idx] = 1;
assert_eq!(a.get(idx), 1, "idx: {idx}");
a.0[idx] = 42;
assert_eq!(a.get(idx), 42, "idx: {idx}");
}
}
#[test]
fn set() {
let mut a = LineValues::default();
for idx in [0, 2] {
a.set(idx, 0);
assert_eq!(a.0[idx], 0, "idx: {idx}");
a.set(idx, 1);
assert_eq!(a.0[idx], 1, "idx: {idx}");
a.set(idx, 42);
assert_eq!(a.0[idx], 42, "idx: {idx}");
}
}
#[test]
fn size() {
assert_eq!(
std::mem::size_of::<LineValues>(),
64usize,
concat!("Size of: ", stringify!(LineValues))
);
}
}
}