Skip to content

Commit 11474cd

Browse files
authored
Merge pull request #162 from marek-g/marek
set_output_report (2nd version)
2 parents 15b84ec + 32a4111 commit 11474cd

File tree

7 files changed

+89
-3
lines changed

7 files changed

+89
-3
lines changed

src/ffi.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ extern "C" {
6363
data: *mut c_uchar,
6464
length: size_t,
6565
) -> c_int;
66+
pub fn hid_send_output_report(
67+
device: *mut HidDevice,
68+
data: *const c_uchar,
69+
length: size_t,
70+
) -> c_int;
6671
pub fn hid_close(device: *mut HidDevice);
6772
pub fn hid_get_manufacturer_string(
6873
device: *mut HidDevice,

src/hidapi.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,24 @@ impl HidDeviceBackendBase for HidDevice {
251251
self.check_size(res)
252252
}
253253

254+
fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
255+
if data.is_empty() {
256+
return Err(HidError::InvalidZeroSizeData);
257+
}
258+
let res = unsafe {
259+
ffi::hid_send_output_report(self._hid_device, data.as_ptr(), data.len() as size_t)
260+
};
261+
let res = self.check_size(res)?;
262+
if res != data.len() {
263+
Err(HidError::IncompleteSendError {
264+
sent: res,
265+
all: data.len(),
266+
})
267+
} else {
268+
Ok(())
269+
}
270+
}
271+
254272
fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
255273
let res = unsafe {
256274
ffi::hid_set_nonblocking(self._hid_device, if blocking { 0i32 } else { 1i32 })

src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@ trait HidDeviceBackendBase {
476476
fn read_timeout(&self, buf: &mut [u8], timeout: i32) -> HidResult<usize>;
477477
fn send_feature_report(&self, data: &[u8]) -> HidResult<()>;
478478
fn get_feature_report(&self, buf: &mut [u8]) -> HidResult<usize>;
479+
fn send_output_report(&self, data: &[u8]) -> HidResult<()>;
479480
fn set_blocking_mode(&self, blocking: bool) -> HidResult<()>;
480481
fn get_device_info(&self) -> HidResult<DeviceInfo>;
481482
fn get_manufacturer_string(&self) -> HidResult<Option<String>>;
@@ -594,6 +595,23 @@ impl HidDevice {
594595
self.inner.get_feature_report(buf)
595596
}
596597

598+
// Send a Output report to the device.
599+
//
600+
// Output reports are sent over the Control endpoint as a Set_Report
601+
// transfer. The first byte of data[] must contain the Report ID.
602+
// For devices which only support a single report, this must be set
603+
// to 0x0. The remaining bytes contain the report data. Since the
604+
// Report ID is mandatory, calls to hid_send_output_report() will
605+
// always contain one more byte than the report contains. For example,
606+
// if a hid report is 16 bytes long, 17 bytes must be passed to
607+
// hid_send_output_report(): the Report ID (or 0x0, for devices
608+
// which do not use numbered reports), followed by the report
609+
// data (16 bytes). In this example, the length passed in
610+
// would be 17.
611+
pub fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
612+
self.inner.send_output_report(data)
613+
}
614+
597615
/// Set the device handle to be in blocking or in non-blocking mode. In
598616
/// non-blocking mode calls to `read()` will return immediately with an empty
599617
/// slice if there is no data to be read. In blocking mode, `read()` will

src/linux_native.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ use nix::{
2222
};
2323

2424
use super::{BusType, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString};
25-
use ioctl::{hidraw_ioc_get_feature, hidraw_ioc_grdescsize, hidraw_ioc_set_feature};
25+
use ioctl::{
26+
hidraw_ioc_get_feature, hidraw_ioc_grdescsize, hidraw_ioc_set_feature, hidraw_ioc_set_output,
27+
};
2628

2729
// Bus values from linux/input.h
2830
const BUS_USB: u16 = 0x03;
@@ -556,6 +558,26 @@ impl HidDeviceBackendBase for HidDevice {
556558
Ok(res)
557559
}
558560

561+
fn send_output_report(&self, buf: &[u8]) -> HidResult<()> {
562+
let res = match unsafe { hidraw_ioc_set_output(self.fd.as_raw_fd(), buf) } {
563+
Ok(n) => n,
564+
Err(e) => {
565+
return Err(HidError::HidApiError {
566+
message: format!("ioctl (SOUTPUT): {e}"),
567+
});
568+
}
569+
};
570+
571+
if res as usize != buf.len() {
572+
return Err(HidError::IncompleteSendError {
573+
sent: res as usize,
574+
all: buf.len(),
575+
});
576+
}
577+
578+
Ok(())
579+
}
580+
559581
fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
560582
self.blocking.set(blocking);
561583
Ok(())

src/linux_native/ioctl.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const HIDRAW_IOC_MAGIC: u8 = b'H';
77
const HIDRAW_IOC_GRDESCSIZE: u8 = 0x01;
88
const HIDRAW_SET_FEATURE: u8 = 0x06;
99
const HIDRAW_GET_FEATURE: u8 = 0x07;
10+
const HIDRAW_SET_OUTPUT: u8 = 0x0b;
1011

1112
ioctl_read!(
1213
hidraw_ioc_grdescsize,
@@ -27,3 +28,9 @@ ioctl_read_buf!(
2728
HIDRAW_GET_FEATURE,
2829
u8
2930
);
31+
ioctl_write_buf!(
32+
hidraw_ioc_set_output,
33+
HIDRAW_IOC_MAGIC,
34+
HIDRAW_SET_OUTPUT,
35+
u8
36+
);

src/windows_native/mod.rs

100644100755
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::windows_native::types::{Handle, Overlapped};
3535
use crate::{DeviceInfo, HidDeviceBackendBase, HidDeviceBackendWindows, HidError, HidResult};
3636
use windows_sys::core::GUID;
3737
use windows_sys::Win32::Devices::HumanInterfaceDevice::{
38-
HidD_GetIndexedString, HidD_SetFeature, HidD_SetNumInputBuffers,
38+
HidD_GetIndexedString, HidD_SetFeature, HidD_SetNumInputBuffers, HidD_SetOutputReport,
3939
};
4040
use windows_sys::Win32::Devices::Properties::{
4141
DEVPKEY_Device_ContainerId, DEVPKEY_Device_InstanceId,
@@ -276,6 +276,22 @@ impl HidDeviceBackendBase for HidDevice {
276276
Ok(bytes_returned as usize)
277277
}
278278

279+
fn send_output_report(&self, data: &[u8]) -> HidResult<()> {
280+
ensure!(!data.is_empty(), Err(HidError::InvalidZeroSizeData));
281+
let mut state = self.feature_state.borrow_mut();
282+
state.fill_buffer(data);
283+
284+
check_boolean(unsafe {
285+
HidD_SetOutputReport(
286+
self.device_handle.as_raw(),
287+
state.buffer_ptr() as _,
288+
state.buffer_len() as u32,
289+
)
290+
})?;
291+
292+
Ok(())
293+
}
294+
279295
fn set_blocking_mode(&self, blocking: bool) -> HidResult<()> {
280296
self.blocking.set(blocking);
281297
Ok(())

0 commit comments

Comments
 (0)