Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: add hid_get_report_descriptor API function #451

Merged
merged 24 commits into from
Mar 12, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a48f6d8
add hid_get_report_descriptor API function
Youw Feb 5, 2022
07d80f1
Merge remote-tracking branch 'upstream/Master' into merge_0_12_master…
JoergAtGithub Jun 12, 2022
2dacc46
Ignore MINGW warning: ISO C forbids zero-size array
JoergAtGithub Jun 12, 2022
987a416
Fixed signed/unsigned compare warning
JoergAtGithub Jun 12, 2022
27251b3
Disable MinGW scanf/printf format warnings
JoergAtGithub Jun 12, 2022
65b9e71
Merge pull request #427 from JoergAtGithub/merge_0_12_master_in_get_d…
Youw Jun 12, 2022
5b6ec28
Introduce HID_API_MAX_REPORT_DESCRIPTOR_SIZE
Youw Sep 10, 2022
b8276a9
Merge branch 'report-descriptor-upd' into get-descriptor
Youw Sep 10, 2022
d9d4c7a
fix build
Youw Sep 10, 2022
7e3a406
Replaced linked list rd_item_byte by buffer array (#455)
JoergAtGithub Sep 12, 2022
795fe85
Merge remote-tracking branch 'upstream/master' into get-descriptor
JoergAtGithub Oct 15, 2022
c8061df
Added two register_device_error statements for hid_get_report_descrip…
JoergAtGithub Oct 15, 2022
b246f67
Merge pull request #465 from JoergAtGithub/get-descriptor
Youw Oct 15, 2022
5626729
Fail CTest when hid report reconstruct doesn't match
Youw Oct 17, 2022
81160e9
Update error message
Youw Oct 17, 2022
53cf563
Add pp_data_dump tool (#467)
JoergAtGithub Jan 2, 2023
e573d64
Merged 0.13.0 into get-descriptor branch
JoergAtGithub Jan 5, 2023
53ad3bd
Merge master into get descriptor
Youw Jan 5, 2023
3bce889
Fix formatting warnings reported by clang_cl (#505)
JoergAtGithub Feb 11, 2023
b74739e
Merge branch 'master' into get-descriptor
Youw Feb 19, 2023
624ae35
CMake option to build with ASAN (#463)
JoergAtGithub Feb 19, 2023
32ea0a2
windows: descriptor_reconstruct: fix the order of sub-collections
jkcmusork Feb 22, 2023
6f15653
windows: descriptor_reconstruct: Test for Xbox One For Windows (#510)
JoergAtGithub Feb 22, 2023
f41e5dc
Merge branch 'master' into get-descriptor
Youw Feb 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions .github/workflows/builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ jobs:
- name: Configure CMake MSVC
shell: cmd
run: |
cmake -B build\msvc -S hidapisrc -DCMAKE_INSTALL_PREFIX=install\msvc -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
cmake -B build\msvc -S hidapisrc -DHIDAPI_WITH_TESTS=ON -DCMAKE_INSTALL_PREFIX=install\msvc -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
- name: Build CMake MSVC
working-directory: build/msvc
run: cmake --build . --target install
run: cmake --build . --config RelWithDebInfo --target install
- name: Check artifacts MSVC
uses: andstor/file-existence-action@v1
with:
Expand All @@ -136,12 +136,15 @@ jobs:
install/msvc/include/hidapi/hidapi.h, \
install/msvc/include/hidapi/hidapi_winapi.h"
allow_failure: true
- name: Run CTest MSVC
working-directory: build/msvc
run: ctest -C RelWithDebInfo --output-on-failure

- name: Configure CMake NMake
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
cmake -G"NMake Makefiles" -B build\nmake -S hidapisrc -DCMAKE_INSTALL_PREFIX=install\nmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
cmake -G"NMake Makefiles" -B build\nmake -S hidapisrc -DHIDAPI_WITH_TESTS=ON -DCMAKE_INSTALL_PREFIX=install\nmake -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%MSVC_COMPILE_FLAGS%"
- name: Build CMake NMake
working-directory: build\nmake
shell: cmd
Expand All @@ -156,11 +159,14 @@ jobs:
install/nmake/include/hidapi/hidapi.h, \
install/nmake/include/hidapi/hidapi_winapi.h"
allow_failure: true
- name: Run CTest NMake
working-directory: build\nmake
run: ctest --output-on-failure

- name: Configure CMake MinGW
shell: cmd
run: |
cmake -G"MinGW Makefiles" -B build\mingw -S hidapisrc -DCMAKE_INSTALL_PREFIX=install\mingw -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%NIX_COMPILE_FLAGS%"
cmake -G"MinGW Makefiles" -B build\mingw -S hidapisrc -DHIDAPI_WITH_TESTS=ON -DCMAKE_INSTALL_PREFIX=install\mingw -DHIDAPI_BUILD_HIDTEST=ON "-DCMAKE_C_FLAGS=%NIX_COMPILE_FLAGS%"
- name: Build CMake MinGW
working-directory: build\mingw
run: cmake --build . --target install
Expand All @@ -172,6 +178,9 @@ jobs:
install/mingw/include/hidapi/hidapi.h, \
install/mingw/include/hidapi/hidapi_winapi.h"
allow_failure: true
- name: Run CTest MinGW
working-directory: build\mingw
run: ctest --output-on-failure

- name: Check Meson build
shell: cmd
Expand Down
2 changes: 2 additions & 0 deletions BUILD.cmake.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ Some of the [standard](https://cmake.org/cmake/help/latest/manual/cmake-variable
HIDAPI-specific CMake variables:

- `HIDAPI_BUILD_HIDTEST` - when set to TRUE, build a small test application `hidtest`;
- `HIDAPI_WITH_TESTS` - when set to TRUE, build all (unit-)tests;
currently this option is only available on Windows, since only Windows backend has tests;

<details>
<summary>Linux-specific variables</summary>
Expand Down
22 changes: 17 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,25 @@ option(BUILD_SHARED_LIBS "Build shared version of the libraries, otherwise build
set(HIDAPI_INSTALL_TARGETS ON)
set(HIDAPI_PRINT_VERSION ON)

add_subdirectory(src)

set(BUILD_HIDTEST_DEFAULT OFF)
set(IS_DEBUG_BUILD OFF)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(BUILD_HIDTEST_DEFAULT ON)
set(IS_DEBUG_BUILD ON)
endif()
option(HIDAPI_BUILD_HIDTEST "Build small console test application hidtest" ${BUILD_HIDTEST_DEFAULT})

if(WIN32)
# so far only Windows has tests
option(HIDAPI_WITH_TESTS "Build HIDAPI (unit-)tests" ${IS_DEBUG_BUILD})
else()
set(HIDAPI_WITH_TESTS OFF)
endif()

if(HIDAPI_WITH_TESTS)
enable_testing()
endif()

add_subdirectory(src)

option(HIDAPI_BUILD_HIDTEST "Build small console test application hidtest" ${IS_DEBUG_BUILD})
if(HIDAPI_BUILD_HIDTEST)
add_subdirectory(hidtest)
endif()
15 changes: 15 additions & 0 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,21 @@ extern "C" {
*/
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);

/** @brief Get a report descriptor from a HID device.

User has to provide a preallocated buffer where descriptor will be copied to.
The recommended size for preallocated buffer is @ref HID_API_MAX_REPORT_DESCRIPTOR_SIZE bytes.

@ingroup API
@param dev A device handle returned from hid_open().
@param buf The buffer to copy descriptor into.
@param buf_size The size of the buffer in bytes.

@returns
This function returns non-negative number of bytes actually copied, or -1 on error.
*/
int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size);

/** @brief Get a string describing the last error which occurred.

This function is intended for logging/debugging purposes.
Expand Down
49 changes: 44 additions & 5 deletions hidtest/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,48 @@ void print_device(struct hid_device_info *cur_dev) {
printf("\n");
}

void print_hid_report_descriptor_from_device(hid_device *device) {
unsigned char descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE];
int res = 0;

printf(" Report Descriptor: ");
res = hid_get_report_descriptor(device, descriptor, sizeof(descriptor));
if (res < 0) {
printf("error getting: %ls", hid_error(device));
}
else {
printf("(%d bytes)", res);
}
for (int i = 0; i < res; i++) {
if (i % 10 == 0) {
printf("\n");
}
printf("0x%02x, ", descriptor[i]);
}
printf("\n");
}

void print_hid_report_descriptor_from_path(const char *path) {
hid_device *device = hid_open_path(path);
if (device) {
print_hid_report_descriptor_from_device(device);
hid_close(device);
}
else {
printf(" Report Descriptor: not available.\n");
Youw marked this conversation as resolved.
Show resolved Hide resolved
}
}

void print_devices(struct hid_device_info *cur_dev) {
while (cur_dev) {
for (; cur_dev; cur_dev = cur_dev->next) {
print_device(cur_dev);
}
}

void print_devices_with_descriptor(struct hid_device_info *cur_dev) {
for (; cur_dev; cur_dev = cur_dev->next) {
print_device(cur_dev);
cur_dev = cur_dev->next;
print_hid_report_descriptor_from_path(cur_dev->path);
}
}

Expand Down Expand Up @@ -103,7 +141,7 @@ int main(int argc, char* argv[])
#endif

devs = hid_enumerate(0x0, 0x0);
print_devices(devs);
print_devices_with_descriptor(devs);
hid_free_enumeration(devs);

// Set up the command buffer.
Expand Down Expand Up @@ -140,8 +178,9 @@ int main(int argc, char* argv[])
res = hid_get_serial_number_string(handle, wstr, MAX_STR);
if (res < 0)
printf("Unable to read serial number string\n");
printf("Serial Number String: (%d) %ls", wstr[0], wstr);
printf("\n");
printf("Serial Number String: (%d) %ls\n", wstr[0], wstr);

print_hid_report_descriptor_from_device(handle);

struct hid_device_info* info = hid_get_device_info(handle);
if (info == NULL) {
Expand Down
6 changes: 6 additions & 0 deletions libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,12 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index
}


int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
{
return hid_get_report_descriptor_libusb(dev->device_handle, dev->interface, dev->report_descriptor_size, buf, buf_size);
}


HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
(void)dev;
Expand Down
41 changes: 41 additions & 0 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,28 @@ static int parse_hid_vid_pid_from_sysfs(const char *sysfs_path, unsigned *bus_ty
return res;
}

static int get_hid_report_descriptor_from_hidraw(hid_device *dev, struct hidraw_report_descriptor *rpt_desc)
{
int desc_size = 0;

/* Get Report Descriptor Size */
int res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
if (res < 0) {
register_device_error_format(dev, "ioctl(GRDESCSIZE): %s", strerror(errno));
return res;
}

/* Get Report Descriptor */
memset(rpt_desc, 0x0, sizeof(*rpt_desc));
rpt_desc->size = desc_size;
res = ioctl(dev->device_handle, HIDIOCGRDESC, rpt_desc);
if (res < 0) {
register_device_error_format(dev, "ioctl(GRDESC): %s", strerror(errno));
}

return res;
}

/*
* The caller is responsible for free()ing the (newly-allocated) character
* strings pointed to by serial_number_utf8 and product_name_utf8 after use.
Expand Down Expand Up @@ -1227,6 +1249,25 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index
}


int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
{
struct hidraw_report_descriptor rpt_desc;
int res = get_hid_report_descriptor_from_hidraw(dev, &rpt_desc);
if (res < 0) {
/* error already registered */
return res;
}

if (rpt_desc.size < buf_size) {
buf_size = (size_t) rpt_desc.size;
}

memcpy(buf, rpt_desc.value, buf_size);

return (int) buf_size;
}


/* Passing in NULL means asking for the last global error message. */
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
Expand Down
27 changes: 27 additions & 0 deletions mac/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1430,6 +1430,33 @@ int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev)
return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
}

int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
{
CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey));
if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) {
CFDataRef report_descriptor = (CFDataRef) ref;
const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor);
CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor);
size_t copy_len = (size_t) descriptor_buf_len;

if (descriptor_buf == NULL || descriptor_buf_len < 0) {
register_device_error(dev, "Zero buffer/length");
return -1;
}

if (buf_size < copy_len) {
copy_len = buf_size;
}

memcpy(buf, descriptor_buf, copy_len);
return copy_len;
}
else {
register_device_error(dev, "Failed to get kIOHIDReportDescriptorKey property");
return -1;
}
}

HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
if (dev) {
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ project(hidapi VERSION "${VERSION}" LANGUAGES C)

# Defaults and required options

if(NOT DEFINED HIDAPI_WITH_TESTS)
set(HIDAPI_WITH_TESTS OFF)
endif()
if(NOT DEFINED BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON)
endif()
Expand Down
6 changes: 6 additions & 0 deletions windows/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_winapi.h")
set(SOURCES
hid.c
hidapi_cfgmgr32.h
hidapi_descriptor_reconstruct.c
hidapi_descriptor_reconstruct.h
hidapi_hidclass.h
hidapi_hidpi.h
hidapi_hidsdi.h
Expand Down Expand Up @@ -43,3 +45,7 @@ if(HIDAPI_INSTALL_TARGETS)
endif()

hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in")

if(HIDAPI_WITH_TESTS)
add_subdirectory(test)
endif()
22 changes: 21 additions & 1 deletion windows/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
extern "C" {
#endif

#include "hidapi.h"
#include "hidapi_winapi.h"

#include <windows.h>

Expand Down Expand Up @@ -1307,6 +1307,22 @@ int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *conta
}


int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
{
PHIDP_PREPARSED_DATA pp_data = NULL;

if (!HidD_GetPreparsedData(dev->device_handle, &pp_data) || pp_data == NULL) {
register_string_error(dev, L"HidD_GetPreparsedData");
return -1;
}

int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, buf, buf_size);

HidD_FreePreparsedData(pp_data);

return res;
}

HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
if (dev) {
Expand All @@ -1320,6 +1336,10 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
return last_global_error_str;
}

#ifndef hidapi_winapi_EXPORTS
#include "hidapi_descriptor_reconstruct.c"
#endif

#ifdef __cplusplus
} /* extern "C" */
#endif
Loading