From 66b1da51fd904e01e34c8f00dd924f7a70ac7436 Mon Sep 17 00:00:00 2001 From: Taylor Richards Date: Tue, 19 Aug 2025 14:30:36 -0400 Subject: [PATCH 1/2] remove hidapi lib in favor of SDL_hidapi Funtionally identical under the hood, but offering maximum capability and compatability without adding any system dependencies. The main difference on our side is that hidapi functions and types will be prefixed with "SDL_". And SDL will dynamically load in the hidapi features best supported by the running operating system. --- code/CMakeLists.txt | 2 - code/io/spacemouse.cpp | 23 +- code/io/spacemouse.h | 11 +- lib/CMakeLists.txt | 4 - lib/hidapi/AUTHORS.txt | 18 - lib/hidapi/CMakeLists.txt | 61 - lib/hidapi/LICENSE-orig.txt | 9 - lib/hidapi/VERSION | 1 - lib/hidapi/hidapi/hidapi.h | 603 ------ lib/hidapi/libusb/.gitignore | 8 - lib/hidapi/libusb/CMakeLists.txt | 107 -- lib/hidapi/libusb/hid.c | 1850 ------------------- lib/hidapi/libusb/hidapi_libusb.h | 56 - lib/hidapi/linux/.gitignore | 18 - lib/hidapi/linux/CMakeLists.txt | 38 - lib/hidapi/linux/hid.c | 1242 ------------- lib/hidapi/mac/.gitignore | 17 - lib/hidapi/mac/CMakeLists.txt | 48 - lib/hidapi/mac/hid.c | 1444 --------------- lib/hidapi/mac/hidapi_darwin.h | 98 - lib/hidapi/pc/.gitignore | 1 - lib/hidapi/pc/hidapi-hidraw.pc.in | 11 - lib/hidapi/pc/hidapi-libusb.pc.in | 11 - lib/hidapi/pc/hidapi.pc.in | 11 - lib/hidapi/src/CMakeLists.txt | 184 -- lib/hidapi/src/cmake/hidapi-config.cmake.in | 61 - lib/hidapi/windows/.gitignore | 17 - lib/hidapi/windows/CMakeLists.txt | 45 - lib/hidapi/windows/hid.c | 1325 ------------- lib/hidapi/windows/hidapi.rc | 35 - lib/hidapi/windows/hidapi_cfgmgr32.h | 69 - lib/hidapi/windows/hidapi_hidclass.h | 38 - lib/hidapi/windows/hidapi_hidpi.h | 65 - lib/hidapi/windows/hidapi_hidsdi.h | 60 - lib/hidapi/windows/hidapi_winapi.h | 58 - 35 files changed, 15 insertions(+), 7634 deletions(-) delete mode 100644 lib/hidapi/AUTHORS.txt delete mode 100644 lib/hidapi/CMakeLists.txt delete mode 100644 lib/hidapi/LICENSE-orig.txt delete mode 100644 lib/hidapi/VERSION delete mode 100644 lib/hidapi/hidapi/hidapi.h delete mode 100644 lib/hidapi/libusb/.gitignore delete mode 100644 lib/hidapi/libusb/CMakeLists.txt delete mode 100644 lib/hidapi/libusb/hid.c delete mode 100644 lib/hidapi/libusb/hidapi_libusb.h delete mode 100644 lib/hidapi/linux/.gitignore delete mode 100644 lib/hidapi/linux/CMakeLists.txt delete mode 100644 lib/hidapi/linux/hid.c delete mode 100644 lib/hidapi/mac/.gitignore delete mode 100644 lib/hidapi/mac/CMakeLists.txt delete mode 100644 lib/hidapi/mac/hid.c delete mode 100644 lib/hidapi/mac/hidapi_darwin.h delete mode 100644 lib/hidapi/pc/.gitignore delete mode 100644 lib/hidapi/pc/hidapi-hidraw.pc.in delete mode 100644 lib/hidapi/pc/hidapi-libusb.pc.in delete mode 100644 lib/hidapi/pc/hidapi.pc.in delete mode 100644 lib/hidapi/src/CMakeLists.txt delete mode 100644 lib/hidapi/src/cmake/hidapi-config.cmake.in delete mode 100644 lib/hidapi/windows/.gitignore delete mode 100644 lib/hidapi/windows/CMakeLists.txt delete mode 100644 lib/hidapi/windows/hid.c delete mode 100644 lib/hidapi/windows/hidapi.rc delete mode 100644 lib/hidapi/windows/hidapi_cfgmgr32.h delete mode 100644 lib/hidapi/windows/hidapi_hidclass.h delete mode 100644 lib/hidapi/windows/hidapi_hidpi.h delete mode 100644 lib/hidapi/windows/hidapi_hidsdi.h delete mode 100644 lib/hidapi/windows/hidapi_winapi.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 8e113413df9..4cc52dcea77 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -76,8 +76,6 @@ TARGET_LINK_LIBRARIES(code PUBLIC jansson) target_link_libraries(code PUBLIC anl) -target_link_libraries(code PUBLIC hidapi::hidapi) - target_link_libraries(code PUBLIC imgui) IF(FSO_BUILD_WITH_OPENXR) diff --git a/code/io/spacemouse.cpp b/code/io/spacemouse.cpp index 529496db6de..061f89a482d 100644 --- a/code/io/spacemouse.cpp +++ b/code/io/spacemouse.cpp @@ -1,7 +1,5 @@ #include "spacemouse.h" -#include - using namespace io::spacemouse; #pragma pack(push,1) @@ -43,7 +41,7 @@ void SpaceMouse::poll() { case SpaceMouseDefinition::Protocol::CONNEXION_3D_OLD: { static connexion_3d_old_protocol data; - bytes_read = hid_read(m_deviceHandle, reinterpret_cast(&data), sizeof(connexion_3d_old_protocol)); + bytes_read = SDL_hid_read(m_deviceHandle, reinterpret_cast(&data), sizeof(connexion_3d_old_protocol)); if (bytes_read <= 0) continue; @@ -69,7 +67,7 @@ void SpaceMouse::poll() { case SpaceMouseDefinition::Protocol::CONNEXION_3D_NEW: { static connexion_3d_new_protocol data; - bytes_read = hid_read(m_deviceHandle, reinterpret_cast(&data), sizeof(connexion_3d_new_protocol)); + bytes_read = SDL_hid_read(m_deviceHandle, reinterpret_cast(&data), sizeof(connexion_3d_new_protocol)); if (bytes_read <= 0) continue; @@ -106,14 +104,15 @@ void SpaceMouse::pollMaybe() { } } -SpaceMouse::SpaceMouse(const SpaceMouseDefinition& definition, hid_device* deviceHandle, int pollingFrequency) +SpaceMouse::SpaceMouse(const SpaceMouseDefinition& definition, SDL_hid_device* deviceHandle, int pollingFrequency) : m_definition(definition), m_pollingFrequency(pollingFrequency), m_deviceHandle(deviceHandle), m_current({ ZERO_VECTOR, ZERO_ANGLES }), m_keypresses(definition.buttons, false), m_lastPolled(UI_TIMESTAMP::never()) { - hid_set_nonblocking(m_deviceHandle, 1); + SDL_hid_set_nonblocking(m_deviceHandle, 1); } SpaceMouse::~SpaceMouse() { - hid_close(m_deviceHandle); + SDL_hid_close(m_deviceHandle); + SDL_hid_exit(); } const SpaceMouseMovement& SpaceMouse::getMovement() { @@ -146,14 +145,14 @@ const static SCP_vector knownSpaceMice { std::unique_ptr SpaceMouse::searchSpaceMice(int pollingFrequency) { std::unique_ptr mouse = nullptr; - hid_device_info* devices = hid_enumerate(0, 0); + auto devices = SDL_hid_enumerate(0, 0); - for (const hid_device_info* head = devices; head != nullptr && mouse == nullptr; head = head->next) { + for (auto head = devices; head != nullptr && mouse == nullptr; head = head->next) { for (const SpaceMouseDefinition& mouseDef : knownSpaceMice) { if (mouseDef.vendorID != head->vendor_id || mouseDef.productID != head->product_id) continue; - hid_device* device = hid_open_path(head->path); + auto device = SDL_hid_open_path(head->path); if (device != nullptr) { mouse = std::unique_ptr(new SpaceMouse(mouseDef, device, pollingFrequency)); @@ -162,7 +161,7 @@ std::unique_ptr SpaceMouse::searchSpaceMice(int pollingFrequency) { } } - hid_free_enumeration(devices); + SDL_hid_free_enumeration(devices); return mouse; } @@ -182,4 +181,4 @@ void SpaceMouseMovement::handleNonlinearities(std::array m_keypresses; UI_TIMESTAMP m_lastPolled; void poll(); void pollMaybe(); - SpaceMouse(const SpaceMouseDefinition& definition, hid_device* deviceHandle, int pollingFrequency = 10); + SpaceMouse(const SpaceMouseDefinition& definition, SDL_hid_device* deviceHandle, int pollingFrequency = 10); public: ~SpaceMouse(); @@ -68,4 +65,4 @@ namespace io static SpaceMouse* getSharedSpaceMouse(int pollingFrequency = 10); }; } -} \ No newline at end of file +} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 66f18ebf81d..32bae9c16b6 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -51,10 +51,6 @@ add_subdirectory(accidental-noise) ADD_SUBDIRECTORY(lz4) -set(HIDAPI_WITH_LIBUSB OFF) -set(HIDAPI_WITH_HIDRAW ON) -add_subdirectory(hidapi EXCLUDE_FROM_ALL) - ADD_SUBDIRECTORY(imgui) if(FSO_BUILD_WITH_OPENXR) diff --git a/lib/hidapi/AUTHORS.txt b/lib/hidapi/AUTHORS.txt deleted file mode 100644 index 7193d71e0b0..00000000000 --- a/lib/hidapi/AUTHORS.txt +++ /dev/null @@ -1,18 +0,0 @@ - -HIDAPI Authors: - -Alan Ott : - Original Author and Maintainer - Linux, Windows, and Mac implementations - -Ludovic Rousseau : - Formatting for Doxygen documentation - Bug fixes - Correctness fixes - -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/graphs/contributors - diff --git a/lib/hidapi/CMakeLists.txt b/lib/hidapi/CMakeLists.txt deleted file mode 100644 index 521f1d45efb..00000000000 --- a/lib/hidapi/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) - -if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - add_subdirectory(src) - # compatinility with find_package() vs add_subdirectory - set(hidapi_VERSION "${hidapi_VERSION}" PARENT_SCOPE) - return() -endif() -# All of the below in this file is meant for a standalone build. -# When building as a subdirectory of a larger project, most of the options may not make sense for it, -# so it is up to developer to configure those, e.g.: -# -# # a subfolder of a master project, e.g.: 3rdparty/hidapi/CMakeLists.txt -# -# set(HIDAPI_WITH_HIDRAW OFF) -# set(CMAKE_FRAMEWORK ON) -# # and keep everything else to their defaults -# add_subdirectory(hidapi) -# - -set(DEFAULT_CMAKE_BUILD_TYPES "Debug" "Release" "MinSizeRel" "RelWithDebInfo") -if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "${DEFAULT_CMAKE_BUILD_TYPES}" FORCE) -endif() -# This part is for convenience, when used one of the standard build types with cmake-gui -list(FIND DEFAULT_CMAKE_BUILD_TYPES "${CMAKE_BUILD_TYPE}" _build_type_index) -if(${_build_type_index} GREATER -1) - # set it optionally, so a custom CMAKE_BUILD_TYPE can be used as well, if needed - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${DEFAULT_CMAKE_BUILD_TYPES}) -endif() -unset(_build_type_index) -# - -project(hidapi LANGUAGES C) - -if(APPLE) - if(NOT CMAKE_VERSION VERSION_LESS "3.15") - option(CMAKE_FRAMEWORK "Build macOS/iOS Framework version of the library" OFF) - endif() -elseif(NOT WIN32) - if(CMAKE_SYSTEM_NAME MATCHES "Linux") - option(HIDAPI_WITH_HIDRAW "Build HIDRAW-based implementation of HIDAPI" ON) - option(HIDAPI_WITH_LIBUSB "Build LIBUSB-based implementation of HIDAPI" OFF) - endif() -endif() - -option(BUILD_SHARED_LIBS "Build shared version of the libraries, otherwise build statically" OFF) - -set(HIDAPI_INSTALL_TARGETS ON) -set(HIDAPI_PRINT_VERSION ON) - -add_subdirectory(src) - -set(BUILD_HIDTEST_DEFAULT OFF) -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(BUILD_HIDTEST_DEFAULT ON) -endif() -option(HIDAPI_BUILD_HIDTEST "Build small console test application hidtest" ${BUILD_HIDTEST_DEFAULT}) -if(HIDAPI_BUILD_HIDTEST) - add_subdirectory(hidtest) -endif() diff --git a/lib/hidapi/LICENSE-orig.txt b/lib/hidapi/LICENSE-orig.txt deleted file mode 100644 index e3f33808299..00000000000 --- a/lib/hidapi/LICENSE-orig.txt +++ /dev/null @@ -1,9 +0,0 @@ - HIDAPI - Multi-Platform library for - communication with HID devices. - - Copyright 2009, Alan Ott, Signal 11 Software. - All Rights Reserved. - - This software may be used by anyone for any reason so - long as the copyright notice in the source files - remains intact. diff --git a/lib/hidapi/VERSION b/lib/hidapi/VERSION deleted file mode 100644 index 51de3305bb3..00000000000 --- a/lib/hidapi/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.13.0 \ No newline at end of file diff --git a/lib/hidapi/hidapi/hidapi.h b/lib/hidapi/hidapi/hidapi.h deleted file mode 100644 index 2b261f122c4..00000000000 --- a/lib/hidapi/hidapi/hidapi.h +++ /dev/null @@ -1,603 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - Alan Ott - Signal 11 Software - - libusb/hidapi Team - - Copyright 2022, 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_H__ -#define HIDAPI_H__ - -#include - -#ifdef _WIN32 - #define HID_API_EXPORT __declspec(dllexport) - #define HID_API_CALL -#else - #define HID_API_EXPORT /**< API export macro */ - #define HID_API_CALL /**< API call macro */ -#endif - -#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ - -/** @brief Static/compile-time major version of the library. - - @ingroup API -*/ -#define HID_API_VERSION_MAJOR 0 -/** @brief Static/compile-time minor version of the library. - - @ingroup API -*/ -#define HID_API_VERSION_MINOR 13 -/** @brief Static/compile-time patch version of the library. - - @ingroup API -*/ -#define HID_API_VERSION_PATCH 0 - -/* Helper macros */ -#define HID_API_AS_STR_IMPL(x) #x -#define HID_API_AS_STR(x) HID_API_AS_STR_IMPL(x) -#define HID_API_TO_VERSION_STR(v1, v2, v3) HID_API_AS_STR(v1.v2.v3) - -/** @brief Coverts a version as Major/Minor/Patch into a number: - <8 bit major><16 bit minor><8 bit patch>. - - This macro was added in version 0.12.0. - - Convenient function to be used for compile-time checks, like: - #if HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - - @ingroup API -*/ -#define HID_API_MAKE_VERSION(mj, mn, p) (((mj) << 24) | ((mn) << 8) | (p)) - -/** @brief Static/compile-time version of the library. - - This macro was added in version 0.12.0. - - @see @ref HID_API_MAKE_VERSION. - - @ingroup API -*/ -#define HID_API_VERSION HID_API_MAKE_VERSION(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH) - -/** @brief Static/compile-time string version of the library. - - @ingroup API -*/ -#define HID_API_VERSION_STR HID_API_TO_VERSION_STR(HID_API_VERSION_MAJOR, HID_API_VERSION_MINOR, HID_API_VERSION_PATCH) - -/** @brief Maximum expected HID Report descriptor size in bytes. - - Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) - - @ingroup API -*/ -#define HID_API_MAX_REPORT_DESCRIPTOR_SIZE 4096 - -#ifdef __cplusplus -extern "C" { -#endif - struct hid_api_version { - int major; - int minor; - int patch; - }; - - struct hid_device_; - typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ - - /** @brief HID underlying bus types. - - @ingroup API - */ - typedef enum { - /* Unknown bus type */ - HID_API_BUS_UNKNOWN = 0x00, - - /* USB bus - Specifications: - https://usb.org/hid */ - HID_API_BUS_USB = 0x01, - - /* Bluetooth or Bluetooth LE bus - Specifications: - https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/ - https://www.bluetooth.com/specifications/specs/hid-service-1-0/ - https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */ - HID_API_BUS_BLUETOOTH = 0x02, - - /* I2C bus - Specifications: - https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */ - HID_API_BUS_I2C = 0x03, - - /* SPI bus - Specifications: - https://www.microsoft.com/download/details.aspx?id=103325 */ - HID_API_BUS_SPI = 0x04, - } hid_bus_type; - - /** hidapi info structure */ - struct hid_device_info { - /** Platform-specific device path */ - char *path; - /** Device Vendor ID */ - unsigned short vendor_id; - /** Device Product ID */ - unsigned short product_id; - /** Serial Number */ - wchar_t *serial_number; - /** Device Release Number in binary-coded decimal, - also known as Device Version Number */ - unsigned short release_number; - /** Manufacturer String */ - wchar_t *manufacturer_string; - /** Product string */ - wchar_t *product_string; - /** Usage Page for this Device/Interface - (Windows/Mac/hidraw only) */ - unsigned short usage_page; - /** Usage for this Device/Interface - (Windows/Mac/hidraw only) */ - unsigned short usage; - /** The USB interface which this logical device - represents. - - * Valid on both Linux implementations in all cases. - * Valid on the Windows implementation only if the device - contains more than one interface. - * Valid on the Mac implementation if and only if the device - is a USB HID device. */ - int interface_number; - - /** Pointer to the next device */ - struct hid_device_info *next; - - /** Underlying bus type - Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) - */ - hid_bus_type bus_type; - }; - - - /** @brief Initialize the HIDAPI library. - - This function initializes the HIDAPI library. Calling it is not - strictly necessary, as it will be called automatically by - hid_enumerate() and any of the hid_open_*() functions if it is - needed. This function should be called at the beginning of - execution however, if there is a chance of HIDAPI handles - being opened by different threads simultaneously. - - @ingroup API - - @returns - This function returns 0 on success and -1 on error. - Call hid_error(NULL) to get the failure reason. - */ - int HID_API_EXPORT HID_API_CALL hid_init(void); - - /** @brief Finalize the HIDAPI library. - - This function frees all of the static data associated with - HIDAPI. It should be called at the end of execution to avoid - memory leaks. - - @ingroup API - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT HID_API_CALL hid_exit(void); - - /** @brief Enumerate the HID Devices. - - This function returns a linked list of all the HID devices - attached to the system which match vendor_id and product_id. - If @p vendor_id is set to 0 then any vendor matches. - If @p product_id is set to 0 then any product matches. - If @p vendor_id and @p product_id are both set to 0, then - all HID devices will be returned. - - @ingroup API - @param vendor_id The Vendor ID (VID) of the types of device - to open. - @param product_id The Product ID (PID) of the types of - device to open. - - @returns - This function returns a pointer to a linked list of type - struct #hid_device_info, containing information about the HID devices - attached to the system, - or NULL in the case of failure or if no HID devices present in the system. - Call hid_error(NULL) to get the failure reason. - - @note The returned value by this function must to be freed by calling hid_free_enumeration(), - when not needed anymore. - */ - struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); - - /** @brief Free an enumeration Linked List - - This function frees a linked list created by hid_enumerate(). - - @ingroup API - @param devs Pointer to a list of struct_device returned from - hid_enumerate(). - */ - void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); - - /** @brief Open a HID device using a Vendor ID (VID), Product ID - (PID) and optionally a serial number. - - If @p serial_number is NULL, the first device with the - specified VID and PID is opened. - - @ingroup API - @param vendor_id The Vendor ID (VID) of the device to open. - @param product_id The Product ID (PID) of the device to open. - @param serial_number The Serial Number of the device to open - (Optionally NULL). - - @returns - This function returns a pointer to a #hid_device object on - success or NULL on failure. - Call hid_error(NULL) to get the failure reason. - - @note The returned object must be freed by calling hid_close(), - when not needed anymore. - */ - HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); - - /** @brief Open a HID device by its path name. - - The path name be determined by calling hid_enumerate(), or a - platform-specific path name can be used (eg: /dev/hidraw0 on - Linux). - - @ingroup API - @param path The path name of the device to open - - @returns - This function returns a pointer to a #hid_device object on - success or NULL on failure. - Call hid_error(NULL) to get the failure reason. - - @note The returned object must be freed by calling hid_close(), - when not needed anymore. - */ - HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); - - /** @brief Write an Output report to a HID device. - - The first byte of @p data[] must contain the Report ID. For - devices which only support a single report, this must be set - to 0x0. The remaining bytes contain the report data. Since - the Report ID is mandatory, calls to hid_write() will always - contain one more byte than the report contains. For example, - if a hid report is 16 bytes long, 17 bytes must be passed to - hid_write(), the Report ID (or 0x0, for devices with a - single report), followed by the report data (16 bytes). In - this example, the length passed in would be 17. - - hid_write() will send the data on the first OUT endpoint, if - one exists. If it does not, it will send the data through - the Control Endpoint (Endpoint 0). - - @ingroup API - @param dev A device handle returned from hid_open(). - @param data The data to send, including the report number as - the first byte. - @param length The length in bytes of the data to send. - - @returns - This function returns the actual number of bytes written and - -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length); - - /** @brief Read an Input report from a HID device with timeout. - - Input reports are returned - to the host through the INTERRUPT IN endpoint. The first byte will - contain the Report number if the device uses numbered reports. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param data A buffer to put the read data into. - @param length The number of bytes to read. For devices with - multiple reports, make sure to read an extra byte for - the report number. - @param milliseconds timeout in milliseconds or -1 for blocking wait. - - @returns - This function returns the actual number of bytes read and - -1 on error. - Call hid_error(dev) to get the failure reason. - If no packet was available to be read within - the timeout period, this function returns 0. - */ - int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); - - /** @brief Read an Input report from a HID device. - - Input reports are returned - to the host through the INTERRUPT IN endpoint. The first byte will - contain the Report number if the device uses numbered reports. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param data A buffer to put the read data into. - @param length The number of bytes to read. For devices with - multiple reports, make sure to read an extra byte for - the report number. - - @returns - This function returns the actual number of bytes read and - -1 on error. - Call hid_error(dev) to get the failure reason. - If no packet was available to be read and - the handle is in non-blocking mode, this function returns 0. - */ - int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); - - /** @brief Set the device handle to be non-blocking. - - In non-blocking mode calls to hid_read() will return - immediately with a value of 0 if there is no data to be - read. In blocking mode, hid_read() will wait (block) until - there is data to read before returning. - - Nonblocking can be turned on and off at any time. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param nonblock enable or not the nonblocking reads - - 1 to enable nonblocking - - 0 to disable nonblocking. - - @returns - This function returns 0 on success and -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock); - - /** @brief Send a Feature report to the device. - - Feature reports are sent over the Control endpoint as a - Set_Report transfer. The first byte of @p data[] must - contain the Report ID. For devices which only support a - single report, this must be set to 0x0. The remaining bytes - contain the report data. Since the Report ID is mandatory, - calls to hid_send_feature_report() will always contain one - more byte than the report contains. For example, if a hid - report is 16 bytes long, 17 bytes must be passed to - hid_send_feature_report(): the Report ID (or 0x0, for - devices which do not use numbered reports), followed by the - report data (16 bytes). In this example, the length passed - in would be 17. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param data The data to send, including the report number as - the first byte. - @param length The length in bytes of the data to send, including - the report number. - - @returns - This function returns the actual number of bytes written and - -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length); - - /** @brief Get a feature report from a HID device. - - Set the first byte of @p data[] to the Report ID of the - report to be read. Make sure to allow space for this - extra byte in @p data[]. Upon return, the first byte will - still contain the Report ID, and the report data will - start in data[1]. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param data A buffer to put the read data into, including - the Report ID. Set the first byte of @p data[] to the - Report ID of the report to be read, or set it to zero - if your device does not use numbered reports. - @param length The number of bytes to read, including an - extra byte for the report ID. The buffer can be longer - than the actual report. - - @returns - This function returns the number of bytes read plus - one for the report ID (which is still in the first - byte), or -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); - - /** @brief Get a input report from a HID device. - - Since version 0.10.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 10, 0) - - Set the first byte of @p data[] to the Report ID of the - report to be read. Make sure to allow space for this - extra byte in @p data[]. Upon return, the first byte will - still contain the Report ID, and the report data will - start in data[1]. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param data A buffer to put the read data into, including - the Report ID. Set the first byte of @p data[] to the - Report ID of the report to be read, or set it to zero - if your device does not use numbered reports. - @param length The number of bytes to read, including an - extra byte for the report ID. The buffer can be longer - than the actual report. - - @returns - This function returns the number of bytes read plus - one for the report ID (which is still in the first - byte), or -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length); - - /** @brief Close a HID device. - - @ingroup API - @param dev A device handle returned from hid_open(). - */ - void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev); - - /** @brief Get The Manufacturer String from a HID device. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen); - - /** @brief Get The Product String from a HID device. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen); - - /** @brief Get The Serial Number String from a HID device. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen); - - /** @brief Get The struct #hid_device_info from a HID device. - - Since version 0.13.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 13, 0) - - @ingroup API - @param dev A device handle returned from hid_open(). - - @returns - This function returns a pointer to the struct #hid_device_info - for this hid_device, or NULL in the case of failure. - Call hid_error(dev) to get the failure reason. - This struct is valid until the device is closed with hid_close(). - - @note The returned object is owned by the @p dev, and SHOULD NOT be freed by the user. - */ - struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_get_device_info(hid_device *dev); - - /** @brief Get a string from a HID device, based on its string index. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param string_index The index of the string to get. - @param string A wide string buffer to put the data into. - @param maxlen The length of the buffer in multiples of wchar_t. - - @returns - This function returns 0 on success and -1 on error. - Call hid_error(dev) to get the failure reason. - */ - int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen); - - /** @brief Get a string describing the last error which occurred. - - This function is intended for logging/debugging purposes. - - This function guarantees to never return NULL. - If there was no error in the last function call - - the returned string clearly indicates that. - - Any HIDAPI function that can explicitly indicate an execution failure - (e.g. by an error code, or by returning NULL) - may set the error string, - to be returned by this function. - - Strings returned from hid_error() must not be freed by the user, - i.e. owned by HIDAPI library. - Device-specific error string may remain allocated at most until hid_close() is called. - Global error string may remain allocated at most until hid_exit() is called. - - @ingroup API - @param dev A device handle returned from hid_open(), - or NULL to get the last non-device-specific error - (e.g. for errors in hid_open() or hid_enumerate()). - - @returns - A string describing the last error (if any). - */ - HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev); - - /** @brief Get a runtime version of the library. - - This function is thread-safe. - - @ingroup API - - @returns - Pointer to statically allocated struct, that contains version. - */ - HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void); - - - /** @brief Get a runtime version string of the library. - - This function is thread-safe. - - @ingroup API - - @returns - Pointer to statically allocated string, that contains version string. - */ - HID_API_EXPORT const char* HID_API_CALL hid_version_str(void); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/lib/hidapi/libusb/.gitignore b/lib/hidapi/libusb/.gitignore deleted file mode 100644 index 67460db93e8..00000000000 --- a/lib/hidapi/libusb/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.o -*.so -*.la -*.lo -*.a -.libs -.deps -hidtest-libusb diff --git a/lib/hidapi/libusb/CMakeLists.txt b/lib/hidapi/libusb/CMakeLists.txt deleted file mode 100644 index 31f3ddbeefb..00000000000 --- a/lib/hidapi/libusb/CMakeLists.txt +++ /dev/null @@ -1,107 +0,0 @@ -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) - -list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_libusb.h") - -add_library(hidapi_libusb - ${HIDAPI_PUBLIC_HEADERS} - hid.c -) -target_link_libraries(hidapi_libusb PUBLIC hidapi_include) - -if(TARGET usb-1.0) - target_link_libraries(hidapi_libusb PRIVATE usb-1.0) -else() - include(FindPkgConfig) - pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0>=1.0.9) - target_link_libraries(hidapi_libusb PRIVATE PkgConfig::libusb) -endif() - -find_package(Threads REQUIRED) -target_link_libraries(hidapi_libusb PRIVATE Threads::Threads) - -if(HIDAPI_NO_ICONV) - target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV) -else() - if(NOT ANDROID) - include(CheckCSourceCompiles) - - if(NOT CMAKE_VERSION VERSION_LESS 3.11) - message(STATUS "Check for Iconv") - find_package(Iconv) - if(Iconv_FOUND) - if(NOT Iconv_IS_BUILT_IN) - target_link_libraries(hidapi_libusb PRIVATE Iconv::Iconv) - set(CMAKE_REQUIRED_LIBRARIES "Iconv::Iconv") - if(NOT BUILD_SHARED_LIBS) - set(HIDAPI_NEED_EXPORT_ICONV TRUE PARENT_SCOPE) - endif() - endif() - else() - message(STATUS "Iconv Explicitly check '-liconv'") - # Sometime the build environment is not setup - # in a way CMake can find Iconv on its own by default. - # But if we simply link against iconv (-liconv), the build may succeed - # due to other compiler/link flags. - set(CMAKE_REQUIRED_LIBRARIES "iconv") - check_c_source_compiles(" - #include - #include - int main() { - char *a, *b; - size_t i, j; - iconv_t ic; - ic = iconv_open(\"to\", \"from\"); - iconv(ic, &a, &i, &b, &j); - iconv_close(ic); - } - " - Iconv_EXPLICITLY_AT_ENV) - if(Iconv_EXPLICITLY_AT_ENV) - message(STATUS "Iconv Explicitly check '-liconv' - Available") - target_link_libraries(hidapi_libusb PRIVATE iconv) - else() - message(FATAL_ERROR "Iconv is not found, make sure to provide it in the build environment") - endif() - endif() - else() - # otherwise there is 2 options: - # 1) iconv is provided by Standard C library and the build will be just fine - # 2) The _user_ has to provide additiona compilation options for this project/target - endif() - - # check for error: "conflicting types for 'iconv'" - check_c_source_compiles("#include - extern size_t iconv (iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); - int main() {}" - HIDAPI_ICONV_CONST) - if(HIDAPI_ICONV_CONST) - target_compile_definitions(hidapi_libusb PRIVATE "ICONV_CONST=const") - endif() - else() - # On Android Iconv is disabled on the code level anyway, so no issue; - endif() -endif() - -set_target_properties(hidapi_libusb - PROPERTIES - EXPORT_NAME "libusb" - OUTPUT_NAME "hidapi-libusb" - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}" -) - -# compatibility with find_package() -add_library(hidapi::libusb ALIAS hidapi_libusb) -# compatibility with raw library link -add_library(hidapi-libusb ALIAS hidapi_libusb) - -if(HIDAPI_INSTALL_TARGETS) - install(TARGETS hidapi_libusb EXPORT hidapi - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi" - ) -endif() - -hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi-libusb.pc.in") diff --git a/lib/hidapi/libusb/hid.c b/lib/hidapi/libusb/hid.c deleted file mode 100644 index b46b1c78108..00000000000 --- a/lib/hidapi/libusb/hid.c +++ /dev/null @@ -1,1850 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - Alan Ott - Signal 11 Software - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ - -/* C */ -#include -#include -#include -#include -#include -#include - -/* Unix */ -#include -#include -#include -#include -#include -#include -#include -#include - -/* GNU / LibUSB */ -#include -#if !defined(__ANDROID__) && !defined(NO_ICONV) -#include -#ifndef ICONV_CONST -#define ICONV_CONST -#endif -#endif - -#include "hidapi_libusb.h" - -#if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__ - -/* Barrier implementation because Android/Bionic don't have pthread_barrier. - This implementation came from Brent Priddy and was posted on - StackOverflow. It is used with his permission. */ -typedef int pthread_barrierattr_t; -typedef struct pthread_barrier { - pthread_mutex_t mutex; - pthread_cond_t cond; - int count; - int trip_count; -} pthread_barrier_t; - -static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) -{ - if(count == 0) { - errno = EINVAL; - return -1; - } - - if(pthread_mutex_init(&barrier->mutex, 0) < 0) { - return -1; - } - if(pthread_cond_init(&barrier->cond, 0) < 0) { - pthread_mutex_destroy(&barrier->mutex); - return -1; - } - barrier->trip_count = count; - barrier->count = 0; - - return 0; -} - -static int pthread_barrier_destroy(pthread_barrier_t *barrier) -{ - pthread_cond_destroy(&barrier->cond); - pthread_mutex_destroy(&barrier->mutex); - return 0; -} - -static int pthread_barrier_wait(pthread_barrier_t *barrier) -{ - pthread_mutex_lock(&barrier->mutex); - ++(barrier->count); - if(barrier->count >= barrier->trip_count) - { - barrier->count = 0; - pthread_cond_broadcast(&barrier->cond); - pthread_mutex_unlock(&barrier->mutex); - return 1; - } - else - { - pthread_cond_wait(&barrier->cond, &(barrier->mutex)); - pthread_mutex_unlock(&barrier->mutex); - return 0; - } -} - -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef DEBUG_PRINTF -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#else -#define LOG(...) do {} while (0) -#endif - -#ifndef __FreeBSD__ -#define DETACH_KERNEL_DRIVER -#endif - -/* Uncomment to enable the retrieval of Usage and Usage Page in -hid_enumerate(). Warning, on platforms different from FreeBSD -this is very invasive as it requires the detach -and re-attach of the kernel driver. See comments inside hid_enumerate(). -libusb HIDAPI programs are encouraged to use the interface number -instead to differentiate between interfaces on a composite HID device. */ -/*#define INVASIVE_GET_USAGE*/ - -/* Linked List of input reports received from the device. */ -struct input_report { - uint8_t *data; - size_t len; - struct input_report *next; -}; - - -struct hid_device_ { - /* Handle to the actual device. */ - libusb_device_handle *device_handle; - - /* USB Configuration Number of the device */ - int config_number; - /* The interface number of the HID */ - int interface; - - uint16_t report_descriptor_size; - - /* Endpoint information */ - int input_endpoint; - int output_endpoint; - int input_ep_max_packet_size; - - /* Indexes of Strings */ - int manufacturer_index; - int product_index; - int serial_index; - struct hid_device_info* device_info; - - /* Whether blocking reads are used */ - int blocking; /* boolean */ - - /* Read thread objects */ - pthread_t thread; - pthread_mutex_t mutex; /* Protects input_reports */ - pthread_cond_t condition; - pthread_barrier_t barrier; /* Ensures correct startup sequence */ - int shutdown_thread; - int transfer_loop_finished; - struct libusb_transfer *transfer; - - /* List of received input reports. */ - struct input_report *input_reports; - - /* Was kernel driver detached by libusb */ -#ifdef DETACH_KERNEL_DRIVER - int is_driver_detached; -#endif -}; - -static struct hid_api_version api_version = { - .major = HID_API_VERSION_MAJOR, - .minor = HID_API_VERSION_MINOR, - .patch = HID_API_VERSION_PATCH -}; - -static libusb_context *usb_context = NULL; - -uint16_t get_usb_code_for_current_locale(void); -static int return_data(hid_device *dev, unsigned char *data, size_t length); - -static hid_device *new_hid_device(void) -{ - hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); - dev->blocking = 1; - - pthread_mutex_init(&dev->mutex, NULL); - pthread_cond_init(&dev->condition, NULL); - pthread_barrier_init(&dev->barrier, NULL, 2); - - return dev; -} - -static void free_hid_device(hid_device *dev) -{ - /* Clean up the thread objects */ - pthread_barrier_destroy(&dev->barrier); - pthread_cond_destroy(&dev->condition); - pthread_mutex_destroy(&dev->mutex); - - hid_free_enumeration(dev->device_info); - - /* Free the device itself */ - free(dev); -} - -#if 0 -/*TODO: Implement this function on hidapi/libusb.. */ -static void register_error(hid_device *dev, const char *op) -{ - -} -#endif - -/* Get bytes from a HID Report Descriptor. - Only call with a num_bytes of 0, 1, 2, or 4. */ -static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur) -{ - /* Return if there aren't enough bytes. */ - if (cur + num_bytes >= len) - return 0; - - if (num_bytes == 0) - return 0; - else if (num_bytes == 1) { - return rpt[cur+1]; - } - else if (num_bytes == 2) { - return (rpt[cur+2] * 256 + rpt[cur+1]); - } - else if (num_bytes == 4) { - return (rpt[cur+4] * 0x01000000 + - rpt[cur+3] * 0x00010000 + - rpt[cur+2] * 0x00000100 + - rpt[cur+1] * 0x00000001); - } - else - return 0; -} - -/* Retrieves the device's Usage Page and Usage from the report - descriptor. The algorithm is simple, as it just returns the first - Usage and Usage Page that it finds in the descriptor. - The return value is 0 on success and -1 on failure. */ -static int get_usage(uint8_t *report_descriptor, size_t size, - unsigned short *usage_page, unsigned short *usage) -{ - unsigned int i = 0; - int size_code; - int data_len, key_size; - int usage_found = 0, usage_page_found = 0; - - while (i < size) { - int key = report_descriptor[i]; - int key_cmd = key & 0xfc; - - //printf("key: %02hhx\n", key); - - if ((key & 0xf0) == 0xf0) { - /* This is a Long Item. The next byte contains the - length of the data section (value) for this key. - See the HID specification, version 1.11, section - 6.2.2.3, titled "Long Items." */ - if (i+1 < size) - data_len = report_descriptor[i+1]; - else - data_len = 0; /* malformed report */ - key_size = 3; - } - else { - /* This is a Short Item. The bottom two bits of the - key contain the size code for the data section - (value) for this key. Refer to the HID - specification, version 1.11, section 6.2.2.2, - titled "Short Items." */ - size_code = key & 0x3; - switch (size_code) { - case 0: - case 1: - case 2: - data_len = size_code; - break; - case 3: - data_len = 4; - break; - default: - /* Can't ever happen since size_code is & 0x3 */ - data_len = 0; - break; - }; - key_size = 1; - } - - if (key_cmd == 0x4) { - *usage_page = get_bytes(report_descriptor, size, data_len, i); - usage_page_found = 1; - //printf("Usage Page: %x\n", (uint32_t)*usage_page); - } - if (key_cmd == 0x8) { - *usage = get_bytes(report_descriptor, size, data_len, i); - usage_found = 1; - //printf("Usage: %x\n", (uint32_t)*usage); - } - - if (usage_page_found && usage_found) - return 0; /* success */ - - /* Skip over this key and it's associated data */ - i += data_len + key_size; - } - - return -1; /* failure */ -} - -#if defined(__FreeBSD__) && __FreeBSD__ < 10 -/* The libusb version included in FreeBSD < 10 doesn't have this function. In - mainline libusb, it's inlined in libusb.h. This function will bear a striking - resemblance to that one, because there's about one way to code it. - - Note that the data parameter is Unicode in UTF-16LE encoding. - Return value is the number of bytes in data, or LIBUSB_ERROR_*. - */ -static inline int libusb_get_string_descriptor(libusb_device_handle *dev, - uint8_t descriptor_index, uint16_t lang_id, - unsigned char *data, int length) -{ - return libusb_control_transfer(dev, - LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ - LIBUSB_REQUEST_GET_DESCRIPTOR, - (LIBUSB_DT_STRING << 8) | descriptor_index, - lang_id, data, (uint16_t) length, 1000); -} - -#endif - - -/* Get the first language the device says it reports. This comes from - USB string #0. */ -static uint16_t get_first_language(libusb_device_handle *dev) -{ - uint16_t buf[32]; - int len; - - /* Get the string from libusb. */ - len = libusb_get_string_descriptor(dev, - 0x0, /* String ID */ - 0x0, /* Language */ - (unsigned char*)buf, - sizeof(buf)); - if (len < 4) - return 0x0; - - return buf[1]; /* First two bytes are len and descriptor type. */ -} - -static int is_language_supported(libusb_device_handle *dev, uint16_t lang) -{ - uint16_t buf[32]; - int len; - int i; - - /* Get the string from libusb. */ - len = libusb_get_string_descriptor(dev, - 0x0, /* String ID */ - 0x0, /* Language */ - (unsigned char*)buf, - sizeof(buf)); - if (len < 4) - return 0x0; - - - len /= 2; /* language IDs are two-bytes each. */ - /* Start at index 1 because there are two bytes of protocol data. */ - for (i = 1; i < len; i++) { - if (buf[i] == lang) - return 1; - } - - return 0; -} - - -/* This function returns a newly allocated wide string containing the USB - device string numbered by the index. The returned string must be freed - by using free(). */ -static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) -{ - char buf[512]; - int len; - wchar_t *str = NULL; - -#if !defined(__ANDROID__) && !defined(NO_ICONV) /* we don't use iconv on Android, or when it is explicitly disabled */ - wchar_t wbuf[256]; - /* iconv variables */ - iconv_t ic; - size_t inbytes; - size_t outbytes; - size_t res; - ICONV_CONST char *inptr; - char *outptr; -#endif - - /* Determine which language to use. */ - uint16_t lang; - lang = get_usb_code_for_current_locale(); - if (!is_language_supported(dev, lang)) - lang = get_first_language(dev); - - /* Get the string from libusb. */ - len = libusb_get_string_descriptor(dev, - idx, - lang, - (unsigned char*)buf, - sizeof(buf)); - if (len < 2) /* we always skip first 2 bytes */ - return NULL; - -#if defined(__ANDROID__) || defined(NO_ICONV) - - /* Bionic does not have iconv support nor wcsdup() function, so it - has to be done manually. The following code will only work for - code points that can be represented as a single UTF-16 character, - and will incorrectly convert any code points which require more - than one UTF-16 character. - - Skip over the first character (2-bytes). */ - len -= 2; - str = (wchar_t*) malloc((len / 2 + 1) * sizeof(wchar_t)); - int i; - for (i = 0; i < len / 2; i++) { - str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8); - } - str[len / 2] = 0x00000000; - -#else - - /* buf does not need to be explicitly NULL-terminated because - it is only passed into iconv() which does not need it. */ - - /* Initialize iconv. */ - ic = iconv_open("WCHAR_T", "UTF-16LE"); - if (ic == (iconv_t)-1) { - LOG("iconv_open() failed\n"); - return NULL; - } - - /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). - Skip the first character (2-bytes). */ - inptr = buf+2; - inbytes = len-2; - outptr = (char*) wbuf; - outbytes = sizeof(wbuf); - res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); - if (res == (size_t)-1) { - LOG("iconv() failed\n"); - goto err; - } - - /* Write the terminating NULL. */ - wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; - if (outbytes >= sizeof(wbuf[0])) - *((wchar_t*)outptr) = 0x00000000; - - /* Allocate and copy the string. */ - str = wcsdup(wbuf); - -err: - iconv_close(ic); - -#endif - - return str; -} - -/** - Max length of the result: "000-000.000.000.000.000.000.000:000.000" (39 chars). - 64 is used for simplicity/alignment. -*/ -static void get_path(char (*result)[64], libusb_device *dev, int config_number, int interface_number) -{ - char *str = *result; - - /* Note that USB3 port count limit is 7; use 8 here for alignment */ - uint8_t port_numbers[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - int num_ports = libusb_get_port_numbers(dev, port_numbers, 8); - - if (num_ports > 0) { - int n = snprintf(str, sizeof("000-000"), "%u-%u", libusb_get_bus_number(dev), port_numbers[0]); - for (uint8_t i = 1; i < num_ports; i++) { - n += snprintf(&str[n], sizeof(".000"), ".%u", port_numbers[i]); - } - n += snprintf(&str[n], sizeof(":000.000"), ":%u.%u", (uint8_t)config_number, (uint8_t)interface_number); - str[n] = '\0'; - } else { - /* Likely impossible, but check: USB3.0 specs limit number of ports to 7 and buffer size here is 8 */ - if (num_ports == LIBUSB_ERROR_OVERFLOW) { - LOG("make_path() failed. buffer overflow error\n"); - } else { - LOG("make_path() failed. unknown error\n"); - } - str[0] = '\0'; - } -} - -static char *make_path(libusb_device *dev, int config_number, int interface_number) -{ - char str[64]; - get_path(&str, dev, config_number, interface_number); - return strdup(str); -} - -HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version() -{ - return &api_version; -} - -HID_API_EXPORT const char* HID_API_CALL hid_version_str() -{ - return HID_API_VERSION_STR; -} - -int HID_API_EXPORT hid_init(void) -{ - if (!usb_context) { - const char *locale; - - /* Init Libusb */ - if (libusb_init(&usb_context)) - return -1; - - /* Set the locale if it's not set. */ - locale = setlocale(LC_CTYPE, NULL); - if (!locale) - setlocale(LC_CTYPE, ""); - } - - return 0; -} - -int HID_API_EXPORT hid_exit(void) -{ - if (usb_context) { - libusb_exit(usb_context); - usb_context = NULL; - } - - return 0; -} - -static int hid_get_report_descriptor_libusb(libusb_device_handle *handle, int interface_num, uint16_t expected_report_descriptor_size, unsigned char *buf, size_t buf_size) -{ - unsigned char tmp[HID_API_MAX_REPORT_DESCRIPTOR_SIZE]; - - if (expected_report_descriptor_size > HID_API_MAX_REPORT_DESCRIPTOR_SIZE) - expected_report_descriptor_size = HID_API_MAX_REPORT_DESCRIPTOR_SIZE; - - /* Get the HID Report Descriptor. - See USB HID Specificatin, sectin 7.1.1 - */ - int res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8), interface_num, tmp, expected_report_descriptor_size, 5000); - if (res < 0) { - LOG("libusb_control_transfer() for getting the HID Report descriptor failed with %d: %s\n", res, libusb_error_name(res)); - return -1; - } - - if (res > (int)buf_size) - res = (int)buf_size; - - memcpy(buf, tmp, (size_t)res); - return res; -} - -/** - * Requires an opened device with *claimed interface*. - */ -static void fill_device_info_usage(struct hid_device_info *cur_dev, libusb_device_handle *handle, int interface_num, uint16_t expected_report_descriptor_size) -{ - unsigned char hid_report_descriptor[HID_API_MAX_REPORT_DESCRIPTOR_SIZE]; - unsigned short page = 0, usage = 0; - - int res = hid_get_report_descriptor_libusb(handle, interface_num, expected_report_descriptor_size, hid_report_descriptor, sizeof(hid_report_descriptor)); - if (res >= 0) { - /* Parse the usage and usage page - out of the report descriptor. */ - get_usage(hid_report_descriptor, res, &page, &usage); - } - - cur_dev->usage_page = page; - cur_dev->usage = usage; -} - -#ifdef INVASIVE_GET_USAGE -static void invasive_fill_device_info_usage(struct hid_device_info *cur_dev, libusb_device_handle *handle, int interface_num, uint16_t report_descriptor_size) -{ - int res = 0; - -#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.\n"); - else - detached = 1; - } -#endif - - res = libusb_claim_interface(handle, interface_num); - if (res >= 0) { - fill_device_info_usage(cur_dev, handle, interface_num, report_descriptor_size); - - /* 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) %s\n", res, libusb_error_name(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"); - } -#endif -} -#endif /* INVASIVE_GET_USAGE */ - -/** - * Create and fill up most of hid_device_info fields. - * usage_page/usage is not filled up. - */ -static struct hid_device_info * create_device_info_for_device(libusb_device *device, libusb_device_handle *handle, struct libusb_device_descriptor *desc, int config_number, int interface_num) -{ - struct hid_device_info *cur_dev = calloc(1, sizeof(struct hid_device_info)); - if (cur_dev == NULL) { - return NULL; - } - - /* VID/PID */ - cur_dev->vendor_id = desc->idVendor; - cur_dev->product_id = desc->idProduct; - - cur_dev->release_number = desc->bcdDevice; - - cur_dev->interface_number = interface_num; - - cur_dev->bus_type = HID_API_BUS_USB; - - cur_dev->path = make_path(device, config_number, interface_num); - - if (!handle) { - return cur_dev; - } - - 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); - - return cur_dev; -} - -static uint16_t get_report_descriptor_size_from_interface_descriptors(const struct libusb_interface_descriptor *intf_desc) -{ - int i = 0; - int found_hid_report_descriptor = 0; - uint16_t result = HID_API_MAX_REPORT_DESCRIPTOR_SIZE; - const unsigned char *extra = intf_desc->extra; - int extra_length = intf_desc->extra_length; - - /* - "extra" contains a HID descriptor - See section 6.2.1 of HID 1.1 specification. - */ - - while (extra_length >= 2) { /* Descriptor header: bLength/bDescriptorType */ - if (extra[1] == LIBUSB_DT_HID) { /* bDescriptorType */ - if (extra_length < 6) { - LOG("Broken HID descriptor: not enough data\n"); - break; - } - unsigned char bNumDescriptors = extra[5]; - if (extra_length < (6 + 3 * bNumDescriptors)) { - LOG("Broken HID descriptor: not enough data for Report metadata\n"); - break; - } - for (i = 0; i < bNumDescriptors; i++) { - if (extra[6 + 3 * i] == LIBUSB_DT_REPORT) { - result = (uint16_t)extra[6 + 3 * i + 2] << 8 | extra[6 + 3 * i + 1]; - found_hid_report_descriptor = 1; - break; - } - } - - if (!found_hid_report_descriptor) { - /* We expect to find exactly 1 HID descriptor (LIBUSB_DT_HID) - which should contain exactly one HID Report Descriptor metadata (LIBUSB_DT_REPORT). */ - LOG("Broken HID descriptor: missing Report descriptor\n"); - } - break; - } - - if (extra[0] == 0) { /* bLength */ - LOG("Broken HID Interface descriptors: zero-sized descriptor\n"); - break; - } - - /* Iterate over to the next Descriptor */ - extra_length -= extra[0]; - extra += extra[0]; - } - - return result; -} - -struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) -{ - libusb_device **devs; - libusb_device *dev; - libusb_device_handle *handle = NULL; - ssize_t num_devs; - int i = 0; - - struct hid_device_info *root = NULL; /* return object */ - struct hid_device_info *cur_dev = NULL; - - if(hid_init() < 0) - return NULL; - - num_devs = libusb_get_device_list(usb_context, &devs); - if (num_devs < 0) - return NULL; - while ((dev = devs[i++]) != NULL) { - struct libusb_device_descriptor desc; - struct libusb_config_descriptor *conf_desc = NULL; - int j, k; - - 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); - if (conf_desc) { - for (j = 0; j < conf_desc->bNumInterfaces; 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]; - if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { - struct hid_device_info *tmp; - - res = libusb_open(dev, &handle); - -#ifdef __ANDROID__ - if (handle) { - /* 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 - - tmp = create_device_info_for_device(dev, handle, &desc, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); - if (tmp) { -#ifdef INVASIVE_GET_USAGE - /* TODO: have a runtime check for this section. */ - - /* - 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 - optional. For composite devices, use the interface - field in the hid_device_info struct to distinguish - between interfaces. */ - if (handle) { - uint16_t report_descriptor_size = get_report_descriptor_size_from_interface_descriptors(intf_desc); - - invasive_fill_device_info_usage(tmp, handle, intf_desc->bInterfaceNumber, report_descriptor_size); - } -#endif /* INVASIVE_GET_USAGE */ - - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; - } - - if (res >= 0) - libusb_close(handle); - } - } /* altsettings */ - } /* interfaces */ - libusb_free_config_descriptor(conf_desc); - } - } - - libusb_free_device_list(devs, 1); - - return root; -} - -void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) -{ - struct hid_device_info *d = devs; - while (d) { - struct hid_device_info *next = d->next; - free(d->path); - free(d->serial_number); - free(d->manufacturer_string); - free(d->product_string); - free(d); - d = next; - } -} - -hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) -{ - 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) { - if (cur_dev->vendor_id == vendor_id && - cur_dev->product_id == product_id) { - if (serial_number) { - if (cur_dev->serial_number && - wcscmp(serial_number, cur_dev->serial_number) == 0) { - path_to_open = cur_dev->path; - break; - } - } - else { - path_to_open = cur_dev->path; - break; - } - } - cur_dev = cur_dev->next; - } - - if (path_to_open) { - /* Open the device */ - handle = hid_open_path(path_to_open); - } - - hid_free_enumeration(devs); - - return handle; -} - -static void read_callback(struct libusb_transfer *transfer) -{ - hid_device *dev = transfer->user_data; - int res; - - if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { - - struct input_report *rpt = (struct input_report*) malloc(sizeof(*rpt)); - rpt->data = (uint8_t*) malloc(transfer->actual_length); - memcpy(rpt->data, transfer->buffer, transfer->actual_length); - rpt->len = transfer->actual_length; - rpt->next = NULL; - - pthread_mutex_lock(&dev->mutex); - - /* Attach the new report object to the end of the list. */ - if (dev->input_reports == NULL) { - /* The list is empty. Put it at the root. */ - dev->input_reports = rpt; - pthread_cond_signal(&dev->condition); - } - else { - /* Find the end of the list and attach. */ - struct input_report *cur = dev->input_reports; - int num_queued = 0; - while (cur->next != NULL) { - cur = cur->next; - num_queued++; - } - cur->next = rpt; - - /* Pop one off if we've reached 30 in the queue. This - way we don't grow forever if the user never reads - anything from the device. */ - if (num_queued > 30) { - return_data(dev, NULL, 0); - } - } - pthread_mutex_unlock(&dev->mutex); - } - else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { - dev->shutdown_thread = 1; - } - else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { - dev->shutdown_thread = 1; - } - else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { - //LOG("Timeout (normal)\n"); - } - else { - LOG("Unknown transfer code: %d\n", transfer->status); - } - - if (dev->shutdown_thread) { - dev->transfer_loop_finished = 1; - return; - } - - /* Re-submit the transfer object. */ - res = libusb_submit_transfer(transfer); - if (res != 0) { - LOG("Unable to submit URB: (%d) %s\n", res, libusb_error_name(res)); - dev->shutdown_thread = 1; - dev->transfer_loop_finished = 1; - } -} - - -static void *read_thread(void *param) -{ - int res; - hid_device *dev = param; - uint8_t *buf; - const size_t length = dev->input_ep_max_packet_size; - - /* Set up the transfer object. */ - buf = (uint8_t*) malloc(length); - dev->transfer = libusb_alloc_transfer(0); - libusb_fill_interrupt_transfer(dev->transfer, - dev->device_handle, - dev->input_endpoint, - buf, - length, - read_callback, - dev, - 5000/*timeout*/); - - /* Make the first submission. Further submissions are made - from inside read_callback() */ - res = libusb_submit_transfer(dev->transfer); - if(res < 0) { - LOG("libusb_submit_transfer failed: %d %s. Stopping read_thread from running\n", res, libusb_error_name(res)); - dev->shutdown_thread = 1; - dev->transfer_loop_finished = 1; - } - - /* Notify the main thread that the read thread is up and running. */ - pthread_barrier_wait(&dev->barrier); - - /* Handle all the events. */ - while (!dev->shutdown_thread) { - res = libusb_handle_events(usb_context); - if (res < 0) { - /* There was an error. */ - LOG("read_thread(): (%d) %s\n", res, libusb_error_name(res)); - - /* Break out of this loop only on fatal error.*/ - if (res != LIBUSB_ERROR_BUSY && - res != LIBUSB_ERROR_TIMEOUT && - res != LIBUSB_ERROR_OVERFLOW && - res != LIBUSB_ERROR_INTERRUPTED) { - dev->shutdown_thread = 1; - break; - } - } - } - - /* Cancel any transfer that may be pending. This call will fail - if no transfers are pending, but that's OK. */ - libusb_cancel_transfer(dev->transfer); - - while (!dev->transfer_loop_finished) - libusb_handle_events_completed(usb_context, &dev->transfer_loop_finished); - - /* Now that the read thread is stopping, Wake any threads which are - waiting on data (in hid_read_timeout()). Do this under a mutex to - make sure that a thread which is about to go to sleep waiting on - the condition actually will go to sleep before the condition is - signaled. */ - pthread_mutex_lock(&dev->mutex); - pthread_cond_broadcast(&dev->condition); - pthread_mutex_unlock(&dev->mutex); - - /* The dev->transfer->buffer and dev->transfer objects are cleaned up - in hid_close(). They are not cleaned up here because this thread - could end either due to a disconnect or due to a user - call to hid_close(). In both cases the objects can be safely - cleaned up after the call to pthread_join() (in hid_close()), but - since hid_close() calls libusb_cancel_transfer(), on these objects, - they can not be cleaned up here. */ - - return NULL; -} - - -static int hidapi_initialize_device(hid_device *dev, int config_number, 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) { - LOG("Unable to detach Kernel Driver: (%d) %s\n", res, libusb_error_name(res)); - 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) %s\n", intf_desc->bInterfaceNumber, res, libusb_error_name(res)); - -#ifdef DETACH_KERNEL_DRIVER - if (dev->is_driver_detached) { - res = libusb_attach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); - if (res < 0) - LOG("Failed to reattach the driver to kernel: (%d) %s\n", res, libusb_error_name(res)); - } -#endif - 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 USB information */ - dev->config_number = config_number; - dev->interface = intf_desc->bInterfaceNumber; - - dev->report_descriptor_size = get_report_descriptor_size_from_interface_descriptors(intf_desc); - - 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 = NULL; - libusb_device *usb_dev = NULL; - int res = 0; - int d = 0; - int good_open = 0; - - if(hid_init() < 0) - return NULL; - - dev = new_hid_device(); - - libusb_get_device_list(usb_context, &devs); - while ((usb_dev = devs[d++]) != NULL && !good_open) { - struct libusb_config_descriptor *conf_desc = NULL; - int j,k; - - if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) - continue; - 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 && !good_open; k++) { - const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; - if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { - char dev_path[64]; - get_path(&dev_path, usb_dev, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); - if (!strcmp(dev_path, path)) { - /* Matched Paths. Open this device */ - - /* OPEN HERE */ - res = libusb_open(usb_dev, &dev->device_handle); - if (res < 0) { - LOG("can't open device\n"); - break; - } - good_open = hidapi_initialize_device(dev, conf_desc->bConfigurationValue, intf_desc); - if (!good_open) - libusb_close(dev->device_handle); - } - } - } - } - libusb_free_config_descriptor(conf_desc); - } - - libusb_free_device_list(devs, 1); - - /* If we have a good handle, return it. */ - if (good_open) { - return dev; - } - else { - /* Unable to open any devices. */ - free_hid_device(dev); - return NULL; - } -} - - -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, conf_desc->bConfigurationValue, 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; - int report_number; - int skipped_report_id = 0; - - if (!data || (length ==0)) { - return -1; - } - - report_number = data[0]; - - if (report_number == 0x0) { - data++; - length--; - skipped_report_id = 1; - } - - - if (dev->output_endpoint <= 0) { - /* No interrupt out endpoint. Use the Control Endpoint */ - res = libusb_control_transfer(dev->device_handle, - LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, - 0x09/*HID Set_Report*/, - (2/*HID output*/ << 8) | report_number, - dev->interface, - (unsigned char *)data, length, - 1000/*timeout millis*/); - - if (res < 0) - return -1; - - if (skipped_report_id) - length++; - - return length; - } - else { - /* Use the interrupt out endpoint */ - int actual_length; - res = libusb_interrupt_transfer(dev->device_handle, - dev->output_endpoint, - (unsigned char*)data, - length, - &actual_length, 1000); - - if (res < 0) - return -1; - - if (skipped_report_id) - actual_length++; - - return actual_length; - } -} - -/* Helper function, to simplify hid_read(). - This should be called with dev->mutex locked. */ -static int return_data(hid_device *dev, unsigned char *data, size_t length) -{ - /* Copy the data out of the linked list item (rpt) into the - return buffer (data), and delete the liked list item. */ - struct input_report *rpt = dev->input_reports; - size_t len = (length < rpt->len)? length: rpt->len; - if (len > 0) - memcpy(data, rpt->data, len); - dev->input_reports = rpt->next; - free(rpt->data); - free(rpt); - return len; -} - -static void cleanup_mutex(void *param) -{ - hid_device *dev = param; - pthread_mutex_unlock(&dev->mutex); -} - - -int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) -{ -#if 0 - int transferred; - int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000); - LOG("transferred: %d\n", transferred); - return transferred; -#endif - /* by initialising this variable right here, GCC gives a compilation warning/error: */ - /* error: variable ‘bytes_read’ might be clobbered by ‘longjmp’ or ‘vfork’ [-Werror=clobbered] */ - int bytes_read; /* = -1; */ - - pthread_mutex_lock(&dev->mutex); - pthread_cleanup_push(&cleanup_mutex, dev); - - bytes_read = -1; - - /* There's an input report queued up. Return it. */ - if (dev->input_reports) { - /* Return the first one */ - bytes_read = return_data(dev, data, length); - goto ret; - } - - if (dev->shutdown_thread) { - /* This means the device has been disconnected. - An error code of -1 should be returned. */ - bytes_read = -1; - goto ret; - } - - if (milliseconds == -1) { - /* Blocking */ - while (!dev->input_reports && !dev->shutdown_thread) { - pthread_cond_wait(&dev->condition, &dev->mutex); - } - if (dev->input_reports) { - bytes_read = return_data(dev, data, length); - } - } - else if (milliseconds > 0) { - /* Non-blocking, but called with timeout. */ - int res; - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += milliseconds / 1000; - ts.tv_nsec += (milliseconds % 1000) * 1000000; - if (ts.tv_nsec >= 1000000000L) { - ts.tv_sec++; - ts.tv_nsec -= 1000000000L; - } - - while (!dev->input_reports && !dev->shutdown_thread) { - res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); - if (res == 0) { - if (dev->input_reports) { - bytes_read = return_data(dev, data, length); - break; - } - - /* If we're here, there was a spurious wake up - or the read thread was shutdown. Run the - loop again (ie: don't break). */ - } - else if (res == ETIMEDOUT) { - /* Timed out. */ - bytes_read = 0; - break; - } - else { - /* Error. */ - bytes_read = -1; - break; - } - } - } - else { - /* Purely non-blocking */ - bytes_read = 0; - } - -ret: - pthread_mutex_unlock(&dev->mutex); - pthread_cleanup_pop(0); - - return bytes_read; -} - -int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) -{ - return hid_read_timeout(dev, data, length, dev->blocking ? -1 : 0); -} - -int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) -{ - dev->blocking = !nonblock; - - return 0; -} - - -int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) -{ - int res = -1; - int skipped_report_id = 0; - int report_number = data[0]; - - if (report_number == 0x0) { - data++; - length--; - skipped_report_id = 1; - } - - res = libusb_control_transfer(dev->device_handle, - LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, - 0x09/*HID set_report*/, - (3/*HID feature*/ << 8) | report_number, - dev->interface, - (unsigned char *)data, length, - 1000/*timeout millis*/); - - if (res < 0) - return -1; - - /* Account for the report ID */ - if (skipped_report_id) - length++; - - return length; -} - -int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) -{ - int res = -1; - int skipped_report_id = 0; - int report_number = data[0]; - - if (report_number == 0x0) { - /* Offset the return buffer by 1, so that the report ID - will remain in byte 0. */ - data++; - length--; - skipped_report_id = 1; - } - res = libusb_control_transfer(dev->device_handle, - LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, - 0x01/*HID get_report*/, - (3/*HID feature*/ << 8) | report_number, - dev->interface, - (unsigned char *)data, length, - 1000/*timeout millis*/); - - if (res < 0) - return -1; - - if (skipped_report_id) - res++; - - return res; -} - -int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) -{ - int res = -1; - int skipped_report_id = 0; - int report_number = data[0]; - - if (report_number == 0x0) { - /* Offset the return buffer by 1, so that the report ID - will remain in byte 0. */ - data++; - length--; - skipped_report_id = 1; - } - res = libusb_control_transfer(dev->device_handle, - LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_IN, - 0x01/*HID get_report*/, - (1/*HID Input*/ << 8) | report_number, - dev->interface, - (unsigned char *)data, length, - 1000/*timeout millis*/); - - if (res < 0) - return -1; - - if (skipped_report_id) - res++; - - return res; -} - -void HID_API_EXPORT hid_close(hid_device *dev) -{ - if (!dev) - return; - - /* Cause read_thread() to stop. */ - dev->shutdown_thread = 1; - libusb_cancel_transfer(dev->transfer); - - /* Wait for read_thread() to end. */ - pthread_join(dev->thread, NULL); - - /* Clean up the Transfer objects allocated in read_thread(). */ - free(dev->transfer->buffer); - dev->transfer->buffer = NULL; - libusb_free_transfer(dev->transfer); - - /* release the interface */ - libusb_release_interface(dev->device_handle, dev->interface); - - /* reattach the kernel driver if it was detached */ -#ifdef DETACH_KERNEL_DRIVER - if (dev->is_driver_detached) { - int res = libusb_attach_kernel_driver(dev->device_handle, dev->interface); - if (res < 0) - LOG("Failed to reattach the driver to kernel.\n"); - } -#endif - - /* Close the handle */ - libusb_close(dev->device_handle); - - /* Clear out the queue of received reports. */ - pthread_mutex_lock(&dev->mutex); - while (dev->input_reports) { - return_data(dev, NULL, 0); - } - pthread_mutex_unlock(&dev->mutex); - - free_hid_device(dev); -} - - -int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - return hid_get_indexed_string(dev, dev->manufacturer_index, string, maxlen); -} - -int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - return hid_get_indexed_string(dev, dev->product_index, string, maxlen); -} - -int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - return hid_get_indexed_string(dev, dev->serial_index, string, maxlen); -} - -HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) { - if (!dev->device_info) { - struct libusb_device_descriptor desc; - libusb_device *usb_device = libusb_get_device(dev->device_handle); - libusb_get_device_descriptor(usb_device, &desc); - - dev->device_info = create_device_info_for_device(usb_device, dev->device_handle, &desc, dev->config_number, dev->interface); - // device error already set by create_device_info_for_device, if any - - if (dev->device_info) { - fill_device_info_usage(dev->device_info, dev->device_handle, dev->interface, dev->report_descriptor_size); - } - } - - return dev->device_info; -} - -int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) -{ - wchar_t *str; - - str = get_usb_string(dev->device_handle, string_index); - if (str) { - wcsncpy(string, str, maxlen); - string[maxlen-1] = L'\0'; - free(str); - return 0; - } - else - return -1; -} - - -HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) -{ - (void)dev; - return L"hid_error is not implemented yet"; -} - - -struct lang_map_entry { - const char *name; - const char *string_code; - uint16_t usb_code; -}; - -#define LANG(name,code,usb_code) { name, code, usb_code } -static struct lang_map_entry lang_map[] = { - LANG("Afrikaans", "af", 0x0436), - LANG("Albanian", "sq", 0x041C), - LANG("Arabic - United Arab Emirates", "ar_ae", 0x3801), - LANG("Arabic - Bahrain", "ar_bh", 0x3C01), - LANG("Arabic - Algeria", "ar_dz", 0x1401), - LANG("Arabic - Egypt", "ar_eg", 0x0C01), - LANG("Arabic - Iraq", "ar_iq", 0x0801), - LANG("Arabic - Jordan", "ar_jo", 0x2C01), - LANG("Arabic - Kuwait", "ar_kw", 0x3401), - LANG("Arabic - Lebanon", "ar_lb", 0x3001), - LANG("Arabic - Libya", "ar_ly", 0x1001), - LANG("Arabic - Morocco", "ar_ma", 0x1801), - LANG("Arabic - Oman", "ar_om", 0x2001), - LANG("Arabic - Qatar", "ar_qa", 0x4001), - LANG("Arabic - Saudi Arabia", "ar_sa", 0x0401), - LANG("Arabic - Syria", "ar_sy", 0x2801), - LANG("Arabic - Tunisia", "ar_tn", 0x1C01), - LANG("Arabic - Yemen", "ar_ye", 0x2401), - LANG("Armenian", "hy", 0x042B), - LANG("Azeri - Latin", "az_az", 0x042C), - LANG("Azeri - Cyrillic", "az_az", 0x082C), - LANG("Basque", "eu", 0x042D), - LANG("Belarusian", "be", 0x0423), - LANG("Bulgarian", "bg", 0x0402), - LANG("Catalan", "ca", 0x0403), - LANG("Chinese - China", "zh_cn", 0x0804), - LANG("Chinese - Hong Kong SAR", "zh_hk", 0x0C04), - LANG("Chinese - Macau SAR", "zh_mo", 0x1404), - LANG("Chinese - Singapore", "zh_sg", 0x1004), - LANG("Chinese - Taiwan", "zh_tw", 0x0404), - LANG("Croatian", "hr", 0x041A), - LANG("Czech", "cs", 0x0405), - LANG("Danish", "da", 0x0406), - LANG("Dutch - Netherlands", "nl_nl", 0x0413), - LANG("Dutch - Belgium", "nl_be", 0x0813), - LANG("English - Australia", "en_au", 0x0C09), - LANG("English - Belize", "en_bz", 0x2809), - LANG("English - Canada", "en_ca", 0x1009), - LANG("English - Caribbean", "en_cb", 0x2409), - LANG("English - Ireland", "en_ie", 0x1809), - LANG("English - Jamaica", "en_jm", 0x2009), - LANG("English - New Zealand", "en_nz", 0x1409), - LANG("English - Philippines", "en_ph", 0x3409), - LANG("English - Southern Africa", "en_za", 0x1C09), - LANG("English - Trinidad", "en_tt", 0x2C09), - LANG("English - Great Britain", "en_gb", 0x0809), - LANG("English - United States", "en_us", 0x0409), - LANG("Estonian", "et", 0x0425), - LANG("Farsi", "fa", 0x0429), - LANG("Finnish", "fi", 0x040B), - LANG("Faroese", "fo", 0x0438), - LANG("French - France", "fr_fr", 0x040C), - LANG("French - Belgium", "fr_be", 0x080C), - LANG("French - Canada", "fr_ca", 0x0C0C), - LANG("French - Luxembourg", "fr_lu", 0x140C), - LANG("French - Switzerland", "fr_ch", 0x100C), - LANG("Gaelic - Ireland", "gd_ie", 0x083C), - LANG("Gaelic - Scotland", "gd", 0x043C), - LANG("German - Germany", "de_de", 0x0407), - LANG("German - Austria", "de_at", 0x0C07), - LANG("German - Liechtenstein", "de_li", 0x1407), - LANG("German - Luxembourg", "de_lu", 0x1007), - LANG("German - Switzerland", "de_ch", 0x0807), - LANG("Greek", "el", 0x0408), - LANG("Hebrew", "he", 0x040D), - LANG("Hindi", "hi", 0x0439), - LANG("Hungarian", "hu", 0x040E), - LANG("Icelandic", "is", 0x040F), - LANG("Indonesian", "id", 0x0421), - LANG("Italian - Italy", "it_it", 0x0410), - LANG("Italian - Switzerland", "it_ch", 0x0810), - LANG("Japanese", "ja", 0x0411), - LANG("Korean", "ko", 0x0412), - LANG("Latvian", "lv", 0x0426), - LANG("Lithuanian", "lt", 0x0427), - LANG("F.Y.R.O. Macedonia", "mk", 0x042F), - LANG("Malay - Malaysia", "ms_my", 0x043E), - LANG("Malay – Brunei", "ms_bn", 0x083E), - LANG("Maltese", "mt", 0x043A), - LANG("Marathi", "mr", 0x044E), - LANG("Norwegian - Bokml", "no_no", 0x0414), - LANG("Norwegian - Nynorsk", "no_no", 0x0814), - LANG("Polish", "pl", 0x0415), - LANG("Portuguese - Portugal", "pt_pt", 0x0816), - LANG("Portuguese - Brazil", "pt_br", 0x0416), - LANG("Raeto-Romance", "rm", 0x0417), - LANG("Romanian - Romania", "ro", 0x0418), - LANG("Romanian - Republic of Moldova", "ro_mo", 0x0818), - LANG("Russian", "ru", 0x0419), - LANG("Russian - Republic of Moldova", "ru_mo", 0x0819), - LANG("Sanskrit", "sa", 0x044F), - LANG("Serbian - Cyrillic", "sr_sp", 0x0C1A), - LANG("Serbian - Latin", "sr_sp", 0x081A), - LANG("Setsuana", "tn", 0x0432), - LANG("Slovenian", "sl", 0x0424), - LANG("Slovak", "sk", 0x041B), - LANG("Sorbian", "sb", 0x042E), - LANG("Spanish - Spain (Traditional)", "es_es", 0x040A), - LANG("Spanish - Argentina", "es_ar", 0x2C0A), - LANG("Spanish - Bolivia", "es_bo", 0x400A), - LANG("Spanish - Chile", "es_cl", 0x340A), - LANG("Spanish - Colombia", "es_co", 0x240A), - LANG("Spanish - Costa Rica", "es_cr", 0x140A), - LANG("Spanish - Dominican Republic", "es_do", 0x1C0A), - LANG("Spanish - Ecuador", "es_ec", 0x300A), - LANG("Spanish - Guatemala", "es_gt", 0x100A), - LANG("Spanish - Honduras", "es_hn", 0x480A), - LANG("Spanish - Mexico", "es_mx", 0x080A), - LANG("Spanish - Nicaragua", "es_ni", 0x4C0A), - LANG("Spanish - Panama", "es_pa", 0x180A), - LANG("Spanish - Peru", "es_pe", 0x280A), - LANG("Spanish - Puerto Rico", "es_pr", 0x500A), - LANG("Spanish - Paraguay", "es_py", 0x3C0A), - LANG("Spanish - El Salvador", "es_sv", 0x440A), - LANG("Spanish - Uruguay", "es_uy", 0x380A), - LANG("Spanish - Venezuela", "es_ve", 0x200A), - LANG("Southern Sotho", "st", 0x0430), - LANG("Swahili", "sw", 0x0441), - LANG("Swedish - Sweden", "sv_se", 0x041D), - LANG("Swedish - Finland", "sv_fi", 0x081D), - LANG("Tamil", "ta", 0x0449), - LANG("Tatar", "tt", 0X0444), - LANG("Thai", "th", 0x041E), - LANG("Turkish", "tr", 0x041F), - LANG("Tsonga", "ts", 0x0431), - LANG("Ukrainian", "uk", 0x0422), - LANG("Urdu", "ur", 0x0420), - LANG("Uzbek - Cyrillic", "uz_uz", 0x0843), - LANG("Uzbek – Latin", "uz_uz", 0x0443), - LANG("Vietnamese", "vi", 0x042A), - LANG("Xhosa", "xh", 0x0434), - LANG("Yiddish", "yi", 0x043D), - LANG("Zulu", "zu", 0x0435), - LANG(NULL, NULL, 0x0), -}; - -uint16_t get_usb_code_for_current_locale(void) -{ - char *locale; - char search_string[64]; - char *ptr; - struct lang_map_entry *lang; - - /* Get the current locale. */ - locale = setlocale(0, NULL); - if (!locale) - return 0x0; - - /* Make a copy of the current locale string. */ - strncpy(search_string, locale, sizeof(search_string)); - search_string[sizeof(search_string)-1] = '\0'; - - /* Chop off the encoding part, and make it lower case. */ - ptr = search_string; - while (*ptr) { - *ptr = tolower(*ptr); - if (*ptr == '.') { - *ptr = '\0'; - break; - } - ptr++; - } - - /* Find the entry which matches the string code of our locale. */ - lang = lang_map; - while (lang->string_code) { - if (!strcmp(lang->string_code, search_string)) { - return lang->usb_code; - } - lang++; - } - - /* There was no match. Find with just the language only. */ - /* Chop off the variant. Chop it off at the '_'. */ - ptr = search_string; - while (*ptr) { - *ptr = tolower(*ptr); - if (*ptr == '_') { - *ptr = '\0'; - break; - } - ptr++; - } - -#if 0 /* TODO: Do we need this? */ - /* Find the entry which matches the string code of our language. */ - lang = lang_map; - while (lang->string_code) { - if (!strcmp(lang->string_code, search_string)) { - return lang->usb_code; - } - lang++; - } -#endif - - /* Found nothing. */ - return 0x0; -} - -#ifdef __cplusplus -} -#endif diff --git a/lib/hidapi/libusb/hidapi_libusb.h b/lib/hidapi/libusb/hidapi_libusb.h deleted file mode 100644 index 29207687ed6..00000000000 --- a/lib/hidapi/libusb/hidapi_libusb.h +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************* - 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 - - * Since version 0.11.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 11, 0). - */ - -#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/lib/hidapi/linux/.gitignore b/lib/hidapi/linux/.gitignore deleted file mode 100644 index 127bf37d9cf..00000000000 --- a/lib/hidapi/linux/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -Debug -Release -*.exp -*.ilk -*.lib -*.suo -*.vcproj.* -*.ncb -*.suo -*.dll -*.pdb -*.o -*.so -hidtest-hidraw -.deps -.libs -*.lo -*.la diff --git a/lib/hidapi/linux/CMakeLists.txt b/lib/hidapi/linux/CMakeLists.txt deleted file mode 100644 index 9ff9d2f7902..00000000000 --- a/lib/hidapi/linux/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) - -add_library(hidapi_hidraw - ${HIDAPI_PUBLIC_HEADERS} - hid.c -) -target_link_libraries(hidapi_hidraw PUBLIC hidapi_include) - -find_package(Threads REQUIRED) - -include(FindPkgConfig) -pkg_check_modules(libudev REQUIRED IMPORTED_TARGET libudev) - -target_link_libraries(hidapi_hidraw PRIVATE PkgConfig::libudev Threads::Threads) - -set_target_properties(hidapi_hidraw - PROPERTIES - EXPORT_NAME "hidraw" - OUTPUT_NAME "hidapi-hidraw" - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}" -) - -# compatibility with find_package() -add_library(hidapi::hidraw ALIAS hidapi_hidraw) -# compatibility with raw library link -add_library(hidapi-hidraw ALIAS hidapi_hidraw) - -if(HIDAPI_INSTALL_TARGETS) - install(TARGETS hidapi_hidraw EXPORT hidapi - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi" - ) -endif() - -hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi-hidraw.pc.in") diff --git a/lib/hidapi/linux/hid.c b/lib/hidapi/linux/hid.c deleted file mode 100644 index c9ddec07124..00000000000 --- a/lib/hidapi/linux/hid.c +++ /dev/null @@ -1,1242 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - Alan Ott - Signal 11 Software - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -/* C */ -#include -#include -#include -#include -#include - -/* Unix */ -#include -#include -#include -#include -#include -#include -#include - -/* Linux */ -#include -#include -#include -#include - -#include "hidapi.h" - -#ifdef HIDAPI_ALLOW_BUILD_WORKAROUND_KERNEL_2_6_39 -/* This definitions first appeared in Linux Kernel 2.6.39 in linux/hidraw.h. - hidapi doesn't support kernels older than that, - so we don't define macros below explicitly, to fail builds on old kernels. - For those who really need this as a workaround (e.g. to be able to build on old build machines), - can workaround by defining the macro above. -*/ -#ifndef HIDIOCSFEATURE -#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) -#endif -#ifndef HIDIOCGFEATURE -#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) -#endif - -#endif - - -// HIDIOCGINPUT is not defined in Linux kernel headers < 5.11. -// This definition is from hidraw.h in Linux >= 5.11. -// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f43d3870cafa2a0f3854c1819c8385733db8f9ae -#ifndef HIDIOCGINPUT -#define HIDIOCGINPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0A, len) -#endif - -struct hid_device_ { - int device_handle; - int blocking; - wchar_t *last_error_str; - struct hid_device_info* device_info; -}; - -static struct hid_api_version api_version = { - .major = HID_API_VERSION_MAJOR, - .minor = HID_API_VERSION_MINOR, - .patch = HID_API_VERSION_PATCH -}; - -static wchar_t *last_global_error_str = NULL; - - -static hid_device *new_hid_device(void) -{ - hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); - if (dev == NULL) { - return NULL; - } - - dev->device_handle = -1; - dev->blocking = 1; - dev->last_error_str = NULL; - dev->device_info = NULL; - - return dev; -} - - -/* The caller must free the returned string with free(). */ -static wchar_t *utf8_to_wchar_t(const char *utf8) -{ - wchar_t *ret = NULL; - - if (utf8) { - size_t wlen = mbstowcs(NULL, utf8, 0); - if ((size_t) -1 == wlen) { - return wcsdup(L""); - } - ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t)); - if (ret == NULL) { - /* as much as we can do at this point */ - return NULL; - } - mbstowcs(ret, utf8, wlen+1); - ret[wlen] = 0x0000; - } - - return ret; -} - - -/* Makes a copy of the given error message (and decoded according to the - * currently locale) into the wide string pointer pointed by error_str. - * The last stored error string is freed. - * Use register_error_str(NULL) to free the error message completely. */ -static void register_error_str(wchar_t **error_str, const char *msg) -{ - free(*error_str); - *error_str = utf8_to_wchar_t(msg); -} - -/* Semilar to register_error_str, but allows passing a format string with va_list args into this function. */ -static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args) -{ - char msg[256]; - vsnprintf(msg, sizeof(msg), format, args); - - register_error_str(error_str, msg); -} - -/* Set the last global error to be reported by hid_error(NULL). - * The given error message will be copied (and decoded according to the - * currently locale, so do not pass in string constants). - * The last stored global error message is freed. - * Use register_global_error(NULL) to indicate "no error". */ -static void register_global_error(const char *msg) -{ - register_error_str(&last_global_error_str, msg); -} - -/* Similar to register_global_error, but allows passing a format string into this function. */ -static void register_global_error_format(const char *format, ...) -{ - va_list args; - va_start(args, format); - register_error_str_vformat(&last_global_error_str, format, args); - va_end(args); -} - -/* Set the last error for a device to be reported by hid_error(dev). - * The given error message will be copied (and decoded according to the - * currently locale, so do not pass in string constants). - * The last stored device error message is freed. - * Use register_device_error(dev, NULL) to indicate "no error". */ -static void register_device_error(hid_device *dev, const char *msg) -{ - register_error_str(&dev->last_error_str, msg); -} - -/* Similar to register_device_error, but you can pass a format string into this function. */ -static void register_device_error_format(hid_device *dev, const char *format, ...) -{ - va_list args; - va_start(args, format); - register_error_str_vformat(&dev->last_error_str, format, args); - va_end(args); -} - -/* Get an attribute value from a udev_device and return it as a whar_t - string. The returned string must be freed with free() when done.*/ -static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) -{ - return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name)); -} - -/* - * Gets the size of the HID item at the given position - * Returns 1 if successful, 0 if an invalid key - * Sets data_len and key_size when successful - */ -static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 size, int *data_len, int *key_size) -{ - int key = report_descriptor[pos]; - int size_code; - - /* - * This is a Long Item. The next byte contains the - * length of the data section (value) for this key. - * See the HID specification, version 1.11, section - * 6.2.2.3, titled "Long Items." - */ - if ((key & 0xf0) == 0xf0) { - if (pos + 1 < size) - { - *data_len = report_descriptor[pos + 1]; - *key_size = 3; - return 1; - } - *data_len = 0; /* malformed report */ - *key_size = 0; - } - - /* - * This is a Short Item. The bottom two bits of the - * key contain the size code for the data section - * (value) for this key. Refer to the HID - * specification, version 1.11, section 6.2.2.2, - * titled "Short Items." - */ - size_code = key & 0x3; - switch (size_code) { - case 0: - case 1: - case 2: - *data_len = size_code; - *key_size = 1; - return 1; - case 3: - *data_len = 4; - *key_size = 1; - return 1; - default: - /* Can't ever happen since size_code is & 0x3 */ - *data_len = 0; - *key_size = 0; - break; - }; - - /* malformed report */ - return 0; -} - -/* - * Get bytes from a HID Report Descriptor. - * Only call with a num_bytes of 0, 1, 2, or 4. - */ -static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_t cur) -{ - /* Return if there aren't enough bytes. */ - if (cur + num_bytes >= len) - return 0; - - if (num_bytes == 0) - return 0; - else if (num_bytes == 1) - return rpt[cur + 1]; - else if (num_bytes == 2) - return (rpt[cur + 2] * 256 + rpt[cur + 1]); - else if (num_bytes == 4) - return ( - rpt[cur + 4] * 0x01000000 + - rpt[cur + 3] * 0x00010000 + - rpt[cur + 2] * 0x00000100 + - rpt[cur + 1] * 0x00000001 - ); - else - return 0; -} - -/* - * Retrieves the device's Usage Page and Usage from the report descriptor. - * The algorithm returns the current Usage Page/Usage pair whenever a new - * Collection is found and a Usage Local Item is currently in scope. - * Usage Local Items are consumed by each Main Item (See. 6.2.2.8). - * The algorithm should give similar results as Apple's: - * https://developer.apple.com/documentation/iokit/kiohiddeviceusagepairskey?language=objc - * Physical Collections are also matched (macOS does the same). - * - * This function can be called repeatedly until it returns non-0 - * Usage is found. pos is the starting point (initially 0) and will be updated - * to the next search position. - * - * The return value is 0 when a pair is found. - * 1 when finished processing descriptor. - * -1 on a malformed report. - */ -static int get_next_hid_usage(__u8 *report_descriptor, __u32 size, unsigned int *pos, unsigned short *usage_page, unsigned short *usage) -{ - int data_len, key_size; - int initial = *pos == 0; /* Used to handle case where no top-level application collection is defined */ - int usage_pair_ready = 0; - - /* Usage is a Local Item, it must be set before each Main Item (Collection) before a pair is returned */ - int usage_found = 0; - - while (*pos < size) { - int key = report_descriptor[*pos]; - int key_cmd = key & 0xfc; - - /* Determine data_len and key_size */ - if (!get_hid_item_size(report_descriptor, *pos, size, &data_len, &key_size)) - return -1; /* malformed report */ - - switch (key_cmd) { - case 0x4: /* Usage Page 6.2.2.7 (Global) */ - *usage_page = get_hid_report_bytes(report_descriptor, size, data_len, *pos); - break; - - case 0x8: /* Usage 6.2.2.8 (Local) */ - *usage = get_hid_report_bytes(report_descriptor, size, data_len, *pos); - usage_found = 1; - break; - - case 0xa0: /* Collection 6.2.2.4 (Main) */ - /* A Usage Item (Local) must be found for the pair to be valid */ - if (usage_found) - usage_pair_ready = 1; - - /* Usage is a Local Item, unset it */ - usage_found = 0; - break; - - case 0x80: /* Input 6.2.2.4 (Main) */ - case 0x90: /* Output 6.2.2.4 (Main) */ - case 0xb0: /* Feature 6.2.2.4 (Main) */ - case 0xc0: /* End Collection 6.2.2.4 (Main) */ - /* Usage is a Local Item, unset it */ - usage_found = 0; - break; - } - - /* Skip over this key and its associated data */ - *pos += data_len + key_size; - - /* Return usage pair */ - if (usage_pair_ready) - return 0; - } - - /* If no top-level application collection is found and usage page/usage pair is found, pair is valid - https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/top-level-collections */ - if (initial && usage_found) - return 0; /* success */ - - return 1; /* finished processing */ -} - -/* - * Retrieves the hidraw report descriptor from a file. - * When using this form, /device/report_descriptor, elevated priviledges are not required. - */ -static int get_hid_report_descriptor(const char *rpt_path, struct hidraw_report_descriptor *rpt_desc) -{ - int rpt_handle; - ssize_t res; - - rpt_handle = open(rpt_path, O_RDONLY | O_CLOEXEC); - if (rpt_handle < 0) { - register_global_error_format("open failed (%s): %s", rpt_path, strerror(errno)); - return -1; - } - - /* - * Read in the Report Descriptor - * The sysfs file has a maximum size of 4096 (which is the same as HID_MAX_DESCRIPTOR_SIZE) so we should always - * be ok when reading the descriptor. - * In practice if the HID descriptor is any larger I suspect many other things will break. - */ - memset(rpt_desc, 0x0, sizeof(*rpt_desc)); - res = read(rpt_handle, rpt_desc->value, HID_MAX_DESCRIPTOR_SIZE); - if (res < 0) { - register_global_error_format("read failed (%s): %s", rpt_path, strerror(errno)); - } - rpt_desc->size = (__u32) res; - - close(rpt_handle); - return (int) res; -} - -/* return size of the descriptor, or -1 on failure */ -static int get_hid_report_descriptor_from_sysfs(const char *sysfs_path, struct hidraw_report_descriptor *rpt_desc) -{ - int res = -1; - /* Construct /device/report_descriptor */ - size_t rpt_path_len = strlen(sysfs_path) + 25 + 1; - char* rpt_path = (char*) calloc(1, rpt_path_len); - snprintf(rpt_path, rpt_path_len, "%s/device/report_descriptor", sysfs_path); - - res = get_hid_report_descriptor(rpt_path, rpt_desc); - free(rpt_path); - - return res; -} - -/* return non-zero if successfully parsed */ -static int parse_hid_vid_pid_from_uevent(const char *uevent, unsigned *bus_type, unsigned short *vendor_id, unsigned short *product_id) -{ - char tmp[1024]; - size_t uevent_len = strlen(uevent); - if (uevent_len > sizeof(tmp) - 1) - uevent_len = sizeof(tmp) - 1; - memcpy(tmp, uevent, sizeof(tmp)); - tmp[uevent_len] = '\0'; - - char *saveptr = NULL; - char *line; - char *key; - char *value; - - line = strtok_r(tmp, "\n", &saveptr); - while (line != NULL) { - /* line: "KEY=value" */ - key = line; - value = strchr(line, '='); - if (!value) { - goto next_line; - } - *value = '\0'; - value++; - - if (strcmp(key, "HID_ID") == 0) { - /** - * type vendor product - * HID_ID=0003:000005AC:00008242 - **/ - int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); - if (ret == 3) { - return 1; - } - } - -next_line: - line = strtok_r(NULL, "\n", &saveptr); - } - - register_global_error("Couldn't find/parse HID_ID"); - return 0; -} - -/* return non-zero if successfully parsed */ -static int parse_hid_vid_pid_from_uevent_path(const char *uevent_path, unsigned *bus_type, unsigned short *vendor_id, unsigned short *product_id) -{ - int handle; - ssize_t res; - - handle = open(uevent_path, O_RDONLY | O_CLOEXEC); - if (handle < 0) { - register_global_error_format("open failed (%s): %s", uevent_path, strerror(errno)); - return 0; - } - - char buf[1024]; - res = read(handle, buf, sizeof(buf)); - close(handle); - - if (res < 0) { - register_global_error_format("read failed (%s): %s", uevent_path, strerror(errno)); - return 0; - } - - buf[res] = '\0'; - return parse_hid_vid_pid_from_uevent(buf, bus_type, vendor_id, product_id); -} - -/* return non-zero if successfully read/parsed */ -static int parse_hid_vid_pid_from_sysfs(const char *sysfs_path, unsigned *bus_type, unsigned short *vendor_id, unsigned short *product_id) -{ - int res = 0; - /* Construct /device/uevent */ - size_t uevent_path_len = strlen(sysfs_path) + 14 + 1; - char* uevent_path = (char*) calloc(1, uevent_path_len); - snprintf(uevent_path, uevent_path_len, "%s/device/uevent", sysfs_path); - - res = parse_hid_vid_pid_from_uevent_path(uevent_path, bus_type, vendor_id, product_id); - free(uevent_path); - - 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. - */ -static int parse_uevent_info(const char *uevent, unsigned *bus_type, - unsigned short *vendor_id, unsigned short *product_id, - char **serial_number_utf8, char **product_name_utf8) -{ - char tmp[1024]; - size_t uevent_len = strlen(uevent); - if (uevent_len > sizeof(tmp) - 1) - uevent_len = sizeof(tmp) - 1; - memcpy(tmp, uevent, sizeof(tmp)); - tmp[uevent_len] = '\0'; - - char *saveptr = NULL; - char *line; - char *key; - char *value; - - int found_id = 0; - int found_serial = 0; - int found_name = 0; - - line = strtok_r(tmp, "\n", &saveptr); - while (line != NULL) { - /* line: "KEY=value" */ - key = line; - value = strchr(line, '='); - if (!value) { - goto next_line; - } - *value = '\0'; - value++; - - if (strcmp(key, "HID_ID") == 0) { - /** - * type vendor product - * HID_ID=0003:000005AC:00008242 - **/ - int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id); - if (ret == 3) { - found_id = 1; - } - } else if (strcmp(key, "HID_NAME") == 0) { - /* The caller has to free the product name */ - *product_name_utf8 = strdup(value); - found_name = 1; - } else if (strcmp(key, "HID_UNIQ") == 0) { - /* The caller has to free the serial number */ - *serial_number_utf8 = strdup(value); - found_serial = 1; - } - -next_line: - line = strtok_r(NULL, "\n", &saveptr); - } - - return (found_id && found_name && found_serial); -} - - -static struct hid_device_info * create_device_info_for_device(struct udev_device *raw_dev) -{ - struct hid_device_info *root = NULL; - struct hid_device_info *cur_dev = NULL; - - const char *sysfs_path; - const char *dev_path; - const char *str; - struct udev_device *hid_dev; /* The device's HID udev node. */ - struct udev_device *usb_dev; /* The device's USB udev node. */ - struct udev_device *intf_dev; /* The device's interface (in the USB sense). */ - unsigned short dev_vid; - unsigned short dev_pid; - char *serial_number_utf8 = NULL; - char *product_name_utf8 = NULL; - unsigned bus_type; - int result; - struct hidraw_report_descriptor report_desc; - - sysfs_path = udev_device_get_syspath(raw_dev); - dev_path = udev_device_get_devnode(raw_dev); - - hid_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "hid", - NULL); - - if (!hid_dev) { - /* Unable to find parent hid device. */ - goto end; - } - - result = parse_uevent_info( - udev_device_get_sysattr_value(hid_dev, "uevent"), - &bus_type, - &dev_vid, - &dev_pid, - &serial_number_utf8, - &product_name_utf8); - - if (!result) { - /* parse_uevent_info() failed for at least one field. */ - goto end; - } - - /* Filter out unhandled devices right away */ - switch (bus_type) { - case BUS_BLUETOOTH: - case BUS_I2C: - case BUS_USB: - break; - - default: - goto end; - } - - /* Create the record. */ - root = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); - if (!root) - goto end; - - cur_dev = root; - - /* Fill out the record */ - cur_dev->next = NULL; - cur_dev->path = dev_path? strdup(dev_path): NULL; - - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; - - /* Serial Number */ - cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8); - - /* Release Number */ - cur_dev->release_number = 0x0; - - /* Interface Number */ - cur_dev->interface_number = -1; - - switch (bus_type) { - case BUS_USB: - /* The device pointed to by raw_dev contains information about - the hidraw device. In order to get information about the - USB device, get the parent device with the - subsystem/devtype pair of "usb"/"usb_device". This will - be several levels up the tree, but the function will find - it. */ - usb_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "usb", - "usb_device"); - - /* uhid USB devices - * Since this is a virtual hid interface, no USB information will - * be available. */ - if (!usb_dev) { - /* Manufacturer and Product strings */ - cur_dev->manufacturer_string = wcsdup(L""); - cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); - break; - } - - cur_dev->manufacturer_string = copy_udev_string(usb_dev, "manufacturer"); - cur_dev->product_string = copy_udev_string(usb_dev, "product"); - - cur_dev->bus_type = HID_API_BUS_USB; - - str = udev_device_get_sysattr_value(usb_dev, "bcdDevice"); - cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; - - /* Get a handle to the interface's udev node. */ - intf_dev = udev_device_get_parent_with_subsystem_devtype( - raw_dev, - "usb", - "usb_interface"); - if (intf_dev) { - str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); - cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; - } - - break; - - case BUS_BLUETOOTH: - cur_dev->manufacturer_string = wcsdup(L""); - cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); - - cur_dev->bus_type = HID_API_BUS_BLUETOOTH; - - break; - case BUS_I2C: - cur_dev->manufacturer_string = wcsdup(L""); - cur_dev->product_string = utf8_to_wchar_t(product_name_utf8); - - cur_dev->bus_type = HID_API_BUS_I2C; - - break; - - default: - /* Unknown device type - this should never happen, as we - * check for USB and Bluetooth devices above */ - break; - } - - /* Usage Page and Usage */ - result = get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc); - if (result >= 0) { - unsigned short page = 0, usage = 0; - unsigned int pos = 0; - /* - * Parse the first usage and usage page - * out of the report descriptor. - */ - if (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) { - cur_dev->usage_page = page; - cur_dev->usage = usage; - } - - /* - * Parse any additional usage and usage pages - * out of the report descriptor. - */ - while (!get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage)) { - /* Create new record for additional usage pairs */ - struct hid_device_info *tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); - struct hid_device_info *prev_dev = cur_dev; - - if (!tmp) - continue; - cur_dev->next = tmp; - cur_dev = tmp; - - /* Update fields */ - cur_dev->path = dev_path? strdup(dev_path): NULL; - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; - cur_dev->serial_number = prev_dev->serial_number? wcsdup(prev_dev->serial_number): NULL; - cur_dev->release_number = prev_dev->release_number; - cur_dev->interface_number = prev_dev->interface_number; - cur_dev->manufacturer_string = prev_dev->manufacturer_string? wcsdup(prev_dev->manufacturer_string): NULL; - cur_dev->product_string = prev_dev->product_string? wcsdup(prev_dev->product_string): NULL; - cur_dev->usage_page = page; - cur_dev->usage = usage; - cur_dev->bus_type = prev_dev->bus_type; - } - } - -end: - free(serial_number_utf8); - free(product_name_utf8); - - return root; -} - -static struct hid_device_info * create_device_info_for_hid_device(hid_device *dev) { - struct udev *udev; - struct udev_device *udev_dev; - struct stat s; - int ret = -1; - struct hid_device_info *root = NULL; - - register_device_error(dev, NULL); - - /* Get the dev_t (major/minor numbers) from the file handle. */ - ret = fstat(dev->device_handle, &s); - if (-1 == ret) { - register_device_error(dev, "Failed to stat device handle"); - return NULL; - } - - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - register_device_error(dev, "Couldn't create udev context"); - return NULL; - } - - /* Open a udev device from the dev_t. 'c' means character device. */ - udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); - if (udev_dev) { - root = create_device_info_for_device(udev_dev); - } - - if (!root) { - /* TODO: have a better error reporting via create_device_info_for_device */ - register_device_error(dev, "Couldn't create hid_device_info"); - } - - udev_device_unref(udev_dev); - udev_unref(udev); - - return root; -} - -HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version() -{ - return &api_version; -} - -HID_API_EXPORT const char* HID_API_CALL hid_version_str() -{ - return HID_API_VERSION_STR; -} - -int HID_API_EXPORT hid_init(void) -{ - const char *locale; - - /* indicate no error */ - register_global_error(NULL); - - /* Set the locale if it's not set. */ - locale = setlocale(LC_CTYPE, NULL); - if (!locale) - setlocale(LC_CTYPE, ""); - - return 0; -} - -int HID_API_EXPORT hid_exit(void) -{ - /* Free global error message */ - register_global_error(NULL); - - return 0; -} - -struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) -{ - struct udev *udev; - struct udev_enumerate *enumerate; - struct udev_list_entry *devices, *dev_list_entry; - - struct hid_device_info *root = NULL; /* return object */ - struct hid_device_info *cur_dev = NULL; - - hid_init(); - /* register_global_error: global error is reset by hid_init */ - - /* Create the udev object */ - udev = udev_new(); - if (!udev) { - register_global_error("Couldn't create udev context"); - return NULL; - } - - /* Create a list of the devices in the 'hidraw' subsystem. */ - enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(enumerate, "hidraw"); - udev_enumerate_scan_devices(enumerate); - devices = udev_enumerate_get_list_entry(enumerate); - /* For each item, see if it matches the vid/pid, and if so - create a udev_device record for it */ - udev_list_entry_foreach(dev_list_entry, devices) { - const char *sysfs_path; - unsigned short dev_vid = 0; - unsigned short dev_pid = 0; - unsigned bus_type = 0; - struct udev_device *raw_dev; /* The device's hidraw udev node. */ - struct hid_device_info * tmp; - - /* Get the filename of the /sys entry for the device - and create a udev_device object (dev) representing it */ - sysfs_path = udev_list_entry_get_name(dev_list_entry); - if (!sysfs_path) - continue; - - if (vendor_id != 0 || product_id != 0) { - if (!parse_hid_vid_pid_from_sysfs(sysfs_path, &bus_type, &dev_vid, &dev_pid)) - continue; - - if (vendor_id != 0 && vendor_id != dev_vid) - continue; - if (product_id != 0 && product_id != dev_pid) - continue; - } - - raw_dev = udev_device_new_from_syspath(udev, sysfs_path); - if (!raw_dev) - continue; - - tmp = create_device_info_for_device(raw_dev); - if (tmp) { - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; - - /* move the pointer to the tail of returnd list */ - while (cur_dev->next != NULL) { - cur_dev = cur_dev->next; - } - } - - udev_device_unref(raw_dev); - } - /* Free the enumerator and udev objects. */ - udev_enumerate_unref(enumerate); - udev_unref(udev); - - if (root == NULL) { - if (vendor_id == 0 && product_id == 0) { - register_global_error("No HID devices found in the system."); - } else { - register_global_error("No HID devices with requested VID/PID found in the system."); - } - } - - return root; -} - -void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) -{ - struct hid_device_info *d = devs; - while (d) { - struct hid_device_info *next = d->next; - free(d->path); - free(d->serial_number); - free(d->manufacturer_string); - free(d->product_string); - free(d); - d = next; - } -} - -hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) -{ - struct hid_device_info *devs, *cur_dev; - const char *path_to_open = NULL; - hid_device *handle = NULL; - - /* register_global_error: global error is reset by hid_enumerate/hid_init */ - devs = hid_enumerate(vendor_id, product_id); - if (devs == NULL) { - /* register_global_error: global error is already set by hid_enumerate */ - return NULL; - } - - cur_dev = devs; - while (cur_dev) { - if (cur_dev->vendor_id == vendor_id && - cur_dev->product_id == product_id) { - if (serial_number) { - if (wcscmp(serial_number, cur_dev->serial_number) == 0) { - path_to_open = cur_dev->path; - break; - } - } - else { - path_to_open = cur_dev->path; - break; - } - } - cur_dev = cur_dev->next; - } - - if (path_to_open) { - /* Open the device */ - handle = hid_open_path(path_to_open); - } else { - register_global_error("Device with requested VID/PID/(SerialNumber) not found"); - } - - hid_free_enumeration(devs); - - return handle; -} - -hid_device * HID_API_EXPORT hid_open_path(const char *path) -{ - hid_device *dev = NULL; - - hid_init(); - /* register_global_error: global error is reset by hid_init */ - - dev = new_hid_device(); - if (!dev) { - register_global_error("Couldn't allocate memory"); - return NULL; - } - - dev->device_handle = open(path, O_RDWR | O_CLOEXEC); - - if (dev->device_handle >= 0) { - int res, desc_size = 0; - - /* Make sure this is a HIDRAW device - responds to HIDIOCGRDESCSIZE */ - res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); - if (res < 0) { - hid_close(dev); - register_device_error_format(dev, "ioctl(GRDESCSIZE) error for '%s', not a HIDRAW device?: %s", path, strerror(errno)); - return NULL; - } - - return dev; - } - else { - /* Unable to open a device. */ - free(dev); - register_global_error_format("Failed to open a device with path '%s': %s", path, strerror(errno)); - return NULL; - } -} - - -int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) -{ - int bytes_written; - - if (!data || (length == 0)) { - errno = EINVAL; - register_device_error(dev, strerror(errno)); - return -1; - } - - bytes_written = write(dev->device_handle, data, length); - - register_device_error(dev, (bytes_written == -1)? strerror(errno): NULL); - - return bytes_written; -} - - -int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) -{ - /* Set device error to none */ - register_device_error(dev, NULL); - - int bytes_read; - - if (milliseconds >= 0) { - /* Milliseconds is either 0 (non-blocking) or > 0 (contains - a valid timeout). In both cases we want to call poll() - and wait for data to arrive. Don't rely on non-blocking - operation (O_NONBLOCK) since some kernels don't seem to - properly report device disconnection through read() when - in non-blocking mode. */ - int ret; - struct pollfd fds; - - fds.fd = dev->device_handle; - fds.events = POLLIN; - fds.revents = 0; - ret = poll(&fds, 1, milliseconds); - if (ret == 0) { - /* Timeout */ - return ret; - } - if (ret == -1) { - /* Error */ - register_device_error(dev, strerror(errno)); - return ret; - } - else { - /* Check for errors on the file descriptor. This will - indicate a device disconnection. */ - if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) { - // We cannot use strerror() here as no -1 was returned from poll(). - register_device_error(dev, "hid_read_timeout: unexpected poll error (device disconnected)"); - return -1; - } - } - } - - bytes_read = read(dev->device_handle, data, length); - if (bytes_read < 0) { - if (errno == EAGAIN || errno == EINPROGRESS) - bytes_read = 0; - else - register_device_error(dev, strerror(errno)); - } - - return bytes_read; -} - -int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) -{ - return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); -} - -int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) -{ - /* Do all non-blocking in userspace using poll(), since it looks - like there's a bug in the kernel in some versions where - read() will not return -1 on disconnection of the USB device */ - - dev->blocking = !nonblock; - return 0; /* Success */ -} - - -int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) -{ - int res; - - register_device_error(dev, NULL); - - res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); - if (res < 0) - register_device_error_format(dev, "ioctl (SFEATURE): %s", strerror(errno)); - - return res; -} - -int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) -{ - int res; - - register_device_error(dev, NULL); - - res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); - if (res < 0) - register_device_error_format(dev, "ioctl (GFEATURE): %s", strerror(errno)); - - return res; -} - -int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) -{ - int res; - - register_device_error(dev, NULL); - - res = ioctl(dev->device_handle, HIDIOCGINPUT(length), data); - if (res < 0) - register_device_error_format(dev, "ioctl (GINPUT): %s", strerror(errno)); - - return res; -} - -void HID_API_EXPORT hid_close(hid_device *dev) -{ - if (!dev) - return; - - close(dev->device_handle); - - /* Free the device error message */ - register_device_error(dev, NULL); - - hid_free_enumeration(dev->device_info); - - free(dev); -} - - -int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_device_error(dev, "Zero buffer/length"); - return -1; - } - - struct hid_device_info *info = hid_get_device_info(dev); - if (!info) { - // hid_get_device_info will have set an error already - return -1; - } - - if (info->manufacturer_string) { - wcsncpy(string, info->manufacturer_string, maxlen); - string[maxlen - 1] = L'\0'; - } - else { - string[0] = L'\0'; - } - - return 0; -} - -int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_device_error(dev, "Zero buffer/length"); - return -1; - } - - struct hid_device_info *info = hid_get_device_info(dev); - if (!info) { - // hid_get_device_info will have set an error already - return -1; - } - - if (info->product_string) { - wcsncpy(string, info->product_string, maxlen); - string[maxlen - 1] = L'\0'; - } - else { - string[0] = L'\0'; - } - - return 0; -} - -int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_device_error(dev, "Zero buffer/length"); - return -1; - } - - struct hid_device_info *info = hid_get_device_info(dev); - if (!info) { - // hid_get_device_info will have set an error already - return -1; - } - - if (info->serial_number) { - wcsncpy(string, info->serial_number, maxlen); - string[maxlen - 1] = L'\0'; - } - else { - string[0] = L'\0'; - } - - return 0; -} - - -HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) { - if (!dev->device_info) { - // Lazy initialize device_info - dev->device_info = create_device_info_for_hid_device(dev); - } - - // create_device_info_for_hid_device will set an error if needed - return dev->device_info; -} - -int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) -{ - (void)string_index; - (void)string; - (void)maxlen; - - register_device_error(dev, "hid_get_indexed_string: not supported by hidraw"); - - return -1; -} - - -/* 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) -{ - if (dev) { - if (dev->last_error_str == NULL) - return L"Success"; - return dev->last_error_str; - } - - if (last_global_error_str == NULL) - return L"Success"; - return last_global_error_str; -} diff --git a/lib/hidapi/mac/.gitignore b/lib/hidapi/mac/.gitignore deleted file mode 100644 index 7cc3f0d0d68..00000000000 --- a/lib/hidapi/mac/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -Debug -Release -*.exp -*.ilk -*.lib -*.suo -*.vcproj.* -*.ncb -*.suo -*.dll -*.pdb -*.o -hidapi-hidtest -.deps -.libs -*.la -*.lo diff --git a/lib/hidapi/mac/CMakeLists.txt b/lib/hidapi/mac/CMakeLists.txt deleted file mode 100644 index 4101ddba0a6..00000000000 --- a/lib/hidapi/mac/CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) - -list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_darwin.h") - -add_library(hidapi_darwin - ${HIDAPI_PUBLIC_HEADERS} - hid.c -) - -find_package(Threads REQUIRED) - -target_link_libraries(hidapi_darwin - PUBLIC hidapi_include - PRIVATE Threads::Threads - PRIVATE "-framework IOKit" "-framework CoreFoundation" "-framework AppKit" -) - -set_target_properties(hidapi_darwin - PROPERTIES - EXPORT_NAME "darwin" - OUTPUT_NAME "hidapi" - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - MACHO_COMPATIBILITY_VERSION ${PROJECT_VERSION_MAJOR} - FRAMEWORK_VERSION ${PROJECT_VERSION_MAJOR} - PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}" -) - -# compatibility with find_package() -add_library(hidapi::darwin ALIAS hidapi_darwin) -# compatibility with raw library link -add_library(hidapi ALIAS hidapi_darwin) - -set(PUBLIC_HEADER_DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") -if(NOT CMAKE_FRAMEWORK) - set(PUBLIC_HEADER_DESTINATION "${PUBLIC_HEADER_DESTINATION}/hidapi") -endif() - -if(HIDAPI_INSTALL_TARGETS) - install(TARGETS hidapi_darwin EXPORT hidapi - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - FRAMEWORK DESTINATION "${CMAKE_INSTALL_LIBDIR}" - PUBLIC_HEADER DESTINATION "${PUBLIC_HEADER_DESTINATION}" - ) -endif() - -hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in") diff --git a/lib/hidapi/mac/hid.c b/lib/hidapi/mac/hid.c deleted file mode 100644 index 303ec66537b..00000000000 --- a/lib/hidapi/mac/hid.c +++ /dev/null @@ -1,1444 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - Alan Ott - Signal 11 Software - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -/* See Apple Technical Note TN2187 for details on IOHidManager. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "hidapi_darwin.h" - -/* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */ -extern const double NSAppKitVersionNumber; - -/* Barrier implementation because Mac OSX doesn't have pthread_barrier. - It also doesn't have clock_gettime(). So much for POSIX and SUSv2. - This implementation came from Brent Priddy and was posted on - StackOverflow. It is used with his permission. */ -typedef int pthread_barrierattr_t; -typedef struct pthread_barrier { - pthread_mutex_t mutex; - pthread_cond_t cond; - int count; - int trip_count; -} pthread_barrier_t; - -static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) -{ - (void) attr; - - if(count == 0) { - errno = EINVAL; - return -1; - } - - if(pthread_mutex_init(&barrier->mutex, 0) < 0) { - return -1; - } - if(pthread_cond_init(&barrier->cond, 0) < 0) { - pthread_mutex_destroy(&barrier->mutex); - return -1; - } - barrier->trip_count = count; - barrier->count = 0; - - return 0; -} - -static int pthread_barrier_destroy(pthread_barrier_t *barrier) -{ - pthread_cond_destroy(&barrier->cond); - pthread_mutex_destroy(&barrier->mutex); - return 0; -} - -static int pthread_barrier_wait(pthread_barrier_t *barrier) -{ - pthread_mutex_lock(&barrier->mutex); - ++(barrier->count); - if(barrier->count >= barrier->trip_count) - { - barrier->count = 0; - pthread_cond_broadcast(&barrier->cond); - pthread_mutex_unlock(&barrier->mutex); - return 1; - } - else - { - pthread_cond_wait(&barrier->cond, &(barrier->mutex)); - pthread_mutex_unlock(&barrier->mutex); - return 0; - } -} - -static int return_data(hid_device *dev, unsigned char *data, size_t length); - -/* Linked List of input reports received from the device. */ -struct input_report { - uint8_t *data; - size_t len; - struct input_report *next; -}; - -static struct hid_api_version api_version = { - .major = HID_API_VERSION_MAJOR, - .minor = HID_API_VERSION_MINOR, - .patch = HID_API_VERSION_PATCH -}; - -/* - Run context - */ -static IOHIDManagerRef hid_mgr = 0x0; -static int is_macos_10_10_or_greater = 0; -static IOOptionBits device_open_options = 0; -static wchar_t *last_global_error_str = NULL; -/* --- */ - -struct hid_device_ { - IOHIDDeviceRef device_handle; - IOOptionBits open_options; - int blocking; - int disconnected; - CFStringRef run_loop_mode; - CFRunLoopRef run_loop; - CFRunLoopSourceRef source; - uint8_t *input_report_buf; - CFIndex max_input_report_len; - struct input_report *input_reports; - struct hid_device_info* device_info; - - pthread_t thread; - pthread_mutex_t mutex; /* Protects input_reports */ - pthread_cond_t condition; - pthread_barrier_t barrier; /* Ensures correct startup sequence */ - pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ - int shutdown_thread; - wchar_t *last_error_str; -}; - -static hid_device *new_hid_device(void) -{ - hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); - if (dev == NULL) { - return NULL; - } - - dev->device_handle = NULL; - dev->open_options = device_open_options; - dev->blocking = 1; - dev->disconnected = 0; - dev->run_loop_mode = NULL; - dev->run_loop = NULL; - dev->source = NULL; - dev->input_report_buf = NULL; - dev->input_reports = NULL; - dev->device_info = NULL; - dev->shutdown_thread = 0; - dev->last_error_str = NULL; - - /* Thread objects */ - pthread_mutex_init(&dev->mutex, NULL); - pthread_cond_init(&dev->condition, NULL); - pthread_barrier_init(&dev->barrier, NULL, 2); - pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); - - return dev; -} - -static void free_hid_device(hid_device *dev) -{ - if (!dev) - return; - - /* Delete any input reports still left over. */ - struct input_report *rpt = dev->input_reports; - while (rpt) { - struct input_report *next = rpt->next; - free(rpt->data); - free(rpt); - rpt = next; - } - - /* Free the string and the report buffer. The check for NULL - is necessary here as CFRelease() doesn't handle NULL like - free() and others do. */ - if (dev->run_loop_mode) - CFRelease(dev->run_loop_mode); - if (dev->source) - CFRelease(dev->source); - free(dev->input_report_buf); - hid_free_enumeration(dev->device_info); - - /* Clean up the thread objects */ - pthread_barrier_destroy(&dev->shutdown_barrier); - pthread_barrier_destroy(&dev->barrier); - pthread_cond_destroy(&dev->condition); - pthread_mutex_destroy(&dev->mutex); - - /* Free the structure itself. */ - free(dev); -} - - -/* The caller must free the returned string with free(). */ -static wchar_t *utf8_to_wchar_t(const char *utf8) -{ - wchar_t *ret = NULL; - - if (utf8) { - size_t wlen = mbstowcs(NULL, utf8, 0); - if ((size_t) -1 == wlen) { - return wcsdup(L""); - } - ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t)); - if (ret == NULL) { - /* as much as we can do at this point */ - return NULL; - } - mbstowcs(ret, utf8, wlen+1); - ret[wlen] = 0x0000; - } - - return ret; -} - - -/* Makes a copy of the given error message (and decoded according to the - * currently locale) into the wide string pointer pointed by error_str. - * The last stored error string is freed. - * Use register_error_str(NULL) to free the error message completely. */ -static void register_error_str(wchar_t **error_str, const char *msg) -{ - free(*error_str); - *error_str = utf8_to_wchar_t(msg); -} - -/* Similar to register_error_str, but allows passing a format string with va_list args into this function. */ -static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args) -{ - char msg[1024]; - vsnprintf(msg, sizeof(msg), format, args); - - register_error_str(error_str, msg); -} - -/* Set the last global error to be reported by hid_error(NULL). - * The given error message will be copied (and decoded according to the - * currently locale, so do not pass in string constants). - * The last stored global error message is freed. - * Use register_global_error(NULL) to indicate "no error". */ -static void register_global_error(const char *msg) -{ - register_error_str(&last_global_error_str, msg); -} - -/* Similar to register_global_error, but allows passing a format string into this function. */ -static void register_global_error_format(const char *format, ...) -{ - va_list args; - va_start(args, format); - register_error_str_vformat(&last_global_error_str, format, args); - va_end(args); -} - -/* Set the last error for a device to be reported by hid_error(dev). - * The given error message will be copied (and decoded according to the - * currently locale, so do not pass in string constants). - * The last stored device error message is freed. - * Use register_device_error(dev, NULL) to indicate "no error". */ -static void register_device_error(hid_device *dev, const char *msg) -{ - register_error_str(&dev->last_error_str, msg); -} - -/* Similar to register_device_error, but you can pass a format string into this function. */ -static void register_device_error_format(hid_device *dev, const char *format, ...) -{ - va_list args; - va_start(args, format); - register_error_str_vformat(&dev->last_error_str, format, args); - va_end(args); -} - - -static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key) -{ - CFTypeRef ref = IOHIDDeviceGetProperty(device, key); - if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) { - return (CFArrayRef)ref; - } else { - return NULL; - } -} - -static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) -{ - CFTypeRef ref; - int32_t value; - - ref = IOHIDDeviceGetProperty(device, key); - if (ref) { - if (CFGetTypeID(ref) == CFNumberGetTypeID()) { - CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); - return value; - } - } - return 0; -} - -static CFArrayRef get_usage_pairs(IOHIDDeviceRef device) -{ - return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey)); -} - -static unsigned short get_vendor_id(IOHIDDeviceRef device) -{ - return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); -} - -static unsigned short get_product_id(IOHIDDeviceRef device) -{ - return get_int_property(device, CFSTR(kIOHIDProductIDKey)); -} - -static int32_t get_max_report_length(IOHIDDeviceRef device) -{ - return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); -} - -static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) -{ - CFStringRef str; - - if (!len) - return 0; - - str = (CFStringRef) IOHIDDeviceGetProperty(device, prop); - - buf[0] = 0; - - if (str) { - CFIndex str_len = CFStringGetLength(str); - CFRange range; - CFIndex used_buf_len; - CFIndex chars_copied; - - len --; - - range.location = 0; - range.length = ((size_t) str_len > len)? len: (size_t) str_len; - chars_copied = CFStringGetBytes(str, - range, - kCFStringEncodingUTF32LE, - (char) '?', - FALSE, - (UInt8*)buf, - len * sizeof(wchar_t), - &used_buf_len); - - if (chars_copied <= 0) - buf[0] = 0; - else - buf[chars_copied] = 0; - - return 0; - } - else - return -1; - -} - -static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) -{ - return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); -} - -static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) -{ - return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); -} - -static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) -{ - return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); -} - - -/* Implementation of wcsdup() for Mac. */ -static wchar_t *dup_wcs(const wchar_t *s) -{ - size_t len = wcslen(s); - wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t)); - wcscpy(ret, s); - - return ret; -} - -/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ -static int init_hid_manager(void) -{ - /* Initialize all the HID Manager Objects */ - hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - if (hid_mgr) { - IOHIDManagerSetDeviceMatching(hid_mgr, NULL); - IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - return 0; - } - - register_global_error("Failed to create IOHIDManager"); - return -1; -} - -HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version() -{ - return &api_version; -} - -HID_API_EXPORT const char* HID_API_CALL hid_version_str() -{ - return HID_API_VERSION_STR; -} - -/* Initialize the IOHIDManager if necessary. This is the public function, and - it is safe to call this function repeatedly. Return 0 for success and -1 - for failure. */ -int HID_API_EXPORT hid_init(void) -{ - register_global_error(NULL); - - if (!hid_mgr) { - is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */ - hid_darwin_set_open_exclusive(1); /* Backward compatibility */ - return init_hid_manager(); - } - - /* Already initialized. */ - return 0; -} - -int HID_API_EXPORT hid_exit(void) -{ - if (hid_mgr) { - /* Close the HID manager. */ - IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone); - CFRelease(hid_mgr); - hid_mgr = NULL; - } - - /* Free global error message */ - register_global_error(NULL); - - return 0; -} - -static void process_pending_events(void) { - SInt32 res; - do { - res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); - } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); -} - -static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage) -{ - unsigned short dev_vid; - unsigned short dev_pid; - int BUF_LEN = 256; - wchar_t buf[BUF_LEN]; - CFTypeRef transport_prop; - - struct hid_device_info *cur_dev; - io_object_t iokit_dev; - kern_return_t res; - uint64_t entry_id = 0; - - if (dev == NULL) { - return NULL; - } - - cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info)); - if (cur_dev == NULL) { - return NULL; - } - - dev_vid = get_vendor_id(dev); - dev_pid = get_product_id(dev); - - cur_dev->usage_page = usage_page; - cur_dev->usage = usage; - - /* Fill out the record */ - cur_dev->next = NULL; - - /* Fill in the path (as a unique ID of the service entry) */ - cur_dev->path = NULL; - iokit_dev = IOHIDDeviceGetService(dev); - 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); - cur_dev->serial_number = dup_wcs(buf); - - /* Manufacturer and Product strings */ - get_manufacturer_string(dev, buf, BUF_LEN); - cur_dev->manufacturer_string = dup_wcs(buf); - get_product_string(dev, buf, BUF_LEN); - cur_dev->product_string = dup_wcs(buf); - - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; - - /* Release Number */ - cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); - - /* Interface Number */ - /* We can only retrieve the interface number for USB HID devices. - * IOKit always seems to return 0 when querying a standard USB device - * for its interface. */ - int is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass; - if (is_usb_hid) { - /* Get the interface number */ - cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber)); - } else { - cur_dev->interface_number = -1; - } - - /* Bus Type */ - transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey)); - - if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) { - if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) { - cur_dev->bus_type = HID_API_BUS_USB; - /* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */ - } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) { - cur_dev->bus_type = HID_API_BUS_BLUETOOTH; - } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) { - cur_dev->bus_type = HID_API_BUS_I2C; - } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) { - cur_dev->bus_type = HID_API_BUS_SPI; - } - } - - return cur_dev; -} - -static struct hid_device_info *create_device_info(IOHIDDeviceRef device) -{ - const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); - const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); - - /* Primary should always be first, to match previous behavior. */ - struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage); - struct hid_device_info *cur = root; - - if (!root) - return NULL; - - CFArrayRef usage_pairs = get_usage_pairs(device); - - if (usage_pairs != NULL) { - struct hid_device_info *next = NULL; - for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) { - CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i); - if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) { - continue; - } - - CFTypeRef usage_page_ref, usage_ref; - int32_t usage_page, usage; - - if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) || - !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) || - CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() || - CFGetTypeID(usage_ref) != CFNumberGetTypeID() || - !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) || - !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) { - continue; - } - if (usage_page == primary_usage_page && usage == primary_usage) - continue; /* Already added. */ - - next = create_device_info_with_usage(device, usage_page, usage); - cur->next = next; - if (next != NULL) { - cur = next; - } - } - } - - return root; -} - -struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) -{ - struct hid_device_info *root = NULL; /* return object */ - struct hid_device_info *cur_dev = NULL; - CFIndex num_devices; - int i; - - /* Set up the HID Manager if it hasn't been done */ - if (hid_init() < 0) { - return NULL; - } - /* register_global_error: global error is set/reset by hid_init */ - - /* give the IOHIDManager a chance to update itself */ - process_pending_events(); - - /* Get a list of the Devices */ - CFMutableDictionaryRef matching = NULL; - if (vendor_id != 0 || product_id != 0) { - matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - - if (matching && vendor_id != 0) { - CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id); - CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v); - CFRelease(v); - } - - if (matching && product_id != 0) { - CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id); - CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p); - CFRelease(p); - } - } - IOHIDManagerSetDeviceMatching(hid_mgr, matching); - if (matching != NULL) { - CFRelease(matching); - } - - CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); - - IOHIDDeviceRef *device_array = NULL; - - if (device_set != NULL) { - /* Convert the list into a C array so we can iterate easily. */ - num_devices = CFSetGetCount(device_set); - device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef)); - CFSetGetValues(device_set, (const void **) device_array); - } else { - num_devices = 0; - } - - /* Iterate over each device, making an entry for it. */ - for (i = 0; i < num_devices; i++) { - - IOHIDDeviceRef dev = device_array[i]; - if (!dev) { - continue; - } - - struct hid_device_info *tmp = create_device_info(dev); - if (tmp == NULL) { - continue; - } - - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; - - /* move the pointer to the tail of returnd list */ - while (cur_dev->next != NULL) { - cur_dev = cur_dev->next; - } - } - - free(device_array); - if (device_set != NULL) - CFRelease(device_set); - - if (root == NULL) { - if (vendor_id == 0 && product_id == 0) { - register_global_error("No HID devices found in the system."); - } else { - register_global_error("No HID devices with requested VID/PID found in the system."); - } - } - - return root; -} - -void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) -{ - /* This function is identical to the Linux version. Platform independent. */ - struct hid_device_info *d = devs; - while (d) { - struct hid_device_info *next = d->next; - free(d->path); - free(d->serial_number); - free(d->manufacturer_string); - free(d->product_string); - free(d); - d = next; - } -} - -hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) -{ - /* This function is identical to the Linux version. Platform independent. */ - - struct hid_device_info *devs, *cur_dev; - const char *path_to_open = NULL; - hid_device * handle = NULL; - - /* register_global_error: global error is reset by hid_enumerate/hid_init */ - devs = hid_enumerate(vendor_id, product_id); - if (devs == NULL) { - /* register_global_error: global error is already set by hid_enumerate */ - return NULL; - } - - cur_dev = devs; - while (cur_dev) { - if (cur_dev->vendor_id == vendor_id && - cur_dev->product_id == product_id) { - if (serial_number) { - if (wcscmp(serial_number, cur_dev->serial_number) == 0) { - path_to_open = cur_dev->path; - break; - } - } - else { - path_to_open = cur_dev->path; - break; - } - } - cur_dev = cur_dev->next; - } - - if (path_to_open) { - handle = hid_open_path(path_to_open); - } else { - register_global_error("Device with requested VID/PID/(SerialNumber) not found"); - } - - hid_free_enumeration(devs); - - return handle; -} - -static void hid_device_removal_callback(void *context, IOReturn result, - void *sender) -{ - (void) result; - (void) sender; - - /* Stop the Run Loop for this device. */ - hid_device *d = (hid_device*) context; - - d->disconnected = 1; - CFRunLoopStop(d->run_loop); -} - -/* The Run Loop calls this function for each input report received. - This function puts the data into a linked list to be picked up by - hid_read(). */ -static void hid_report_callback(void *context, IOReturn result, void *sender, - IOHIDReportType report_type, uint32_t report_id, - uint8_t *report, CFIndex report_length) -{ - (void) result; - (void) sender; - (void) report_type; - (void) report_id; - - struct input_report *rpt; - hid_device *dev = (hid_device*) context; - - /* Make a new Input Report object */ - rpt = (struct input_report*) calloc(1, sizeof(struct input_report)); - rpt->data = (uint8_t*) calloc(1, report_length); - memcpy(rpt->data, report, report_length); - rpt->len = report_length; - rpt->next = NULL; - - /* Lock this section */ - pthread_mutex_lock(&dev->mutex); - - /* Attach the new report object to the end of the list. */ - if (dev->input_reports == NULL) { - /* The list is empty. Put it at the root. */ - dev->input_reports = rpt; - } - else { - /* Find the end of the list and attach. */ - struct input_report *cur = dev->input_reports; - int num_queued = 0; - while (cur->next != NULL) { - cur = cur->next; - num_queued++; - } - cur->next = rpt; - - /* Pop one off if we've reached 30 in the queue. This - way we don't grow forever if the user never reads - anything from the device. */ - if (num_queued > 30) { - return_data(dev, NULL, 0); - } - } - - /* Signal a waiting thread that there is data. */ - pthread_cond_signal(&dev->condition); - - /* Unlock */ - pthread_mutex_unlock(&dev->mutex); - -} - -/* This gets called when the read_thread's run loop gets signaled by - hid_close(), and serves to stop the read_thread's run loop. */ -static void perform_signal_callback(void *context) -{ - hid_device *dev = (hid_device*) context; - CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ -} - -static void *read_thread(void *param) -{ - hid_device *dev = (hid_device*) param; - SInt32 code; - - /* Move the device's run loop to this thread. */ - IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); - - /* Create the RunLoopSource which is used to signal the - event loop to stop when hid_close() is called. */ - CFRunLoopSourceContext ctx; - memset(&ctx, 0, sizeof(ctx)); - ctx.version = 0; - ctx.info = dev; - ctx.perform = &perform_signal_callback; - dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); - CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); - - /* Store off the Run Loop so it can be stopped from hid_close() - and on device disconnection. */ - dev->run_loop = CFRunLoopGetCurrent(); - - /* Notify the main thread that the read thread is up and running. */ - pthread_barrier_wait(&dev->barrier); - - /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input - reports into the hid_report_callback(). */ - while (!dev->shutdown_thread && !dev->disconnected) { - code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); - /* Return if the device has been disconnected */ - if (code == kCFRunLoopRunFinished) { - dev->disconnected = 1; - break; - } - - - /* Break if The Run Loop returns Finished or Stopped. */ - if (code != kCFRunLoopRunTimedOut && - code != kCFRunLoopRunHandledSource) { - /* There was some kind of error. Setting - shutdown seems to make sense, but - there may be something else more appropriate */ - dev->shutdown_thread = 1; - break; - } - } - - /* Now that the read thread is stopping, Wake any threads which are - waiting on data (in hid_read_timeout()). Do this under a mutex to - make sure that a thread which is about to go to sleep waiting on - the condition actually will go to sleep before the condition is - signaled. */ - pthread_mutex_lock(&dev->mutex); - pthread_cond_broadcast(&dev->condition); - pthread_mutex_unlock(&dev->mutex); - - /* Wait here until hid_close() is called and makes it past - the call to CFRunLoopWakeUp(). This thread still needs to - be valid when that function is called on the other thread. */ - pthread_barrier_wait(&dev->shutdown_barrier); - - return NULL; -} - -/* \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((mach_port_t) 0, IORegistryEntryIDMatching(entry_id)); - } - } - else { - /* Fallback to older format of the path */ - return IORegistryEntryFromPath((mach_port_t) 0, path); - } - - return MACH_PORT_NULL; -} - -hid_device * HID_API_EXPORT hid_open_path(const char *path) -{ - hid_device *dev = NULL; - io_registry_entry_t entry = MACH_PORT_NULL; - IOReturn ret = kIOReturnInvalid; - char str[32]; - - /* Set up the HID Manager if it hasn't been done */ - if (hid_init() < 0) { - goto return_error; - } - /* register_global_error: global error is set/reset by hid_init */ - - dev = new_hid_device(); - if (!dev) { - register_global_error("Couldn't allocate memory"); - return NULL; - } - - /* Get the IORegistry entry for the given path */ - entry = hid_open_service_registry_from_path(path); - if (entry == MACH_PORT_NULL) { - /* Path wasn't valid (maybe device was removed?) */ - register_global_error("hid_open_path: device mach entry not found with the given path"); - goto return_error; - } - - /* Create an IOHIDDevice for the entry */ - dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); - if (dev->device_handle == NULL) { - /* Error creating the HID device */ - register_global_error("hid_open_path: failed to create IOHIDDevice from the mach entry"); - goto return_error; - } - - /* Open the IOHIDDevice */ - ret = IOHIDDeviceOpen(dev->device_handle, dev->open_options); - if (ret != kIOReturnSuccess) { - register_global_error_format("hid_open_path: failed to open IOHIDDevice from mach entry: (0x%08X) %s", ret, mach_error_string(ret)); - goto return_error; - } - - /* Create the buffers for receiving data */ - dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); - dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t)); - - /* Create the Run Loop Mode for this device. - printing the reference seems to work. */ - sprintf(str, "HIDAPI_%p", (void*) dev->device_handle); - dev->run_loop_mode = - CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); - - /* Attach the device to a Run Loop */ - IOHIDDeviceRegisterInputReportCallback( - dev->device_handle, dev->input_report_buf, dev->max_input_report_len, - &hid_report_callback, dev); - IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); - - /* Start the read thread */ - pthread_create(&dev->thread, NULL, read_thread, dev); - - /* Wait here for the read thread to be initialized. */ - pthread_barrier_wait(&dev->barrier); - - IOObjectRelease(entry); - return dev; - -return_error: - if (dev->device_handle != NULL) - CFRelease(dev->device_handle); - - if (entry != MACH_PORT_NULL) - IOObjectRelease(entry); - - free_hid_device(dev); - return NULL; -} - -static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) -{ - const unsigned char *data_to_send = data; - CFIndex length_to_send = length; - IOReturn res; - unsigned char report_id; - - register_device_error(dev, NULL); - - if (!data || (length == 0)) { - register_device_error(dev, strerror(EINVAL)); - return -1; - } - - report_id = data[0]; - - if (report_id == 0x0) { - /* Not using numbered Reports. - Don't send the report number. */ - data_to_send = data+1; - length_to_send = length-1; - } - - /* Avoid crash if the device has been unplugged. */ - if (dev->disconnected) { - register_device_error(dev, "Device is disconnected"); - return -1; - } - - res = IOHIDDeviceSetReport(dev->device_handle, - type, - report_id, - data_to_send, length_to_send); - - if (res != kIOReturnSuccess) { - register_device_error_format(dev, "IOHIDDeviceSetReport failed: (0x%08X) %s", res, mach_error_string(res)); - return -1; - } - - return (int) length; -} - -static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length) -{ - unsigned char *report = data; - CFIndex report_length = length; - IOReturn res = kIOReturnSuccess; - const unsigned char report_id = data[0]; - - register_device_error(dev, NULL); - - if (report_id == 0x0) { - /* Not using numbered Reports. - Don't send the report number. */ - report = data+1; - report_length = length-1; - } - - /* Avoid crash if the device has been unplugged. */ - if (dev->disconnected) { - register_device_error(dev, "Device is disconnected"); - return -1; - } - - res = IOHIDDeviceGetReport(dev->device_handle, - type, - report_id, - report, &report_length); - - if (res != kIOReturnSuccess) { - register_device_error_format(dev, "IOHIDDeviceGetReport failed: (0x%08X) %s", res, mach_error_string(res)); - return -1; - } - - if (report_id == 0x0) { /* 0 report number still present at the beginning */ - report_length++; - } - - return (int) report_length; -} - -int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) -{ - return set_report(dev, kIOHIDReportTypeOutput, data, length); -} - -/* Helper function, so that this isn't duplicated in hid_read(). */ -static int return_data(hid_device *dev, unsigned char *data, size_t length) -{ - /* Copy the data out of the linked list item (rpt) into the - return buffer (data), and delete the liked list item. */ - struct input_report *rpt = dev->input_reports; - size_t len = (length < rpt->len)? length: rpt->len; - memcpy(data, rpt->data, len); - dev->input_reports = rpt->next; - free(rpt->data); - free(rpt); - return (int) len; -} - -static int cond_wait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - while (!dev->input_reports) { - int res = pthread_cond_wait(cond, mutex); - if (res != 0) - return res; - - /* A res of 0 means we may have been signaled or it may - be a spurious wakeup. Check to see that there's actually - data in the queue before returning, and if not, go back - to sleep. See the pthread_cond_timedwait() man page for - details. */ - - if (dev->shutdown_thread || dev->disconnected) { - return -1; - } - } - - return 0; -} - -static int cond_timedwait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) -{ - while (!dev->input_reports) { - int res = pthread_cond_timedwait(cond, mutex, abstime); - if (res != 0) - return res; - - /* A res of 0 means we may have been signaled or it may - be a spurious wakeup. Check to see that there's actually - data in the queue before returning, and if not, go back - to sleep. See the pthread_cond_timedwait() man page for - details. */ - - if (dev->shutdown_thread || dev->disconnected) { - return -1; - } - } - - return 0; - -} - -int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) -{ - int bytes_read = -1; - - /* Lock the access to the report list. */ - pthread_mutex_lock(&dev->mutex); - - /* There's an input report queued up. Return it. */ - if (dev->input_reports) { - /* Return the first one */ - bytes_read = return_data(dev, data, length); - goto ret; - } - - /* Return if the device has been disconnected. */ - if (dev->disconnected) { - bytes_read = -1; - register_device_error(dev, "hid_read_timeout: device disconnected"); - goto ret; - } - - if (dev->shutdown_thread) { - /* This means the device has been closed (or there - has been an error. An error code of -1 should - be returned. */ - bytes_read = -1; - register_device_error(dev, "hid_read_timeout: thread shutdown"); - goto ret; - } - - /* There is no data. Go to sleep and wait for data. */ - - if (milliseconds == -1) { - /* Blocking */ - int res; - res = cond_wait(dev, &dev->condition, &dev->mutex); - if (res == 0) - bytes_read = return_data(dev, data, length); - else { - /* There was an error, or a device disconnection. */ - register_device_error(dev, "hid_read_timeout: error waiting for more data"); - bytes_read = -1; - } - } - else if (milliseconds > 0) { - /* Non-blocking, but called with timeout. */ - int res; - struct timespec ts; - struct timeval tv; - gettimeofday(&tv, NULL); - TIMEVAL_TO_TIMESPEC(&tv, &ts); - ts.tv_sec += milliseconds / 1000; - ts.tv_nsec += (milliseconds % 1000) * 1000000; - if (ts.tv_nsec >= 1000000000L) { - ts.tv_sec++; - ts.tv_nsec -= 1000000000L; - } - - res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); - if (res == 0) { - bytes_read = return_data(dev, data, length); - } else if (res == ETIMEDOUT) { - bytes_read = 0; - } else { - register_device_error(dev, "hid_read_timeout: error waiting for more data"); - bytes_read = -1; - } - } - else { - /* Purely non-blocking */ - bytes_read = 0; - } - -ret: - /* Unlock */ - pthread_mutex_unlock(&dev->mutex); - return bytes_read; -} - -int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) -{ - return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); -} - -int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) -{ - /* All Nonblocking operation is handled by the library. */ - dev->blocking = !nonblock; - - return 0; -} - -int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) -{ - return set_report(dev, kIOHIDReportTypeFeature, data, length); -} - -int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) -{ - return get_report(dev, kIOHIDReportTypeFeature, data, length); -} - -int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) -{ - return get_report(dev, kIOHIDReportTypeInput, data, length); -} - -void HID_API_EXPORT hid_close(hid_device *dev) -{ - if (!dev) - return; - - /* Disconnect the report callback before close. - See comment below. - */ - if (is_macos_10_10_or_greater || !dev->disconnected) { - IOHIDDeviceRegisterInputReportCallback( - dev->device_handle, dev->input_report_buf, dev->max_input_report_len, - NULL, dev); - IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); - IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); - IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); - } - - /* Cause read_thread() to stop. */ - dev->shutdown_thread = 1; - - /* Wake up the run thread's event loop so that the thread can exit. */ - CFRunLoopSourceSignal(dev->source); - CFRunLoopWakeUp(dev->run_loop); - - /* Notify the read thread that it can shut down now. */ - pthread_barrier_wait(&dev->shutdown_barrier); - - /* Wait for read_thread() to end. */ - pthread_join(dev->thread, NULL); - - /* Close the OS handle to the device, but only if it's not - been unplugged. If it's been unplugged, then calling - IOHIDDeviceClose() will crash. - - UPD: The crash part was true in/until some version of macOS. - Starting with macOS 10.15, there is an opposite effect in some environments: - crash happenes if IOHIDDeviceClose() is not called. - Not leaking a resource in all tested environments. - */ - if (is_macos_10_10_or_greater || !dev->disconnected) { - IOHIDDeviceClose(dev->device_handle, dev->open_options); - } - - /* Clear out the queue of received reports. */ - pthread_mutex_lock(&dev->mutex); - while (dev->input_reports) { - return_data(dev, NULL, 0); - } - pthread_mutex_unlock(&dev->mutex); - CFRelease(dev->device_handle); - - free_hid_device(dev); -} - -int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) - { - register_device_error(dev, "Zero buffer/length"); - return -1; - } - - struct hid_device_info *info = hid_get_device_info(dev); - if (!info) - { - // hid_get_device_info will have set an error already - return -1; - } - - wcsncpy(string, info->manufacturer_string, maxlen); - string[maxlen - 1] = L'\0'; - - return 0; -} - -int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_device_error(dev, "Zero buffer/length"); - return -1; - } - - struct hid_device_info *info = hid_get_device_info(dev); - if (!info) { - // hid_get_device_info will have set an error already - return -1; - } - - wcsncpy(string, info->product_string, maxlen); - string[maxlen - 1] = L'\0'; - - return 0; -} - -int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_device_error(dev, "Zero buffer/length"); - return -1; - } - - struct hid_device_info *info = hid_get_device_info(dev); - if (!info) { - // hid_get_device_info will have set an error already - return -1; - } - - wcsncpy(string, info->serial_number, maxlen); - string[maxlen - 1] = L'\0'; - - return 0; -} - -HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) { - if (!dev->device_info) { - dev->device_info = create_device_info(dev->device_handle); - if (!dev->device_info) { - register_device_error(dev, "Failed to create hid_device_info"); - } - } - - return dev->device_info; -} - -int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) -{ - (void) dev; - (void) string_index; - (void) string; - (void) maxlen; - - register_device_error(dev, "hid_get_indexed_string: not available on this platform"); - return -1; -} - -int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id) -{ - int res = get_int_property(dev->device_handle, CFSTR(kIOHIDLocationIDKey)); - if (res != 0) { - *location_id = (uint32_t) res; - return 0; - } else { - register_device_error(dev, "Failed to get IOHIDLocationID property"); - return -1; - } -} - -void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive) -{ - device_open_options = (open_exclusive == 0) ? kIOHIDOptionsTypeNone : kIOHIDOptionsTypeSeizeDevice; -} - -int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void) -{ - return (device_open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0; -} - -int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev) -{ - if (!dev) - return -1; - - return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0; -} - -HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) -{ - if (dev) { - if (dev->last_error_str == NULL) - return L"Success"; - return dev->last_error_str; - } - - if (last_global_error_str == NULL) - return L"Success"; - return last_global_error_str; -} diff --git a/lib/hidapi/mac/hidapi_darwin.h b/lib/hidapi/mac/hidapi_darwin.h deleted file mode 100644 index 34c30a07de5..00000000000 --- a/lib/hidapi/mac/hidapi_darwin.h +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - libusb/hidapi Team - - Copyright 2022, 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 - - * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - */ - -#ifndef HIDAPI_DARWIN_H__ -#define HIDAPI_DARWIN_H__ - -#include - -#include "hidapi.h" - -#ifdef __cplusplus -extern "C" { -#endif - - /** @brief Get the location ID for a HID device. - - Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - - @ingroup API - @param dev A device handle returned from hid_open(). - @param location_id The device's location ID on return. - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id); - - - /** @brief Changes the behavior of all further calls to @ref hid_open or @ref hid_open_path. - - By default on Darwin platform all devices opened by HIDAPI with @ref hid_open or @ref hid_open_path - are opened in exclusive mode (see kIOHIDOptionsTypeSeizeDevice). - - Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - - @ingroup API - @param open_exclusive When set to 0 - all further devices will be opened - in non-exclusive mode. Otherwise - all further devices will be opened - in exclusive mode. - - @note During the initialisation by @ref hid_init - this property is set to 1 (TRUE). - This is done to preserve full backward compatibility with previous behavior. - - @note Calling this function before @ref hid_init or after @ref hid_exit has no effect. - */ - void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive); - - /** @brief Getter for option set by @ref hid_darwin_set_open_exclusive. - - Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - - @ingroup API - @return 1 if all further devices will be opened in exclusive mode. - - @note Value returned by this function before calling to @ref hid_init or after @ref hid_exit - is not reliable. - */ - int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void); - - /** @brief Check how the device was opened. - - Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - - @ingroup API - @param dev A device to get property from. - - @return 1 if the device is opened in exclusive mode, 0 - opened in non-exclusive, - -1 - if dev is invalid. - */ - int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/hidapi/pc/.gitignore b/lib/hidapi/pc/.gitignore deleted file mode 100644 index 6fd0ef029e1..00000000000 --- a/lib/hidapi/pc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pc diff --git a/lib/hidapi/pc/hidapi-hidraw.pc.in b/lib/hidapi/pc/hidapi-hidraw.pc.in deleted file mode 100644 index 28b9fe68189..00000000000 --- a/lib/hidapi/pc/hidapi-hidraw.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: hidapi-hidraw -Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the hidraw implementation. -URL: https://github.com/libusb/hidapi -Version: @VERSION@ -Libs: -L${libdir} -lhidapi-hidraw -Cflags: -I${includedir}/hidapi diff --git a/lib/hidapi/pc/hidapi-libusb.pc.in b/lib/hidapi/pc/hidapi-libusb.pc.in deleted file mode 100644 index d51e98f2b2f..00000000000 --- a/lib/hidapi/pc/hidapi-libusb.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: hidapi-libusb -Description: C Library for USB HID device access from Linux, Mac OS X, FreeBSD, and Windows. This is the libusb implementation. -URL: https://github.com/libusb/hidapi -Version: @VERSION@ -Libs: -L${libdir} -lhidapi-libusb -Cflags: -I${includedir}/hidapi diff --git a/lib/hidapi/pc/hidapi.pc.in b/lib/hidapi/pc/hidapi.pc.in deleted file mode 100644 index 75b11a5dd48..00000000000 --- a/lib/hidapi/pc/hidapi.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: hidapi -Description: C Library for USB/Bluetooth HID device access from Linux, Mac OS X, FreeBSD, and Windows. -URL: https://github.com/libusb/hidapi -Version: @VERSION@ -Libs: -L${libdir} -lhidapi -Cflags: -I${includedir}/hidapi diff --git a/lib/hidapi/src/CMakeLists.txt b/lib/hidapi/src/CMakeLists.txt deleted file mode 100644 index 3d55acefaf4..00000000000 --- a/lib/hidapi/src/CMakeLists.txt +++ /dev/null @@ -1,184 +0,0 @@ -get_filename_component(PROJECT_ROOT "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) - -# Read version from file -file(READ "${PROJECT_ROOT}/VERSION" RAW_VERSION_STR) -string(REGEX MATCH "^([0-9]+\\.[0-9]+\\.[0-9]+)(.*)" VERSION_STR "${RAW_VERSION_STR}") - -if(NOT VERSION_STR) - message(FATAL_ERROR "Broken VERSION file, couldn't parse '${PROJECT_ROOT}/VERSION' with content: '${RAW_VERSION_STR}'") -endif() - -set(VERSION "${CMAKE_MATCH_1}") -string(STRIP "${CMAKE_MATCH_2}" VERSION_SUFFIX) -# compatinility with find_package() vs add_subdirectory -set(hidapi_VERSION "${VERSION}" PARENT_SCOPE) -# - -if(DEFINED HIDAPI_PRINT_VERSION AND HIDAPI_PRINT_VERSION) - set(HIDAPI_PRINT_VERSION "hidapi: v${VERSION}") - if(VERSION_SUFFIX) - set(HIDAPI_PRINT_VERSION "${HIDAPI_PRINT_VERSION} (${VERSION_SUFFIX})") - endif() - message(STATUS "${HIDAPI_PRINT_VERSION}") -endif() - -project(hidapi VERSION "${VERSION}" LANGUAGES C) - -# Defaults and required options - -if(NOT DEFINED BUILD_SHARED_LIBS) - set(BUILD_SHARED_LIBS OFF) -endif() -if(NOT DEFINED HIDAPI_INSTALL_TARGETS) - set(HIDAPI_INSTALL_TARGETS OFF) -endif() -if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE) - set(CMAKE_POSITION_INDEPENDENT_CODE ON) -endif() - -get_directory_property(IS_EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL) -if(IS_EXCLUDE_FROM_ALL) - if(HIDAPI_INSTALL_TARGETS) - message(WARNING "Installing EXCLUDE_FROM_ALL targets in an undefined behavior in CMake.\nDon't add 'hidapi' sundirectory with 'EXCLUDE_FROM_ALL' property, or don't set 'HIDAPI_INSTALL_TARGETS' to TRUE.") - endif() -endif() - -# Helper(s) - -function(hidapi_configure_pc PC_IN_FILE) - file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pc") - - set(VERSION "${VERSION}${VERSION_SUFFIX}") - set(prefix "${CMAKE_INSTALL_PREFIX}") - set(exec_prefix "\${prefix}") - if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") - set(libdir "${CMAKE_INSTALL_LIBDIR}") - else() - set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") - endif() - if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") - set(includedir "${CMAKE_INSTALL_INCLUDEDIR}") - else() - set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") - endif() - - get_filename_component(PC_IN_FILENAME "${PC_IN_FILE}" NAME_WE) - set(PC_FILE "${CMAKE_CURRENT_BINARY_DIR}/pc/${PC_IN_FILENAME}.pc") - configure_file("${PC_IN_FILE}" "${PC_FILE}" @ONLY) - - install(FILES "${PC_FILE}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/") -endfunction() - -# The library - -if(HIDAPI_INSTALL_TARGETS) - include(GNUInstallDirs) -endif() - -add_library(hidapi_include INTERFACE) -target_include_directories(hidapi_include INTERFACE - "$" - "$" -) -set_target_properties(hidapi_include PROPERTIES EXPORT_NAME "include") -set(HIDAPI_PUBLIC_HEADERS "${PROJECT_ROOT}/hidapi/hidapi.h") - -add_library(hidapi::include ALIAS hidapi_include) - -if(HIDAPI_INSTALL_TARGETS) - install(TARGETS hidapi_include EXPORT hidapi) -endif() - -set(EXPORT_ALIAS) -set(EXPORT_COMPONENTS) - -set(HIDAPI_NEED_EXPORT_THREADS FALSE) -set(HIDAPI_NEED_EXPORT_LIBUSB FALSE) -set(HIDAPI_NEED_EXPORT_LIBUDEV FALSE) -set(HIDAPI_NEED_EXPORT_ICONV FALSE) - -if(WIN32) - target_include_directories(hidapi_include INTERFACE - "$" - ) - add_subdirectory("${PROJECT_ROOT}/windows" windows) - set(EXPORT_ALIAS winapi) - list(APPEND EXPORT_COMPONENTS winapi) -elseif(APPLE) - target_include_directories(hidapi_include INTERFACE - "$" - ) - add_subdirectory("${PROJECT_ROOT}/mac" mac) - set(EXPORT_ALIAS darwin) - list(APPEND EXPORT_COMPONENTS darwin) - if(NOT BUILD_SHARED_LIBS) - set(HIDAPI_NEED_EXPORT_THREADS TRUE) - endif() -else() - if(NOT DEFINED HIDAPI_WITH_LIBUSB) - set(HIDAPI_WITH_LIBUSB ON) - endif() - if(CMAKE_SYSTEM_NAME MATCHES "Linux") - if(NOT DEFINED HIDAPI_WITH_HIDRAW) - set(HIDAPI_WITH_HIDRAW ON) - endif() - if(HIDAPI_WITH_HIDRAW) - add_subdirectory("${PROJECT_ROOT}/linux" linux) - list(APPEND EXPORT_COMPONENTS hidraw) - set(EXPORT_ALIAS hidraw) - if(NOT BUILD_SHARED_LIBS) - set(HIDAPI_NEED_EXPORT_THREADS TRUE) - set(HIDAPI_NEED_EXPORT_LIBUDEV TRUE) - endif() - endif() - else() - set(HIDAPI_WITH_LIBUSB ON) - endif() - if(HIDAPI_WITH_LIBUSB) - target_include_directories(hidapi_include INTERFACE - "$" - ) - if(NOT DEFINED HIDAPI_NO_ICONV) - set(HIDAPI_NO_ICONV OFF) - endif() - add_subdirectory("${PROJECT_ROOT}/libusb" libusb) - list(APPEND EXPORT_COMPONENTS libusb) - if(NOT EXPORT_ALIAS) - set(EXPORT_ALIAS libusb) - endif() - if(NOT BUILD_SHARED_LIBS) - set(HIDAPI_NEED_EXPORT_THREADS TRUE) - if(NOT TARGET usb-1.0) - set(HIDAPI_NEED_EXPORT_LIBUSB TRUE) - endif() - endif() - elseif(NOT TARGET hidapi_hidraw) - message(FATAL_ERROR "Select at least one option to build: HIDAPI_WITH_LIBUSB or HIDAPI_WITH_HIDRAW") - endif() -endif() - -add_library(hidapi::hidapi ALIAS hidapi_${EXPORT_ALIAS}) - -if(HIDAPI_INSTALL_TARGETS) - include(CMakePackageConfigHelpers) - set(EXPORT_DENERATED_LOCATION "${CMAKE_BINARY_DIR}/export_generated") - set(EXPORT_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/hidapi") - write_basic_package_version_file("${EXPORT_DENERATED_LOCATION}/hidapi-config-version.cmake" - COMPATIBILITY SameMajorVersion - ) - configure_package_config_file("cmake/hidapi-config.cmake.in" "${EXPORT_DENERATED_LOCATION}/hidapi-config.cmake" - INSTALL_DESTINATION "${EXPORT_DESTINATION}" - NO_SET_AND_CHECK_MACRO - ) - - install(EXPORT hidapi - DESTINATION "${EXPORT_DESTINATION}" - NAMESPACE hidapi:: - FILE "libhidapi.cmake" - ) - install(FILES - "${EXPORT_DENERATED_LOCATION}/hidapi-config-version.cmake" - "${EXPORT_DENERATED_LOCATION}/hidapi-config.cmake" - DESTINATION "${EXPORT_DESTINATION}" - ) -endif() diff --git a/lib/hidapi/src/cmake/hidapi-config.cmake.in b/lib/hidapi/src/cmake/hidapi-config.cmake.in deleted file mode 100644 index 4226c8cd80f..00000000000 --- a/lib/hidapi/src/cmake/hidapi-config.cmake.in +++ /dev/null @@ -1,61 +0,0 @@ -@PACKAGE_INIT@ - -set(hidapi_VERSION_MAJOR "@hidapi_VERSION_MAJOR@") -set(hidapi_VERSION_MINOR "@hidapi_VERSION_MINOR@") -set(hidapi_VERSION_PATCH "@hidapi_VERSION_PATCH@") -set(hidapi_VERSION "@hidapi_VERSION@") -set(hidapi_VERSION_STR "@hidapi_VERSION@@VERSION_SUFFIX@") - -set(hidapi_FOUND FALSE) - -set(HIDAPI_NEED_EXPORT_THREADS @HIDAPI_NEED_EXPORT_THREADS@) -set(HIDAPI_NEED_EXPORT_LIBUSB @HIDAPI_NEED_EXPORT_LIBUSB@) -set(HIDAPI_NEED_EXPORT_LIBUDEV @HIDAPI_NEED_EXPORT_LIBUDEV@) -set(HIDAPI_NEED_EXPORT_ICONV @HIDAPI_NEED_EXPORT_ICONV@) - -if(HIDAPI_NEED_EXPORT_THREADS) - if(CMAKE_VERSION VERSION_LESS 3.4.3) - message(FATAL_ERROR "This file relies on consumers using CMake 3.4.3 or greater.") - endif() - find_package(Threads REQUIRED) -endif() - -if(HIDAPI_NEED_EXPORT_LIBUSB OR HIDAPI_NEED_EXPORT_LIBUDEV) - if(CMAKE_VERSION VERSION_LESS 3.6.3) - message(FATAL_ERROR "This file relies on consumers using CMake 3.6.3 or greater.") - endif() - find_package(PkgConfig) - if(HIDAPI_NEED_EXPORT_LIBUSB) - pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0>=1.0.9) - endif() - if(HIDAPI_NEED_EXPORT_LIBUDEV) - pkg_check_modules(libudev REQUIRED IMPORTED_TARGET libudev) - endif() -endif() - -if(HIDAPI_NEED_EXPORT_ICONV) - if(CMAKE_VERSION VERSION_LESS 3.11) - message(WARNING "HIDAPI requires CMake target Iconv::Iconv, make sure to provide it") - else() - find_package(Iconv REQUIRED) - endif() -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/libhidapi.cmake") - -set(hidapi_FOUND TRUE) - -foreach(_component @EXPORT_COMPONENTS@) - if(TARGET hidapi::${_component}) - set(hidapi_${_component}_FOUND TRUE) - endif() -endforeach() - -check_required_components(hidapi) - -if(NOT TARGET hidapi::hidapi) - add_library(hidapi::hidapi INTERFACE IMPORTED) - set_target_properties(hidapi::hidapi PROPERTIES - INTERFACE_LINK_LIBRARIES hidapi::@EXPORT_ALIAS@ - ) -endif() diff --git a/lib/hidapi/windows/.gitignore b/lib/hidapi/windows/.gitignore deleted file mode 100644 index c2ad395ec0b..00000000000 --- a/lib/hidapi/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -Debug -Release -.vs/ -*.exp -*.ilk -*.lib -*.suo -*.vcproj.* -*.vcxproj.* -*.ncb -*.suo -*.dll -*.pdb -.deps -.libs -*.lo -*.la diff --git a/lib/hidapi/windows/CMakeLists.txt b/lib/hidapi/windows/CMakeLists.txt deleted file mode 100644 index 74839fb51ad..00000000000 --- a/lib/hidapi/windows/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_winapi.h") - -set(SOURCES - hid.c - hidapi_cfgmgr32.h - hidapi_hidclass.h - hidapi_hidpi.h - hidapi_hidsdi.h -) - -if(BUILD_SHARED_LIBS) - list(APPEND SOURCES hidapi.rc) -endif() - -add_library(hidapi_winapi - ${HIDAPI_PUBLIC_HEADERS} - ${SOURCES} -) -target_link_libraries(hidapi_winapi - PUBLIC hidapi_include -) - -set_target_properties(hidapi_winapi - PROPERTIES - EXPORT_NAME "winapi" - OUTPUT_NAME "hidapi" - VERSION ${PROJECT_VERSION} - PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}" -) - -# compatibility with find_package() -add_library(hidapi::winapi ALIAS hidapi_winapi) -# compatibility with raw library link -add_library(hidapi ALIAS hidapi_winapi) - -if(HIDAPI_INSTALL_TARGETS) - install(TARGETS hidapi_winapi EXPORT hidapi - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/hidapi" - ) -endif() - -hidapi_configure_pc("${PROJECT_ROOT}/pc/hidapi.pc.in") diff --git a/lib/hidapi/windows/hid.c b/lib/hidapi/windows/hid.c deleted file mode 100644 index 37692766792..00000000000 --- a/lib/hidapi/windows/hid.c +++ /dev/null @@ -1,1325 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - Alan Ott - Signal 11 Software - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -/* Do not warn about wcsncpy usage. - https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */ -#define _CRT_SECURE_NO_WARNINGS -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include "hidapi.h" - -#include - -#ifndef _NTDEF_ -typedef LONG NTSTATUS; -#endif - -#ifdef __MINGW32__ -#include -#include -#define WC_ERR_INVALID_CHARS 0x00000080 -#endif - -#ifdef __CYGWIN__ -#include -#include -#define _wcsdup wcsdup -#endif - -/*#define HIDAPI_USE_DDK*/ - -#include -#include "hidapi_cfgmgr32.h" -#include "hidapi_hidclass.h" -#include "hidapi_hidsdi.h" - -#include -#include -#include - -#ifdef MIN -#undef MIN -#endif -#define MIN(x,y) ((x) < (y)? (x): (y)) - -/* MAXIMUM_USB_STRING_LENGTH from usbspec.h is 255 */ -/* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */ -#define MAX_STRING_WCHARS 256 - -static struct hid_api_version api_version = { - .major = HID_API_VERSION_MAJOR, - .minor = HID_API_VERSION_MINOR, - .patch = HID_API_VERSION_PATCH -}; - -#ifndef HIDAPI_USE_DDK -/* Since we're not building with the DDK, and the HID header - files aren't part of the Windows SDK, we define what we need ourselves. - In lookup_functions(), the function pointers - defined below are set. */ - -static HidD_GetHidGuid_ HidD_GetHidGuid; -static HidD_GetAttributes_ HidD_GetAttributes; -static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; -static HidD_GetManufacturerString_ HidD_GetManufacturerString; -static HidD_GetProductString_ HidD_GetProductString; -static HidD_SetFeature_ HidD_SetFeature; -static HidD_GetFeature_ HidD_GetFeature; -static HidD_GetInputReport_ HidD_GetInputReport; -static HidD_GetIndexedString_ HidD_GetIndexedString; -static HidD_GetPreparsedData_ HidD_GetPreparsedData; -static HidD_FreePreparsedData_ HidD_FreePreparsedData; -static HidP_GetCaps_ HidP_GetCaps; -static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; - -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 CM_Get_Device_Interface_List_SizeW_ CM_Get_Device_Interface_List_SizeW = NULL; -static CM_Get_Device_Interface_ListW_ CM_Get_Device_Interface_ListW = NULL; - -static HMODULE hid_lib_handle = NULL; -static HMODULE cfgmgr32_lib_handle = NULL; -static BOOLEAN hidapi_initialized = FALSE; - -static void free_library_handles() -{ - if (hid_lib_handle) - FreeLibrary(hid_lib_handle); - hid_lib_handle = NULL; - if (cfgmgr32_lib_handle) - FreeLibrary(cfgmgr32_lib_handle); - cfgmgr32_lib_handle = NULL; -} - -static int lookup_functions() -{ - hid_lib_handle = LoadLibraryW(L"hid.dll"); - if (hid_lib_handle == NULL) { - goto err; - } - - cfgmgr32_lib_handle = LoadLibraryW(L"cfgmgr32.dll"); - if (cfgmgr32_lib_handle == NULL) { - goto err; - } - -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-function-type" -#endif -#define RESOLVE(lib_handle, x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) goto err; - - RESOLVE(hid_lib_handle, HidD_GetHidGuid); - RESOLVE(hid_lib_handle, HidD_GetAttributes); - RESOLVE(hid_lib_handle, HidD_GetSerialNumberString); - RESOLVE(hid_lib_handle, HidD_GetManufacturerString); - RESOLVE(hid_lib_handle, HidD_GetProductString); - RESOLVE(hid_lib_handle, HidD_SetFeature); - RESOLVE(hid_lib_handle, HidD_GetFeature); - RESOLVE(hid_lib_handle, HidD_GetInputReport); - RESOLVE(hid_lib_handle, HidD_GetIndexedString); - RESOLVE(hid_lib_handle, HidD_GetPreparsedData); - RESOLVE(hid_lib_handle, HidD_FreePreparsedData); - RESOLVE(hid_lib_handle, HidP_GetCaps); - RESOLVE(hid_lib_handle, HidD_SetNumInputBuffers); - - RESOLVE(cfgmgr32_lib_handle, CM_Locate_DevNodeW); - RESOLVE(cfgmgr32_lib_handle, CM_Get_Parent); - RESOLVE(cfgmgr32_lib_handle, CM_Get_DevNode_PropertyW); - RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_PropertyW); - RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_List_SizeW); - RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_ListW); - -#undef RESOLVE -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif - - return 0; - -err: - free_library_handles(); - return -1; -} - -#endif /* HIDAPI_USE_DDK */ - -struct hid_device_ { - HANDLE device_handle; - BOOL blocking; - USHORT output_report_length; - unsigned char *write_buf; - size_t input_report_length; - USHORT feature_report_length; - unsigned char *feature_buf; - wchar_t *last_error_str; - BOOL read_pending; - char *read_buf; - OVERLAPPED ol; - OVERLAPPED write_ol; - struct hid_device_info* device_info; -}; - -static hid_device *new_hid_device() -{ - hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); - - if (dev == NULL) { - return NULL; - } - - dev->device_handle = INVALID_HANDLE_VALUE; - dev->blocking = TRUE; - dev->output_report_length = 0; - dev->write_buf = NULL; - dev->input_report_length = 0; - dev->feature_report_length = 0; - dev->feature_buf = NULL; - dev->last_error_str = NULL; - dev->read_pending = FALSE; - dev->read_buf = NULL; - 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->device_info = NULL; - - return dev; -} - -static void free_hid_device(hid_device *dev) -{ - CloseHandle(dev->ol.hEvent); - CloseHandle(dev->write_ol.hEvent); - CloseHandle(dev->device_handle); - free(dev->last_error_str); - dev->last_error_str = NULL; - free(dev->write_buf); - free(dev->feature_buf); - free(dev->read_buf); - hid_free_enumeration(dev->device_info); - free(dev); -} - -static void register_winapi_error_to_buffer(wchar_t **error_buffer, const WCHAR *op) -{ - free(*error_buffer); - *error_buffer = NULL; - - /* Only clear out error messages if NULL is passed into op */ - if (!op) { - return; - } - - WCHAR system_err_buf[1024]; - DWORD error_code = GetLastError(); - - DWORD system_err_len = FormatMessageW( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error_code, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - system_err_buf, ARRAYSIZE(system_err_buf), - NULL); - - DWORD op_len = (DWORD)wcslen(op); - - DWORD op_prefix_len = - op_len - + 15 /*: (0x00000000) */ - ; - DWORD msg_len = - + op_prefix_len - + system_err_len - ; - - *error_buffer = (WCHAR *)calloc(msg_len + 1, sizeof (WCHAR)); - WCHAR *msg = *error_buffer; - - if (!msg) - return; - - int printf_written = swprintf(msg, msg_len + 1, L"%.*ls: (0x%08X) %.*ls", op_len, op, error_code, system_err_len, system_err_buf); - - if (printf_written < 0) - { - /* Highly unlikely */ - msg[0] = L'\0'; - return; - } - - /* Get rid of the CR and LF that FormatMessage() sticks at the - end of the message. Thanks Microsoft! */ - while(msg[msg_len-1] == L'\r' || msg[msg_len-1] == L'\n' || msg[msg_len-1] == L' ') - { - msg[msg_len-1] = L'\0'; - msg_len--; - } -} - -static void register_string_error_to_buffer(wchar_t **error_buffer, const WCHAR *string_error) -{ - free(*error_buffer); - *error_buffer = NULL; - - if (string_error) { - *error_buffer = _wcsdup(string_error); - } -} - -static void register_winapi_error(hid_device *dev, const WCHAR *op) -{ - register_winapi_error_to_buffer(&dev->last_error_str, op); -} - -static void register_string_error(hid_device *dev, const WCHAR *string_error) -{ - register_string_error_to_buffer(&dev->last_error_str, string_error); -} - -static wchar_t *last_global_error_str = NULL; - -static void register_global_winapi_error(const WCHAR *op) -{ - register_winapi_error_to_buffer(&last_global_error_str, op); -} - -static void register_global_error(const WCHAR *string_error) -{ - register_string_error_to_buffer(&last_global_error_str, string_error); -} - -static HANDLE open_device(const wchar_t *path, BOOL open_rw) -{ - HANDLE handle; - DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0; - DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE; - - handle = CreateFileW(path, - desired_access, - share_mode, - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ - 0); - - return handle; -} - -HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version() -{ - return &api_version; -} - -HID_API_EXPORT const char* HID_API_CALL hid_version_str() -{ - return HID_API_VERSION_STR; -} - -int HID_API_EXPORT hid_init(void) -{ - register_global_error(NULL); -#ifndef HIDAPI_USE_DDK - if (!hidapi_initialized) { - if (lookup_functions() < 0) { - register_global_winapi_error(L"resolve DLL functions"); - return -1; - } - hidapi_initialized = TRUE; - } -#endif - return 0; -} - -int HID_API_EXPORT hid_exit(void) -{ -#ifndef HIDAPI_USE_DDK - free_library_handles(); - hidapi_initialized = FALSE; -#endif - register_global_error(NULL); - return 0; -} - -static void* hid_internal_get_devnode_property(DEVINST dev_node, const DEVPROPKEY* property_key, DEVPROPTYPE expected_property_type) -{ - ULONG len = 0; - CONFIGRET cr; - DEVPROPTYPE property_type; - PBYTE property_value = NULL; - - cr = CM_Get_DevNode_PropertyW(dev_node, property_key, &property_type, NULL, &len, 0); - if (cr != CR_BUFFER_SMALL || property_type != expected_property_type) - return NULL; - - property_value = (PBYTE)calloc(len, sizeof(BYTE)); - cr = CM_Get_DevNode_PropertyW(dev_node, property_key, &property_type, property_value, &len, 0); - if (cr != CR_SUCCESS) { - free(property_value); - return NULL; - } - - return property_value; -} - -static void* hid_internal_get_device_interface_property(const wchar_t* interface_path, const DEVPROPKEY* property_key, DEVPROPTYPE expected_property_type) -{ - ULONG len = 0; - CONFIGRET cr; - DEVPROPTYPE property_type; - PBYTE property_value = NULL; - - cr = CM_Get_Device_Interface_PropertyW(interface_path, property_key, &property_type, NULL, &len, 0); - if (cr != CR_BUFFER_SMALL || property_type != expected_property_type) - return NULL; - - property_value = (PBYTE)calloc(len, sizeof(BYTE)); - cr = CM_Get_Device_Interface_PropertyW(interface_path, property_key, &property_type, property_value, &len, 0); - if (cr != CR_SUCCESS) { - free(property_value); - return NULL; - } - - return property_value; -} - -static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_node) -{ - wchar_t *manufacturer_string, *serial_number, *product_string; - /* Manufacturer String */ - manufacturer_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_Manufacturer, DEVPROP_TYPE_STRING); - if (manufacturer_string) { - free(dev->manufacturer_string); - dev->manufacturer_string = manufacturer_string; - } - - /* Serial Number String (MAC Address) */ - serial_number = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, DEVPROP_TYPE_STRING); - if (serial_number) { - free(dev->serial_number); - dev->serial_number = serial_number; - } - - /* Get devnode grandparent to reach out Bluetooth LE device node */ - if (CM_Get_Parent(&dev_node, dev_node, 0) != CR_SUCCESS) - return; - - /* Product String */ - product_string = hid_internal_get_devnode_property(dev_node, &DEVPKEY_NAME, DEVPROP_TYPE_STRING); - if (product_string) { - free(dev->product_string); - dev->product_string = product_string; - } -} - -/* USB Device Interface Number. - It can be parsed out of the Hardware ID if a USB device is has multiple interfaces (composite device). - See https://docs.microsoft.com/windows-hardware/drivers/hid/hidclass-hardware-ids-for-top-level-collections - and https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers - - hardware_id is always expected to be uppercase. -*/ -static int hid_internal_get_interface_number(const wchar_t* hardware_id) -{ - int interface_number; - wchar_t *startptr, *endptr; - const wchar_t *interface_token = L"&MI_"; - - startptr = wcsstr(hardware_id, interface_token); - if (!startptr) - return -1; - - startptr += wcslen(interface_token); - interface_number = wcstol(startptr, &endptr, 16); - if (endptr == startptr) - return -1; - - return interface_number; -} - -static void hid_internal_get_info(const wchar_t* interface_path, struct hid_device_info* dev) -{ - wchar_t *device_id = NULL, *compatible_ids = NULL, *hardware_ids = NULL; - CONFIGRET cr; - DEVINST dev_node; - - /* Get the device id from interface path */ - device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); - if (!device_id) - 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 the hardware ids from devnode */ - hardware_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_HardwareIds, DEVPROP_TYPE_STRING_LIST); - if (!hardware_ids) - goto end; - - /* Search for interface number in hardware ids */ - for (wchar_t* hardware_id = hardware_ids; *hardware_id; hardware_id += wcslen(hardware_id) + 1) { - /* Normalize to upper case */ - for (wchar_t* p = hardware_id; *p; ++p) *p = towupper(*p); - - dev->interface_number = hid_internal_get_interface_number(hardware_id); - - if (dev->interface_number != -1) - break; - } - - /* 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 */ - compatible_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST); - if (!compatible_ids) - 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); - - /* USB devices - https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support - https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */ - if (wcsstr(compatible_id, L"USB") != NULL) { - dev->bus_type = HID_API_BUS_USB; - break; - } - - /* Bluetooth devices - https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */ - if (wcsstr(compatible_id, L"BTHENUM") != NULL) { - dev->bus_type = HID_API_BUS_BLUETOOTH; - break; - } - - /* 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); - - dev->bus_type = HID_API_BUS_BLUETOOTH; - break; - } - - /* I2C devices - https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */ - if (wcsstr(compatible_id, L"PNP0C50") != NULL) { - dev->bus_type = HID_API_BUS_I2C; - break; - } - - /* SPI devices - https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */ - if (wcsstr(compatible_id, L"PNP0C51") != NULL) { - dev->bus_type = HID_API_BUS_SPI; - break; - } - } -end: - free(device_id); - free(hardware_ids); - free(compatible_ids); -} - -static char *hid_internal_UTF16toUTF8(const wchar_t *src) -{ - char *dst = NULL; - int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL); - if (len) { - dst = (char*)calloc(len, sizeof(char)); - if (dst == NULL) { - return NULL; - } - WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dst, len, NULL, NULL); - } - - return dst; -} - -static wchar_t *hid_internal_UTF8toUTF16(const char *src) -{ - wchar_t *dst = NULL; - int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0); - if (len) { - dst = (wchar_t*)calloc(len, sizeof(wchar_t)); - if (dst == NULL) { - return NULL; - } - MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dst, len); - } - - return dst; -} - -static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HANDLE handle) -{ - struct hid_device_info *dev = NULL; /* return object */ - HIDD_ATTRIBUTES attrib; - PHIDP_PREPARSED_DATA pp_data = NULL; - HIDP_CAPS caps; - wchar_t string[MAX_STRING_WCHARS]; - - /* Create the record. */ - dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info)); - - /* Fill out the record */ - dev->next = NULL; - dev->path = hid_internal_UTF16toUTF8(path); - - attrib.Size = sizeof(HIDD_ATTRIBUTES); - if (HidD_GetAttributes(handle, &attrib)) { - /* 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. */ - if (HidD_GetPreparsedData(handle, &pp_data)) { - if (HidP_GetCaps(pp_data, &caps) == HIDP_STATUS_SUCCESS) { - dev->usage_page = caps.UsagePage; - dev->usage = caps.Usage; - } - - HidD_FreePreparsedData(pp_data); - } - - /* Serial Number */ - string[0] = L'\0'; - HidD_GetSerialNumberString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; - dev->serial_number = _wcsdup(string); - - /* Manufacturer String */ - string[0] = L'\0'; - HidD_GetManufacturerString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; - dev->manufacturer_string = _wcsdup(string); - - /* Product String */ - string[0] = L'\0'; - HidD_GetProductString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; - dev->product_string = _wcsdup(string); - - hid_internal_get_info(path, dev); - - return dev; -} - -struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) -{ - struct hid_device_info *root = NULL; /* return object */ - struct hid_device_info *cur_dev = NULL; - GUID interface_class_guid; - CONFIGRET cr; - wchar_t* device_interface_list = NULL; - DWORD len; - - if (hid_init() < 0) { - /* register_global_error: global error is reset by hid_init */ - return NULL; - } - - /* Retrieve HID Interface Class GUID - https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */ - HidD_GetHidGuid(&interface_class_guid); - - /* Get the list of all device interfaces belonging to the HID class. */ - /* Retry in case of list was changed between calls to - CM_Get_Device_Interface_List_SizeW and CM_Get_Device_Interface_ListW */ - do { - cr = CM_Get_Device_Interface_List_SizeW(&len, &interface_class_guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); - if (cr != CR_SUCCESS) { - register_global_error(L"Failed to get size of HID device interface list"); - break; - } - - if (device_interface_list != NULL) { - free(device_interface_list); - } - - device_interface_list = (wchar_t*)calloc(len, sizeof(wchar_t)); - if (device_interface_list == NULL) { - register_global_error(L"Failed to allocate memory for HID device interface list"); - return NULL; - } - cr = CM_Get_Device_Interface_ListW(&interface_class_guid, NULL, device_interface_list, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); - if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) { - register_global_error(L"Failed to get HID device interface list"); - } - } while (cr == CR_BUFFER_SMALL); - - if (cr != CR_SUCCESS) { - goto end_of_function; - } - - /* Iterate over each device interface in the HID class, looking for the right one. */ - for (wchar_t* device_interface = device_interface_list; *device_interface; device_interface += wcslen(device_interface) + 1) { - HANDLE device_handle = INVALID_HANDLE_VALUE; - HIDD_ATTRIBUTES attrib; - - /* Open read-only handle to the device */ - device_handle = open_device(device_interface, FALSE); - - /* Check validity of device_handle. */ - if (device_handle == INVALID_HANDLE_VALUE) { - /* Unable to open the device. */ - continue; - } - - /* Get the Vendor ID and Product ID for this device. */ - attrib.Size = sizeof(HIDD_ATTRIBUTES); - if (!HidD_GetAttributes(device_handle, &attrib)) { - goto cont_close; - } - - /* Check the VID/PID to see if we should add this - device to the enumeration list. */ - if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && - (product_id == 0x0 || attrib.ProductID == product_id)) { - - /* VID/PID match. Create the record. */ - struct hid_device_info *tmp = hid_internal_get_device_info(device_interface, device_handle); - - if (tmp == NULL) { - goto cont_close; - } - - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; - } - -cont_close: - CloseHandle(device_handle); - } - - if (root == NULL) { - if (vendor_id == 0 && product_id == 0) { - register_global_error(L"No HID devices found in the system."); - } else { - register_global_error(L"No HID devices with requested VID/PID found in the system."); - } - } - -end_of_function: - free(device_interface_list); - - return root; -} - -void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) -{ - /* TODO: Merge this with the Linux version. This function is platform-independent. */ - struct hid_device_info *d = devs; - while (d) { - struct hid_device_info *next = d->next; - free(d->path); - free(d->serial_number); - free(d->manufacturer_string); - free(d->product_string); - free(d); - d = next; - } -} - -HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) -{ - /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ - struct hid_device_info *devs, *cur_dev; - const char *path_to_open = NULL; - hid_device *handle = NULL; - - /* register_global_error: global error is reset by hid_enumerate/hid_init */ - devs = hid_enumerate(vendor_id, product_id); - if (!devs) { - /* register_global_error: global error is already set by hid_enumerate */ - return NULL; - } - - cur_dev = devs; - while (cur_dev) { - if (cur_dev->vendor_id == vendor_id && - cur_dev->product_id == product_id) { - if (serial_number) { - if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) { - path_to_open = cur_dev->path; - break; - } - } - else { - path_to_open = cur_dev->path; - break; - } - } - cur_dev = cur_dev->next; - } - - if (path_to_open) { - /* Open the device */ - handle = hid_open_path(path_to_open); - } else { - register_global_error(L"Device with requested VID/PID/(SerialNumber) not found"); - } - - hid_free_enumeration(devs); - - return handle; -} - -HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) -{ - hid_device *dev = NULL; - wchar_t* interface_path = NULL; - HANDLE device_handle = INVALID_HANDLE_VALUE; - PHIDP_PREPARSED_DATA pp_data = NULL; - HIDP_CAPS caps; - - if (hid_init() < 0) { - /* register_global_error: global error is reset by hid_init */ - goto end_of_function; - } - - interface_path = hid_internal_UTF8toUTF16(path); - if (!interface_path) { - register_string_error(dev, L"Path conversion failure"); - goto end_of_function; - } - - /* Open a handle to the device */ - device_handle = open_device(interface_path, TRUE); - - /* Check validity of write_handle. */ - if (device_handle == INVALID_HANDLE_VALUE) { - /* System devices, such as keyboards and mice, cannot be opened in - read-write mode, because the system takes exclusive control over - them. This is to prevent keyloggers. However, feature reports - can still be sent and received. Retry opening the device, but - without read/write access. */ - device_handle = open_device(interface_path, FALSE); - - /* Check the validity of the limited device_handle. */ - if (device_handle == INVALID_HANDLE_VALUE) { - register_global_winapi_error(L"open_device"); - goto end_of_function; - } - } - - /* Set the Input Report buffer size to 64 reports. */ - if (!HidD_SetNumInputBuffers(device_handle, 64)) { - register_global_winapi_error(L"set input buffers"); - goto end_of_function; - } - - /* Get the Input Report length for the device. */ - if (!HidD_GetPreparsedData(device_handle, &pp_data)) { - register_global_winapi_error(L"get preparsed data"); - goto end_of_function; - } - - if (HidP_GetCaps(pp_data, &caps) != HIDP_STATUS_SUCCESS) { - register_global_error(L"HidP_GetCaps"); - goto end_of_function; - } - - dev = new_hid_device(); - - if (dev == NULL) { - register_global_error(L"hid_device allocation error"); - goto end_of_function; - } - - dev->device_handle = device_handle; - device_handle = INVALID_HANDLE_VALUE; - - dev->output_report_length = caps.OutputReportByteLength; - dev->input_report_length = caps.InputReportByteLength; - dev->feature_report_length = caps.FeatureReportByteLength; - dev->read_buf = (char*) malloc(dev->input_report_length); - dev->device_info = hid_internal_get_device_info(interface_path, dev->device_handle); - -end_of_function: - free(interface_path); - CloseHandle(device_handle); - - if (pp_data) { - HidD_FreePreparsedData(pp_data); - } - - return dev; -} - -int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) -{ - DWORD bytes_written = 0; - int function_result = -1; - BOOL res; - BOOL overlapped = FALSE; - - unsigned char *buf; - - if (!data || !length) { - register_string_error(dev, L"Zero buffer/length"); - return function_result; - } - - register_string_error(dev, NULL); - - /* Make sure the right number of bytes are passed to WriteFile. Windows - expects the number of bytes which are in the _longest_ report (plus - one for the report number) bytes even if the data is a report - which is shorter than that. Windows gives us this value in - caps.OutputReportByteLength. If a user passes in fewer bytes than this, - use cached temporary buffer which is the proper size. */ - if (length >= dev->output_report_length) { - /* The user passed the right number of bytes. Use the buffer as-is. */ - buf = (unsigned char *) data; - } else { - if (dev->write_buf == NULL) - dev->write_buf = (unsigned char *) malloc(dev->output_report_length); - 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); - - if (!res) { - if (GetLastError() != ERROR_IO_PENDING) { - /* WriteFile() failed. Return error. */ - register_winapi_error(dev, L"WriteFile"); - goto end_of_function; - } - overlapped = TRUE; - } - - if (overlapped) { - /* Wait for the transaction to complete. This makes - hid_write() synchronous. */ - res = WaitForSingleObject(dev->write_ol.hEvent, 1000); - if (res != WAIT_OBJECT_0) { - /* There was a Timeout. */ - register_winapi_error(dev, L"hid_write/WaitForSingleObject"); - goto end_of_function; - } - - /* Get the result. */ - res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*wait*/); - if (res) { - function_result = bytes_written; - } - else { - /* The Write operation failed. */ - register_winapi_error(dev, L"hid_write/GetOverlappedResult"); - goto end_of_function; - } - } - -end_of_function: - return function_result; -} - - -int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) -{ - DWORD bytes_read = 0; - size_t copy_len = 0; - BOOL res = FALSE; - BOOL overlapped = FALSE; - - if (!data || !length) { - register_string_error(dev, L"Zero buffer/length"); - return -1; - } - - register_string_error(dev, NULL); - - /* Copy the handle for convenience. */ - HANDLE ev = dev->ol.hEvent; - - if (!dev->read_pending) { - /* Start an Overlapped I/O read. */ - dev->read_pending = TRUE; - 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. - Clean up and return error. */ - register_winapi_error(dev, L"ReadFile"); - CancelIo(dev->device_handle); - dev->read_pending = FALSE; - goto end_of_function; - } - overlapped = TRUE; - } - } - else { - overlapped = TRUE; - } - - if (overlapped) { - if (milliseconds >= 0) { - /* See if there is any data yet. */ - res = WaitForSingleObject(ev, milliseconds); - if (res != WAIT_OBJECT_0) { - /* There was no data this time. Return zero bytes available, - but leave the Overlapped I/O running. */ - return 0; - } - } - - /* Either WaitForSingleObject() told us that ReadFile has completed, or - we are in non-blocking mode. Get the number of bytes read. The actual - data has been copied to the data[] array which was passed to ReadFile(). */ - res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); - } - /* Set pending back to false, even if GetOverlappedResult() returned error. */ - dev->read_pending = FALSE; - - if (res && bytes_read > 0) { - if (dev->read_buf[0] == 0x0) { - /* If report numbers aren't being used, but Windows sticks a report - number (0x0) on the beginning of the report anyway. To make this - work like the other platforms, and to make it work more like the - HID spec, we'll skip over this byte. */ - bytes_read--; - copy_len = length > bytes_read ? bytes_read : length; - memcpy(data, dev->read_buf+1, copy_len); - } - else { - /* Copy the whole buffer, report number and all. */ - copy_len = length > bytes_read ? bytes_read : length; - memcpy(data, dev->read_buf, copy_len); - } - } - if (!res) { - register_winapi_error(dev, L"hid_read_timeout/GetOverlappedResult"); - } - -end_of_function: - if (!res) { - return -1; - } - - return (int) copy_len; -} - -int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) -{ - return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); -} - -int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) -{ - dev->blocking = !nonblock; - return 0; /* Success */ -} - -int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) -{ - BOOL res = FALSE; - unsigned char *buf; - size_t length_to_send; - - if (!data || !length) { - register_string_error(dev, L"Zero buffer/length"); - return -1; - } - - register_string_error(dev, NULL); - - /* Windows expects at least caps.FeatureReportByteLength bytes passed - to HidD_SetFeature(), even if the report is shorter. Any less sent and - the function fails with error ERROR_INVALID_PARAMETER set. Any more - and HidD_SetFeature() silently truncates the data sent in the report - to caps.FeatureReportByteLength. */ - if (length >= dev->feature_report_length) { - buf = (unsigned char *) data; - length_to_send = length; - } else { - if (dev->feature_buf == NULL) - dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length); - buf = dev->feature_buf; - memcpy(buf, data, length); - memset(buf + length, 0, dev->feature_report_length - length); - length_to_send = dev->feature_report_length; - } - - res = HidD_SetFeature(dev->device_handle, (PVOID)buf, (DWORD) length_to_send); - - if (!res) { - register_winapi_error(dev, L"HidD_SetFeature"); - return -1; - } - - return (int) length; -} - -static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *data, size_t length) -{ - BOOL res; - DWORD bytes_returned = 0; - - OVERLAPPED ol; - memset(&ol, 0, sizeof(ol)); - - if (!data || !length) { - register_string_error(dev, L"Zero buffer/length"); - return -1; - } - - register_string_error(dev, NULL); - - res = DeviceIoControl(dev->device_handle, - report_type, - data, (DWORD) length, - data, (DWORD) length, - &bytes_returned, &ol); - - if (!res) { - if (GetLastError() != ERROR_IO_PENDING) { - /* DeviceIoControl() failed. Return error. */ - register_winapi_error(dev, L"Get Input/Feature 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_winapi_error(dev, L"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; -} - -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) -{ - /* 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) -{ - if (!dev) - return; - - CancelIo(dev->device_handle); - free_hid_device(dev); -} - -int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_string_error(dev, L"Zero buffer/length"); - return -1; - } - - if (!dev->device_info) { - register_string_error(dev, L"NULL device info"); - return -1; - } - - wcsncpy(string, dev->device_info->manufacturer_string, maxlen); - string[maxlen - 1] = L'\0'; - - register_string_error(dev, NULL); - - return 0; -} - -int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_string_error(dev, L"Zero buffer/length"); - return -1; - } - - if (!dev->device_info) { - register_string_error(dev, L"NULL device info"); - return -1; - } - - wcsncpy(string, dev->device_info->product_string, maxlen); - string[maxlen - 1] = L'\0'; - - register_string_error(dev, NULL); - - return 0; -} - -int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) -{ - if (!string || !maxlen) { - register_string_error(dev, L"Zero buffer/length"); - return -1; - } - - if (!dev->device_info) { - register_string_error(dev, L"NULL device info"); - return -1; - } - - wcsncpy(string, dev->device_info->serial_number, maxlen); - string[maxlen - 1] = L'\0'; - - register_string_error(dev, NULL); - - return 0; -} - -HID_API_EXPORT struct hid_device_info * HID_API_CALL hid_get_device_info(hid_device *dev) { - if (!dev->device_info) - { - register_string_error(dev, L"NULL device info"); - return NULL; - } - - return dev->device_info; -} - -int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) -{ - BOOL res; - - res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS)); - if (!res) { - register_winapi_error(dev, L"HidD_GetIndexedString"); - return -1; - } - - register_string_error(dev, NULL); - - return 0; -} - -int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id) -{ - wchar_t *interface_path = NULL, *device_id = NULL; - CONFIGRET cr = CR_FAILURE; - DEVINST dev_node; - DEVPROPTYPE property_type; - ULONG len; - - if (!container_id) { - register_string_error(dev, L"Invalid Container ID"); - return -1; - } - - register_string_error(dev, NULL); - - interface_path = hid_internal_UTF8toUTF16(dev->device_info->path); - if (!interface_path) { - register_string_error(dev, L"Path conversion failure"); - goto end; - } - - /* Get the device id from interface path */ - device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); - if (!device_id) { - register_string_error(dev, L"Failed to get device interface property InstanceId"); - 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) { - register_string_error(dev, L"Failed to locate device node"); - goto end; - } - - /* Get the container id from devnode */ - len = sizeof(*container_id); - cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_ContainerId, &property_type, (PBYTE)container_id, &len, 0); - if (cr == CR_SUCCESS && property_type != DEVPROP_TYPE_GUID) - cr = CR_FAILURE; - - if (cr != CR_SUCCESS) - register_string_error(dev, L"Failed to read ContainerId property from device node"); - -end: - free(interface_path); - free(device_id); - - return cr == CR_SUCCESS ? 0 : -1; -} - - -HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) -{ - if (dev) { - if (dev->last_error_str == NULL) - return L"Success"; - return (wchar_t*)dev->last_error_str; - } - - if (last_global_error_str == NULL) - return L"Success"; - return last_global_error_str; -} - -#ifdef __cplusplus -} /* extern "C" */ -#endif diff --git a/lib/hidapi/windows/hidapi.rc b/lib/hidapi/windows/hidapi.rc deleted file mode 100644 index 530917e95bb..00000000000 --- a/lib/hidapi/windows/hidapi.rc +++ /dev/null @@ -1,35 +0,0 @@ -#include "winresrc.h" - -#include "hidapi.h" - -// English -LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT -VS_VERSION_INFO VERSIONINFO - FILEVERSION HID_API_VERSION_MAJOR,HID_API_VERSION_MINOR,HID_API_VERSION_PATCH,0 - PRODUCTVERSION HID_API_VERSION_MAJOR,HID_API_VERSION_MINOR,HID_API_VERSION_PATCH,0 - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK - FILEFLAGS 0 -#ifdef _DEBUG - | VS_FF_DEBUG -#endif - FILEOS VOS_NT_WINDOWS32 - FILETYPE VFT_DLL -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "04090000" - BEGIN - VALUE "CompanyName", "libusb/hidapi Team" - VALUE "FileDescription", "A multi-platform library to interface with HID devices (USB, Bluetooth, etc.)" - VALUE "FileVersion", HID_API_VERSION_STR - VALUE "ProductName", "HIDAPI" - VALUE "ProductVersion", HID_API_VERSION_STR - VALUE "Licence", "https://github.com/libusb/hidapi/blob/master/LICENSE.txt" - VALUE "Comments", "https://github.com/libusb/hidapi" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 0 - END -END diff --git a/lib/hidapi/windows/hidapi_cfgmgr32.h b/lib/hidapi/windows/hidapi_cfgmgr32.h deleted file mode 100644 index 720906e3d91..00000000000 --- a/lib/hidapi/windows/hidapi_cfgmgr32.h +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -#ifndef HIDAPI_CFGMGR32_H -#define HIDAPI_CFGMGR32_H - -#ifdef HIDAPI_USE_DDK - -#include -#include -#include -#include - -#else - -/* This part of the header mimics cfgmgr32.h, - but only what is used by HIDAPI */ - -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 CR_FAILURE (0x00000013) - -#define CM_LOCATE_DEVNODE_NORMAL 0x00000000 - -#define CM_GET_DEVICE_INTERFACE_LIST_PRESENT (0x00000000) - -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); -typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_List_SizeW_)(PULONG pulLen, LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, ULONG ulFlags); -typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_ListW_)(LPGUID InterfaceClassGuid, DEVINSTID_W pDeviceID, PZZWSTR Buffer, ULONG BufferLen, ULONG ulFlags); - -// from devpkey.h -static DEVPROPKEY DEVPKEY_NAME = { { 0xb725f130, 0x47ef, 0x101a, {0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac} }, 10 }; // DEVPROP_TYPE_STRING -static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, {0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57} }, 256 }; // DEVPROP_TYPE_STRING -static DEVPROPKEY DEVPKEY_Device_HardwareIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 3 }; // DEVPROP_TYPE_STRING_LIST -static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, {0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0} }, 4 }; // DEVPROP_TYPE_STRING_LIST -static DEVPROPKEY DEVPKEY_Device_ContainerId = { { 0x8c7ed206, 0x3f8a, 0x4827, {0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c} }, 2 }; // DEVPROP_TYPE_GUID - -// from propkey.h -static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_DeviceAddress = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 1 }; // DEVPROP_TYPE_STRING -static PROPERTYKEY PKEY_DeviceInterface_Bluetooth_Manufacturer = { { 0x2bd67d8b, 0x8beb, 0x48d5, {0x87, 0xe0, 0x6c, 0xda, 0x34, 0x28, 0x04, 0x0a} }, 4 }; // DEVPROP_TYPE_STRING - -#endif - -#endif /* HIDAPI_CFGMGR32_H */ diff --git a/lib/hidapi/windows/hidapi_hidclass.h b/lib/hidapi/windows/hidapi_hidclass.h deleted file mode 100644 index 13bd6f22bd5..00000000000 --- a/lib/hidapi/windows/hidapi_hidclass.h +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -#ifndef HIDAPI_HIDCLASS_H -#define HIDAPI_HIDCLASS_H - -#ifdef HIDAPI_USE_DDK - -#include - -#else - -/* This part of the header mimics hidclass.h, - but only what is used by HIDAPI */ - -#define HID_OUT_CTL_CODE(id) CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) -#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) -#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) - -#endif - -#endif /* HIDAPI_HIDCLASS_H */ diff --git a/lib/hidapi/windows/hidapi_hidpi.h b/lib/hidapi/windows/hidapi_hidpi.h deleted file mode 100644 index 343ddeb0570..00000000000 --- a/lib/hidapi/windows/hidapi_hidpi.h +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -#ifndef HIDAPI_HIDPI_H -#define HIDAPI_HIDPI_H - -#ifdef HIDAPI_USE_DDK - -#include - -#else - -/* This part of the header mimics hidpi.h, - but only what is used by HIDAPI */ - -typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA; - -typedef struct _HIDP_CAPS -{ - USAGE Usage; - USAGE UsagePage; - USHORT InputReportByteLength; - USHORT OutputReportByteLength; - USHORT FeatureReportByteLength; - USHORT Reserved[17]; - - USHORT NumberLinkCollectionNodes; - - USHORT NumberInputButtonCaps; - USHORT NumberInputValueCaps; - USHORT NumberInputDataIndices; - - USHORT NumberOutputButtonCaps; - USHORT NumberOutputValueCaps; - USHORT NumberOutputDataIndices; - - USHORT NumberFeatureButtonCaps; - USHORT NumberFeatureValueCaps; - USHORT NumberFeatureDataIndices; -} HIDP_CAPS, *PHIDP_CAPS; - -#define HIDP_STATUS_SUCCESS 0x00110000 -#define HIDP_STATUS_INVALID_PREPARSED_DATA 0xc0110001 - -typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, PHIDP_CAPS caps); - -#endif - -#endif /* HIDAPI_HIDPI_H */ diff --git a/lib/hidapi/windows/hidapi_hidsdi.h b/lib/hidapi/windows/hidapi_hidsdi.h deleted file mode 100644 index 453f89973d6..00000000000 --- a/lib/hidapi/windows/hidapi_hidsdi.h +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - libusb/hidapi Team - - Copyright 2022, 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 . -********************************************************/ - -#ifndef HIDAPI_HIDSDI_H -#define HIDAPI_HIDSDI_H - -#ifdef HIDAPI_USE_DDK - -#include - -#else - -/* This part of the header mimics hidsdi.h, - but only what is used by HIDAPI */ - -typedef USHORT USAGE; - -#include "hidapi_hidpi.h" - -typedef struct _HIDD_ATTRIBUTES{ - ULONG Size; - USHORT VendorID; - USHORT ProductID; - USHORT VersionNumber; -} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; - -typedef struct _HIDP_PREPARSED_DATA * PHIDP_PREPARSED_DATA; - -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); -typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); -typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); -typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); -typedef BOOLEAN (__stdcall *HidD_GetInputReport_)(HANDLE handle, PVOID data, ULONG length); -typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); -typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data); -typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); -typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); - -#endif - -#endif /* HIDAPI_HIDSDI_H */ diff --git a/lib/hidapi/windows/hidapi_winapi.h b/lib/hidapi/windows/hidapi_winapi.h deleted file mode 100644 index 884712af867..00000000000 --- a/lib/hidapi/windows/hidapi_winapi.h +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************* - HIDAPI - Multi-Platform library for - communication with HID devices. - - libusb/hidapi Team - - Copyright 2022, 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 - * - * Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - */ - -#ifndef HIDAPI_WINAPI_H__ -#define HIDAPI_WINAPI_H__ - -#include - -#include "hidapi.h" - -#ifdef __cplusplus -extern "C" { -#endif - - /** @brief Get the container ID for a HID device. - - Since version 0.12.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 12, 0) - - This function returns the `DEVPKEY_Device_ContainerId` property of - the given device. This can be used to correlate different - interfaces/ports on the same hardware device. - - @ingroup API - @param dev A device handle returned from hid_open(). - @param guid The device's container ID on return. - - @returns - This function returns 0 on success and -1 on error. - */ - int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id); - -#ifdef __cplusplus -} -#endif - -#endif From 3af2941fd67fdd56a06a623abc57bae19b0e621b Mon Sep 17 00:00:00 2001 From: Taylor Richards Date: Sat, 25 Jul 2026 06:00:02 -0400 Subject: [PATCH 2/2] remove residual SDL_hid_exit call --- code/io/spacemouse.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/io/spacemouse.cpp b/code/io/spacemouse.cpp index 061f89a482d..c47e9f9e8aa 100644 --- a/code/io/spacemouse.cpp +++ b/code/io/spacemouse.cpp @@ -112,7 +112,6 @@ SpaceMouse::SpaceMouse(const SpaceMouseDefinition& definition, SDL_hid_device* d SpaceMouse::~SpaceMouse() { SDL_hid_close(m_deviceHandle); - SDL_hid_exit(); } const SpaceMouseMovement& SpaceMouse::getMovement() {