Skip to content

Commit 4578ea2

Browse files
authored
Add hid_send_output_report() (#677)
`hid_send_output_report` sends report over control pipe, unlike existing `hid_write` which would send the data on the first interrupt OUT endpoint, if one exists and only falls back to control pipe.
1 parent 6c2de30 commit 4578ea2

File tree

7 files changed

+154
-38
lines changed

7 files changed

+154
-38
lines changed

hidapi/hidapi.h

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,9 @@ extern "C" {
307307
single report), followed by the report data (16 bytes). In
308308
this example, the length passed in would be 17.
309309
310-
hid_write() will send the data on the first OUT endpoint, if
311-
one exists. If it does not, it will send the data through
312-
the Control Endpoint (Endpoint 0).
310+
hid_write() will send the data on the first interrupt OUT
311+
endpoint, if one exists. If it does not the behaviour is as
312+
@ref hid_send_output_report
313313
314314
@ingroup API
315315
@param dev A device handle returned from hid_open().
@@ -445,6 +445,40 @@ extern "C" {
445445
*/
446446
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
447447

448+
/** @brief Send a Output report to the device.
449+
450+
Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0)
451+
452+
Output reports are sent over the Control endpoint as a
453+
Set_Report transfer. The first byte of @p data[] must
454+
contain the Report ID. For devices which only support a
455+
single report, this must be set to 0x0. The remaining bytes
456+
contain the report data. Since the Report ID is mandatory,
457+
calls to hid_send_output_report() will always contain one
458+
more byte than the report contains. For example, if a hid
459+
report is 16 bytes long, 17 bytes must be passed to
460+
hid_send_output_report(): the Report ID (or 0x0, for
461+
devices which do not use numbered reports), followed by the
462+
report data (16 bytes). In this example, the length passed
463+
in would be 17.
464+
465+
This function sets the return value of hid_error().
466+
467+
@ingroup API
468+
@param dev A device handle returned from hid_open().
469+
@param data The data to send, including the report number as
470+
the first byte.
471+
@param length The length in bytes of the data to send, including
472+
the report number.
473+
474+
@returns
475+
This function returns the actual number of bytes written and
476+
-1 on error.
477+
478+
@see @ref hid_write
479+
*/
480+
int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device* dev, const unsigned char* data, size_t length);
481+
448482
/** @brief Get a input report from a HID device.
449483
450484
Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0)

libusb/hid.c

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,6 +1409,11 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t
14091409
int report_number;
14101410
int skipped_report_id = 0;
14111411

1412+
if (dev->output_endpoint <= 0) {
1413+
/* No interrupt out endpoint. Use the Control Endpoint */
1414+
return hid_send_output_report(dev, data, length);
1415+
}
1416+
14121417
if (!data || (length ==0)) {
14131418
return -1;
14141419
}
@@ -1421,42 +1426,21 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t
14211426
skipped_report_id = 1;
14221427
}
14231428

1429+
/* Use the interrupt out endpoint */
1430+
int actual_length;
1431+
res = libusb_interrupt_transfer(dev->device_handle,
1432+
dev->output_endpoint,
1433+
(unsigned char*)data,
1434+
length,
1435+
&actual_length, 1000);
14241436

1425-
if (dev->output_endpoint <= 0) {
1426-
/* No interrupt out endpoint. Use the Control Endpoint */
1427-
res = libusb_control_transfer(dev->device_handle,
1428-
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
1429-
0x09/*HID Set_Report*/,
1430-
(2/*HID output*/ << 8) | report_number,
1431-
dev->interface,
1432-
(unsigned char *)data, length,
1433-
1000/*timeout millis*/);
1434-
1435-
if (res < 0)
1436-
return -1;
1437-
1438-
if (skipped_report_id)
1439-
length++;
1440-
1441-
return length;
1442-
}
1443-
else {
1444-
/* Use the interrupt out endpoint */
1445-
int actual_length;
1446-
res = libusb_interrupt_transfer(dev->device_handle,
1447-
dev->output_endpoint,
1448-
(unsigned char*)data,
1449-
length,
1450-
&actual_length, 1000);
1451-
1452-
if (res < 0)
1453-
return -1;
1437+
if (res < 0)
1438+
return -1;
14541439

1455-
if (skipped_report_id)
1456-
actual_length++;
1440+
if (skipped_report_id)
1441+
actual_length++;
14571442

1458-
return actual_length;
1459-
}
1443+
return actual_length;
14601444
}
14611445

14621446
/* Helper function, to simplify hid_read().
@@ -1638,6 +1622,36 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data,
16381622
return res;
16391623
}
16401624

1625+
int HID_API_EXPORT hid_send_output_report(hid_device *dev, const unsigned char *data, size_t length)
1626+
{
1627+
int res = -1;
1628+
int skipped_report_id = 0;
1629+
int report_number = data[0];
1630+
1631+
if (report_number == 0x0) {
1632+
data++;
1633+
length--;
1634+
skipped_report_id = 1;
1635+
}
1636+
1637+
res = libusb_control_transfer(dev->device_handle,
1638+
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
1639+
0x09/*HID set_report*/,
1640+
(2/*HID output*/ << 8) | report_number,
1641+
dev->interface,
1642+
(unsigned char *)data, length,
1643+
1000/*timeout millis*/);
1644+
1645+
if (res < 0)
1646+
return -1;
1647+
1648+
/* Account for the report ID */
1649+
if (skipped_report_id)
1650+
length++;
1651+
1652+
return length;
1653+
}
1654+
16411655
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
16421656
{
16431657
int res = -1;

linux/hid.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,15 @@
6161
#endif
6262

6363

64-
// HIDIOCGINPUT is not defined in Linux kernel headers < 5.11.
65-
// This definition is from hidraw.h in Linux >= 5.11.
64+
// HIDIOCGINPUT and HIDIOCSOUTPUT are not defined in Linux kernel headers < 5.11.
65+
// These definitions are from hidraw.h in Linux >= 5.11.
6666
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f43d3870cafa2a0f3854c1819c8385733db8f9ae
6767
#ifndef HIDIOCGINPUT
6868
#define HIDIOCGINPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0A, len)
6969
#endif
70+
#ifndef HIDIOCSOUTPUT
71+
#define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len)
72+
#endif
7073

7174
struct hid_device_ {
7275
int device_handle;
@@ -1196,6 +1199,19 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data,
11961199
return res;
11971200
}
11981201

1202+
int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device *dev, const unsigned char *data, size_t length)
1203+
{
1204+
int res;
1205+
1206+
register_device_error(dev, NULL);
1207+
1208+
res = ioctl(dev->device_handle, HIDIOCSOUTPUT(length), data);
1209+
if (res < 0)
1210+
register_device_error_format(dev, "ioctl (SOUTPUT): %s", strerror(errno));
1211+
1212+
return res;
1213+
}
1214+
11991215
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
12001216
{
12011217
int res;

mac/hid.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,11 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data,
13411341
return get_report(dev, kIOHIDReportTypeFeature, data, length);
13421342
}
13431343

1344+
int HID_API_EXPORT hid_send_output_feature_report(hid_device *dev, const unsigned char *data, size_t length)
1345+
{
1346+
return set_report(dev, kIOHIDReportTypeOutput, data, length);
1347+
}
1348+
13441349
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
13451350
{
13461351
return get_report(dev, kIOHIDReportTypeInput, data, length);

netbsd/hid.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,11 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned
934934
return get_report(dev, data, length, UHID_FEATURE_REPORT);
935935
}
936936

937+
int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device *dev, const unsigned char *data, size_t length)
938+
{
939+
return set_report(dev, data, length, UHID_OUTPUT_REPORT);
940+
}
941+
937942
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
938943
{
939944
return get_report(dev, data, length, UHID_INPUT_REPORT);

windows/hid.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ static HidD_GetManufacturerString_ HidD_GetManufacturerString;
9797
static HidD_GetProductString_ HidD_GetProductString;
9898
static HidD_SetFeature_ HidD_SetFeature;
9999
static HidD_GetFeature_ HidD_GetFeature;
100+
static HidD_SetOutputReport_ HidD_SetOutputReport;
100101
static HidD_GetInputReport_ HidD_GetInputReport;
101102
static HidD_GetIndexedString_ HidD_GetIndexedString;
102103
static HidD_GetPreparsedData_ HidD_GetPreparsedData;
@@ -150,6 +151,7 @@ static int lookup_functions()
150151
RESOLVE(hid_lib_handle, HidD_GetProductString);
151152
RESOLVE(hid_lib_handle, HidD_SetFeature);
152153
RESOLVE(hid_lib_handle, HidD_GetFeature);
154+
RESOLVE(hid_lib_handle, HidD_SetOutputReport);
153155
RESOLVE(hid_lib_handle, HidD_GetInputReport);
154156
RESOLVE(hid_lib_handle, HidD_GetIndexedString);
155157
RESOLVE(hid_lib_handle, HidD_GetPreparsedData);
@@ -1313,6 +1315,45 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned
13131315
return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length);
13141316
}
13151317

1318+
int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device* dev, const unsigned char* data, size_t length)
1319+
{
1320+
BOOL res = FALSE;
1321+
unsigned char *buf;
1322+
size_t length_to_send;
1323+
1324+
if (!data || !length) {
1325+
register_string_error(dev, L"Zero buffer/length");
1326+
return -1;
1327+
}
1328+
1329+
register_string_error(dev, NULL);
1330+
1331+
/* Windows expects at least caps.OutputeportByteLength bytes passed
1332+
to HidD_SetOutputReport(), even if the report is shorter. Any less sent and
1333+
the function fails with error ERROR_INVALID_PARAMETER set. Any more
1334+
and HidD_SetOutputReport() silently truncates the data sent in the report
1335+
to caps.OutputReportByteLength. */
1336+
if (length >= dev->output_report_length) {
1337+
buf = (unsigned char *) data;
1338+
length_to_send = length;
1339+
} else {
1340+
if (dev->write_buf == NULL)
1341+
dev->write_buf = (unsigned char *) malloc(dev->output_report_length);
1342+
buf = dev->write_buf;
1343+
memcpy(buf, data, length);
1344+
memset(buf + length, 0, dev->output_report_length - length);
1345+
length_to_send = dev->output_report_length;
1346+
}
1347+
1348+
res = HidD_SetOutputReport(dev->device_handle, (PVOID)buf, (DWORD) length_to_send);
1349+
if (!res) {
1350+
register_string_error(dev, L"HidD_SetOutputReport");
1351+
return -1;
1352+
}
1353+
1354+
return (int) length;
1355+
}
1356+
13161357
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
13171358
{
13181359
/* We could use HidD_GetInputReport() instead, but it doesn't give us an actual length, unfortunately */

windows/hidapi_hidsdi.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID bu
4747
typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
4848
typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
4949
typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
50+
typedef BOOLEAN (__stdcall* HidD_SetOutputReport_)(HANDLE handle, PVOID data, ULONG length);
5051
typedef BOOLEAN (__stdcall *HidD_GetInputReport_)(HANDLE handle, PVOID data, ULONG length);
5152
typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
5253
typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);

0 commit comments

Comments
 (0)