diff --git a/.gitignore b/.gitignore index 17db28f..3e0f44c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ target Cargo.lock .idea/ hidapi-rs.iml +.helix/ +.vscode/ diff --git a/src/ffi.rs b/src/ffi.rs index f2aecb9..6d272db 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -68,6 +68,11 @@ extern "C" { data: *const c_uchar, length: size_t, ) -> c_int; + pub fn hid_get_input_report( + device: *mut HidDevice, + data: *mut c_uchar, + length: size_t, + ) -> c_int; pub fn hid_close(device: *mut HidDevice); pub fn hid_get_manufacturer_string( device: *mut HidDevice, diff --git a/src/hidapi.rs b/src/hidapi.rs index 983eaa4..229632a 100644 --- a/src/hidapi.rs +++ b/src/hidapi.rs @@ -269,6 +269,16 @@ impl HidDeviceBackendBase for HidDevice { } } + fn get_input_report(&self, data: &mut [u8]) -> HidResult { + if data.is_empty() { + return Err(HidError::InvalidZeroSizeData); + } + let res = unsafe { + ffi::hid_get_input_report(self._hid_device, data.as_mut_ptr(), data.len() as size_t) + }; + self.check_size(res) + } + fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> { let res = unsafe { ffi::hid_set_nonblocking(self._hid_device, if blocking { 0i32 } else { 1i32 }) diff --git a/src/lib.rs b/src/lib.rs index e85ede4..a331e3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -477,6 +477,8 @@ trait HidDeviceBackendBase { fn send_feature_report(&self, data: &[u8]) -> HidResult<()>; fn get_feature_report(&self, buf: &mut [u8]) -> HidResult; fn send_output_report(&self, data: &[u8]) -> HidResult<()>; + #[cfg(any(hidapi, target_os = "linux"))] + fn get_input_report(&self, data: &mut [u8]) -> HidResult; fn set_blocking_mode(&self, blocking: bool) -> HidResult<()>; fn get_device_info(&self) -> HidResult; fn get_manufacturer_string(&self) -> HidResult>; @@ -595,23 +597,36 @@ impl HidDevice { self.inner.get_feature_report(buf) } - // Send a Output report to the device. - // - // Output reports are sent over the Control endpoint as a Set_Report - // transfer. The first byte of data[] must contain the Report ID. - // For devices which only support a single report, this must be set - // to 0x0. The remaining bytes contain the report data. Since the - // Report ID is mandatory, calls to hid_send_output_report() will - // always contain one more byte than the report contains. For example, - // if a hid report is 16 bytes long, 17 bytes must be passed to - // hid_send_output_report(): the Report ID (or 0x0, for devices - // which do not use numbered reports), followed by the report - // data (16 bytes). In this example, the length passed in - // would be 17. + /// Send a Output report to the device. + /// + /// Output reports are sent over the Control endpoint as a Set_Report + /// transfer. The first byte of data[] must contain the Report ID. + /// For devices which only support a single report, this must be set + /// to 0x0. The remaining bytes contain the report data. Since the + /// Report ID is mandatory, calls to hid_send_output_report() will + /// always contain one more byte than the report contains. For example, + /// if a hid report is 16 bytes long, 17 bytes must be passed to + /// hid_send_output_report(): the Report ID (or 0x0, for devices + /// which do not use numbered reports), followed by the report + /// data (16 bytes). In this example, the length passed in + /// would be 17. pub fn send_output_report(&self, data: &[u8]) -> HidResult<()> { self.inner.send_output_report(data) } + /// Get a input report from a HID device + /// + /// Set the first byte of data to the report id of the report to be read. + /// Set the first byte to zero if your device does not use numbered reports. + /// After calling the function, the first byte will still contain the same report id. + /// + /// If successful, returns the number of bytes read plus one for the report ID (which is still + /// in the first byte). + #[cfg(any(hidapi, target_os = "linux"))] + pub fn get_input_report(&self, data: &mut [u8]) -> HidResult { + self.inner.get_input_report(data) + } + /// Set the device handle to be in blocking or in non-blocking mode. In /// non-blocking mode calls to `read()` will return immediately with an empty /// slice if there is no data to be read. In blocking mode, `read()` will diff --git a/src/linux_native.rs b/src/linux_native.rs index 0125324..6552be2 100644 --- a/src/linux_native.rs +++ b/src/linux_native.rs @@ -23,7 +23,8 @@ use nix::{ use super::{BusType, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString}; use ioctl::{ - hidraw_ioc_get_feature, hidraw_ioc_grdescsize, hidraw_ioc_set_feature, hidraw_ioc_set_output, + hidraw_ioc_get_feature, hidraw_ioc_get_input, hidraw_ioc_grdescsize, hidraw_ioc_set_feature, + hidraw_ioc_set_output, }; // Bus values from linux/input.h @@ -578,6 +579,15 @@ impl HidDeviceBackendBase for HidDevice { Ok(()) } + fn get_input_report(&self, data: &mut [u8]) -> HidResult { + match unsafe { hidraw_ioc_get_input(self.fd.as_raw_fd(), data) } { + Ok(n) => Ok(n as usize), + Err(e) => Err(HidError::HidApiError { + message: format!("ioctl (GINPUT): {e}"), + }), + } + } + fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> { self.blocking.set(blocking); Ok(()) diff --git a/src/linux_native/ioctl.rs b/src/linux_native/ioctl.rs index 9d97460..ecafa18 100644 --- a/src/linux_native/ioctl.rs +++ b/src/linux_native/ioctl.rs @@ -8,6 +8,7 @@ const HIDRAW_IOC_GRDESCSIZE: u8 = 0x01; const HIDRAW_SET_FEATURE: u8 = 0x06; const HIDRAW_GET_FEATURE: u8 = 0x07; const HIDRAW_SET_OUTPUT: u8 = 0x0b; +const HIDRAW_GET_INPUT: u8 = 0x0a; ioctl_read!( hidraw_ioc_grdescsize, @@ -34,3 +35,4 @@ ioctl_write_buf!( HIDRAW_SET_OUTPUT, u8 ); +ioctl_read_buf!(hidraw_ioc_get_input, HIDRAW_IOC_MAGIC, HIDRAW_GET_INPUT, u8);