Skip to content

Commit

Permalink
Merge branch 'master' into libusb-error
Browse files Browse the repository at this point in the history
  • Loading branch information
Youw authored Nov 20, 2024
2 parents af4b47d + ff67c77 commit c2ed2ca
Show file tree
Hide file tree
Showing 10 changed files with 487 additions and 12 deletions.
4 changes: 4 additions & 0 deletions hidtest/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ int main(int argc, char* argv[])
return 1;
}

#if defined(_WIN32) && HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0)
hid_winapi_set_write_timeout(handle, 5000);
#endif

// Read the Manufacturer String
wstr[0] = 0x0000;
res = hid_get_manufacturer_string(handle, wstr, MAX_STR);
Expand Down
8 changes: 8 additions & 0 deletions libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,10 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
return NULL;

dev = new_hid_device();
if (!dev) {
LOG("hid_open_path failed: Couldn't allocate memory\n");
return NULL;
}

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

dev = new_hid_device();
if (!dev) {
LOG("libusb_wrap_sys_device failed: Couldn't allocate memory\n");
return NULL;
}

res = libusb_wrap_sys_device(usb_context, sys_dev, &dev->device_handle);
if (res < 0) {
Expand Down
46 changes: 40 additions & 6 deletions windows/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ struct hid_device_ {
OVERLAPPED ol;
OVERLAPPED write_ol;
struct hid_device_info* device_info;
DWORD write_timeout_ms;
};

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

return dev;
}
Expand Down Expand Up @@ -1043,7 +1045,10 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)

end_of_function:
free(interface_path);
CloseHandle(device_handle);

if (device_handle != INVALID_HANDLE_VALUE) {
CloseHandle(device_handle);
}

if (pp_data) {
HidD_FreePreparsedData(pp_data);
Expand All @@ -1052,6 +1057,11 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
return dev;
}

void HID_API_EXPORT_CALL hid_winapi_set_write_timeout(hid_device *dev, unsigned long timeout)
{
dev->write_timeout_ms = timeout;
}

int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
{
DWORD bytes_written = 0;
Expand All @@ -1078,15 +1088,22 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *
/* The user passed the right number of bytes. Use the buffer as-is. */
buf = (unsigned char *) data;
} else {
if (dev->write_buf == NULL)
if (dev->write_buf == NULL) {
dev->write_buf = (unsigned char *) malloc(dev->output_report_length);

if (dev->write_buf == NULL) {
register_string_error(dev, L"hid_write/malloc");
goto end_of_function;
}
}

buf = dev->write_buf;
memcpy(buf, data, length);
memset(buf + length, 0, dev->output_report_length - length);
length = dev->output_report_length;
}

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

if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
Expand All @@ -1095,12 +1112,15 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *
goto end_of_function;
}
overlapped = TRUE;
} else {
/* WriteFile() succeeded synchronously. */
function_result = bytes_written;
}

if (overlapped) {
/* Wait for the transaction to complete. This makes
hid_write() synchronous. */
res = WaitForSingleObject(dev->write_ol.hEvent, 1000);
res = WaitForSingleObject(dev->write_ol.hEvent, dev->write_timeout_ms);
if (res != WAIT_OBJECT_0) {
/* There was a Timeout. */
register_winapi_error(dev, L"hid_write/WaitForSingleObject");
Expand Down Expand Up @@ -1243,8 +1263,15 @@ int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const u
buf = (unsigned char *) data;
length_to_send = length;
} else {
if (dev->feature_buf == NULL)
if (dev->feature_buf == NULL) {
dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length);

if (dev->feature_buf == NULL) {
register_string_error(dev, L"hid_send_feature_report/malloc");
return -1;
}
}

buf = dev->feature_buf;
memcpy(buf, data, length);
memset(buf + length, 0, dev->feature_report_length - length);
Expand Down Expand Up @@ -1337,8 +1364,15 @@ int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device* dev, const un
buf = (unsigned char *) data;
length_to_send = length;
} else {
if (dev->write_buf == NULL)
if (dev->write_buf == NULL) {
dev->write_buf = (unsigned char *) malloc(dev->output_report_length);

if (dev->write_buf == NULL) {
register_string_error(dev, L"hid_send_output_report/malloc");
return -1;
}
}

buf = dev->write_buf;
memcpy(buf, data, length);
memset(buf + length, 0, dev->output_report_length - length);
Expand Down
54 changes: 49 additions & 5 deletions windows/hidapi_descriptor_reconstruct.c
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,10 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
}
}

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

while (list->next != NULL)
{
Expand All @@ -552,12 +555,20 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
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]);
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);
}
if (list->ReportID != 0) {
devicehasReportIDs = TRUE;
}
last_bit_position[list->MainItemType][list->ReportID] = list->LastBit;
last_report_item_lookup[list->MainItemType][list->ReportID] = list;
}
}
if (list->next->MainItemType == rd_collection_end) {
// Store the node before the collection end - the last occurence is the end of the top level collection
node_before_top_level_coll_end = list;
}
list = list->next;
}

// Add 8 bit padding at each report end
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) {
Expand All @@ -566,10 +577,31 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
if (padding < 8) {
// Insert padding item after item referenced in last_report_item_lookup
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]);
if (last_report_item_lookup[rt_idx][reportid_idx] == node_before_top_level_coll_end) {
// If this padding item is at the end of the top level collection, update node_before_top_level_coll_end
node_before_top_level_coll_end = last_report_item_lookup[rt_idx][reportid_idx]->next;
}
last_bit_position[rt_idx][reportid_idx] += padding;
}
}
}
}

// Add full byte padding at the end of the report descriptor (only reconstructable, for devices without Report IDs)
for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) {
if (!devicehasReportIDs && pp_data->caps_info[rt_idx].NumberOfCaps > 0 && pp_data->caps_info[rt_idx].ReportByteLength > 0) {
// ReportID 0 means this device uses not Report IDs
// => Maximum one report per type possible, so we can take the size from the buffer size for the report type
const unsigned char reportId = 0;
// ReportByteLength is the report length in bytes plus the one byte for the optional ReportID
int padding = (pp_data->caps_info[rt_idx].ReportByteLength - 1) * 8 - (last_bit_position[rt_idx][reportId] + 1);

if (padding > 0) {
// Insert padding item after item referenced in last_report_item_lookup
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);
}
}
}
}


Expand Down Expand Up @@ -652,13 +684,25 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha
else if (main_item_list->TypeOfNode == rd_item_node_padding) {
// Padding
// The preparsed data doesn't contain any information about padding. Therefore all undefined gaps
// in the reports are filled with the same style of constant padding.
// in the reports are filled with the same style of constant bit or byte padding.

if ((main_item_list->LastBit - main_item_list->FirstBit + 1) % 8 == 0) {
// Padding in full bytes

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

// Write "Report Count" for padding always as 1
rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
// Write "Report Count" with number of padding bytes
rd_write_short_item(rd_global_report_count, (main_item_list->LastBit - main_item_list->FirstBit + 1) / 8, &rpt_desc);
} else {
// Padding in bits

// Write "Report Size" with number of padding bits
rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc);

// Write "Report Count" for padding bits is 1
rd_write_short_item(rd_global_report_count, 1, &rpt_desc);
}

if (rt_idx == HidP_Input) {
// 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
Expand Down
20 changes: 20 additions & 0 deletions windows/hidapi_winapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,26 @@ extern "C" {
*/
int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size);

/**
* @brief Sets the timeout for hid_write operation.
*
* Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0)
*
* The default timeout is 1sec for winapi backend.
*
* In case if 1sec is not enough, on in case of multi-platform development,
* the recommended value is 5sec, e.g. to match (unconfigurable) 5sec timeout
* set for hidraw (linux kernel) implementation.
*
* When the timeout is set to 0, hid_write function becomes non-blocking and would exit immediately.
* When the timeout is set to INFINITE ((DWORD)-1), the function will not exit,
* until the write operation is performed or an error occured.
* See dwMilliseconds parameter documentation of WaitForSingleObject function.
*
* @param timeout New timeout value in milliseconds.
*/
void HID_API_EXPORT_CALL hid_winapi_set_write_timeout(hid_device *dev, unsigned long timeout);

#ifdef __cplusplus
}
#endif
Expand Down
1 change: 1 addition & 0 deletions windows/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ set(HID_DESCRIPTOR_RECONSTRUCT_TEST_CASES
047F_C056_0003_FFA0
047F_C056_0005_000B
045E_02FF_0005_0001
1532_00A3_0002_0001
)

set(CMAKE_VERSION_SUPPORTS_ENVIRONMENT_MODIFICATION "3.22")
Expand Down
2 changes: 1 addition & 1 deletion windows/test/data/045E_02FF_0005_0001_expected.rpt_desc
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
0x00, 0x81, 0x02, 0x05, 0x01, 0x09, 0x39, 0x15, 0x01, 0x25,
0x08, 0x35, 0x00, 0x46, 0x3B, 0x10, 0x65, 0x0E, 0x75, 0x04,
0x95, 0x01, 0x81, 0x42, 0x75, 0x04, 0x95, 0x01, 0x81, 0x03,
0xC0,
0x75, 0x08, 0x95, 0x02, 0x81, 0x03, 0xC0,
Loading

0 comments on commit c2ed2ca

Please sign in to comment.