From 1c60b2feb26539e8ba60c6293fcc68ea455aa8c8 Mon Sep 17 00:00:00 2001 From: Marek G Date: Sat, 7 Dec 2024 00:15:01 +0100 Subject: [PATCH 1/4] Update hidapi --- etc/hidapi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/hidapi b/etc/hidapi index d0856c0..ff67c77 160000 --- a/etc/hidapi +++ b/etc/hidapi @@ -1 +1 @@ -Subproject commit d0856c05cecbb1522c24fd2f1ed1e144b001f349 +Subproject commit ff67c77daddbd8e61ad3873ac16f8edc005f943f From b1745f5aa423411cb6cafd9e0f2d88965928be90 Mon Sep 17 00:00:00 2001 From: Marek G Date: Sat, 7 Dec 2024 00:28:38 +0100 Subject: [PATCH 2/4] Add send_output_report --- src/ffi.rs | 5 +++++ src/hidapi.rs | 18 ++++++++++++++++++ src/lib.rs | 18 ++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/src/ffi.rs b/src/ffi.rs index 7840ddf..f2aecb9 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -63,6 +63,11 @@ extern "C" { data: *mut c_uchar, length: size_t, ) -> c_int; + pub fn hid_send_output_report( + device: *mut HidDevice, + data: *const 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 701574e..983eaa4 100644 --- a/src/hidapi.rs +++ b/src/hidapi.rs @@ -251,6 +251,24 @@ impl HidDeviceBackendBase for HidDevice { self.check_size(res) } + fn send_output_report(&self, data: &[u8]) -> HidResult<()> { + if data.is_empty() { + return Err(HidError::InvalidZeroSizeData); + } + let res = unsafe { + ffi::hid_send_output_report(self._hid_device, data.as_ptr(), data.len() as size_t) + }; + let res = self.check_size(res)?; + if res != data.len() { + Err(HidError::IncompleteSendError { + sent: res, + all: data.len(), + }) + } else { + Ok(()) + } + } + 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 21ce7b8..e85ede4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -476,6 +476,7 @@ trait HidDeviceBackendBase { fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult; 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<()>; fn set_blocking_mode(&self, blocking: bool) -> HidResult<()>; fn get_device_info(&self) -> HidResult; fn get_manufacturer_string(&self) -> HidResult>; @@ -594,6 +595,23 @@ 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. + pub fn send_output_report(&self, data: &[u8]) -> HidResult<()> { + self.inner.send_output_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 From 0cec7e1b01602dfbabce15fcd6f96f262638ed59 Mon Sep 17 00:00:00 2001 From: Marek G Date: Mon, 16 Dec 2024 17:17:45 +0100 Subject: [PATCH 3/4] Implement send_output_report() for linux-native --- src/linux_native.rs | 24 +++++++++++++++++++++++- src/linux_native/ioctl.rs | 7 +++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/linux_native.rs b/src/linux_native.rs index 464fe4e..0125324 100644 --- a/src/linux_native.rs +++ b/src/linux_native.rs @@ -22,7 +22,9 @@ use nix::{ }; use super::{BusType, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString}; -use ioctl::{hidraw_ioc_get_feature, hidraw_ioc_grdescsize, hidraw_ioc_set_feature}; +use ioctl::{ + hidraw_ioc_get_feature, hidraw_ioc_grdescsize, hidraw_ioc_set_feature, hidraw_ioc_set_output, +}; // Bus values from linux/input.h const BUS_USB: u16 = 0x03; @@ -556,6 +558,26 @@ impl HidDeviceBackendBase for HidDevice { Ok(res) } + fn send_output_report(&self, buf: &[u8]) -> HidResult<()> { + let res = match unsafe { hidraw_ioc_set_output(self.fd.as_raw_fd(), buf) } { + Ok(n) => n, + Err(e) => { + return Err(HidError::HidApiError { + message: format!("ioctl (SOUTPUT): {e}"), + }); + } + }; + + if res as usize != buf.len() { + return Err(HidError::IncompleteSendError { + sent: res as usize, + all: buf.len(), + }); + } + + Ok(()) + } + 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 67f7829..9d97460 100644 --- a/src/linux_native/ioctl.rs +++ b/src/linux_native/ioctl.rs @@ -7,6 +7,7 @@ const HIDRAW_IOC_MAGIC: u8 = b'H'; const HIDRAW_IOC_GRDESCSIZE: u8 = 0x01; const HIDRAW_SET_FEATURE: u8 = 0x06; const HIDRAW_GET_FEATURE: u8 = 0x07; +const HIDRAW_SET_OUTPUT: u8 = 0x0b; ioctl_read!( hidraw_ioc_grdescsize, @@ -27,3 +28,9 @@ ioctl_read_buf!( HIDRAW_GET_FEATURE, u8 ); +ioctl_write_buf!( + hidraw_ioc_set_output, + HIDRAW_IOC_MAGIC, + HIDRAW_SET_OUTPUT, + u8 +); From 32a4111fed71931f84ba9ffd563085025120a3d2 Mon Sep 17 00:00:00 2001 From: Marek G Date: Mon, 16 Dec 2024 20:40:20 +0100 Subject: [PATCH 4/4] Implement send_output_report() for windows-native --- src/windows_native/mod.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) mode change 100644 => 100755 src/windows_native/mod.rs diff --git a/src/windows_native/mod.rs b/src/windows_native/mod.rs old mode 100644 new mode 100755 index f29fd76..4c1e097 --- a/src/windows_native/mod.rs +++ b/src/windows_native/mod.rs @@ -35,7 +35,7 @@ use crate::windows_native::types::{Handle, Overlapped}; use crate::{DeviceInfo, HidDeviceBackendBase, HidDeviceBackendWindows, HidError, HidResult}; use windows_sys::core::GUID; use windows_sys::Win32::Devices::HumanInterfaceDevice::{ - HidD_GetIndexedString, HidD_SetFeature, HidD_SetNumInputBuffers, + HidD_GetIndexedString, HidD_SetFeature, HidD_SetNumInputBuffers, HidD_SetOutputReport, }; use windows_sys::Win32::Devices::Properties::{ DEVPKEY_Device_ContainerId, DEVPKEY_Device_InstanceId, @@ -276,6 +276,22 @@ impl HidDeviceBackendBase for HidDevice { Ok(bytes_returned as usize) } + fn send_output_report(&self, data: &[u8]) -> HidResult<()> { + ensure!(!data.is_empty(), Err(HidError::InvalidZeroSizeData)); + let mut state = self.feature_state.borrow_mut(); + state.fill_buffer(data); + + check_boolean(unsafe { + HidD_SetOutputReport( + self.device_handle.as_raw(), + state.buffer_ptr() as _, + state.buffer_len() as u32, + ) + })?; + + Ok(()) + } + fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> { self.blocking.set(blocking); Ok(())