From db3e8e714435c3cda0ff52f4bac0e83bda94951a Mon Sep 17 00:00:00 2001 From: CEnnis91 Date: Sat, 13 Nov 2021 13:17:18 -0500 Subject: [PATCH] update lib and fix windows: https://github.com/libusb/hidapi/pull/334 git subrepo pull custom/dependencies/hidapi subrepo: subdir: "custom/dependencies/hidapi" merged: "b66d7c2c" upstream: origin: "https://github.com/libusb/hidapi.git" branch: "master" commit: "b66d7c2c" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo" commit: "2f68596" --- .../hidapi/.github/workflows/builds.yml | 18 +- custom/dependencies/hidapi/.gitignore | 4 + custom/dependencies/hidapi/.gitrepo | 4 +- custom/dependencies/hidapi/AUTHORS.txt | 2 +- custom/dependencies/hidapi/BUILD.autotools.md | 10 +- custom/dependencies/hidapi/BUILD.cmake.md | 6 + custom/dependencies/hidapi/BUILD.md | 24 +- custom/dependencies/hidapi/README.md | 29 +- custom/dependencies/hidapi/VERSION | 2 +- custom/dependencies/hidapi/configure.ac | 3 + custom/dependencies/hidapi/hidtest/test.c | 7 +- .../dependencies/hidapi/libusb/CMakeLists.txt | 2 + custom/dependencies/hidapi/libusb/Makefile.am | 2 +- custom/dependencies/hidapi/libusb/hid.c | 460 ++++++++------ .../hidapi/libusb/hidapi_libusb.h | 54 ++ custom/dependencies/hidapi/mac/hid.c | 63 +- custom/dependencies/hidapi/src/CMakeLists.txt | 3 + custom/dependencies/hidapi/udev/69-hid.rules | 13 +- .../dependencies/hidapi/windows/Makefile.am | 1 - .../hidapi/windows/ddk_build/.gitignore | 2 - .../hidapi/windows/ddk_build/hidapi.def | 18 - .../hidapi/windows/ddk_build/makefile | 49 -- .../hidapi/windows/ddk_build/sources | 23 - custom/dependencies/hidapi/windows/hid.c | 560 +++++++++++------- .../hidapi/windows/hidapi.vcxproj.filters | 27 + .../hidapi/windows/hidtest.vcxproj.filters | 22 + 26 files changed, 863 insertions(+), 545 deletions(-) create mode 100644 custom/dependencies/hidapi/libusb/hidapi_libusb.h delete mode 100644 custom/dependencies/hidapi/windows/ddk_build/.gitignore delete mode 100644 custom/dependencies/hidapi/windows/ddk_build/hidapi.def delete mode 100644 custom/dependencies/hidapi/windows/ddk_build/makefile delete mode 100644 custom/dependencies/hidapi/windows/ddk_build/sources create mode 100644 custom/dependencies/hidapi/windows/hidapi.vcxproj.filters create mode 100644 custom/dependencies/hidapi/windows/hidtest.vcxproj.filters diff --git a/custom/dependencies/hidapi/.github/workflows/builds.yml b/custom/dependencies/hidapi/.github/workflows/builds.yml index 72d1c3358..f1bd56c69 100644 --- a/custom/dependencies/hidapi/.github/workflows/builds.yml +++ b/custom/dependencies/hidapi/.github/workflows/builds.yml @@ -47,7 +47,10 @@ jobs: - name: Check artifacts uses: andstor/file-existence-action@v1 with: - files: "install/shared/lib/libhidapi.dylib, install/shared/include/hidapi/hidapi.h, install/framework/lib/hidapi.framework/hidapi, install/framework/lib/hidapi.framework/Headers/hidapi.h" + files: "install/shared/lib/libhidapi.dylib, \ + install/shared/include/hidapi/hidapi.h, \ + install/framework/lib/hidapi.framework/hidapi, \ + install/framework/lib/hidapi.framework/Headers/hidapi.h" allow_failure: true ubuntu-cmake: @@ -59,7 +62,9 @@ jobs: with: path: hidapisrc - name: Install dependencies - run: sudo apt install libudev-dev libusb-1.0-0-dev + run: | + sudo apt update + sudo apt install libudev-dev libusb-1.0-0-dev - name: Configure CMake run: | rm -rf build install @@ -74,7 +79,14 @@ jobs: - name: Check artifacts uses: andstor/file-existence-action@v1 with: - files: "install/shared/lib/libhidapi-libusb.so, install/shared/lib/libhidapi-hidraw.so, install/shared/include/hidapi/hidapi.h, install/static/lib/libhidapi-libusb.a, install/static/lib/libhidapi-hidraw.a, install/static/include/hidapi/hidapi.h" + files: "install/shared/lib/libhidapi-libusb.so, \ + install/shared/lib/libhidapi-hidraw.so, \ + install/shared/include/hidapi/hidapi.h, \ + install/shared/include/hidapi/hidapi_libusb.h, \ + install/static/lib/libhidapi-libusb.a, \ + install/static/lib/libhidapi-hidraw.a, \ + install/static/include/hidapi/hidapi.h, \ + install/static/include/hidapi/hidapi_libusb.h" allow_failure: true windows-cmake-msvc: diff --git a/custom/dependencies/hidapi/.gitignore b/custom/dependencies/hidapi/.gitignore index 9963751f3..24c4c2e51 100644 --- a/custom/dependencies/hidapi/.gitignore +++ b/custom/dependencies/hidapi/.gitignore @@ -21,4 +21,8 @@ Makefile stamp-h1 libtool +# macOS .DS_Store + +# Qt Creator +CMakeLists.txt.user diff --git a/custom/dependencies/hidapi/.gitrepo b/custom/dependencies/hidapi/.gitrepo index 5d64ed0e9..023fc09b8 100644 --- a/custom/dependencies/hidapi/.gitrepo +++ b/custom/dependencies/hidapi/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/libusb/hidapi.git branch = master - commit = 382138ee67adf539f60fc986a3409cf1f1fc3cee - parent = b0b794fdc973191c6e3c46cc1cd0adb45f81d033 + commit = b66d7c2c889d39cdce8eedc50692c318911b04a9 + parent = 9ede812a19ae69de06ec4d0fb5227b67c1b0327c method = merge cmdver = 0.4.3 diff --git a/custom/dependencies/hidapi/AUTHORS.txt b/custom/dependencies/hidapi/AUTHORS.txt index e08cb1619..7193d71e0 100644 --- a/custom/dependencies/hidapi/AUTHORS.txt +++ b/custom/dependencies/hidapi/AUTHORS.txt @@ -14,5 +14,5 @@ libusb/hidapi Team: Development/maintainance since June 4th 2019 For a comprehensive list of contributions, see the commit list at github: - https://github.com/libusb/hidapi/commits/master + https://github.com/libusb/hidapi/graphs/contributors diff --git a/custom/dependencies/hidapi/BUILD.autotools.md b/custom/dependencies/hidapi/BUILD.autotools.md index 1bac2bbaf..24b20a5af 100644 --- a/custom/dependencies/hidapi/BUILD.autotools.md +++ b/custom/dependencies/hidapi/BUILD.autotools.md @@ -1,4 +1,12 @@ -# Building HIDAPI using Autotools +# Building HIDAPI using Autotools (deprecated) + +--- +**NOTE**: for all intentions and purposes the Autotools build scripts for HIDAPI are _deprecated_ and going to be obsolete in the future. +HIDAPI Team recommends using CMake build for HIDAPI. +If you are already using Autotools build scripts provided by HIDAPI, +consider switching to CMake build scripts as soon as possible. + +--- To be able to use Autotools to build HIDAPI, it has to be [installed](#installing-autotools)/available in the system. diff --git a/custom/dependencies/hidapi/BUILD.cmake.md b/custom/dependencies/hidapi/BUILD.cmake.md index c40f89396..364c815e3 100644 --- a/custom/dependencies/hidapi/BUILD.cmake.md +++ b/custom/dependencies/hidapi/BUILD.cmake.md @@ -179,6 +179,12 @@ set(BUILD_SHARED_LIBS FALSE) # HIDAPI as static library on all platforms add_subdirectory(hidapi) ``` +
+ NOTE + + If you project happen to use `BUILD_SHARED_LIBS` as a `CACHE` variable globally for you project, setting it as simple variable, as showed above _will have not affect_ up until _CMake 3.13_. See [CMP0077](https://cmake.org/cmake/help/latest/policy/CMP0077.html) for details. +

+ There are several important differences in the behavior of HIDAPI CMake build system when CMake is built as standalone package vs subdirectory build: 1) In _standalone build_ a number of standard and HIDAPI-specific variables are marked as _cache variables_ or _options_. diff --git a/custom/dependencies/hidapi/BUILD.md b/custom/dependencies/hidapi/BUILD.md index 51abc656c..b78e82fe4 100644 --- a/custom/dependencies/hidapi/BUILD.md +++ b/custom/dependencies/hidapi/BUILD.md @@ -17,12 +17,14 @@ For various reasons you may need to build HIDAPI on your own. It can be done in several different ways: -- using [Autotools](BUILD.autotools.md); - using [CMake](BUILD.cmake.md); +- using [Autotools](BUILD.autotools.md) (deprecated); - using [manual makefiles](#building-the-manual-way-on-unix-platforms). **Autotools** build system is historically first mature build system for -HIDAPI. Most common usage of it is in its separate README: [BUILD.autotools.md](BUILD.autotools.md). +HIDAPI. Most common usage of it is in its separate README: [BUILD.autotools.md](BUILD.autotools.md).
+NOTE: for all intentions and purposes the Autotools build scripts for HIDAPI are _deprecated_ and going to be obsolete in the future. +HIDAPI Team recommends using CMake build for HIDAPI. **CMake** build system is de facto an industry standard for many open-source and proprietary projects and solutions. HIDAPI is one of the projects which uses the power of CMake for its advantage. @@ -104,19 +106,5 @@ To build HIDAPI using MinGW or Cygwin using Autotools, use a general Autotools Any windows builds (MSVC or MinGW/Cygwin) are also supported by [CMake](BUILD.cmake.md). -HIDAPI can also be built using the Windows DDK (now also called the Windows -Driver Kit or WDK). This method was originally required for the HIDAPI build -but not anymore. However, some users still prefer this method. It is not as -well supported anymore but should still work. Patches are welcome if it does -not. To build using the DDK: - - 1. Install the Windows Driver Kit (WDK) from Microsoft. - 2. From the Start menu, in the Windows Driver Kits folder, select Build - Environments, then your operating system, then the x86 Free Build - Environment (or one that is appropriate for your system). - 3. From the console, change directory to the `windows/ddk_build/` directory, - which is part of the HIDAPI distribution. - 4. Type build. - 5. You can find the output files (DLL and LIB) in a subdirectory created - by the build system which is appropriate for your environment. On - Windows XP, this directory is `objfre_wxp_x86/i386`. +If you are looking for information regarding DDK build of HIDAPI +- the build has been broken for a while and now the support files are obsolete. diff --git a/custom/dependencies/hidapi/README.md b/custom/dependencies/hidapi/README.md index 53705cbb3..c2f378f89 100644 --- a/custom/dependencies/hidapi/README.md +++ b/custom/dependencies/hidapi/README.md @@ -20,6 +20,7 @@ It was moved to [libusb/hidapi](https://github.com/libusb/hidapi) on June 4th, 2 * [About](#about) * [Test GUI](#test-gui) + * [Console Test App](#console-test-app) * [What Does the API Look Like?](#what-does-the-api-look-like) * [License](#license) * [Installing HIDAPI](#installing-hidapi) @@ -28,7 +29,7 @@ It was moved to [libusb/hidapi](https://github.com/libusb/hidapi) on June 4th, 2 ## About -HIDAPI has four back-ends: +### HIDAPI has four back-ends: * Windows (using `hid.dll`) * Linux/hidraw (using the Kernel's hidraw driver) * libusb (using libusb-1.0 - Linux/BSD/other UNIX-like systems) @@ -45,7 +46,7 @@ for unprivileged users to be able to access HID devices with hidapi. Refer to the [69-hid.rules](udev/69-hid.rules) file in the `udev` directory for an example. -__Linux/hidraw__ (`linux/hid.c`): +#### __Linux/hidraw__ (`linux/hid.c`): This back-end uses the hidraw interface in the Linux kernel, and supports both USB and Bluetooth HID devices. It requires kernel version at least 2.6.39 @@ -55,7 +56,7 @@ Keyboards, mice, and some other devices which are blacklisted from having hidraw nodes will not work. Fortunately, for nearly all the uses of hidraw, this is not a problem. -__Linux/FreeBSD/libusb__ (`libusb/hid.c`): +#### __Linux/FreeBSD/libusb__ (`libusb/hid.c`): This back-end uses libusb-1.0 to communicate directly to a USB device. This back-end will of course not work with Bluetooth devices. @@ -65,7 +66,7 @@ back-end will of course not work with Bluetooth devices. HIDAPI also comes with a Test GUI. The Test GUI is cross-platform and uses Fox Toolkit . It will build on every platform which HIDAPI supports. Since it relies on a 3rd party library, building it -is optional but recommended because it is so useful when debugging hardware. +is optional but it is useful when debugging hardware. NOTE: Test GUI based on Fox Toolkit is not actively developed nor supported by HIDAPI team. It is kept as a historical artifact. It may even work sometime @@ -74,6 +75,14 @@ or on some platforms, but it is not going to get any new features or bugfixes. Instructions for installing Fox-Toolkit on each platform is not provided. Make sure to use Fox-Toolkit v1.6 if you choose to use it. +### Console Test App + +If you want to play around with your HID device before starting +any development with HIDAPI and using a GUI app is not an option for you, you may try [`hidapitester`](https://github.com/todbot/hidapitester). + +This app has a console interface for most of the features supported +by HIDAPI library. + ## What Does the API Look Like? The API provides the most commonly used HID functions including sending @@ -83,15 +92,13 @@ Generic HID sample looks like this (with error checking removed for simplicity): **Warning: Only run the code you understand, and only when it conforms to the -device spec. Writing data at random to your HID devices can break them.** +device spec. Writing data (`hid_write`) at random to your HID devices can break them.** ```c -#ifdef WIN32 -#include -#endif -#include -#include -#include "hidapi.h" +#include // printf +#include // wprintf + +#include #define MAX_STR 255 diff --git a/custom/dependencies/hidapi/VERSION b/custom/dependencies/hidapi/VERSION index eb1336c84..d9df1bbc0 100644 --- a/custom/dependencies/hidapi/VERSION +++ b/custom/dependencies/hidapi/VERSION @@ -1 +1 @@ -0.11.0-dev +0.11.0 diff --git a/custom/dependencies/hidapi/configure.ac b/custom/dependencies/hidapi/configure.ac index 8d9311cc4..19cc1e607 100644 --- a/custom/dependencies/hidapi/configure.ac +++ b/custom/dependencies/hidapi/configure.ac @@ -2,6 +2,9 @@ AC_PREREQ(2.63) AC_INIT([hidapi],[m4_normalize(m4_builtin([include], VERSION))],[https://github.com/libusb/hidapi/issues]) +echo "This build script for HIDAPI is deprecated." +echo "Consider using CMake instead." + # Library soname version # Follow the following rules (particularly the ones in the second link): # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html diff --git a/custom/dependencies/hidapi/hidtest/test.c b/custom/dependencies/hidapi/hidtest/test.c index bbea421e3..03956ead0 100644 --- a/custom/dependencies/hidapi/hidtest/test.c +++ b/custom/dependencies/hidapi/hidtest/test.c @@ -18,7 +18,8 @@ #include #include #include -#include "hidapi.h" + +#include // Headers needed for sleeping. #ifdef _WIN32 @@ -175,7 +176,7 @@ int main(int argc, char* argv[]) printf("waiting...\n"); if (res < 0) printf("Unable to read()\n"); - #ifdef WIN32 + #ifdef _WIN32 Sleep(500); #else usleep(500*1000); @@ -193,7 +194,7 @@ int main(int argc, char* argv[]) /* Free static HIDAPI objects. */ hid_exit(); -#ifdef WIN32 +#ifdef _WIN32 system("pause"); #endif diff --git a/custom/dependencies/hidapi/libusb/CMakeLists.txt b/custom/dependencies/hidapi/libusb/CMakeLists.txt index 99e2a9623..f4392d7a8 100644 --- a/custom/dependencies/hidapi/libusb/CMakeLists.txt +++ b/custom/dependencies/hidapi/libusb/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR) +list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_libusb.h") + add_library(hidapi_libusb ${HIDAPI_PUBLIC_HEADERS} hid.c diff --git a/custom/dependencies/hidapi/libusb/Makefile.am b/custom/dependencies/hidapi/libusb/Makefile.am index 1da06bc16..6964ebbbc 100644 --- a/custom/dependencies/hidapi/libusb/Makefile.am +++ b/custom/dependencies/hidapi/libusb/Makefile.am @@ -29,6 +29,6 @@ libhidapi_la_LIBADD = $(LIBS_LIBUSB) endif hdrdir = $(includedir)/hidapi -hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h +hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h hidapi_libusb.h EXTRA_DIST = Makefile-manual diff --git a/custom/dependencies/hidapi/libusb/hid.c b/custom/dependencies/hidapi/libusb/hid.c index 4e297192a..756a5916c 100644 --- a/custom/dependencies/hidapi/libusb/hid.c +++ b/custom/dependencies/hidapi/libusb/hid.c @@ -49,7 +49,7 @@ #include #endif -#include "hidapi.h" +#include "hidapi_libusb.h" #if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__ @@ -569,12 +569,16 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, struct libusb_device_descriptor desc; struct libusb_config_descriptor *conf_desc = NULL; int j, k; - int interface_num = 0; int res = libusb_get_device_descriptor(dev, &desc); unsigned short dev_vid = desc.idVendor; unsigned short dev_pid = desc.idProduct; + if ((vendor_id != 0x0 && vendor_id != dev_vid) || + (product_id != 0x0 && product_id != dev_pid)) { + continue; + } + res = libusb_get_active_config_descriptor(dev, &conf_desc); if (res < 0) libusb_get_config_descriptor(dev, 0, &conf_desc); @@ -585,129 +589,124 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc; intf_desc = &intf->altsetting[k]; if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { - interface_num = intf_desc->bInterfaceNumber; - - /* Check the VID/PID against the arguments */ - if ((vendor_id == 0x0 || vendor_id == dev_vid) && - (product_id == 0x0 || product_id == dev_pid)) { - struct hid_device_info *tmp; + int interface_num = intf_desc->bInterfaceNumber; + struct hid_device_info *tmp; - /* VID/PID match. Create the record. */ - tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; - /* Fill out the record */ - cur_dev->next = NULL; - cur_dev->path = make_path(dev, interface_num, conf_desc->bConfigurationValue); + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = make_path(dev, interface_num, conf_desc->bConfigurationValue); - res = libusb_open(dev, &handle); + res = libusb_open(dev, &handle); - if (res >= 0) { + if (res >= 0) { #ifdef __ANDROID__ - /* There is (a potential) libusb Android backend, in which - device descriptor is not accurate up until the device is opened. - https://github.com/libusb/libusb/pull/874#discussion_r632801373 - A workaround is to re-read the descriptor again. - Even if it is not going to be accepted into libusb master, - having it here won't do any harm, since reading the device descriptor - is as cheap as copy 18 bytes of data. */ - libusb_get_device_descriptor(dev, &desc); + /* There is (a potential) libusb Android backend, in which + device descriptor is not accurate up until the device is opened. + https://github.com/libusb/libusb/pull/874#discussion_r632801373 + A workaround is to re-read the descriptor again. + Even if it is not going to be accepted into libusb master, + having it here won't do any harm, since reading the device descriptor + is as cheap as copy 18 bytes of data. */ + libusb_get_device_descriptor(dev, &desc); #endif - /* Serial Number */ - if (desc.iSerialNumber > 0) - cur_dev->serial_number = - get_usb_string(handle, desc.iSerialNumber); + /* Serial Number */ + if (desc.iSerialNumber > 0) + cur_dev->serial_number = + get_usb_string(handle, desc.iSerialNumber); - /* Manufacturer and Product strings */ - if (desc.iManufacturer > 0) - cur_dev->manufacturer_string = - get_usb_string(handle, desc.iManufacturer); - if (desc.iProduct > 0) - cur_dev->product_string = - get_usb_string(handle, desc.iProduct); + /* Manufacturer and Product strings */ + if (desc.iManufacturer > 0) + cur_dev->manufacturer_string = + get_usb_string(handle, desc.iManufacturer); + if (desc.iProduct > 0) + cur_dev->product_string = + get_usb_string(handle, desc.iProduct); #ifdef INVASIVE_GET_USAGE { - /* - This section is removed because it is too - invasive on the system. Getting a Usage Page - and Usage requires parsing the HID Report - descriptor. Getting a HID Report descriptor - involves claiming the interface. Claiming the - interface involves detaching the kernel driver. - Detaching the kernel driver is hard on the system - because it will unclaim interfaces (if another - app has them claimed) and the re-attachment of - the driver will sometimes change /dev entry names. - It is for these reasons that this section is - #if 0. For composite devices, use the interface - field in the hid_device_info struct to distinguish - between interfaces. */ - unsigned char data[256]; + /* + This section is removed because it is too + invasive on the system. Getting a Usage Page + and Usage requires parsing the HID Report + descriptor. Getting a HID Report descriptor + involves claiming the interface. Claiming the + interface involves detaching the kernel driver. + Detaching the kernel driver is hard on the system + because it will unclaim interfaces (if another + app has them claimed) and the re-attachment of + the driver will sometimes change /dev entry names. + It is for these reasons that this section is + #if 0. For composite devices, use the interface + field in the hid_device_info struct to distinguish + between interfaces. */ + unsigned char data[256]; #ifdef DETACH_KERNEL_DRIVER - int detached = 0; - /* Usage Page and Usage */ - res = libusb_kernel_driver_active(handle, interface_num); - if (res == 1) { - res = libusb_detach_kernel_driver(handle, interface_num); - if (res < 0) - LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); - else - detached = 1; - } + int detached = 0; + /* Usage Page and Usage */ + res = libusb_kernel_driver_active(handle, interface_num); + if (res == 1) { + res = libusb_detach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't detach kernel driver, even though a kernel driver was attached.\n"); + else + detached = 1; + } #endif - res = libusb_claim_interface(handle, interface_num); + res = libusb_claim_interface(handle, interface_num); + if (res >= 0) { + /* Get the HID Report Descriptor. */ + res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); if (res >= 0) { - /* Get the HID Report Descriptor. */ - res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); - if (res >= 0) { - unsigned short page=0, usage=0; - /* Parse the usage and usage page - out of the report descriptor. */ - get_usage(data, res, &page, &usage); - cur_dev->usage_page = page; - cur_dev->usage = usage; - } - else - LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); - - /* Release the interface */ - res = libusb_release_interface(handle, interface_num); - if (res < 0) - LOG("Can't release the interface.\n"); + unsigned short page=0, usage=0; + /* Parse the usage and usage page + out of the report descriptor. */ + get_usage(data, res, &page, &usage); + cur_dev->usage_page = page; + cur_dev->usage = usage; } else - LOG("Can't claim interface %d\n", res); + LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); + + /* Release the interface */ + res = libusb_release_interface(handle, interface_num); + if (res < 0) + LOG("Can't release the interface.\n"); + } + else + LOG("Can't claim interface %d\n", res); #ifdef DETACH_KERNEL_DRIVER - /* Re-attach kernel driver if necessary. */ - if (detached) { - res = libusb_attach_kernel_driver(handle, interface_num); - if (res < 0) - LOG("Couldn't re-attach kernel driver.\n"); - } + /* Re-attach kernel driver if necessary. */ + if (detached) { + res = libusb_attach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't re-attach kernel driver.\n"); + } #endif } #endif /* INVASIVE_GET_USAGE */ - libusb_close(handle); - } - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; + libusb_close(handle); + } + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; - /* Release Number */ - cur_dev->release_number = desc.bcdDevice; + /* Release Number */ + cur_dev->release_number = desc.bcdDevice; - /* Interface Number */ - cur_dev->interface_number = interface_num; - } + /* Interface Number */ + cur_dev->interface_number = interface_num; } } /* altsettings */ } /* interfaces */ @@ -910,13 +909,95 @@ static void *read_thread(void *param) } +static int hidapi_initialize_device(hid_device *dev, const struct libusb_interface_descriptor *intf_desc) +{ + int i =0; + int res = 0; + struct libusb_device_descriptor desc; + libusb_get_device_descriptor(libusb_get_device(dev->device_handle), &desc); + +#ifdef DETACH_KERNEL_DRIVER + /* Detach the kernel driver, but only if the + device is managed by the kernel */ + dev->is_driver_detached = 0; + if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { + res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + libusb_close(dev->device_handle); + LOG("Unable to detach Kernel Driver\n"); + return 0; + } + else { + dev->is_driver_detached = 1; + LOG("Driver successfully detached from kernel.\n"); + } + } +#endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + return 0; + } + + /* Store off the string descriptor indexes */ + dev->manufacturer_index = desc.iManufacturer; + dev->product_index = desc.iProduct; + dev->serial_index = desc.iSerialNumber; + + /* Store off the interface number */ + dev->interface = intf_desc->bInterfaceNumber; + + dev->input_endpoint = 0; + dev->input_ep_max_packet_size = 0; + dev->output_endpoint = 0; + + /* Find the INPUT and OUTPUT endpoints. An + OUTPUT endpoint is not required. */ + for (i = 0; i < intf_desc->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep + = &intf_desc->endpoint[i]; + + /* Determine the type and direction of this + endpoint. */ + int is_interrupt = + (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) + == LIBUSB_TRANSFER_TYPE_INTERRUPT; + int is_output = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int is_input = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_IN; + + /* Decide whether to use it for input or output. */ + if (dev->input_endpoint == 0 && + is_interrupt && is_input) { + /* Use this endpoint for INPUT */ + dev->input_endpoint = ep->bEndpointAddress; + dev->input_ep_max_packet_size = ep->wMaxPacketSize; + } + if (dev->output_endpoint == 0 && + is_interrupt && is_output) { + /* Use this endpoint for OUTPUT */ + dev->output_endpoint = ep->bEndpointAddress; + } + } + + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + return 1; +} + + hid_device * HID_API_EXPORT hid_open_path(const char *path) { hid_device *dev = NULL; - libusb_device **devs; - libusb_device *usb_dev; - int res; + libusb_device **devs = NULL; + libusb_device *usb_dev = NULL; + int res = 0; int d = 0; int good_open = 0; @@ -926,19 +1007,16 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) dev = new_hid_device(); libusb_get_device_list(usb_context, &devs); - while ((usb_dev = devs[d++]) != NULL) { - struct libusb_device_descriptor desc; + while ((usb_dev = devs[d++]) != NULL && !good_open) { struct libusb_config_descriptor *conf_desc = NULL; - int i,j,k; - libusb_get_device_descriptor(usb_dev, &desc); + int j,k; if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) continue; - for (j = 0; j < conf_desc->bNumInterfaces; j++) { + for (j = 0; j < conf_desc->bNumInterfaces && !good_open; j++) { const struct libusb_interface *intf = &conf_desc->interface[j]; - for (k = 0; k < intf->num_altsetting; k++) { - const struct libusb_interface_descriptor *intf_desc; - intf_desc = &intf->altsetting[k]; + for (k = 0; k < intf->num_altsetting && !good_open; k++) { + const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber, conf_desc->bConfigurationValue); if (!strcmp(dev_path, path)) { @@ -951,93 +1029,15 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) free(dev_path); break; } - good_open = 1; - -#ifdef __ANDROID__ - /* See remark in hid_enumerate */ - libusb_get_device_descriptor(usb_dev, &desc); -#endif - -#ifdef DETACH_KERNEL_DRIVER - /* Detach the kernel driver, but only if the - device is managed by the kernel */ - dev->is_driver_detached = 0; - if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { - res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); - if (res < 0) { - libusb_close(dev->device_handle); - LOG("Unable to detach Kernel Driver\n"); - free(dev_path); - good_open = 0; - break; - } - else { - dev->is_driver_detached = 1; - LOG("Driver successfully detached from kernel.\n"); - } - } -#endif - res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); - if (res < 0) { - LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); - free(dev_path); + good_open = hidapi_initialize_device(dev, intf_desc); + if (!good_open) libusb_close(dev->device_handle); - good_open = 0; - break; - } - - /* Store off the string descriptor indexes */ - dev->manufacturer_index = desc.iManufacturer; - dev->product_index = desc.iProduct; - dev->serial_index = desc.iSerialNumber; - - /* Store off the interface number */ - dev->interface = intf_desc->bInterfaceNumber; - - /* Find the INPUT and OUTPUT endpoints. An - OUTPUT endpoint is not required. */ - for (i = 0; i < intf_desc->bNumEndpoints; i++) { - const struct libusb_endpoint_descriptor *ep - = &intf_desc->endpoint[i]; - - /* Determine the type and direction of this - endpoint. */ - int is_interrupt = - (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) - == LIBUSB_TRANSFER_TYPE_INTERRUPT; - int is_output = - (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_OUT; - int is_input = - (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_IN; - - /* Decide whether to use it for input or output. */ - if (dev->input_endpoint == 0 && - is_interrupt && is_input) { - /* Use this endpoint for INPUT */ - dev->input_endpoint = ep->bEndpointAddress; - dev->input_ep_max_packet_size = ep->wMaxPacketSize; - } - if (dev->output_endpoint == 0 && - is_interrupt && is_output) { - /* Use this endpoint for OUTPUT */ - dev->output_endpoint = ep->bEndpointAddress; - } - } - - pthread_create(&dev->thread, NULL, read_thread, dev); - - /* Wait here for the read thread to be initialized. */ - pthread_barrier_wait(&dev->barrier); - } free(dev_path); } } } libusb_free_config_descriptor(conf_desc); - } libusb_free_device_list(devs, 1); @@ -1054,6 +1054,80 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) } +HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys_dev, int interface_num) +{ +/* 0x01000107 is a LIBUSB_API_VERSION for 1.0.23 - version when libusb_wrap_sys_device was introduced */ +#if (!defined(HIDAPI_TARGET_LIBUSB_API_VERSION) || HIDAPI_TARGET_LIBUSB_API_VERSION >= 0x01000107) && (LIBUSB_API_VERSION >= 0x01000107) + hid_device *dev = NULL; + struct libusb_config_descriptor *conf_desc = NULL; + const struct libusb_interface_descriptor *selected_intf_desc = NULL; + int res = 0; + int j = 0, k = 0; + + if(hid_init() < 0) + return NULL; + + dev = new_hid_device(); + + res = libusb_wrap_sys_device(usb_context, sys_dev, &dev->device_handle); + if (res < 0) { + LOG("libusb_wrap_sys_device failed: %d %s\n", res, libusb_error_name(res)); + goto err; + } + + res = libusb_get_active_config_descriptor(libusb_get_device(dev->device_handle), &conf_desc); + if (res < 0) + libusb_get_config_descriptor(libusb_get_device(dev->device_handle), 0, &conf_desc); + + if (!conf_desc) { + LOG("Failed to get configuration descriptor: %d %s\n", res, libusb_error_name(res)); + goto err; + } + + /* find matching HID interface */ + for (j = 0; j < conf_desc->bNumInterfaces && !selected_intf_desc; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + if (interface_num < 0 || interface_num == intf_desc->bInterfaceNumber) { + selected_intf_desc = intf_desc; + break; + } + } + } + } + + if (!selected_intf_desc) { + if (interface_num < 0) { + LOG("Sys USB device doesn't contain a HID interface\n"); + } + else { + LOG("Sys USB device doesn't contain a HID interface with number %d\n", interface_num); + } + goto err; + } + + if (!hidapi_initialize_device(dev, selected_intf_desc)) + goto err; + + return dev; + +err: + if (conf_desc) + libusb_free_config_descriptor(conf_desc); + if (dev->device_handle) + libusb_close(dev->device_handle); + free_hid_device(dev); +#else + (void)sys_dev; + (void)interface_num; + LOG("libusb_wrap_sys_device is not available\n"); +#endif + return NULL; +} + + int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { int res; diff --git a/custom/dependencies/hidapi/libusb/hidapi_libusb.h b/custom/dependencies/hidapi/libusb/hidapi_libusb.h new file mode 100644 index 000000000..1f56c2c51 --- /dev/null +++ b/custom/dependencies/hidapi/libusb/hidapi_libusb.h @@ -0,0 +1,54 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + libusb/hidapi Team + + Copyright 2021, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_LIBUSB_H__ +#define HIDAPI_LIBUSB_H__ + +#include + +#include "hidapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** @brief Open a HID device using libusb_wrap_sys_device. + See https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html#ga98f783e115ceff4eaf88a60e6439563c, + for details on libusb_wrap_sys_device. + + @ingroup API + @param sys_dev Platform-specific file descriptor that can be recognised by libusb. + @param interface_num USB interface number of the device to be used as HID interface. + Pass -1 to select first HID interface of the device. + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys_dev, int interface_num); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/custom/dependencies/hidapi/mac/hid.c b/custom/dependencies/hidapi/mac/hid.c index 868244254..12648d9cf 100644 --- a/custom/dependencies/hidapi/mac/hid.c +++ b/custom/dependencies/hidapi/mac/hid.c @@ -381,7 +381,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, struct hid_device_info *cur_dev; io_object_t iokit_dev; kern_return_t res; - io_string_t path; + uint64_t entry_id; if (dev == NULL) { return NULL; @@ -401,13 +401,30 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, /* Fill out the record */ cur_dev->next = NULL; - /* Fill in the path (IOService plane) */ + /* Fill in the path (as a unique ID of the service entry) */ + cur_dev->path = NULL; iokit_dev = IOHIDDeviceGetService(dev); - res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); - if (res == KERN_SUCCESS) - cur_dev->path = strdup(path); - else + if (iokit_dev != MACH_PORT_NULL) { + res = IORegistryEntryGetRegistryEntryID(iokit_dev, &entry_id); + } + else { + res = KERN_INVALID_ARGUMENT; + } + + if (res == KERN_SUCCESS) { + /* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long, + so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need + 9+1+20+1=31 bytes byffer, but allocate 32 for simple alignment */ + cur_dev->path = calloc(1, 32); + if (cur_dev->path != NULL) { + sprintf(cur_dev->path, "DevSrvsID:%llu", entry_id); + } + } + + if (cur_dev->path == NULL) { + /* for whatever reason, trying to keep it a non-NULL string */ cur_dev->path = strdup(""); + } /* Serial Number */ get_serial_number(dev, buf, BUF_LEN); @@ -759,11 +776,33 @@ static void *read_thread(void *param) return NULL; } -/* hid_open_path() - * - * path must be a valid path to an IOHIDDevice in the IOService plane - * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" - */ +/* \p path must be one of: + - in format 'DevSrvsID:' (as returned by hid_enumerate); + - a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath, + e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"); + Second format is for compatibility with paths accepted by older versions of HIDAPI. +*/ +static io_registry_entry_t hid_open_service_registry_from_path(const char *path) +{ + if (path == NULL) + return MACH_PORT_NULL; + + /* Get the IORegistry entry for the given path */ + if (strncmp("DevSrvsID:", path, 10) == 0) { + char *endptr; + uint64_t entry_id = strtoull(path + 10, &endptr, 10); + if (*endptr == '\0') { + return IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(entry_id)); + } + } + else { + /* Fallback to older format of the path */ + return IORegistryEntryFromPath(kIOMasterPortDefault, path); + } + + return MACH_PORT_NULL; +} + hid_device * HID_API_EXPORT hid_open_path(const char *path) { hid_device *dev = NULL; @@ -777,7 +816,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) dev = new_hid_device(); /* Get the IORegistry entry for the given path */ - entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); + entry = hid_open_service_registry_from_path(path); if (entry == MACH_PORT_NULL) { /* Path wasn't valid (maybe device was removed?) */ goto return_error; diff --git a/custom/dependencies/hidapi/src/CMakeLists.txt b/custom/dependencies/hidapi/src/CMakeLists.txt index ae59369ed..8f454e53f 100644 --- a/custom/dependencies/hidapi/src/CMakeLists.txt +++ b/custom/dependencies/hidapi/src/CMakeLists.txt @@ -125,6 +125,9 @@ else() set(HIDAPI_WITH_LIBUSB ON) endif() if(HIDAPI_WITH_LIBUSB) + target_include_directories(hidapi_include INTERFACE + "$" + ) add_subdirectory("${PROJECT_ROOT}/libusb" libusb) list(APPEND EXPORT_COMPONENTS libusb) if(NOT EXPORT_ALIAS) diff --git a/custom/dependencies/hidapi/udev/69-hid.rules b/custom/dependencies/hidapi/udev/69-hid.rules index dc2df9045..a27113ca8 100644 --- a/custom/dependencies/hidapi/udev/69-hid.rules +++ b/custom/dependencies/hidapi/udev/69-hid.rules @@ -7,7 +7,7 @@ # those of your device. # HIDAPI/libusb -SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uaccess" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uaccess" # If you are using the hidraw implementation (linux/hid.c), then do something # like the following, substituting the VID and PID with your device. @@ -16,8 +16,11 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uacce KERNEL=="hidraw*", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uaccess" # Once done, optionally rename this file for your application, and drop it into -# /etc/udev/rules.d. Note that these rules must have priorty before 70-uaccess.rules -# for example, name the file /etc/udev/rules.d/69-my-application-hid.rules. +# /etc/udev/rules.d/. +# NOTE: these rules must have priorty before 73-seat-late.rules. +# (Small discussion/explanation in systemd repo: +# https://github.com/systemd/systemd/issues/4288#issuecomment-348166161) +# for example, name the file /etc/udev/rules.d/70-my-application-hid.rules. # Then, replug your device or run: # sudo udevadm control --reload-rules && sudo udevadm trigger @@ -25,8 +28,8 @@ KERNEL=="hidraw*", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", TAG+="uacc # must be lower case. # TAG+="uaccess" only gives permission to physically present users, which -# is appropriate in most scenarios. If you require remote access to the -# device, add +# is appropriate in most scenarios. If you require access to the device +# from a remote session (e.g. over SSH), add # GROUP="plugdev", MODE="660" # to the end of the udev rule lines, add your user to the plugdev group with: # usermod -aG plugdev USERNAME diff --git a/custom/dependencies/hidapi/windows/Makefile.am b/custom/dependencies/hidapi/windows/Makefile.am index 97e261ac9..2ea5c0d8b 100644 --- a/custom/dependencies/hidapi/windows/Makefile.am +++ b/custom/dependencies/hidapi/windows/Makefile.am @@ -8,7 +8,6 @@ hdrdir = $(includedir)/hidapi hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h EXTRA_DIST = \ - ddk_build \ hidapi.vcproj \ hidtest.vcproj \ Makefile-manual \ diff --git a/custom/dependencies/hidapi/windows/ddk_build/.gitignore b/custom/dependencies/hidapi/windows/ddk_build/.gitignore deleted file mode 100644 index 823ff0b2c..000000000 --- a/custom/dependencies/hidapi/windows/ddk_build/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.log -obj*_*_* \ No newline at end of file diff --git a/custom/dependencies/hidapi/windows/ddk_build/hidapi.def b/custom/dependencies/hidapi/windows/ddk_build/hidapi.def deleted file mode 100644 index 01fe47d64..000000000 --- a/custom/dependencies/hidapi/windows/ddk_build/hidapi.def +++ /dev/null @@ -1,18 +0,0 @@ -LIBRARY hidapi -EXPORTS - hid_open @1 - hid_write @2 - hid_read @3 - hid_close @4 - hid_get_product_string @5 - hid_get_manufacturer_string @6 - hid_get_serial_number_string @7 - hid_get_indexed_string @8 - hid_error @9 - hid_set_nonblocking @10 - hid_enumerate @11 - hid_open_path @12 - hid_send_feature_report @13 - hid_get_feature_report @14 - hid_get_input_report @15 - \ No newline at end of file diff --git a/custom/dependencies/hidapi/windows/ddk_build/makefile b/custom/dependencies/hidapi/windows/ddk_build/makefile deleted file mode 100644 index 637f712a4..000000000 --- a/custom/dependencies/hidapi/windows/ddk_build/makefile +++ /dev/null @@ -1,49 +0,0 @@ -############################################################################# -# -# Copyright (C) Microsoft Corporation 1995, 1996 -# All Rights Reserved. -# -# MAKEFILE for HID directory -# -############################################################################# - -!IFDEF WIN95_BUILD - -ROOT=..\..\..\.. - -VERSIONLIST = debug retail -IS_32 = TRUE -IS_SDK = TRUE -IS_PRIVATE = TRUE -IS_SDK = TRUE -IS_DDK = TRUE -WIN32 = TRUE -COMMONMKFILE = hidapi.mk - -!include $(ROOT)\dev\master.mk - - -!ELSE - -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!IF DEFINED(_NT_TARGET_VERSION) -! IF $(_NT_TARGET_VERSION)>=0x501 -! INCLUDE $(NTMAKEENV)\makefile.def -! ELSE -# Only warn once per directory -! INCLUDE $(NTMAKEENV)\makefile.plt -! IF "$(BUILD_PASS)"=="PASS1" -! message BUILDMSG: Warning : The sample "$(MAKEDIR)" is not valid for the current OS target. -! ENDIF -! ENDIF -!ELSE -! INCLUDE $(NTMAKEENV)\makefile.def -!ENDIF - -!ENDIF - diff --git a/custom/dependencies/hidapi/windows/ddk_build/sources b/custom/dependencies/hidapi/windows/ddk_build/sources deleted file mode 100644 index 7f06a0963..000000000 --- a/custom/dependencies/hidapi/windows/ddk_build/sources +++ /dev/null @@ -1,23 +0,0 @@ -TARGETNAME=hidapi -TARGETTYPE=DYNLINK -UMTYPE=console -UMENTRY=main - -MSC_WARNING_LEVEL=/W3 /WX - -TARGETLIBS=$(SDK_LIB_PATH)\hid.lib \ - $(SDK_LIB_PATH)\setupapi.lib \ - $(SDK_LIB_PATH)\kernel32.lib \ - $(SDK_LIB_PATH)\comdlg32.lib - -USE_MSVCRT=1 - -INCLUDES= ..\..\hidapi -SOURCES= ..\hid.c \ - - -TARGET_DESTINATION=retail - -MUI=0 -MUI_COMMENT="HID Interface DLL" - diff --git a/custom/dependencies/hidapi/windows/hid.c b/custom/dependencies/hidapi/windows/hid.c index 61fa7e23c..6d8394cdc 100644 --- a/custom/dependencies/hidapi/windows/hid.c +++ b/custom/dependencies/hidapi/windows/hid.c @@ -8,7 +8,7 @@ 8/22/2009 Copyright 2009, All Rights Reserved. - + At the discretion of the user of this library, this software may be licensed under the terms of the GNU General Public License v3, a BSD-Style license, or the @@ -20,6 +20,12 @@ https://github.com/libusb/hidapi . ********************************************************/ +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +// Do not warn about mbsrtowcs and wcsncpy usage. +// https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt +#define _CRT_SECURE_NO_WARNINGS +#endif + #include #ifndef _NTDEF_ @@ -27,6 +33,7 @@ typedef LONG NTSTATUS; #endif #ifdef __MINGW32__ +#include #include #include #endif @@ -63,18 +70,14 @@ extern "C" { #include #include - +#include +#include #include "hidapi.h" #undef MIN #define MIN(x,y) ((x) < (y)? (x): (y)) -#ifdef _MSC_VER - /* Thanks Microsoft, but I know how to use strncpy(). */ - #pragma warning(disable:4996) -#endif - #ifdef __cplusplus extern "C" { #endif @@ -110,6 +113,7 @@ static struct hid_api_version api_version = { typedef void* PHIDP_PREPARSED_DATA; #define HIDP_STATUS_SUCCESS 0x110000 + typedef void (__stdcall *HidD_GetHidGuid_)(LPGUID hid_guid); typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); @@ -123,6 +127,7 @@ static struct hid_api_version api_version = { typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); + static HidD_GetHidGuid_ HidD_GetHidGuid; static HidD_GetAttributes_ HidD_GetAttributes; static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; static HidD_GetManufacturerString_ HidD_GetManufacturerString; @@ -138,6 +143,34 @@ static struct hid_api_version api_version = { static HMODULE lib_handle = NULL; static BOOLEAN initialized = FALSE; + + typedef DWORD RETURN_TYPE; + typedef RETURN_TYPE CONFIGRET; + typedef DWORD DEVNODE, DEVINST; + typedef DEVNODE* PDEVNODE, * PDEVINST; + typedef WCHAR* DEVNODEID_W, * DEVINSTID_W; + +#define CR_SUCCESS (0x00000000) +#define CR_BUFFER_SMALL (0x0000001A) + +#define CM_LOCATE_DEVNODE_NORMAL 0x00000000 + +#define DEVPROP_TYPEMOD_LIST 0x00002000 + +#define DEVPROP_TYPE_STRING 0x00000012 +#define DEVPROP_TYPE_STRING_LIST (DEVPROP_TYPE_STRING|DEVPROP_TYPEMOD_LIST) + + typedef CONFIGRET(__stdcall* CM_Locate_DevNodeW_)(PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags); + typedef CONFIGRET(__stdcall* CM_Get_Parent_)(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags); + typedef CONFIGRET(__stdcall* CM_Get_DevNode_PropertyW_)(DEVINST dnDevInst, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags); + typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_PropertyW_)(LPCWSTR pszDeviceInterface, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags); + + static CM_Locate_DevNodeW_ CM_Locate_DevNodeW = NULL; + static CM_Get_Parent_ CM_Get_Parent = NULL; + static CM_Get_DevNode_PropertyW_ CM_Get_DevNode_PropertyW = NULL; + static CM_Get_Device_Interface_PropertyW_ CM_Get_Device_Interface_PropertyW = NULL; + + static HMODULE cfgmgr32_lib_handle = NULL; #endif /* HIDAPI_USE_DDK */ struct hid_device_ { @@ -153,7 +186,8 @@ struct hid_device_ { BOOL read_pending; char *read_buf; OVERLAPPED ol; - OVERLAPPED write_ol; + OVERLAPPED write_ol; + struct hid_device_info* device_info; }; static hid_device *new_hid_device() @@ -173,7 +207,8 @@ static hid_device *new_hid_device() memset(&dev->ol, 0, sizeof(dev->ol)); dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); memset(&dev->write_ol, 0, sizeof(dev->write_ol)); - dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); + dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); + dev->device_info = NULL; return dev; } @@ -181,12 +216,13 @@ static hid_device *new_hid_device() static void free_hid_device(hid_device *dev) { CloseHandle(dev->ol.hEvent); - CloseHandle(dev->write_ol.hEvent); + CloseHandle(dev->write_ol.hEvent); CloseHandle(dev->device_handle); LocalFree(dev->last_error_str); free(dev->write_buf); free(dev->feature_buf); free(dev->read_buf); + free(dev->device_info); free(dev); } @@ -202,13 +238,13 @@ static void register_error(hid_device *dev, const char *op) MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msg, 0/*sz*/, NULL); - + /* Get rid of the CR and LF that FormatMessage() sticks at the end of the message. Thanks Microsoft! */ ptr = msg; while (*ptr) { - if (*ptr == '\r') { - *ptr = 0x0000; + if (*ptr == L'\r') { + *ptr = L'\0'; break; } ptr++; @@ -230,6 +266,7 @@ static int lookup_functions() # pragma GCC diagnostic ignored "-Wcast-function-type" #endif #define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetHidGuid); RESOLVE(HidD_GetAttributes); RESOLVE(HidD_GetSerialNumberString); RESOLVE(HidD_GetManufacturerString); @@ -250,6 +287,29 @@ static int lookup_functions() else return -1; + cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll"); + if (cfgmgr32_lib_handle) { +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-function-type" +#endif +#define RESOLVE(x) x = (x##_)GetProcAddress(cfgmgr32_lib_handle, #x); + RESOLVE(CM_Locate_DevNodeW); + RESOLVE(CM_Get_Parent); + RESOLVE(CM_Get_DevNode_PropertyW); + RESOLVE(CM_Get_Device_Interface_PropertyW); +#undef RESOLVE +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + } + else { + CM_Locate_DevNodeW = NULL; + CM_Get_Parent = NULL; + CM_Get_DevNode_PropertyW = NULL; + CM_Get_Device_Interface_PropertyW = NULL; + } + return 0; } #endif @@ -301,19 +361,232 @@ int HID_API_EXPORT hid_exit(void) if (lib_handle) FreeLibrary(lib_handle); lib_handle = NULL; + if (cfgmgr32_lib_handle) + FreeLibrary(cfgmgr32_lib_handle); + cfgmgr32_lib_handle = NULL; initialized = FALSE; #endif return 0; } +static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_node) +{ + ULONG len; + CONFIGRET cr; + DEVPROPTYPE property_type; + + static DEVPROPKEY DEVPKEY_NAME = { { 0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac }, 10 }; // DEVPROP_TYPE_STRING + static DEVPROPKEY PKEY_DeviceInterface_Bluetooth_DeviceAddress = { { 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A }, 1 }; // DEVPROP_TYPE_STRING + static DEVPROPKEY PKEY_DeviceInterface_Bluetooth_Manufacturer = { { 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A }, 4 }; // DEVPROP_TYPE_STRING + + /* Manufacturer String */ + len = 0; + cr = CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_Manufacturer, &property_type, NULL, &len, 0); + if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) { + free(dev->manufacturer_string); + dev->manufacturer_string = (wchar_t*)calloc(len, sizeof(BYTE)); + CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_Manufacturer, &property_type, (PBYTE)dev->manufacturer_string, &len, 0); + } + + /* Serial Number String (MAC Address) */ + len = 0; + cr = CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_DeviceAddress, &property_type, NULL, &len, 0); + if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) { + free(dev->serial_number); + dev->serial_number = (wchar_t*)calloc(len, sizeof(BYTE)); + CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_DeviceAddress, &property_type, (PBYTE)dev->serial_number, &len, 0); + } + + /* Get devnode grandparent to reach out Bluetooth LE device node */ + cr = CM_Get_Parent(&dev_node, dev_node, 0); + if (cr != CR_SUCCESS) + return; + + /* Product String */ + len = 0; + cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_NAME, &property_type, NULL, &len, 0); + if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) { + free(dev->product_string); + dev->product_string = (wchar_t*)calloc(len, sizeof(BYTE)); + CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_NAME, &property_type, (PBYTE)dev->product_string, &len, 0); + } +} + +static void hid_internal_get_info(struct hid_device_info* dev) +{ + const char *tmp = NULL; + wchar_t *interface_path = NULL, *device_id = NULL, *compatible_ids = NULL; + mbstate_t state; + ULONG len; + CONFIGRET cr; + DEVPROPTYPE property_type; + DEVINST dev_node; + + static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57 }, 256 }; // DEVPROP_TYPE_STRING + static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}, 4 }; // DEVPROP_TYPE_STRING_LIST + + if (!CM_Get_Device_Interface_PropertyW || + !CM_Locate_DevNodeW || + !CM_Get_Parent || + !CM_Get_DevNode_PropertyW) + goto end; + + tmp = dev->path; + + len = (ULONG)strlen(tmp); + interface_path = (wchar_t*)calloc(len + 1, sizeof(wchar_t)); + memset(&state, 0, sizeof(state)); + + if (mbsrtowcs(interface_path, &tmp, len, &state) == (size_t)-1) + goto end; + + /* Get the device id from interface path */ + len = 0; + cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0); + if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) { + device_id = (wchar_t*)calloc(len, sizeof(BYTE)); + cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0); + } + if (cr != CR_SUCCESS) + goto end; + + /* Open devnode from device id */ + cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL); + if (cr != CR_SUCCESS) + goto end; + + /* Get devnode parent */ + cr = CM_Get_Parent(&dev_node, dev_node, 0); + if (cr != CR_SUCCESS) + goto end; + + /* Get the compatible ids from parent devnode */ + len = 0; + cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_CompatibleIds, &property_type, NULL, &len, 0); + if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING_LIST) { + compatible_ids = (wchar_t*)calloc(len, sizeof(BYTE)); + cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_CompatibleIds, &property_type, (PBYTE)compatible_ids, &len, 0); + } + if (cr != CR_SUCCESS) + goto end; + + /* Now we can parse parent's compatible IDs to find out the device bus type */ + for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) { + /* Normalize to upper case */ + for (wchar_t* p = compatible_id; *p; ++p) *p = towupper(*p); + + /* Bluetooth LE devices */ + if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) { + /* HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices + Request this info via dev node properties instead. + https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html */ + hid_internal_get_ble_info(dev, dev_node); + break; + } + } +end: + free(interface_path); + free(device_id); + free(compatible_ids); +} + +static struct hid_device_info *hid_get_device_info(const char *path, HANDLE handle) +{ + struct hid_device_info *dev = NULL; /* return object */ + + BOOL res; + HIDD_ATTRIBUTES attrib; + PHIDP_PREPARSED_DATA pp_data = NULL; + HIDP_CAPS caps; + + #define WSTR_LEN 512 + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ + + /* Create the record. */ + dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info)); + + /* Fill out the record */ + dev->next = NULL; + + if (path) { + size_t len = strlen(path); + dev->path = (char*)calloc(len + 1, sizeof(char)); + memcpy(dev->path, path, len + 1); + } + else + dev->path = NULL; + + attrib.Size = sizeof(HIDD_ATTRIBUTES); + res = HidD_GetAttributes(handle, &attrib); + if (res) { + /* VID/PID */ + dev->vendor_id = attrib.VendorID; + dev->product_id = attrib.ProductID; + + /* Release Number */ + dev->release_number = attrib.VersionNumber; + } + + /* Get the Usage Page and Usage for this device. */ + res = HidD_GetPreparsedData(handle, &pp_data); + if (res) { + NTSTATUS nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + dev->usage_page = caps.UsagePage; + dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Serial Number */ + wstr[0] = L'\0'; + res = HidD_GetSerialNumberString(handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN - 1] = L'\0'; + dev->serial_number = _wcsdup(wstr); + + /* Manufacturer String */ + wstr[0] = L'\0'; + res = HidD_GetManufacturerString(handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN - 1] = L'\0'; + dev->manufacturer_string = _wcsdup(wstr); + + /* Product String */ + wstr[0] = L'\0'; + res = HidD_GetProductString(handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN - 1] = L'\0'; + dev->product_string = _wcsdup(wstr); + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + https://docs.microsoft.com/windows-hardware/drivers/hid/hidclass-hardware-ids-for-top-level-collections + or search for "HIDClass Hardware IDs for Top-Level Collections" at Microsoft Docs. If it's not + in the path, it's set to -1. */ + dev->interface_number = -1; + if (dev->path) { + char* interface_component = strstr(dev->path, "&mi_"); + if (interface_component) { + char* hex_str = interface_component + 4; + char* endptr = NULL; + dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + dev->interface_number = -1; + } + } + } + + hid_internal_get_info(dev); + + return dev; +} + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) { BOOL res; struct hid_device_info *root = NULL; /* return object */ struct hid_device_info *cur_dev = NULL; - - /* Hard-coded GUID retreived by HidD_GetHidGuid */ - GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + GUID interface_class_guid; /* Windows objects for interacting with the driver. */ SP_DEVINFO_DATA devinfo_data; @@ -326,27 +599,31 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor if (hid_init() < 0) return NULL; + /* Retrieve HID Interface Class GUID + https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */ + HidD_GetHidGuid(&interface_class_guid); + /* Initialize the Windows objects. */ memset(&devinfo_data, 0x0, sizeof(devinfo_data)); devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); /* Get information for all the devices belonging to the HID class. */ - device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - + device_info_set = SetupDiGetClassDevsA(&interface_class_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + /* Iterate over each device in the HID class, looking for the right one. */ - + for (;;) { - HANDLE write_handle = INVALID_HANDLE_VALUE; + HANDLE read_handle = INVALID_HANDLE_VALUE; DWORD required_size = 0; HIDD_ATTRIBUTES attrib; res = SetupDiEnumDeviceInterfaces(device_info_set, NULL, - &InterfaceClassGuid, + &interface_class_guid, device_index, &device_interface_data); - + if (!res) { /* A return of FALSE from this function means that there are no more devices. */ @@ -398,20 +675,19 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); - /* Open a handle to the device */ - write_handle = open_device(device_interface_detail_data->DevicePath, FALSE); + /* Open read-only handle to the device */ + read_handle = open_device(device_interface_detail_data->DevicePath, FALSE); - /* Check validity of write_handle. */ - if (write_handle == INVALID_HANDLE_VALUE) { + /* Check validity of read_handle. */ + if (read_handle == INVALID_HANDLE_VALUE) { /* Unable to open the device. */ //register_error(dev, "CreateFile"); - goto cont_close; - } - + goto cont; + } /* Get the Vendor ID and Product ID for this device. */ attrib.Size = sizeof(HIDD_ATTRIBUTES); - HidD_GetAttributes(write_handle, &attrib); + HidD_GetAttributes(read_handle, &attrib); //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); /* Check the VID/PID to see if we should add this @@ -419,17 +695,13 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && (product_id == 0x0 || attrib.ProductID == product_id)) { - #define WSTR_LEN 512 - const char *str; - struct hid_device_info *tmp; - PHIDP_PREPARSED_DATA pp_data = NULL; - HIDP_CAPS caps; - NTSTATUS nt_res; - wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ - size_t len; - /* VID/PID match. Create the record. */ - tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + struct hid_device_info *tmp = hid_get_device_info(device_interface_detail_data->DevicePath, read_handle); + + if (tmp == NULL) { + goto cont_close; + } + if (cur_dev) { cur_dev->next = tmp; } @@ -437,84 +709,10 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor root = tmp; } cur_dev = tmp; - - /* Get the Usage Page and Usage for this device. */ - res = HidD_GetPreparsedData(write_handle, &pp_data); - if (res) { - nt_res = HidP_GetCaps(pp_data, &caps); - if (nt_res == HIDP_STATUS_SUCCESS) { - cur_dev->usage_page = caps.UsagePage; - cur_dev->usage = caps.Usage; - } - - HidD_FreePreparsedData(pp_data); - } - - /* Fill out the record */ - cur_dev->next = NULL; - str = device_interface_detail_data->DevicePath; - if (str) { - len = strlen(str); - cur_dev->path = (char*) calloc(len+1, sizeof(char)); - strncpy(cur_dev->path, str, len+1); - cur_dev->path[len] = '\0'; - } - else - cur_dev->path = NULL; - - /* Serial Number */ - wstr[0]= 0x0000; - res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); - wstr[WSTR_LEN-1] = 0x0000; - if (res) { - cur_dev->serial_number = _wcsdup(wstr); - } - - /* Manufacturer String */ - wstr[0]= 0x0000; - res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); - wstr[WSTR_LEN-1] = 0x0000; - if (res) { - cur_dev->manufacturer_string = _wcsdup(wstr); - } - - /* Product String */ - wstr[0]= 0x0000; - res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); - wstr[WSTR_LEN-1] = 0x0000; - if (res) { - cur_dev->product_string = _wcsdup(wstr); - } - - /* VID/PID */ - cur_dev->vendor_id = attrib.VendorID; - cur_dev->product_id = attrib.ProductID; - - /* Release Number */ - cur_dev->release_number = attrib.VersionNumber; - - /* Interface Number. It can sometimes be parsed out of the path - on Windows if a device has multiple interfaces. See - http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or - search for "Hardware IDs for HID Devices" at MSDN. If it's not - in the path, it's set to -1. */ - cur_dev->interface_number = -1; - if (cur_dev->path) { - char *interface_component = strstr(cur_dev->path, "&mi_"); - if (interface_component) { - char *hex_str = interface_component + 4; - char *endptr = NULL; - cur_dev->interface_number = strtol(hex_str, &endptr, 16); - if (endptr == hex_str) { - /* The parsing failed. Set interface_number to -1. */ - cur_dev->interface_number = -1; - } - } - } } cont_close: - CloseHandle(write_handle); + CloseHandle(read_handle); cont: /* We no longer need the detail data. It can be freed */ free(device_interface_detail_data); @@ -527,7 +725,6 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor SetupDiDestroyDeviceInfoList(device_info_set); return root; - } void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) @@ -552,7 +749,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi struct hid_device_info *devs, *cur_dev; const char *path_to_open = NULL; hid_device *handle = NULL; - + devs = hid_enumerate(vendor_id, product_id); cur_dev = devs; while (cur_dev) { @@ -578,7 +775,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi } hid_free_enumeration(devs); - + return handle; } @@ -631,7 +828,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) } nt_res = HidP_GetCaps(pp_data, &caps); if (nt_res != HIDP_STATUS_SUCCESS) { - register_error(dev, "HidP_GetCaps"); + register_error(dev, "HidP_GetCaps"); goto err_pp_data; } dev->output_report_length = caps.OutputReportByteLength; @@ -641,11 +838,13 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) dev->read_buf = (char*) malloc(dev->input_report_length); + dev->device_info = hid_get_device_info(path, dev->device_handle); + return dev; err_pp_data: HidD_FreePreparsedData(pp_data); -err: +err: free_hid_device(dev); return NULL; } @@ -683,7 +882,7 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * } res = WriteFile(dev->device_handle, buf, (DWORD) length, NULL, &dev->write_ol); - + if (!res) { if (GetLastError() != ERROR_IO_PENDING) { /* WriteFile() failed. Return error. */ @@ -736,7 +935,7 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char memset(dev->read_buf, 0, dev->input_report_length); ResetEvent(ev); res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol); - + if (!res) { if (GetLastError() != ERROR_IO_PENDING) { /* ReadFile() has failed. @@ -745,11 +944,11 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char dev->read_pending = FALSE; goto end_of_function; } - overlapped = TRUE; - } + overlapped = TRUE; + } } else { - overlapped = TRUE; + overlapped = TRUE; } if (overlapped) { @@ -787,13 +986,13 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char memcpy(data, dev->read_buf, copy_len); } } - + end_of_function: if (!res) { register_error(dev, "GetOverlappedResult"); return -1; } - + return (int) copy_len; } @@ -841,25 +1040,16 @@ int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const u return (int) length; } - -int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *data, size_t length) { BOOL res; -#if 0 - res = HidD_GetFeature(dev->device_handle, data, length); - if (!res) { - register_error(dev, "HidD_GetFeature"); - return -1; - } - return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ -#else - DWORD bytes_returned; + DWORD bytes_returned = 0; OVERLAPPED ol; memset(&ol, 0, sizeof(ol)); res = DeviceIoControl(dev->device_handle, - IOCTL_HID_GET_FEATURE, + report_type, data, (DWORD) length, data, (DWORD) length, &bytes_returned, &ol); @@ -867,7 +1057,7 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned if (!res) { if (GetLastError() != ERROR_IO_PENDING) { /* DeviceIoControl() failed. Return error. */ - register_error(dev, "Send Feature Report DeviceIoControl"); + register_error(dev, "Get Input/Feature Report DeviceIoControl"); return -1; } } @@ -877,56 +1067,30 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); if (!res) { /* The operation failed. */ - register_error(dev, "Send Feature Report GetOverLappedResult"); + register_error(dev, "Get Input/Feature Report GetOverLappedResult"); return -1; } + /* When numbered reports aren't used, + bytes_returned seem to include only what is actually received from the device + (not including the first byte with 0, as an indication "no numbered reports"). */ + if (data[0] == 0x0) { + bytes_returned++; + } + return bytes_returned; -#endif } +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + /* We could use HidD_GetFeature() instead, but it doesn't give us an actual length, unfortunately */ + return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length); +} int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) { - BOOL res; -#if 0 - res = HidD_GetInputReport(dev->device_handle, data, length); - if (!res) { - register_error(dev, "HidD_GetInputReport"); - return -1; - } - return length; -#else - DWORD bytes_returned; - - OVERLAPPED ol; - memset(&ol, 0, sizeof(ol)); - - res = DeviceIoControl(dev->device_handle, - IOCTL_HID_GET_INPUT_REPORT, - data, (DWORD) length, - data, (DWORD) length, - &bytes_returned, &ol); - - if (!res) { - if (GetLastError() != ERROR_IO_PENDING) { - /* DeviceIoControl() failed. Return error. */ - register_error(dev, "Send Input Report DeviceIoControl"); - return -1; - } - } - - /* Wait here until the write is done. This makes - hid_get_feature_report() synchronous. */ - res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); - if (!res) { - /* The operation failed. */ - register_error(dev, "Send Input Report GetOverLappedResult"); - return -1; - } - - return bytes_returned; -#endif + /* We could use HidD_GetInputReport() instead, but it doesn't give us an actual length, unfortunately */ + return hid_get_report(dev, IOCTL_HID_GET_INPUT_REPORT, data, length); } void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) @@ -939,39 +1103,33 @@ void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) { - BOOL res; - - res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS)); - if (!res) { - register_error(dev, "HidD_GetManufacturerString"); + if (!dev->device_info || !string || !maxlen) return -1; - } + + wcsncpy(string, dev->device_info->manufacturer_string, maxlen); + string[maxlen] = L'\0'; return 0; } int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) { - BOOL res; - - res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS)); - if (!res) { - register_error(dev, "HidD_GetProductString"); + if (!dev->device_info || !string || !maxlen) return -1; - } + + wcsncpy(string, dev->device_info->product_string, maxlen); + string[maxlen] = L'\0'; return 0; } int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) { - BOOL res; - - res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS)); - if (!res) { - register_error(dev, "HidD_GetSerialNumberString"); + if (!dev->device_info || !string || !maxlen) return -1; - } + + wcsncpy(string, dev->device_info->serial_number, maxlen); + string[maxlen] = L'\0'; return 0; } @@ -1006,7 +1164,7 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) /*#define PICPGM*/ /*#define S11*/ #define P32 -#ifdef S11 +#ifdef S11 unsigned short VendorID = 0xa0a0; unsigned short ProductID = 0x0001; #endif @@ -1036,7 +1194,7 @@ int __cdecl main(int argc, char* argv[]) memset(buf,0x00,sizeof(buf)); buf[0] = 0; buf[1] = 0x81; - + /* Open the device. */ int handle = open(VendorID, ProductID, L"12345"); diff --git a/custom/dependencies/hidapi/windows/hidapi.vcxproj.filters b/custom/dependencies/hidapi/windows/hidapi.vcxproj.filters new file mode 100644 index 000000000..af3a78a3a --- /dev/null +++ b/custom/dependencies/hidapi/windows/hidapi.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/custom/dependencies/hidapi/windows/hidtest.vcxproj.filters b/custom/dependencies/hidapi/windows/hidtest.vcxproj.filters new file mode 100644 index 000000000..98b7e73e2 --- /dev/null +++ b/custom/dependencies/hidapi/windows/hidtest.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + +