hidraw 0.0.7

Rust hidraw library.
Documentation
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();
			// TODO: use as_bytes_mut when stable
			*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();
			// TODO: use as_bytes_mut when stable
			*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) => {
		//#[doc = include_str!(concat!(stringify!($name), ".md"))]
		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() })
	}

	//#[doc = include_str!("get_raw_info.md")]
	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())
	}

	//#[doc = include_str!("get_report_descriptor.md")]
	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);
}