From aaf5f3d172db22dd3063a0347741e55b887a8b60 Mon Sep 17 00:00:00 2001 From: Ihor Dutchak Date: Tue, 18 May 2021 16:39:57 +0300 Subject: [PATCH] add libusb-specific hid_libusb_wrap_sys_device Rationale: on Android one must use UsbManager, to access any USB device. As a result, libraries like libusb can only use file descriptors that are provided by UsbManager. libusb has an API to use such file descriptors: hid_libusb_wrap_sys_device. Having hid_libusb_wrap_sys_device currently is the only way to make hidapi work on Android without root access and without custom Android builds. Relevant info: https://github.com/libusb/libusb/pull/830/files --- .github/workflows/builds.yml | 14 +- libusb/CMakeLists.txt | 3 +- libusb/Makefile.am | 2 +- libusb/hid.c | 460 ++++++++++++++++++++--------------- libusb/hidapi_libusb.h | 54 ++++ 5 files changed, 336 insertions(+), 197 deletions(-) create mode 100644 libusb/hidapi_libusb.h diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 2baac3256..f1bd56c69 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -47,7 +47,10 @@ jobs: - name: Check artifacts uses: andstor/file-existence-action@v1 with: - files: "install/shared/lib/libhidapi.dylib, install/shared/include/hidapi/hidapi.h, install/framework/lib/hidapi.framework/hidapi, install/framework/lib/hidapi.framework/Headers/hidapi.h" + files: "install/shared/lib/libhidapi.dylib, \ + install/shared/include/hidapi/hidapi.h, \ + install/framework/lib/hidapi.framework/hidapi, \ + install/framework/lib/hidapi.framework/Headers/hidapi.h" allow_failure: true ubuntu-cmake: @@ -76,7 +79,14 @@ jobs: - name: Check artifacts uses: andstor/file-existence-action@v1 with: - files: "install/shared/lib/libhidapi-libusb.so, install/shared/lib/libhidapi-hidraw.so, install/shared/include/hidapi/hidapi.h, install/static/lib/libhidapi-libusb.a, install/static/lib/libhidapi-hidraw.a, install/static/include/hidapi/hidapi.h" + files: "install/shared/lib/libhidapi-libusb.so, \ + install/shared/lib/libhidapi-hidraw.so, \ + install/shared/include/hidapi/hidapi.h, \ + install/shared/include/hidapi/hidapi_libusb.h, \ + install/static/lib/libhidapi-libusb.a, \ + install/static/lib/libhidapi-hidraw.a, \ + install/static/include/hidapi/hidapi.h, \ + install/static/include/hidapi/hidapi_libusb.h" allow_failure: true windows-cmake-msvc: diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt index 99e2a9623..c7873de08 100644 --- a/libusb/CMakeLists.txt +++ b/libusb/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR) add_library(hidapi_libusb ${HIDAPI_PUBLIC_HEADERS} hid.c + hidapi_libusb.h ) target_link_libraries(hidapi_libusb PUBLIC hidapi_include) @@ -23,7 +24,7 @@ set_target_properties(hidapi_libusb OUTPUT_NAME "hidapi-libusb" VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} - PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS}" + PUBLIC_HEADER "${HIDAPI_PUBLIC_HEADERS};hidapi_libusb.h" ) # compatibility with find_package() diff --git a/libusb/Makefile.am b/libusb/Makefile.am index 1da06bc16..6964ebbbc 100644 --- a/libusb/Makefile.am +++ b/libusb/Makefile.am @@ -29,6 +29,6 @@ libhidapi_la_LIBADD = $(LIBS_LIBUSB) endif hdrdir = $(includedir)/hidapi -hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h +hdr_HEADERS = $(top_srcdir)/hidapi/hidapi.h hidapi_libusb.h EXTRA_DIST = Makefile-manual diff --git a/libusb/hid.c b/libusb/hid.c index 4e297192a..756a5916c 100644 --- a/libusb/hid.c +++ b/libusb/hid.c @@ -49,7 +49,7 @@ #include #endif -#include "hidapi.h" +#include "hidapi_libusb.h" #if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__ @@ -569,12 +569,16 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, struct libusb_device_descriptor desc; struct libusb_config_descriptor *conf_desc = NULL; int j, k; - int interface_num = 0; int res = libusb_get_device_descriptor(dev, &desc); unsigned short dev_vid = desc.idVendor; unsigned short dev_pid = desc.idProduct; + if ((vendor_id != 0x0 && vendor_id != dev_vid) || + (product_id != 0x0 && product_id != dev_pid)) { + continue; + } + res = libusb_get_active_config_descriptor(dev, &conf_desc); if (res < 0) libusb_get_config_descriptor(dev, 0, &conf_desc); @@ -585,129 +589,124 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc; intf_desc = &intf->altsetting[k]; if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { - interface_num = intf_desc->bInterfaceNumber; - - /* Check the VID/PID against the arguments */ - if ((vendor_id == 0x0 || vendor_id == dev_vid) && - (product_id == 0x0 || product_id == dev_pid)) { - struct hid_device_info *tmp; + int interface_num = intf_desc->bInterfaceNumber; + struct hid_device_info *tmp; - /* VID/PID match. Create the record. */ - tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; - /* Fill out the record */ - cur_dev->next = NULL; - cur_dev->path = make_path(dev, interface_num, conf_desc->bConfigurationValue); + /* Fill out the record */ + cur_dev->next = NULL; + cur_dev->path = make_path(dev, interface_num, conf_desc->bConfigurationValue); - res = libusb_open(dev, &handle); + res = libusb_open(dev, &handle); - if (res >= 0) { + if (res >= 0) { #ifdef __ANDROID__ - /* There is (a potential) libusb Android backend, in which - device descriptor is not accurate up until the device is opened. - https://github.com/libusb/libusb/pull/874#discussion_r632801373 - A workaround is to re-read the descriptor again. - Even if it is not going to be accepted into libusb master, - having it here won't do any harm, since reading the device descriptor - is as cheap as copy 18 bytes of data. */ - libusb_get_device_descriptor(dev, &desc); + /* There is (a potential) libusb Android backend, in which + device descriptor is not accurate up until the device is opened. + https://github.com/libusb/libusb/pull/874#discussion_r632801373 + A workaround is to re-read the descriptor again. + Even if it is not going to be accepted into libusb master, + having it here won't do any harm, since reading the device descriptor + is as cheap as copy 18 bytes of data. */ + libusb_get_device_descriptor(dev, &desc); #endif - /* Serial Number */ - if (desc.iSerialNumber > 0) - cur_dev->serial_number = - get_usb_string(handle, desc.iSerialNumber); + /* Serial Number */ + if (desc.iSerialNumber > 0) + cur_dev->serial_number = + get_usb_string(handle, desc.iSerialNumber); - /* Manufacturer and Product strings */ - if (desc.iManufacturer > 0) - cur_dev->manufacturer_string = - get_usb_string(handle, desc.iManufacturer); - if (desc.iProduct > 0) - cur_dev->product_string = - get_usb_string(handle, desc.iProduct); + /* Manufacturer and Product strings */ + if (desc.iManufacturer > 0) + cur_dev->manufacturer_string = + get_usb_string(handle, desc.iManufacturer); + if (desc.iProduct > 0) + cur_dev->product_string = + get_usb_string(handle, desc.iProduct); #ifdef INVASIVE_GET_USAGE { - /* - This section is removed because it is too - invasive on the system. Getting a Usage Page - and Usage requires parsing the HID Report - descriptor. Getting a HID Report descriptor - involves claiming the interface. Claiming the - interface involves detaching the kernel driver. - Detaching the kernel driver is hard on the system - because it will unclaim interfaces (if another - app has them claimed) and the re-attachment of - the driver will sometimes change /dev entry names. - It is for these reasons that this section is - #if 0. For composite devices, use the interface - field in the hid_device_info struct to distinguish - between interfaces. */ - unsigned char data[256]; + /* + This section is removed because it is too + invasive on the system. Getting a Usage Page + and Usage requires parsing the HID Report + descriptor. Getting a HID Report descriptor + involves claiming the interface. Claiming the + interface involves detaching the kernel driver. + Detaching the kernel driver is hard on the system + because it will unclaim interfaces (if another + app has them claimed) and the re-attachment of + the driver will sometimes change /dev entry names. + It is for these reasons that this section is + #if 0. For composite devices, use the interface + field in the hid_device_info struct to distinguish + between interfaces. */ + unsigned char data[256]; #ifdef DETACH_KERNEL_DRIVER - int detached = 0; - /* Usage Page and Usage */ - res = libusb_kernel_driver_active(handle, interface_num); - if (res == 1) { - res = libusb_detach_kernel_driver(handle, interface_num); - if (res < 0) - LOG("Couldn't detach kernel driver, even though a kernel driver was attached."); - else - detached = 1; - } + int detached = 0; + /* Usage Page and Usage */ + res = libusb_kernel_driver_active(handle, interface_num); + if (res == 1) { + res = libusb_detach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't detach kernel driver, even though a kernel driver was attached.\n"); + else + detached = 1; + } #endif - res = libusb_claim_interface(handle, interface_num); + res = libusb_claim_interface(handle, interface_num); + if (res >= 0) { + /* Get the HID Report Descriptor. */ + res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); if (res >= 0) { - /* Get the HID Report Descriptor. */ - res = libusb_control_transfer(handle, LIBUSB_ENDPOINT_IN|LIBUSB_RECIPIENT_INTERFACE, LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_REPORT << 8)|interface_num, 0, data, sizeof(data), 5000); - if (res >= 0) { - unsigned short page=0, usage=0; - /* Parse the usage and usage page - out of the report descriptor. */ - get_usage(data, res, &page, &usage); - cur_dev->usage_page = page; - cur_dev->usage = usage; - } - else - LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); - - /* Release the interface */ - res = libusb_release_interface(handle, interface_num); - if (res < 0) - LOG("Can't release the interface.\n"); + unsigned short page=0, usage=0; + /* Parse the usage and usage page + out of the report descriptor. */ + get_usage(data, res, &page, &usage); + cur_dev->usage_page = page; + cur_dev->usage = usage; } else - LOG("Can't claim interface %d\n", res); + LOG("libusb_control_transfer() for getting the HID report failed with %d\n", res); + + /* Release the interface */ + res = libusb_release_interface(handle, interface_num); + if (res < 0) + LOG("Can't release the interface.\n"); + } + else + LOG("Can't claim interface %d\n", res); #ifdef DETACH_KERNEL_DRIVER - /* Re-attach kernel driver if necessary. */ - if (detached) { - res = libusb_attach_kernel_driver(handle, interface_num); - if (res < 0) - LOG("Couldn't re-attach kernel driver.\n"); - } + /* Re-attach kernel driver if necessary. */ + if (detached) { + res = libusb_attach_kernel_driver(handle, interface_num); + if (res < 0) + LOG("Couldn't re-attach kernel driver.\n"); + } #endif } #endif /* INVASIVE_GET_USAGE */ - libusb_close(handle); - } - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; + libusb_close(handle); + } + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; - /* Release Number */ - cur_dev->release_number = desc.bcdDevice; + /* Release Number */ + cur_dev->release_number = desc.bcdDevice; - /* Interface Number */ - cur_dev->interface_number = interface_num; - } + /* Interface Number */ + cur_dev->interface_number = interface_num; } } /* altsettings */ } /* interfaces */ @@ -910,13 +909,95 @@ static void *read_thread(void *param) } +static int hidapi_initialize_device(hid_device *dev, const struct libusb_interface_descriptor *intf_desc) +{ + int i =0; + int res = 0; + struct libusb_device_descriptor desc; + libusb_get_device_descriptor(libusb_get_device(dev->device_handle), &desc); + +#ifdef DETACH_KERNEL_DRIVER + /* Detach the kernel driver, but only if the + device is managed by the kernel */ + dev->is_driver_detached = 0; + if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { + res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + libusb_close(dev->device_handle); + LOG("Unable to detach Kernel Driver\n"); + return 0; + } + else { + dev->is_driver_detached = 1; + LOG("Driver successfully detached from kernel.\n"); + } + } +#endif + res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + return 0; + } + + /* Store off the string descriptor indexes */ + dev->manufacturer_index = desc.iManufacturer; + dev->product_index = desc.iProduct; + dev->serial_index = desc.iSerialNumber; + + /* Store off the interface number */ + dev->interface = intf_desc->bInterfaceNumber; + + dev->input_endpoint = 0; + dev->input_ep_max_packet_size = 0; + dev->output_endpoint = 0; + + /* Find the INPUT and OUTPUT endpoints. An + OUTPUT endpoint is not required. */ + for (i = 0; i < intf_desc->bNumEndpoints; i++) { + const struct libusb_endpoint_descriptor *ep + = &intf_desc->endpoint[i]; + + /* Determine the type and direction of this + endpoint. */ + int is_interrupt = + (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) + == LIBUSB_TRANSFER_TYPE_INTERRUPT; + int is_output = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_OUT; + int is_input = + (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) + == LIBUSB_ENDPOINT_IN; + + /* Decide whether to use it for input or output. */ + if (dev->input_endpoint == 0 && + is_interrupt && is_input) { + /* Use this endpoint for INPUT */ + dev->input_endpoint = ep->bEndpointAddress; + dev->input_ep_max_packet_size = ep->wMaxPacketSize; + } + if (dev->output_endpoint == 0 && + is_interrupt && is_output) { + /* Use this endpoint for OUTPUT */ + dev->output_endpoint = ep->bEndpointAddress; + } + } + + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + return 1; +} + + hid_device * HID_API_EXPORT hid_open_path(const char *path) { hid_device *dev = NULL; - libusb_device **devs; - libusb_device *usb_dev; - int res; + libusb_device **devs = NULL; + libusb_device *usb_dev = NULL; + int res = 0; int d = 0; int good_open = 0; @@ -926,19 +1007,16 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) dev = new_hid_device(); libusb_get_device_list(usb_context, &devs); - while ((usb_dev = devs[d++]) != NULL) { - struct libusb_device_descriptor desc; + while ((usb_dev = devs[d++]) != NULL && !good_open) { struct libusb_config_descriptor *conf_desc = NULL; - int i,j,k; - libusb_get_device_descriptor(usb_dev, &desc); + int j,k; if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) continue; - for (j = 0; j < conf_desc->bNumInterfaces; j++) { + for (j = 0; j < conf_desc->bNumInterfaces && !good_open; j++) { const struct libusb_interface *intf = &conf_desc->interface[j]; - for (k = 0; k < intf->num_altsetting; k++) { - const struct libusb_interface_descriptor *intf_desc; - intf_desc = &intf->altsetting[k]; + for (k = 0; k < intf->num_altsetting && !good_open; k++) { + const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber, conf_desc->bConfigurationValue); if (!strcmp(dev_path, path)) { @@ -951,93 +1029,15 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) free(dev_path); break; } - good_open = 1; - -#ifdef __ANDROID__ - /* See remark in hid_enumerate */ - libusb_get_device_descriptor(usb_dev, &desc); -#endif - -#ifdef DETACH_KERNEL_DRIVER - /* Detach the kernel driver, but only if the - device is managed by the kernel */ - dev->is_driver_detached = 0; - if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { - res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber); - if (res < 0) { - libusb_close(dev->device_handle); - LOG("Unable to detach Kernel Driver\n"); - free(dev_path); - good_open = 0; - break; - } - else { - dev->is_driver_detached = 1; - LOG("Driver successfully detached from kernel.\n"); - } - } -#endif - res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); - if (res < 0) { - LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); - free(dev_path); + good_open = hidapi_initialize_device(dev, intf_desc); + if (!good_open) libusb_close(dev->device_handle); - good_open = 0; - break; - } - - /* Store off the string descriptor indexes */ - dev->manufacturer_index = desc.iManufacturer; - dev->product_index = desc.iProduct; - dev->serial_index = desc.iSerialNumber; - - /* Store off the interface number */ - dev->interface = intf_desc->bInterfaceNumber; - - /* Find the INPUT and OUTPUT endpoints. An - OUTPUT endpoint is not required. */ - for (i = 0; i < intf_desc->bNumEndpoints; i++) { - const struct libusb_endpoint_descriptor *ep - = &intf_desc->endpoint[i]; - - /* Determine the type and direction of this - endpoint. */ - int is_interrupt = - (ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) - == LIBUSB_TRANSFER_TYPE_INTERRUPT; - int is_output = - (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_OUT; - int is_input = - (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) - == LIBUSB_ENDPOINT_IN; - - /* Decide whether to use it for input or output. */ - if (dev->input_endpoint == 0 && - is_interrupt && is_input) { - /* Use this endpoint for INPUT */ - dev->input_endpoint = ep->bEndpointAddress; - dev->input_ep_max_packet_size = ep->wMaxPacketSize; - } - if (dev->output_endpoint == 0 && - is_interrupt && is_output) { - /* Use this endpoint for OUTPUT */ - dev->output_endpoint = ep->bEndpointAddress; - } - } - - pthread_create(&dev->thread, NULL, read_thread, dev); - - /* Wait here for the read thread to be initialized. */ - pthread_barrier_wait(&dev->barrier); - } free(dev_path); } } } libusb_free_config_descriptor(conf_desc); - } libusb_free_device_list(devs, 1); @@ -1054,6 +1054,80 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) } +HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys_dev, int interface_num) +{ +/* 0x01000107 is a LIBUSB_API_VERSION for 1.0.23 - version when libusb_wrap_sys_device was introduced */ +#if (!defined(HIDAPI_TARGET_LIBUSB_API_VERSION) || HIDAPI_TARGET_LIBUSB_API_VERSION >= 0x01000107) && (LIBUSB_API_VERSION >= 0x01000107) + hid_device *dev = NULL; + struct libusb_config_descriptor *conf_desc = NULL; + const struct libusb_interface_descriptor *selected_intf_desc = NULL; + int res = 0; + int j = 0, k = 0; + + if(hid_init() < 0) + return NULL; + + dev = new_hid_device(); + + res = libusb_wrap_sys_device(usb_context, sys_dev, &dev->device_handle); + if (res < 0) { + LOG("libusb_wrap_sys_device failed: %d %s\n", res, libusb_error_name(res)); + goto err; + } + + res = libusb_get_active_config_descriptor(libusb_get_device(dev->device_handle), &conf_desc); + if (res < 0) + libusb_get_config_descriptor(libusb_get_device(dev->device_handle), 0, &conf_desc); + + if (!conf_desc) { + LOG("Failed to get configuration descriptor: %d %s\n", res, libusb_error_name(res)); + goto err; + } + + /* find matching HID interface */ + for (j = 0; j < conf_desc->bNumInterfaces && !selected_intf_desc; j++) { + const struct libusb_interface *intf = &conf_desc->interface[j]; + for (k = 0; k < intf->num_altsetting; k++) { + const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { + if (interface_num < 0 || interface_num == intf_desc->bInterfaceNumber) { + selected_intf_desc = intf_desc; + break; + } + } + } + } + + if (!selected_intf_desc) { + if (interface_num < 0) { + LOG("Sys USB device doesn't contain a HID interface\n"); + } + else { + LOG("Sys USB device doesn't contain a HID interface with number %d\n", interface_num); + } + goto err; + } + + if (!hidapi_initialize_device(dev, selected_intf_desc)) + goto err; + + return dev; + +err: + if (conf_desc) + libusb_free_config_descriptor(conf_desc); + if (dev->device_handle) + libusb_close(dev->device_handle); + free_hid_device(dev); +#else + (void)sys_dev; + (void)interface_num; + LOG("libusb_wrap_sys_device is not available\n"); +#endif + return NULL; +} + + int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { int res; diff --git a/libusb/hidapi_libusb.h b/libusb/hidapi_libusb.h new file mode 100644 index 000000000..1f56c2c51 --- /dev/null +++ b/libusb/hidapi_libusb.h @@ -0,0 +1,54 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + libusb/hidapi Team + + Copyright 2021, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_LIBUSB_H__ +#define HIDAPI_LIBUSB_H__ + +#include + +#include "hidapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + + /** @brief Open a HID device using libusb_wrap_sys_device. + See https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html#ga98f783e115ceff4eaf88a60e6439563c, + for details on libusb_wrap_sys_device. + + @ingroup API + @param sys_dev Platform-specific file descriptor that can be recognised by libusb. + @param interface_num USB interface number of the device to be used as HID interface. + Pass -1 to select first HID interface of the device. + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys_dev, int interface_num); + +#ifdef __cplusplus +} +#endif + +#endif