use std::mem::MaybeUninit;
use std::os::unix::prelude::*;
use std::ptr::addr_of_mut;
use libc::ioctl;
use sys::{hidraw_report_descriptor, HIDIOCGRAWINFO, HIDIOCGRDESC, HIDIOCGRDESCSIZE};
use util::cvt;
use crate::{RawInfo, Result};
use super::Device;
mod util;
const STRING_LEN: usize = 256;
macro_rules! __get_report {
($name: ident, $ioctl: ident) => {
#[inline(always)]
pub(super) unsafe fn $name<T>(&self, report_id: u8, size: usize) -> crate::Result<T> {
let mut result = MaybeUninit::<T>::zeroed();
*result.as_mut_ptr().cast() = report_id;
let s = cvt(ioctl(self.as_raw_fd(), sys::$ioctl!(size), result.as_mut_ptr()))?;
if size == s as usize {
if report_id == *result.as_ptr().cast() {
Ok(result.assume_init())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Report id mismatch",
))
}
} else {
Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
}
}
};
() => {};
}
macro_rules! __get_report_read {
($name: ident) => {
#[inline(always)]
pub(super) unsafe fn $name<T>(&self, report_id: u8, size: usize) -> crate::Result<T> {
let mut result = MaybeUninit::<T>::zeroed();
*result.as_mut_ptr().cast() = report_id;
let s = cvt(libc::read(self.as_raw_fd(), result.as_mut_ptr().cast(), size))?;
if size == s as usize {
if report_id == *result.as_ptr().cast() {
Ok(result.assume_init())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Report id mismatch",
))
}
} else {
Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
}
}
};
() => {};
}
macro_rules! get_report {
($name:ident, $base:ident) => {
#[doc = include_str!(concat!(stringify!($name), ".md"))]
pub fn $name<T>(&mut self, report_id: u8) -> Result<T> {
unsafe { self.$base(report_id, std::mem::size_of::<T>()) }
}
};
() => {};
}
macro_rules! get_report_with_size {
($name:ident, $base:ident) => {
#[cfg(feature = "unsafe_reports")]
#[cfg_attr(docsrs, doc(cfg(feature = "unsafe_reports")))]
#[doc = include_str!(concat!(stringify!($name), ".md"))]
pub unsafe fn $name<T>(&mut self, report_id: u8, size: usize) -> Result<T> {
self.$base(report_id, size)
}
};
() => {};
}
macro_rules! get_report_fns {
($t:ident) => {
paste::paste! {
get_report_fns!(
[<__get_ $t _report>],
[<get_ $t _report>],
[<get_ $t _report_with_size>],
[<HIDIOCG $t:upper>]
);
}
};
($t:ident with_read) => {
paste::paste! {
get_report_fns!(
[<__get_ $t _report>],
[<__get_ $t _report_read>],
[<get_ $t _report>],
[<get_ $t _report_with_size>],
[<get_ $t _report_read>],
[<get_ $t _report_read_with_size>],
[<HIDIOCG $t:upper>]
);
}
};
($base:ident, $name:ident, $with_size:ident, $ioctl:ident) => {
__get_report!($base, $ioctl);
get_report!($name, $base);
get_report_with_size!($with_size, $base);
};
($base:ident, $base_read:ident, $name:ident, $with_size:ident, $read:ident, $read_with_size:ident, $ioctl:ident) => {
__get_report!($base, $ioctl);
__get_report_read!($base_read);
get_report!($name, $base);
get_report_with_size!($with_size, $base);
get_report!($read, $base_read);
get_report_with_size!($read_with_size, $base_read);
};
() => {};
}
macro_rules! __get_string {
($name: ident, $ioctl: ident) => {
#[inline(always)]
pub(super) fn $name(&self) -> Result<String> {
let mut result = [0; STRING_LEN];
let len = cvt(unsafe { ioctl(self.as_raw_fd(), sys::$ioctl!(result.len()), result.as_mut_ptr()) })?;
Ok(unsafe { std::str::from_utf8_unchecked(&result[..(len as usize - 1)]) }.to_owned())
}
};
() => {};
}
macro_rules! get_string {
($name: ident, $base: ident) => {
pub fn $name(&mut self) -> Result<String> {
self.$base()
}
};
() => {};
}
macro_rules! get_string_fns {
($name: ident, $ioctl: ident) => {
paste::paste! {
get_string_fns!(
[<__ $name>],
$name,
$ioctl
);
}
};
($base: ident, $name: ident, $ioctl: ident) => {
__get_string!($base, $ioctl);
get_string!($name, $base);
};
() => {};
}
macro_rules! __send_report {
($name: ident, $ioctl: ident) => {
#[inline(always)]
pub(super) unsafe fn $name<T>(&self, report: &T, size: usize) -> crate::Result<()> {
let s = cvt(ioctl(self.as_raw_fd(), sys::$ioctl!(size), report as *const T))?;
if size == s as usize {
Ok(())
} else {
Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
}
}
};
() => {};
}
macro_rules! __send_report_write {
($name: ident) => {
#[inline(always)]
pub(super) unsafe fn $name<T>(&self, report: &T, size: usize) -> crate::Result<()> {
let s = cvt(libc::write(self.as_raw_fd(), (report as *const T).cast(), size))?;
if size == s as usize {
Ok(())
} else {
Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))
}
}
};
() => {};
}
macro_rules! send_report {
($name:ident, $base:ident) => {
#[doc = include_str!(concat!(stringify!($name), ".md"))]
pub fn $name<T>(&mut self, report: &T) -> Result<()> {
unsafe { self.$base(report, std::mem::size_of::<T>()) }
}
};
() => {};
}
macro_rules! send_report_with_size {
($name:ident, $base:ident) => {
#[cfg(feature = "unsafe_reports")]
#[cfg_attr(docsrs, doc(cfg(feature = "unsafe_reports")))]
#[doc = include_str!(concat!(stringify!($name), ".md"))]
pub unsafe fn $name<T>(&mut self, report: &T, size: usize) -> Result<()> {
self.$base(report, size)
}
};
() => {};
}
macro_rules! send_report_fns {
($t:ident) => {
paste::paste! {
send_report_fns!(
[<__send_ $t _report>],
[<send_ $t _report>],
[<send_ $t _report_with_size>],
[<HIDIOCS $t:upper>]
);
}
};
($t:ident with_write) => {
paste::paste! {
send_report_fns!(
[<__send_ $t _report>],
[<__send_ $t _report_write>],
[<send_ $t _report>],
[<send_ $t _report_with_size>],
[<send_ $t _report_write>],
[<send_ $t _report_write_with_size>],
[<HIDIOCS $t:upper>]
);
}
};
($base:ident, $name:ident, $with_size:ident, $ioctl:ident) => {
__send_report!($base, $ioctl);
send_report!($name, $base);
send_report_with_size!($with_size, $base);
};
($base:ident, $base_write:ident, $name:ident, $with_size:ident, $write:ident, $write_with_size:ident, $ioctl:ident) => {
__send_report!($base, $ioctl);
__send_report_write!($base_write);
send_report!($name, $base);
send_report_with_size!($with_size, $base);
send_report!($write, $base_write);
send_report_with_size!($write_with_size, $base_write);
};
() => {};
}
impl Device {
get_report_fns!(feature);
get_report_fns!(input with_read);
get_report_fns!(output);
get_string_fns!(get_physical_address, HIDIOCGRAWPHYS);
#[inline(always)]
pub(super) fn __get_raw_info(&self) -> Result<RawInfo> {
let mut result = MaybeUninit::<RawInfo>::zeroed();
cvt(unsafe { ioctl(self.as_raw_fd(), HIDIOCGRAWINFO!(), result.as_mut_ptr()) })?;
Ok(unsafe { result.assume_init() })
}
pub fn get_raw_info(&mut self) -> Result<RawInfo> {
Self::__get_raw_info(self)
}
get_string_fns!(get_raw_name, HIDIOCGRAWNAME);
get_string_fns!(get_raw_unique, HIDIOCGRAWUNIQ);
#[inline(always)]
pub(super) fn __get_report_descriptor(&self) -> Result<Vec<u8>> {
let mut result = MaybeUninit::<hidraw_report_descriptor>::zeroed();
let result_ptr = result.as_mut_ptr();
cvt(unsafe { ioctl(self.as_raw_fd(), HIDIOCGRDESCSIZE!(), addr_of_mut!((*result_ptr).size)) })?;
cvt(unsafe { ioctl(self.as_raw_fd(), HIDIOCGRDESC!(), result_ptr) })?;
let result = unsafe { result.assume_init() };
Ok(result.value[..result.size as _].to_owned())
}
pub fn get_report_descriptor(&mut self) -> Result<Vec<u8>> {
self.__get_report_descriptor()
}
send_report_fns!(feature);
send_report_fns!(input);
send_report_fns!(output with_write);
}