Skip to content

Commit c2ed2ca

Browse files
authored
Merge branch 'master' into libusb-error
2 parents af4b47d + ff67c77 commit c2ed2ca

10 files changed

Lines changed: 487 additions & 12 deletions

hidtest/test.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ int main(int argc, char* argv[])
198198
return 1;
199199
}
200200

201+
#if defined(_WIN32) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0)
202+
hid_winapi_set_write_timeout(handle, 5000);
203+
#endif
204+
201205
// Read the Manufacturer String
202206
wstr[0] = 0x0000;
203207
res = hid_get_manufacturer_string(handle, wstr, MAX_STR);

libusb/hid.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,10 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
13711371
return NULL;
13721372

13731373
dev = new_hid_device();
1374+
if (!dev) {
1375+
LOG("hid_open_path failed: Couldn't allocate memory\n");
1376+
return NULL;
1377+
}
13741378

13751379
libusb_get_device_list(usb_context, &devs);
13761380
while ((usb_dev = devs[d++]) != NULL && !good_open) {
@@ -1442,6 +1446,10 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys
14421446
return NULL;
14431447

14441448
dev = new_hid_device();
1449+
if (!dev) {
1450+
LOG("libusb_wrap_sys_device failed: Couldn't allocate memory\n");
1451+
return NULL;
1452+
}
14451453

14461454
res = libusb_wrap_sys_device(usb_context, sys_dev, &dev->device_handle);
14471455
if (res < 0) {

windows/hid.c

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ struct hid_device_ {
194194
OVERLAPPED ol;
195195
OVERLAPPED write_ol;
196196
struct hid_device_info* device_info;
197+
DWORD write_timeout_ms;
197198
};
198199

199200
static hid_device *new_hid_device()
@@ -219,6 +220,7 @@ static hid_device *new_hid_device()
219220
memset(&dev->write_ol, 0, sizeof(dev->write_ol));
220221
dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);
221222
dev->device_info = NULL;
223+
dev->write_timeout_ms = 1000;
222224

223225
return dev;
224226
}
@@ -1043,7 +1045,10 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
10431045

10441046
end_of_function:
10451047
free(interface_path);
1046-
CloseHandle(device_handle);
1048+
1049+
if (device_handle != INVALID_HANDLE_VALUE) {
1050+
CloseHandle(device_handle);
1051+
}
10471052

10481053
if (pp_data) {
10491054
HidD_FreePreparsedData(pp_data);
@@ -1052,6 +1057,11 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
10521057
return dev;
10531058
}
10541059

1060+
void HID_API_EXPORT_CALL hid_winapi_set_write_timeout(hid_device *dev, unsigned long timeout)
1061+
{
1062+
dev->write_timeout_ms = timeout;
1063+
}
1064+
10551065
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
10561066
{
10571067
DWORD bytes_written = 0;
@@ -1078,15 +1088,22 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *
10781088
/* The user passed the right number of bytes. Use the buffer as-is. */
10791089
buf = (unsigned char *) data;
10801090
} else {
1081-
if (dev->write_buf == NULL)
1091+
if (dev->write_buf == NULL) {
10821092
dev->write_buf = (unsigned char *) malloc(dev->output_report_length);
1093+
1094+
if (dev->write_buf == NULL) {
1095+
register_string_error(dev, L"hid_write/malloc");
1096+
goto end_of_function;
1097+
}
1098+
}
1099+
10831100
buf = dev->write_buf;
10841101
memcpy(buf, data, length);
10851102
memset(buf + length, 0, dev->output_report_length - length);
10861103
length = dev->output_report_length;
10871104
}
10881105

1089-
res = WriteFile(dev->device_handle, buf, (DWORD) length, NULL, &dev->write_ol);
1106+
res = WriteFile(dev->device_handle, buf, (DWORD) length, &bytes_written, &dev->write_ol);
10901107

10911108
if (!res) {
10921109
if (GetLastError() != ERROR_IO_PENDING) {
@@ -1095,12 +1112,15 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *
10951112
goto end_of_function;
10961113
}
10971114
overlapped = TRUE;
1115+
} else {
1116+
/* WriteFile() succeeded synchronously. */
1117+
function_result = bytes_written;
10981118
}
10991119

11001120
if (overlapped) {
11011121
/* Wait for the transaction to complete. This makes
11021122
hid_write() synchronous. */
1103-
res = WaitForSingleObject(dev->write_ol.hEvent, 1000);
1123+
res = WaitForSingleObject(dev->write_ol.hEvent, dev->write_timeout_ms);
11041124
if (res != WAIT_OBJECT_0) {
11051125
/* There was a Timeout. */
11061126
register_winapi_error(dev, L"hid_write/WaitForSingleObject");
@@ -1243,8 +1263,15 @@ int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const u
12431263
buf = (unsigned char *) data;
12441264
length_to_send = length;
12451265
} else {
1246-
if (dev->feature_buf == NULL)
1266+
if (dev->feature_buf == NULL) {
12471267
dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length);
1268+
1269+
if (dev->feature_buf == NULL) {
1270+
register_string_error(dev, L"hid_send_feature_report/malloc");
1271+
return -1;
1272+
}
1273+
}
1274+
12481275
buf = dev->feature_buf;
12491276
memcpy(buf, data, length);
12501277
memset(buf + length, 0, dev->feature_report_length - length);
@@ -1337,8 +1364,15 @@ int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device* dev, const un
13371364
buf = (unsigned char *) data;
13381365
length_to_send = length;
13391366
} else {
1340-
if (dev->write_buf == NULL)
1367+
if (dev->write_buf == NULL) {
13411368
dev->write_buf = (unsigned char *) malloc(dev->output_report_length);
1369+
1370+
if (dev->write_buf == NULL) {
1371+
register_string_error(dev, L"hid_send_output_report/malloc");
1372+
return -1;
1373+
}
1374+
}
1375+
13421376
buf = dev->write_buf;
13431377
memcpy(buf, data, length);
13441378
memset(buf + length, 0, dev->output_report_length - length);

windows/hidapi_descriptor_reconstruct.c

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,10 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
537537
}
538538
}
539539

540+
BOOLEAN devicehasReportIDs = FALSE;
540541
struct rd_main_item_node *list = main_item_list; // List root;
542+
// Windows pp_data are per top-level collection, therefore top level coll end is unique
543+
struct rd_main_item_node* node_before_top_level_coll_end = NULL;
541544

542545
while (list->next != NULL)
543546
{
@@ -552,12 +555,20 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
552555
struct rd_main_item_node *list_node = rd_search_main_item_list_for_bit_position(last_bit_position[list->MainItemType][list->ReportID], list->MainItemType, list->ReportID, &last_report_item_lookup[list->MainItemType][list->ReportID]);
553556
rd_insert_main_item_node(last_bit_position[list->MainItemType][list->ReportID] + 1, list->FirstBit - 1, rd_item_node_padding, -1, 0, list->MainItemType, list->ReportID, &list_node);
554557
}
558+
if (list->ReportID != 0) {
559+
devicehasReportIDs = TRUE;
560+
}
555561
last_bit_position[list->MainItemType][list->ReportID] = list->LastBit;
556562
last_report_item_lookup[list->MainItemType][list->ReportID] = list;
557563
}
558564
}
565+
if (list->next->MainItemType == rd_collection_end) {
566+
// Store the node before the collection end - the last occurence is the end of the top level collection
567+
node_before_top_level_coll_end = list;
568+
}
559569
list = list->next;
560570
}
571+
561572
// Add 8 bit padding at each report end
562573
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
563574
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
@@ -566,10 +577,31 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
566577
if (padding < 8) {
567578
// Insert padding item after item referenced in last_report_item_lookup
568579
rd_insert_main_item_node(last_bit_position[rt_idx][reportid_idx] + 1, last_bit_position[rt_idx][reportid_idx] + padding, rd_item_node_padding, -1, 0, (rd_main_items) rt_idx, (unsigned char) reportid_idx, &last_report_item_lookup[rt_idx][reportid_idx]);
580+
if (last_report_item_lookup[rt_idx][reportid_idx] == node_before_top_level_coll_end) {
581+
// If this padding item is at the end of the top level collection, update node_before_top_level_coll_end
582+
node_before_top_level_coll_end = last_report_item_lookup[rt_idx][reportid_idx]->next;
583+
}
584+
last_bit_position[rt_idx][reportid_idx] += padding;
569585
}
570586
}
571587
}
572588
}
589+
590+
// Add full byte padding at the end of the report descriptor (only reconstructable, for devices without Report IDs)
591+
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
592+
if (!devicehasReportIDs && pp_data->caps_info[rt_idx].NumberOfCaps > 0 && pp_data->caps_info[rt_idx].ReportByteLength > 0) {
593+
// ReportID 0 means this device uses not Report IDs
594+
// => Maximum one report per type possible, so we can take the size from the buffer size for the report type
595+
const unsigned char reportId = 0;
596+
// ReportByteLength is the report length in bytes plus the one byte for the optional ReportID
597+
int padding = (pp_data->caps_info[rt_idx].ReportByteLength - 1) * 8 - (last_bit_position[rt_idx][reportId] + 1);
598+
599+
if (padding > 0) {
600+
// Insert padding item after item referenced in last_report_item_lookup
601+
rd_insert_main_item_node(last_bit_position[rt_idx][reportId] + 1, last_bit_position[rt_idx][reportId] + padding, rd_item_node_padding, -1, 0, (rd_main_items)rt_idx, reportId, &node_before_top_level_coll_end);
602+
}
603+
}
604+
}
573605
}
574606

575607

@@ -652,13 +684,25 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
652684
else if (main_item_list->TypeOfNode == rd_item_node_padding) {
653685
// Padding
654686
// The preparsed data doesn't contain any information about padding. Therefore all undefined gaps
655-
// in the reports are filled with the same style of constant padding.
687+
// in the reports are filled with the same style of constant bit or byte padding.
688+
689+
if ((main_item_list->LastBit - main_item_list->FirstBit + 1) % 8 == 0) {
690+
// Padding in full bytes
656691

657-
// Write "Report Size" with number of padding bits
658-
rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);
692+
// Write "Report Size" for padding bytes is 8
693+
rd_write_short_item(rd_global_report_size, 8, &rpt_desc);
659694

660-
// Write "Report Count" for padding always as 1
661-
rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
695+
// Write "Report Count" with number of padding bytes
696+
rd_write_short_item(rd_global_report_count, (main_item_list->LastBit - main_item_list->FirstBit + 1) / 8, &rpt_desc);
697+
} else {
698+
// Padding in bits
699+
700+
// Write "Report Size" with number of padding bits
701+
rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);
702+
703+
// Write "Report Count" for padding bits is 1
704+
rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
705+
}
662706

663707
if (rt_idx == HidP_Input) {
664708
// Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const

windows/hidapi_winapi.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,26 @@ extern "C" {
6767
*/
6868
int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size);
6969

70+
/**
71+
* @brief Sets the timeout for hid_write operation.
72+
*
73+
* Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0)
74+
*
75+
* The default timeout is 1sec for winapi backend.
76+
*
77+
* In case if 1sec is not enough, on in case of multi-platform development,
78+
* the recommended value is 5sec, e.g. to match (unconfigurable) 5sec timeout
79+
* set for hidraw (linux kernel) implementation.
80+
*
81+
* When the timeout is set to 0, hid_write function becomes non-blocking and would exit immediately.
82+
* When the timeout is set to INFINITE ((DWORD)-1), the function will not exit,
83+
* until the write operation is performed or an error occured.
84+
* See dwMilliseconds parameter documentation of WaitForSingleObject function.
85+
*
86+
* @param timeout New timeout value in milliseconds.
87+
*/
88+
void HID_API_EXPORT_CALL hid_winapi_set_write_timeout(hid_device *dev, unsigned long timeout);
89+
7090
#ifdef __cplusplus
7191
}
7292
#endif

windows/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ set(HID_DESCRIPTOR_RECONSTRUCT_TEST_CASES
4040
047F_C056_0003_FFA0
4141
047F_C056_0005_000B
4242
045E_02FF_0005_0001
43+
1532_00A3_0002_0001
4344
)
4445

4546
set(CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION "3.22")

windows/test/data/045E_02FF_0005_0001_expected.rpt_desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
0x00, 0x81, 0x02, 0x05, 0x01, 0x09, 0x39, 0x15, 0x01, 0x25,
1010
0x08, 0x35, 0x00, 0x46, 0x3B, 0x10, 0x65, 0x0E, 0x75, 0x04,
1111
0x95, 0x01, 0x81, 0x42, 0x75, 0x04, 0x95, 0x01, 0x81, 0x03,
12-
0xC0,
12+
0x75, 0x08, 0x95, 0x02, 0x81, 0x03, 0xC0,

0 commit comments

Comments
 (0)