diff --git a/hid_disabled.go b/hid_disabled.go index cd78e2f..89c2c9a 100644 --- a/hid_disabled.go +++ b/hid_disabled.go @@ -95,6 +95,24 @@ func (dev *Device) GetInputReport(b []byte) (int, error) { return 0, ErrUnsupportedPlatform } +// SendOutputReport sends a output report to a HID device +// +// Output 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 SendOutputReport() 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 +// SendOutputReport(): 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. +func (dev *Device) SendOutputReport(b []byte) (int, error) { + return 0, ErrUnsupportedPlatform +} + // SetNonblocking sets the device handle to be non-blocking. // // In non-blocking mode calls to Read() will return diff --git a/hid_enabled.go b/hid_enabled.go index 7c82ffd..d3d4339 100644 --- a/hid_enabled.go +++ b/hid_enabled.go @@ -316,7 +316,7 @@ readAgain: return 0, ErrDeviceClosed } // Device not closed, some other error occurred - message := C.hid_error(device) + message := C.hid_read_error(device) if message == nil { return 0, errors.New("hidapi: unknown failure") } @@ -363,7 +363,7 @@ readAgain: return 0, ErrDeviceClosed } // Device not closed, some other error occurred - message := C.hid_error(device) + message := C.hid_read_error(device) if message == nil { return 0, errors.New("hidapi: unknown failure") } @@ -459,6 +459,56 @@ func (dev *Device) GetInputReport(b []byte) (int, error) { return read, nil } +// SendOutputReport sends a output report to a HID device +// +// Output 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 SendOutputReport() 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 +// SendOutputReport(): 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. +func (dev *Device) SendOutputReport(b []byte) (int, error) { + // Abort if nothing to write + if len(b) == 0 { + return 0, nil + } + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + + // Send the feature report + written := int(C.hid_send_output_report(device, (*C.uchar)(&b[0]), C.size_t(len(b)))) + if written == -1 { + // If the write failed, verify if closed or other error + dev.lock.Lock() + device = dev.device + dev.lock.Unlock() + + if device == nil { + return 0, ErrDeviceClosed + } + // Device not closed, some other error occurred + message := C.hid_error(device) + if message == nil { + return 0, errors.New("hidapi: unknown failure") + } + failure, _ := wcharTToString(message) + return 0, errors.New("hidapi: " + failure) + } + return written, nil +} + // SetNonblocking sets the device handle to be non-blocking. // // In non-blocking mode calls to Read() will return diff --git a/hid_windows.go b/hid_windows.go index c7a2b7a..759e9b3 100644 --- a/hid_windows.go +++ b/hid_windows.go @@ -47,3 +47,30 @@ func (dev *Device) GetContainerId() (*windows.GUID, error) { return guid, nil } + +const ( + INFINITE = -1 +) + +// SetWriteTimeout sets the timeout for Write operation. +// The default timeout is 1sec for winapi backend. +// In case if 1sec is not enough, on in case of multi-platform development, +// the recommended value is 5sec, e.g. to match (unconfigurable) 5sec timeout +// set for hidraw (linux kernel) implementation. +// When the timeout is set to 0, hid_write function becomes non-blocking and would exit immediately. +// When the timeout is set to INFINITE (-1), the function will not exit, +// until the write operation is performed or an error occurred. +func (dev *Device) SetWriteTimeout(timeout int32) error { + // Abort if device closed in between + dev.lock.Lock() + device := dev.device + dev.lock.Unlock() + + if device == nil { + return ErrDeviceClosed + } + + C.hid_winapi_set_write_timeout(device, C.ulong(timeout)) + + return nil +} diff --git a/hidapi/AUTHORS.txt b/hidapi/AUTHORS.txt index 7193d71..7c2a035 100644 --- a/hidapi/AUTHORS.txt +++ b/hidapi/AUTHORS.txt @@ -11,7 +11,7 @@ Ludovic Rousseau : Correctness fixes libusb/hidapi Team: - Development/maintainance since June 4th 2019 + Development/maintenance 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/hidapi/README.md b/hidapi/README.md index 257b9f3..cf5e6e0 100644 --- a/hidapi/README.md +++ b/hidapi/README.md @@ -2,10 +2,10 @@ | CI instance | Status | |----------------------|--------| -| `Linux/macOS/Windows (master)` | [![GitHub Builds](https://github.com/libusb/hidapi/workflows/GitHub%20Builds/badge.svg?branch=master)](https://github.com/libusb/hidapi/actions/workflows/builds.yml?query=branch%3Amaster) | +| `Linux/macOS/Windows (master)` | [![GitHub Builds](https://github.com/libusb/hidapi/actions/workflows/builds.yml/badge.svg?branch=master)](https://github.com/libusb/hidapi/actions/workflows/builds.yml?query=branch%3Amaster) | | `Windows (master)` | [![Build status](https://ci.appveyor.com/api/projects/status/xfmr5fo8w0re8ded/branch/master?svg=true)](https://ci.appveyor.com/project/libusb/hidapi/branch/master) | | `BSD, last build (branch/PR)` | [![builds.sr.ht status](https://builds.sr.ht/~z3ntu/hidapi.svg)](https://builds.sr.ht/~z3ntu/hidapi) | -| `Coverity Scan (last)` | ![Coverity Scan](https://scan.coverity.com/projects/583/badge.svg) | +| `Coverity Scan (last)` | [![Coverity Scan](https://scan.coverity.com/projects/583/badge.svg)](https://scan.coverity.com/projects/hidapi) | HIDAPI is a multi-platform library which allows an application to interface with USB and Bluetooth HID-Class devices on Windows, Linux, FreeBSD, and macOS. diff --git a/hidapi/VERSION b/hidapi/VERSION index 0548fb4..7092c7c 100644 --- a/hidapi/VERSION +++ b/hidapi/VERSION @@ -1 +1 @@ -0.14.0 \ No newline at end of file +0.15.0 \ No newline at end of file diff --git a/hidapi/hidapi.h b/hidapi/hidapi.h index 744ceb0..c101d55 100644 --- a/hidapi/hidapi.h +++ b/hidapi/hidapi.h @@ -52,7 +52,7 @@ @ingroup API */ -#define HID_API_VERSION_MINOR 14 +#define HID_API_VERSION_MINOR 15 /** @brief Static/compile-time patch version of the library. @ingroup API @@ -307,9 +307,9 @@ extern "C" { 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). + hid_write() will send the data on the first interrupt OUT + endpoint, if one exists. If it does not the behaviour is as + @ref hid_send_output_report @ingroup API @param dev A device handle returned from hid_open(). @@ -341,9 +341,11 @@ extern "C" { @returns This function returns the actual number of bytes read and -1 on error. - Call hid_error(dev) to get the failure reason. + Call hid_read_error(dev) to get the failure reason. If no packet was available to be read within the timeout period, this function returns 0. + + @note This function doesn't change the buffer returned by the hid_error(dev). */ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); @@ -363,12 +365,36 @@ extern "C" { @returns This function returns the actual number of bytes read and -1 on error. - Call hid_error(dev) to get the failure reason. + Call hid_read_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. + + @note This function doesn't change the buffer returned by the hid_error(dev). */ int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length); + /** @brief Get a string describing the last error which occurred during hid_read/hid_read_timeout. + + Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0) + + This function is intended for logging/debugging purposes. + + This function guarantees to never return NULL for a valid @ref dev. + If there was no error in the last call to hid_read/hid_read_error - + the returned string clearly indicates that. + + Strings returned from hid_read_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. + + @ingroup API + @param dev A device handle. Shall never be NULL. + + @returns + A string describing the hid_read/hid_read_timeout error (if any). + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_read_error(hid_device *dev); + /** @brief Set the device handle to be non-blocking. In non-blocking mode calls to hid_read() will return @@ -445,6 +471,40 @@ extern "C" { */ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length); + /** @brief Send a Output report to the device. + + Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0) + + Output 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_output_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_output_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. + + This function sets the return value of hid_error(). + + @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. + + @see @ref hid_write + */ + int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device* dev, const 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) diff --git a/hidapi/libusb/hid.c b/hidapi/libusb/hid.c index 4ac3f86..bd7f227 100644 --- a/hidapi/libusb/hid.c +++ b/hidapi/libusb/hid.c @@ -37,7 +37,6 @@ #include #include #include -#include #include /* GNU / LibUSB */ @@ -55,66 +54,10 @@ #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; - } -} - +#ifndef HIDAPI_THREAD_MODEL_INCLUDE +#define HIDAPI_THREAD_MODEL_INCLUDE "hidapi_thread_pthread.h" #endif +#include HIDAPI_THREAD_MODEL_INCLUDE #ifdef __cplusplus extern "C" { @@ -176,10 +119,7 @@ struct hid_device_ { 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 */ + hidapi_thread_state thread_state; int shutdown_thread; int transfer_loop_finished; struct libusb_transfer *transfer; @@ -207,11 +147,12 @@ 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)); + if (!dev) + return NULL; + dev->blocking = 1; - pthread_mutex_init(&dev->mutex, NULL); - pthread_cond_init(&dev->condition, NULL); - pthread_barrier_init(&dev->barrier, NULL, 2); + hidapi_thread_state_init(&dev->thread_state); return dev; } @@ -219,9 +160,7 @@ static hid_device *new_hid_device(void) 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); + hidapi_thread_state_destroy(&dev->thread_state); hid_free_enumeration(dev->device_info); @@ -322,8 +261,16 @@ static int get_usage(uint8_t *report_descriptor, size_t size, //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; + if (data_len == 4) { /* Usages 5.5 / Usage Page 6.2.2.7 */ + *usage_page = get_bytes(report_descriptor, size, 2, i + 2); + usage_page_found = 1; + *usage = get_bytes(report_descriptor, size, 2, i); + usage_found = 1; + } + else { + *usage = get_bytes(report_descriptor, size, data_len, i); + usage_found = 1; + } //printf("Usage: %x\n", (uint32_t)*usage); } @@ -580,7 +527,7 @@ static int hid_get_report_descriptor_libusb(libusb_device_handle *handle, int in expected_report_descriptor_size = HID_API_MAX_REPORT_DESCRIPTOR_SIZE; /* Get the HID Report Descriptor. - See USB HID Specificatin, sectin 7.1.1 + See USB HID Specification, section 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) { @@ -747,6 +694,107 @@ static uint16_t get_report_descriptor_size_from_interface_descriptors(const stru return result; } +static int is_xbox360(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc) +{ + static const int xb360_iface_subclass = 93; + static const int xb360_iface_protocol = 1; /* Wired */ + static const int xb360w_iface_protocol = 129; /* Wireless */ + static const int supported_vendors[] = { + 0x0079, /* GPD Win 2 */ + 0x044f, /* Thrustmaster */ + 0x045e, /* Microsoft */ + 0x046d, /* Logitech */ + 0x056e, /* Elecom */ + 0x06a3, /* Saitek */ + 0x0738, /* Mad Catz */ + 0x07ff, /* Mad Catz */ + 0x0e6f, /* PDP */ + 0x0f0d, /* Hori */ + 0x1038, /* SteelSeries */ + 0x11c9, /* Nacon */ + 0x12ab, /* Unknown */ + 0x1430, /* RedOctane */ + 0x146b, /* BigBen */ + 0x1532, /* Razer Sabertooth */ + 0x15e4, /* Numark */ + 0x162e, /* Joytech */ + 0x1689, /* Razer Onza */ + 0x1949, /* Lab126, Inc. */ + 0x1bad, /* Harmonix */ + 0x20d6, /* PowerA */ + 0x24c6, /* PowerA */ + 0x2c22, /* Qanba */ + 0x2dc8, /* 8BitDo */ + 0x9886, /* ASTRO Gaming */ + }; + + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + intf_desc->bInterfaceSubClass == xb360_iface_subclass && + (intf_desc->bInterfaceProtocol == xb360_iface_protocol || + intf_desc->bInterfaceProtocol == xb360w_iface_protocol)) { + size_t i; + for (i = 0; i < sizeof(supported_vendors)/sizeof(supported_vendors[0]); ++i) { + if (vendor_id == supported_vendors[i]) { + return 1; + } + } + } + return 0; +} + +static int is_xboxone(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc) +{ + static const int xb1_iface_subclass = 71; + static const int xb1_iface_protocol = 208; + static const int supported_vendors[] = { + 0x044f, /* Thrustmaster */ + 0x045e, /* Microsoft */ + 0x0738, /* Mad Catz */ + 0x0e6f, /* PDP */ + 0x0f0d, /* Hori */ + 0x10f5, /* Turtle Beach */ + 0x1532, /* Razer Wildcat */ + 0x20d6, /* PowerA */ + 0x24c6, /* PowerA */ + 0x2dc8, /* 8BitDo */ + 0x2e24, /* Hyperkin */ + 0x3537, /* GameSir */ + }; + + if (intf_desc->bInterfaceNumber == 0 && + intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + intf_desc->bInterfaceSubClass == xb1_iface_subclass && + intf_desc->bInterfaceProtocol == xb1_iface_protocol) { + size_t i; + for (i = 0; i < sizeof(supported_vendors)/sizeof(supported_vendors[0]); ++i) { + if (vendor_id == supported_vendors[i]) { + return 1; + } + } + } + return 0; +} + +static int should_enumerate_interface(unsigned short vendor_id, const struct libusb_interface_descriptor *intf_desc) +{ +#if 0 + printf("Checking interface 0x%x %d/%d/%d/%d\n", vendor_id, intf_desc->bInterfaceNumber, intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol); +#endif + + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) + return 1; + + /* Also enumerate Xbox 360 controllers */ + if (is_xbox360(vendor_id, intf_desc)) + return 1; + + /* Also enumerate Xbox One controllers */ + if (is_xboxone(vendor_id, intf_desc)) + return 1; + + return 0; +} + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { libusb_device **devs; @@ -770,6 +818,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, int j, k; int res = libusb_get_device_descriptor(dev, &desc); + if (res < 0) + continue; + unsigned short dev_vid = desc.idVendor; unsigned short dev_pid = desc.idProduct; @@ -787,7 +838,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, 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) { + if (should_enumerate_interface(dev_vid, intf_desc)) { struct hid_device_info *tmp; res = libusb_open(dev, &handle); @@ -845,6 +896,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, libusb_close(handle); handle = NULL; } + break; } } /* altsettings */ } /* interfaces */ @@ -907,7 +959,7 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const return handle; } -static void read_callback(struct libusb_transfer *transfer) +static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer) { hid_device *dev = transfer->user_data; int res; @@ -920,13 +972,13 @@ static void read_callback(struct libusb_transfer *transfer) rpt->len = transfer->actual_length; rpt->next = NULL; - pthread_mutex_lock(&dev->mutex); + hidapi_thread_mutex_lock(&dev->thread_state); /* 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); + hidapi_thread_cond_signal(&dev->thread_state); } else { /* Find the end of the list and attach. */ @@ -945,7 +997,7 @@ static void read_callback(struct libusb_transfer *transfer) return_data(dev, NULL, 0); } } - pthread_mutex_unlock(&dev->mutex); + hidapi_thread_mutex_unlock(&dev->thread_state); } else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { dev->shutdown_thread = 1; @@ -989,7 +1041,7 @@ static void *read_thread(void *param) dev->device_handle, dev->input_endpoint, buf, - length, + (int)length, read_callback, dev, 5000/*timeout*/); @@ -1004,7 +1056,7 @@ static void *read_thread(void *param) } /* Notify the main thread that the read thread is up and running. */ - pthread_barrier_wait(&dev->barrier); + hidapi_thread_barrier_wait(&dev->thread_state); /* Handle all the events. */ while (!dev->shutdown_thread) { @@ -1036,23 +1088,86 @@ static void *read_thread(void *param) 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); + hidapi_thread_mutex_lock(&dev->thread_state); + hidapi_thread_cond_broadcast(&dev->thread_state); + hidapi_thread_mutex_unlock(&dev->thread_state); /* 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 + cleaned up after the call to hidapi_thread_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 void init_xbox360(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, const struct libusb_config_descriptor *conf_desc) +{ + (void)conf_desc; + + if ((idVendor == 0x05ac && idProduct == 0x055b) /* Gamesir-G3w */ || + idVendor == 0x0f0d /* Hori Xbox controllers */) { + unsigned char data[20]; + + /* The HORIPAD FPS for Nintendo Switch requires this to enable input reports. + This VID/PID is also shared with other HORI controllers, but they all seem + to be fine with this as well. + */ + memset(data, 0, sizeof(data)); + libusb_control_transfer(device_handle, 0xC1, 0x01, 0x100, 0x0, data, sizeof(data), 100); + } +} -static int hidapi_initialize_device(hid_device *dev, int config_number, const struct libusb_interface_descriptor *intf_desc) +static void init_xboxone(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, const struct libusb_config_descriptor *conf_desc) +{ + static const int vendor_microsoft = 0x045e; + static const int xb1_iface_subclass = 71; + static const int xb1_iface_protocol = 208; + int j, k, res; + + (void)idProduct; + + 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->altsetting[k]; + if (intf_desc->bInterfaceClass == LIBUSB_CLASS_VENDOR_SPEC && + intf_desc->bInterfaceSubClass == xb1_iface_subclass && + intf_desc->bInterfaceProtocol == xb1_iface_protocol) { + int bSetAlternateSetting = 0; + + /* Newer Microsoft Xbox One controllers have a high speed alternate setting */ + if (idVendor == vendor_microsoft && + intf_desc->bInterfaceNumber == 0 && intf_desc->bAlternateSetting == 1) { + bSetAlternateSetting = 1; + } else if (intf_desc->bInterfaceNumber != 0 && intf_desc->bAlternateSetting == 0) { + bSetAlternateSetting = 1; + } + + if (bSetAlternateSetting) { + res = libusb_claim_interface(device_handle, intf_desc->bInterfaceNumber); + if (res < 0) { + LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); + continue; + } + + LOG("Setting alternate setting for VID/PID 0x%x/0x%x interface %d to %d\n", idVendor, idProduct, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting); + + res = libusb_set_interface_alt_setting(device_handle, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting); + if (res < 0) { + LOG("xbox init: can't set alt setting %d: %d\n", intf_desc->bInterfaceNumber, res); + } + + libusb_release_interface(device_handle, intf_desc->bInterfaceNumber); + } + } + } + } +} + +static int hidapi_initialize_device(hid_device *dev, const struct libusb_interface_descriptor *intf_desc, const struct libusb_config_descriptor *conf_desc) { int i =0; int res = 0; @@ -1089,13 +1204,23 @@ static int hidapi_initialize_device(hid_device *dev, int config_number, const st return 0; } + /* Initialize XBox 360 controllers */ + if (is_xbox360(desc.idVendor, intf_desc)) { + init_xbox360(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc); + } + + /* Initialize XBox One controllers */ + if (is_xboxone(desc.idVendor, intf_desc)) { + init_xboxone(dev->device_handle, desc.idVendor, desc.idProduct, conf_desc); + } + /* 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->config_number = conf_desc->bConfigurationValue; dev->interface = intf_desc->bInterfaceNumber; dev->report_descriptor_size = get_report_descriptor_size_from_interface_descriptors(intf_desc); @@ -1136,10 +1261,10 @@ static int hidapi_initialize_device(hid_device *dev, int config_number, const st } } - pthread_create(&dev->thread, NULL, read_thread, dev); + hidapi_thread_create(&dev->thread_state, read_thread, dev); /* Wait here for the read thread to be initialized. */ - pthread_barrier_wait(&dev->barrier); + hidapi_thread_barrier_wait(&dev->thread_state); return 1; } @@ -1158,19 +1283,32 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) return NULL; dev = new_hid_device(); + if (!dev) { + LOG("hid_open_path failed: Couldn't allocate memory\n"); + return NULL; + } libusb_get_device_list(usb_context, &devs); while ((usb_dev = devs[d++]) != NULL && !good_open) { + struct libusb_device_descriptor desc; struct libusb_config_descriptor *conf_desc = NULL; int j,k; - if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) + res = libusb_get_device_descriptor(usb_dev, &desc); + if (res < 0) + continue; + + res = libusb_get_active_config_descriptor(usb_dev, &conf_desc); + if (res < 0) + libusb_get_config_descriptor(usb_dev, 0, &conf_desc); + if (!conf_desc) 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) { + if (should_enumerate_interface(desc.idVendor, intf_desc)) { char dev_path[64]; get_path(&dev_path, usb_dev, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); if (!strcmp(dev_path, path)) { @@ -1182,7 +1320,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) LOG("can't open device\n"); break; } - good_open = hidapi_initialize_device(dev, conf_desc->bConfigurationValue, intf_desc); + good_open = hidapi_initialize_device(dev, intf_desc, conf_desc); if (!good_open) libusb_close(dev->device_handle); } @@ -1220,6 +1358,10 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys return NULL; dev = new_hid_device(); + if (!dev) { + LOG("libusb_wrap_sys_device failed: Couldn't allocate memory\n"); + return NULL; + } res = libusb_wrap_sys_device(usb_context, sys_dev, &dev->device_handle); if (res < 0) { @@ -1260,7 +1402,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_libusb_wrap_sys_device(intptr_t sys goto err; } - if (!hidapi_initialize_device(dev, conf_desc->bConfigurationValue, selected_intf_desc)) + if (!hidapi_initialize_device(dev, selected_intf_desc, conf_desc)) goto err; return dev; @@ -1286,6 +1428,11 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t int report_number; int skipped_report_id = 0; + if (dev->output_endpoint <= 0) { + /* No interrupt out endpoint. Use the Control Endpoint */ + return hid_send_output_report(dev, data, length); + } + if (!data || (length ==0)) { return -1; } @@ -1298,42 +1445,21 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t skipped_report_id = 1; } + /* Use the interrupt out endpoint */ + int actual_length; + res = libusb_interrupt_transfer(dev->device_handle, + dev->output_endpoint, + (unsigned char*)data, + (int)length, + &actual_length, 1000); - 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 (res < 0) + return -1; - if (skipped_report_id) - actual_length++; + if (skipped_report_id) + actual_length++; - return actual_length; - } + return actual_length; } /* Helper function, to simplify hid_read(). @@ -1349,13 +1475,13 @@ static int return_data(hid_device *dev, unsigned char *data, size_t length) dev->input_reports = rpt->next; free(rpt->data); free(rpt); - return len; + return (int)len; } static void cleanup_mutex(void *param) { hid_device *dev = param; - pthread_mutex_unlock(&dev->mutex); + hidapi_thread_mutex_unlock(&dev->thread_state); } @@ -1371,8 +1497,8 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t /* 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); + hidapi_thread_mutex_lock(&dev->thread_state); + hidapi_thread_cleanup_push(cleanup_mutex, dev); bytes_read = -1; @@ -1393,7 +1519,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t if (milliseconds == -1) { /* Blocking */ while (!dev->input_reports && !dev->shutdown_thread) { - pthread_cond_wait(&dev->condition, &dev->mutex); + hidapi_thread_cond_wait(&dev->thread_state); } if (dev->input_reports) { bytes_read = return_data(dev, data, length); @@ -1402,17 +1528,12 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t 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; - } + hidapi_timespec ts; + hidapi_thread_gettime(&ts); + hidapi_thread_addtime(&ts, milliseconds); while (!dev->input_reports && !dev->shutdown_thread) { - res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); + res = hidapi_thread_cond_timedwait(&dev->thread_state, &ts); if (res == 0) { if (dev->input_reports) { bytes_read = return_data(dev, data, length); @@ -1423,7 +1544,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t or the read thread was shutdown. Run the loop again (ie: don't break). */ } - else if (res == ETIMEDOUT) { + else if (res == HIDAPI_THREAD_TIMED_OUT) { /* Timed out. */ bytes_read = 0; break; @@ -1441,17 +1562,26 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t } ret: - pthread_mutex_unlock(&dev->mutex); - pthread_cleanup_pop(0); + hidapi_thread_mutex_unlock(&dev->thread_state); + hidapi_thread_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); } + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev) +{ + (void)dev; + return L"hid_read_error is not implemented yet"; +} + + int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) { dev->blocking = !nonblock; @@ -1487,7 +1617,7 @@ int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char if (skipped_report_id) length++; - return length; + return (int)length; } int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) @@ -1520,6 +1650,36 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, return res; } +int HID_API_EXPORT hid_send_output_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*/, + (2/*HID output*/ << 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_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) { int res = -1; @@ -1560,7 +1720,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) libusb_cancel_transfer(dev->transfer); /* Wait for read_thread() to end. */ - pthread_join(dev->thread, NULL); + hidapi_thread_join(&dev->thread_state); /* Clean up the Transfer objects allocated in read_thread(). */ free(dev->transfer->buffer); @@ -1583,11 +1743,11 @@ void HID_API_EXPORT hid_close(hid_device *dev) libusb_close(dev->device_handle); /* Clear out the queue of received reports. */ - pthread_mutex_lock(&dev->mutex); + hidapi_thread_mutex_lock(&dev->thread_state); while (dev->input_reports) { return_data(dev, NULL, 0); } - pthread_mutex_unlock(&dev->mutex); + hidapi_thread_mutex_unlock(&dev->thread_state); free_hid_device(dev); } @@ -1811,7 +1971,7 @@ uint16_t get_usb_code_for_current_locale(void) return 0x0; /* Make a copy of the current locale string. */ - strncpy(search_string, locale, sizeof(search_string)); + strncpy(search_string, locale, sizeof(search_string)-1); search_string[sizeof(search_string)-1] = '\0'; /* Chop off the encoding part, and make it lower case. */ diff --git a/hidapi/libusb/hidapi_thread_pthread.h b/hidapi/libusb/hidapi_thread_pthread.h new file mode 100644 index 0000000..0abe733 --- /dev/null +++ b/hidapi/libusb/hidapi_thread_pthread.h @@ -0,0 +1,174 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + libusb/hidapi Team + + Sam Lantinga + + Copyright 2023, 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 . +********************************************************/ + +#include + +#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 + +#define HIDAPI_THREAD_TIMED_OUT ETIMEDOUT + +typedef struct timespec hidapi_timespec; + +typedef struct +{ + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + +} hidapi_thread_state; + +static void hidapi_thread_state_init(hidapi_thread_state *state) +{ + pthread_mutex_init(&state->mutex, NULL); + pthread_cond_init(&state->condition, NULL); + pthread_barrier_init(&state->barrier, NULL, 2); +} + +static void hidapi_thread_state_destroy(hidapi_thread_state *state) +{ + pthread_barrier_destroy(&state->barrier); + pthread_cond_destroy(&state->condition); + pthread_mutex_destroy(&state->mutex); +} + +#define hidapi_thread_cleanup_push pthread_cleanup_push +#define hidapi_thread_cleanup_pop pthread_cleanup_pop + +static void hidapi_thread_mutex_lock(hidapi_thread_state *state) +{ + pthread_mutex_lock(&state->mutex); +} + +static void hidapi_thread_mutex_unlock(hidapi_thread_state *state) +{ + pthread_mutex_unlock(&state->mutex); +} + +static void hidapi_thread_cond_wait(hidapi_thread_state *state) +{ + pthread_cond_wait(&state->condition, &state->mutex); +} + +static int hidapi_thread_cond_timedwait(hidapi_thread_state *state, hidapi_timespec *ts) +{ + return pthread_cond_timedwait(&state->condition, &state->mutex, ts); +} + +static void hidapi_thread_cond_signal(hidapi_thread_state *state) +{ + pthread_cond_signal(&state->condition); +} + +static void hidapi_thread_cond_broadcast(hidapi_thread_state *state) +{ + pthread_cond_broadcast(&state->condition); +} + +static void hidapi_thread_barrier_wait(hidapi_thread_state *state) +{ + pthread_barrier_wait(&state->barrier); +} + +static void hidapi_thread_create(hidapi_thread_state *state, void *(*func)(void*), void *func_arg) +{ + pthread_create(&state->thread, NULL, func, func_arg); +} + +static void hidapi_thread_join(hidapi_thread_state *state) +{ + pthread_join(state->thread, NULL); +} + +static void hidapi_thread_gettime(hidapi_timespec *ts) +{ + clock_gettime(CLOCK_REALTIME, ts); +} + +static void hidapi_thread_addtime(hidapi_timespec *ts, int milliseconds) +{ + ts->tv_sec += milliseconds / 1000; + ts->tv_nsec += (milliseconds % 1000) * 1000000; + if (ts->tv_nsec >= 1000000000L) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000L; + } +} diff --git a/hidapi/linux/hid.c b/hidapi/linux/hid.c index a499f04..148e488 100644 --- a/hidapi/linux/hid.c +++ b/hidapi/linux/hid.c @@ -61,17 +61,21 @@ #endif -// HIDIOCGINPUT is not defined in Linux kernel headers < 5.11. -// This definition is from hidraw.h in Linux >= 5.11. +// HIDIOCGINPUT and HIDIOCSOUTPUT are not defined in Linux kernel headers < 5.11. +// These definitions are 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 +#ifndef HIDIOCSOUTPUT +#define HIDIOCSOUTPUT(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x0B, len) +#endif struct hid_device_ { int device_handle; int blocking; wchar_t *last_error_str; + wchar_t *last_read_error_str; struct hid_device_info* device_info; }; @@ -94,6 +98,7 @@ static hid_device *new_hid_device(void) dev->device_handle = -1; dev->blocking = 1; dev->last_error_str = NULL; + dev->last_read_error_str = NULL; dev->device_info = NULL; return dev; @@ -192,7 +197,7 @@ static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) * 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) +static int get_hid_item_size(const __u8 *report_descriptor, __u32 size, unsigned int pos, int *data_len, int *key_size) { int key = report_descriptor[pos]; int size_code; @@ -248,7 +253,7 @@ static int get_hid_item_size(__u8 *report_descriptor, unsigned int pos, __u32 si * 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) +static __u32 get_hid_report_bytes(const __u8 *rpt, size_t len, size_t num_bytes, size_t cur) { /* Return if there aren't enough bytes. */ if (cur + num_bytes >= len) @@ -271,6 +276,60 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_ return 0; } +/* + * Iterates until the end of a Collection. + * Assumes that *pos is exactly at the beginning of a Collection. + * Skips all nested Collection, i.e. iterates until the end of current level Collection. + * + * The return value is non-0 when an end of current Collection is found, + * 0 when error is occured (broken Descriptor, end of a Collection is found before its begin, + * or no Collection is found at all). + */ +static int hid_iterate_over_collection(const __u8 *report_descriptor, __u32 size, unsigned int *pos, int *data_len, int *key_size) +{ + int collection_level = 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, size, *pos, data_len, key_size)) + return 0; /* malformed report */ + + switch (key_cmd) { + case 0xa0: /* Collection 6.2.2.4 (Main) */ + collection_level++; + break; + case 0xc0: /* End Collection 6.2.2.4 (Main) */ + collection_level--; + break; + } + + if (collection_level < 0) { + /* Broken descriptor or someone is using this function wrong, + * i.e. should be called exactly at the collection start */ + return 0; + } + + if (collection_level == 0) { + /* Found it! + * Also possible when called not at the collection start, but should not happen if used correctly */ + return 1; + } + + *pos += *data_len + *key_size; + } + + return 0; /* Did not find the end of a Collection */ +} + +struct hid_usage_iterator { + unsigned int pos; + int usage_page_found; + unsigned short usage_page; +}; + /* * Retrieves the device's Usage Page and Usage from the report descriptor. * The algorithm returns the current Usage Page/Usage pair whenever a new @@ -288,70 +347,71 @@ static __u32 get_hid_report_bytes(__u8 *rpt, size_t len, size_t num_bytes, size_ * 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) +static int get_next_hid_usage(const __u8 *report_descriptor, __u32 size, struct hid_usage_iterator *ctx, 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; + int initial = ctx->pos == 0; /* Used to handle case where no top-level application collection is defined */ - /* 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]; + while (ctx->pos < size) { + int key = report_descriptor[ctx->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)) + if (!get_hid_item_size(report_descriptor, size, ctx->pos, &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); + ctx->usage_page = get_hid_report_bytes(report_descriptor, size, data_len, ctx->pos); + ctx->usage_page_found = 1; break; case 0x8: /* Usage 6.2.2.8 (Local) */ - *usage = get_hid_report_bytes(report_descriptor, size, data_len, *pos); - usage_found = 1; + if (data_len == 4) { /* Usages 5.5 / Usage Page 6.2.2.7 */ + ctx->usage_page = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos + 2); + ctx->usage_page_found = 1; + *usage = get_hid_report_bytes(report_descriptor, size, 2, ctx->pos); + usage_found = 1; + } + else { + *usage = get_hid_report_bytes(report_descriptor, size, data_len, ctx->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; + if (!hid_iterate_over_collection(report_descriptor, size, &ctx->pos, &data_len, &key_size)) { + return -1; + } - /* Usage is a Local Item, unset it */ - usage_found = 0; - break; + /* A pair is valid - to be reported when Collection is found */ + if (usage_found && ctx->usage_page_found) { + *usage_page = ctx->usage_page; + return 0; + } - 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; + ctx->pos += data_len + key_size; } /* 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) + if (initial && usage_found && ctx->usage_page_found) { + *usage_page = ctx->usage_page; 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. + * When using this form, /device/report_descriptor, elevated privileges are not required. */ static int get_hid_report_descriptor(const char *rpt_path, struct hidraw_report_descriptor *rpt_desc) { @@ -388,6 +448,8 @@ static int get_hid_report_descriptor_from_sysfs(const char *sysfs_path, struct h /* Construct /device/report_descriptor */ size_t rpt_path_len = strlen(sysfs_path) + 25 + 1; char* rpt_path = (char*) calloc(1, rpt_path_len); + if (!rpt_path) + return -1; snprintf(rpt_path, rpt_path_len, "%s/device/report_descriptor", sysfs_path); res = get_hid_report_descriptor(rpt_path, rpt_desc); @@ -512,6 +574,11 @@ static int parse_uevent_info(const char *uevent, unsigned *bus_type, char **serial_number_utf8, char **product_name_utf8) { char tmp[1024]; + + if (!uevent) { + return 0; + } + size_t uevent_len = strlen(uevent); if (uevent_len > sizeof(tmp) - 1) uevent_len = sizeof(tmp) - 1; @@ -719,15 +786,24 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device } /* Usage Page and Usage */ - result = get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc); + + if (sysfs_path) { + result = get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc); + } + else { + result = -1; + } + if (result >= 0) { unsigned short page = 0, usage = 0; - unsigned int pos = 0; + struct hid_usage_iterator usage_iterator; + memset(&usage_iterator, 0, sizeof(usage_iterator)); + /* * 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)) { + if (!get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &page, &usage)) { cur_dev->usage_page = page; cur_dev->usage = usage; } @@ -736,13 +812,13 @@ static struct hid_device_info * create_device_info_for_device(struct udev_device * 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)) { + while (!get_next_hid_usage(report_desc.value, report_desc.size, &usage_iterator, &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; + break; cur_dev->next = tmp; cur_dev = tmp; @@ -787,6 +863,7 @@ static struct hid_device_info * create_device_info_for_hid_device(hid_device *de /* Create the udev object */ udev = udev_new(); if (!udev) { + errno = ENOMEM; register_device_error(dev, "Couldn't create udev context"); return NULL; } @@ -799,6 +876,7 @@ static struct hid_device_info * create_device_info_for_hid_device(hid_device *de if (!root) { /* TODO: have a better error reporting via create_device_info_for_device */ + errno = EIO; register_device_error(dev, "Couldn't create hid_device_info"); } @@ -905,7 +983,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, } cur_dev = tmp; - /* move the pointer to the tail of returnd list */ + /* move the pointer to the tail of returned list */ while (cur_dev->next != NULL) { cur_dev = cur_dev->next; } @@ -994,6 +1072,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) dev = new_hid_device(); if (!dev) { + errno = ENOMEM; register_global_error("Couldn't allocate memory"); return NULL; } @@ -1006,8 +1085,8 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) /* 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_global_error_format("ioctl(GRDESCSIZE) error for '%s', not a HIDRAW device?: %s", path, strerror(errno)); + hid_close(dev); return NULL; } @@ -1028,7 +1107,7 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t if (!data || (length == 0)) { errno = EINVAL; - register_device_error(dev, strerror(errno)); + register_device_error(dev, "Zero buffer/length"); return -1; } @@ -1042,8 +1121,14 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { + if (!data || (length == 0)) { + errno = EINVAL; + register_error_str(&dev->last_read_error_str, "Zero buffer/length"); + return -1; + } + /* Set device error to none */ - register_device_error(dev, NULL); + register_error_str(&dev->last_read_error_str, NULL); int bytes_read; @@ -1067,7 +1152,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t } if (ret == -1) { /* Error */ - register_device_error(dev, strerror(errno)); + register_error_str(&dev->last_read_error_str, strerror(errno)); return ret; } else { @@ -1075,7 +1160,8 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t 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)"); + errno = EIO; + register_error_str(&dev->last_read_error_str, "hid_read_timeout: unexpected poll error (device disconnected)"); return -1; } } @@ -1086,7 +1172,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t if (errno == EAGAIN || errno == EINPROGRESS) bytes_read = 0; else - register_device_error(dev, strerror(errno)); + register_error_str(&dev->last_read_error_str, strerror(errno)); } return bytes_read; @@ -1097,6 +1183,13 @@ 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); } +HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev) +{ + if (dev->last_read_error_str == NULL) + return L"Success"; + return dev->last_read_error_str; +} + int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) { /* Do all non-blocking in userspace using poll(), since it looks @@ -1112,6 +1205,12 @@ int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char { int res; + if (!data || (length == 0)) { + errno = EINVAL; + register_device_error(dev, "Zero buffer/length"); + return -1; + } + register_device_error(dev, NULL); res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); @@ -1125,6 +1224,12 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, { int res; + if (!data || (length == 0)) { + errno = EINVAL; + register_device_error(dev, "Zero buffer/length"); + return -1; + } + register_device_error(dev, NULL); res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); @@ -1134,10 +1239,35 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, return res; } +int HID_API_EXPORT HID_API_CALL hid_send_output_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + + if (!data || (length == 0)) { + errno = EINVAL; + register_device_error(dev, "Zero buffer/length"); + return -1; + } + + register_device_error(dev, NULL); + + res = ioctl(dev->device_handle, HIDIOCSOUTPUT(length), data); + if (res < 0) + register_device_error_format(dev, "ioctl (SOUTPUT): %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; + if (!data || (length == 0)) { + errno = EINVAL; + register_device_error(dev, "Zero buffer/length"); + return -1; + } + register_device_error(dev, NULL); res = ioctl(dev->device_handle, HIDIOCGINPUT(length), data); @@ -1154,8 +1284,8 @@ void HID_API_EXPORT hid_close(hid_device *dev) close(dev->device_handle); - /* Free the device error message */ - register_device_error(dev, NULL); + free(dev->last_error_str); + free(dev->last_read_error_str); hid_free_enumeration(dev->device_info); @@ -1166,6 +1296,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) { if (!string || !maxlen) { + errno = EINVAL; register_device_error(dev, "Zero buffer/length"); return -1; } @@ -1190,6 +1321,7 @@ int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *st int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) { if (!string || !maxlen) { + errno = EINVAL; register_device_error(dev, "Zero buffer/length"); return -1; } @@ -1214,6 +1346,7 @@ int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) { if (!string || !maxlen) { + errno = EINVAL; register_device_error(dev, "Zero buffer/length"); return -1; } @@ -1237,7 +1370,10 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) { - if (!dev->device_info) { + if (dev->device_info) { + register_device_error(dev, NULL); + } + else { // Lazy initialize device_info dev->device_info = create_device_info_for_hid_device(dev); } @@ -1252,6 +1388,7 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index (void)string; (void)maxlen; + errno = ENOSYS; register_device_error(dev, "hid_get_indexed_string: not supported by hidraw"); return -1; @@ -1261,6 +1398,15 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size) { struct hidraw_report_descriptor rpt_desc; + + if (!buf || !buf_size) { + errno = EINVAL; + register_device_error(dev, "Zero buffer/length"); + return -1; + } + + register_device_error(dev, NULL); + int res = get_hid_report_descriptor_from_hidraw(dev, &rpt_desc); if (res < 0) { /* error already registered */ diff --git a/hidapi/mac/hid.c b/hidapi/mac/hid.c index 5c0914a..a91bc19 100644 --- a/hidapi/mac/hid.c +++ b/hidapi/mac/hid.c @@ -38,9 +38,6 @@ #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 @@ -57,15 +54,15 @@ static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrie { (void) attr; - if(count == 0) { + if (count == 0) { errno = EINVAL; return -1; } - if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + if (pthread_mutex_init(&barrier->mutex, 0) < 0) { return -1; } - if(pthread_cond_init(&barrier->cond, 0) < 0) { + if (pthread_cond_init(&barrier->cond, 0) < 0) { pthread_mutex_destroy(&barrier->mutex); return -1; } @@ -86,16 +83,18 @@ static int pthread_barrier_wait(pthread_barrier_t *barrier) { pthread_mutex_lock(&barrier->mutex); ++(barrier->count); - if(barrier->count >= barrier->trip_count) - { + if (barrier->count >= barrier->trip_count) { barrier->count = 0; - pthread_cond_broadcast(&barrier->cond); pthread_mutex_unlock(&barrier->mutex); + pthread_cond_broadcast(&barrier->cond); return 1; } - else - { - pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + else { + do { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + } + while (barrier->count != 0); + pthread_mutex_unlock(&barrier->mutex); return 0; } @@ -143,6 +142,7 @@ struct hid_device_ { pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ int shutdown_thread; wchar_t *last_error_str; + wchar_t *last_read_error_str; }; static hid_device *new_hid_device(void) @@ -164,6 +164,7 @@ static hid_device *new_hid_device(void) dev->device_info = NULL; dev->shutdown_thread = 0; dev->last_error_str = NULL; + dev->last_read_error_str = NULL; /* Thread objects */ pthread_mutex_init(&dev->mutex, NULL); @@ -196,6 +197,8 @@ static void free_hid_device(hid_device *dev) if (dev->source) CFRelease(dev->source); free(dev->input_report_buf); + free(dev->last_error_str); + free(dev->last_read_error_str); hid_free_enumeration(dev->device_info); /* Clean up the thread objects */ @@ -376,7 +379,7 @@ static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t buf[0] = 0; - if (str) { + if (str && CFGetTypeID(str) == CFStringGetTypeID()) { CFIndex str_len = CFStringGetLength(str); CFRange range; CFIndex used_buf_len; @@ -466,7 +469,7 @@ int HID_API_EXPORT hid_init(void) register_global_error(NULL); if (!hid_mgr) { - is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */ + is_macos_10_10_or_greater = (kCFCoreFoundationVersionNumber >= 1151.16); /* kCFCoreFoundationVersionNumber10_10 */ hid_darwin_set_open_exclusive(1); /* Backward compatibility */ return init_hid_manager(); } @@ -769,7 +772,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, } cur_dev = tmp; - /* move the pointer to the tail of returnd list */ + /* move the pointer to the tail of returned list */ while (cur_dev->next != NULL) { cur_dev = cur_dev->next; } @@ -1102,13 +1105,13 @@ static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char IOReturn res; unsigned char report_id; - register_device_error(dev, NULL); - if (!data || (length == 0)) { - register_device_error(dev, strerror(EINVAL)); + register_device_error(dev, "Zero buffer/length"); return -1; } + register_device_error(dev, NULL); + report_id = data[0]; if (report_id == 0x0) { @@ -1142,10 +1145,17 @@ static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data unsigned char *report = data; CFIndex report_length = length; IOReturn res = kIOReturnSuccess; - const unsigned char report_id = data[0]; + unsigned char report_id; + + if (!data || (length == 0)) { + register_device_error(dev, "Zero buffer/length"); + return -1; + } register_device_error(dev, NULL); + report_id = data[0]; + if (report_id == 0x0) { /* Not using numbered Reports. Don't send the report number. */ @@ -1188,7 +1198,9 @@ static int return_data(hid_device *dev, unsigned char *data, size_t length) 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); + if (data != NULL) { + memcpy(data, rpt->data, len); + } dev->input_reports = rpt->next; free(rpt->data); free(rpt); @@ -1235,13 +1247,19 @@ static int cond_timedwait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t } return 0; - } int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { int bytes_read = -1; + if (!data || (length == 0)) { + register_error_str(&dev->last_read_error_str, "Zero buffer/length"); + return -1; + } + + register_error_str(&dev->last_read_error_str, NULL); + /* Lock the access to the report list. */ pthread_mutex_lock(&dev->mutex); @@ -1255,7 +1273,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t /* Return if the device has been disconnected. */ if (dev->disconnected) { bytes_read = -1; - register_device_error(dev, "hid_read_timeout: device disconnected"); + register_error_str(&dev->last_read_error_str, "hid_read_timeout: device disconnected"); goto ret; } @@ -1264,7 +1282,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t has been an error. An error code of -1 should be returned. */ bytes_read = -1; - register_device_error(dev, "hid_read_timeout: thread shutdown"); + register_error_str(&dev->last_read_error_str, "hid_read_timeout: thread shutdown"); goto ret; } @@ -1278,7 +1296,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t 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"); + register_error_str(&dev->last_read_error_str, "hid_read_timeout: error waiting for more data"); bytes_read = -1; } } @@ -1302,7 +1320,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t } else if (res == ETIMEDOUT) { bytes_read = 0; } else { - register_device_error(dev, "hid_read_timeout: error waiting for more data"); + register_error_str(&dev->last_read_error_str, "hid_read_timeout: error waiting for more data"); bytes_read = -1; } } @@ -1322,6 +1340,13 @@ 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); } +HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev) +{ + if (dev->last_read_error_str == NULL) + return L"Success"; + return dev->last_read_error_str; +} + int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) { /* All Nonblocking operation is handled by the library. */ @@ -1340,6 +1365,11 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, return get_report(dev, kIOHIDReportTypeFeature, data, length); } +int HID_API_EXPORT hid_send_output_report(hid_device *dev, const unsigned char *data, size_t length) +{ + return set_report(dev, kIOHIDReportTypeOutput, 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); @@ -1459,7 +1489,10 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s } HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) { - if (!dev->device_info) { + if (dev->device_info) { + register_device_error(dev, NULL); + } + else { dev->device_info = create_device_info(dev->device_handle); if (!dev->device_info) { register_device_error(dev, "Failed to create hid_device_info"); @@ -1482,6 +1515,13 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id) { + if (!location_id) { + register_device_error(dev, "Location ID is NULL"); + return -1; + } + + register_device_error(dev, NULL); + int res = get_int_property(dev->device_handle, CFSTR(kIOHIDLocationIDKey)); if (res != 0) { *location_id = (uint32_t) res; @@ -1504,23 +1544,27 @@ int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void) 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; } int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size) { + if (!buf || !buf_size) { + register_device_error(dev, "Zero buffer/length"); + return -1; + } + + register_device_error(dev, NULL); + CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey)); if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) { CFDataRef report_descriptor = (CFDataRef) ref; const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor); - CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor); + const CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor); size_t copy_len = (size_t) descriptor_buf_len; if (descriptor_buf == NULL || descriptor_buf_len < 0) { - register_device_error(dev, "Zero buffer/length"); + register_device_error(dev, "Zero descriptor from device"); return -1; } diff --git a/hidapi/windows/hid.c b/hidapi/windows/hid.c index 6999691..e878547 100644 --- a/hidapi/windows/hid.c +++ b/hidapi/windows/hid.c @@ -69,6 +69,15 @@ typedef LONG NTSTATUS; /* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */ #define MAX_STRING_WCHARS 256 +/* For certain USB devices, using a buffer larger or equal to 127 wchars results + in successful completion of HID API functions, but a broken string is stored + in the output buffer. This behaviour persists even if HID API is bypassed and + HID IOCTLs are passed to the HID driver directly. Therefore, for USB devices, + the buffer MUST NOT exceed 126 WCHARs. +*/ + +#define MAX_STRING_WCHARS_USB 126 + static struct hid_api_version api_version = { .major = HID_API_VERSION_MAJOR, .minor = HID_API_VERSION_MINOR, @@ -88,6 +97,7 @@ static HidD_GetManufacturerString_ HidD_GetManufacturerString; static HidD_GetProductString_ HidD_GetProductString; static HidD_SetFeature_ HidD_SetFeature; static HidD_GetFeature_ HidD_GetFeature; +static HidD_SetOutputReport_ HidD_SetOutputReport; static HidD_GetInputReport_ HidD_GetInputReport; static HidD_GetIndexedString_ HidD_GetIndexedString; static HidD_GetPreparsedData_ HidD_GetPreparsedData; @@ -141,6 +151,7 @@ static int lookup_functions() RESOLVE(hid_lib_handle, HidD_GetProductString); RESOLVE(hid_lib_handle, HidD_SetFeature); RESOLVE(hid_lib_handle, HidD_GetFeature); + RESOLVE(hid_lib_handle, HidD_SetOutputReport); RESOLVE(hid_lib_handle, HidD_GetInputReport); RESOLVE(hid_lib_handle, HidD_GetIndexedString); RESOLVE(hid_lib_handle, HidD_GetPreparsedData); @@ -178,11 +189,13 @@ struct hid_device_ { USHORT feature_report_length; unsigned char *feature_buf; wchar_t *last_error_str; + wchar_t *last_read_error_str; BOOL read_pending; char *read_buf; OVERLAPPED ol; OVERLAPPED write_ol; struct hid_device_info* device_info; + DWORD write_timeout_ms; }; static hid_device *new_hid_device() @@ -201,13 +214,15 @@ static hid_device *new_hid_device() dev->feature_report_length = 0; dev->feature_buf = NULL; dev->last_error_str = NULL; + dev->last_read_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->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL); dev->device_info = NULL; + dev->write_timeout_ms = 1000; return dev; } @@ -218,7 +233,9 @@ static void free_hid_device(hid_device *dev) CloseHandle(dev->write_ol.hEvent); CloseHandle(dev->device_handle); free(dev->last_error_str); + free(dev->last_read_error_str); dev->last_error_str = NULL; + dev->last_read_error_str = NULL; free(dev->write_buf); free(dev->feature_buf); free(dev->read_buf); @@ -447,7 +464,7 @@ static void hid_internal_get_usb_info(struct hid_device_info* dev, DEVINST dev_n { wchar_t *device_id = NULL, *hardware_ids = NULL; - device_id = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); + device_id = (wchar_t *)hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); if (!device_id) goto end; @@ -465,7 +482,7 @@ static void hid_internal_get_usb_info(struct hid_device_info* dev, DEVINST dev_n } /* Get the hardware ids from devnode */ - hardware_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_HardwareIds, DEVPROP_TYPE_STRING_LIST); + hardware_ids = (wchar_t *)hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_HardwareIds, DEVPROP_TYPE_STRING_LIST); if (!hardware_ids) goto end; @@ -496,7 +513,7 @@ static void hid_internal_get_usb_info(struct hid_device_info* dev, DEVINST dev_n /* Try to get USB device manufacturer string if not provided by HidD_GetManufacturerString. */ if (wcslen(dev->manufacturer_string) == 0) { - wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_Manufacturer, DEVPROP_TYPE_STRING); + wchar_t* manufacturer_string = (wchar_t *)hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_Manufacturer, DEVPROP_TYPE_STRING); if (manufacturer_string) { free(dev->manufacturer_string); dev->manufacturer_string = manufacturer_string; @@ -516,7 +533,7 @@ static void hid_internal_get_usb_info(struct hid_device_info* dev, DEVINST dev_n /* Get the device id of the USB device. */ free(device_id); - device_id = hid_internal_get_devnode_property(usb_dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); + device_id = (wchar_t *)hid_internal_get_devnode_property(usb_dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); if (!device_id) goto end; @@ -555,7 +572,7 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n { if (wcslen(dev->manufacturer_string) == 0) { /* Manufacturer Name String (UUID: 0x2A29) */ - wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_Manufacturer, DEVPROP_TYPE_STRING); + wchar_t* manufacturer_string = (wchar_t *)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; @@ -564,7 +581,7 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n if (wcslen(dev->serial_number) == 0) { /* Serial Number String (UUID: 0x2A25) */ - wchar_t* serial_number = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, DEVPROP_TYPE_STRING); + wchar_t* serial_number = (wchar_t *)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; @@ -573,13 +590,13 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n if (wcslen(dev->product_string) == 0) { /* Model Number String (UUID: 0x2A24) */ - wchar_t* product_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_ModelNumber, DEVPROP_TYPE_STRING); + wchar_t* product_string = (wchar_t *)hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_ModelNumber, DEVPROP_TYPE_STRING); if (!product_string) { DEVINST parent_dev_node = 0; /* Fallback: Get devnode grandparent to reach out Bluetooth LE device node */ if (CM_Get_Parent(&parent_dev_node, dev_node, 0) == CR_SUCCESS) { /* Device Name (UUID: 0x2A00) */ - product_string = hid_internal_get_devnode_property(parent_dev_node, &DEVPKEY_NAME, DEVPROP_TYPE_STRING); + product_string = (wchar_t *)hid_internal_get_devnode_property(parent_dev_node, &DEVPKEY_NAME, DEVPROP_TYPE_STRING); } } @@ -590,14 +607,25 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n } } -static void hid_internal_get_info(const wchar_t* interface_path, struct hid_device_info* dev) +/* Unfortunately, HID_API_BUS_xxx constants alone aren't enough to distinguish between BLUETOOTH and BLE */ + +#define HID_API_BUS_FLAG_BLE 0x01 + +typedef struct hid_internal_detect_bus_type_result_ { + DEVINST dev_node; + hid_bus_type bus_type; + unsigned int bus_flags; +} hid_internal_detect_bus_type_result; + +static hid_internal_detect_bus_type_result hid_internal_detect_bus_type(const wchar_t* interface_path) { wchar_t *device_id = NULL, *compatible_ids = NULL; CONFIGRET cr; DEVINST dev_node; + hid_internal_detect_bus_type_result result = { 0 }; /* Get the device id from interface path */ - device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); + device_id = (wchar_t *)hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); if (!device_id) goto end; @@ -612,7 +640,7 @@ static void hid_internal_get_info(const wchar_t* interface_path, struct hid_devi 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); + compatible_ids = (wchar_t *)hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST); if (!compatible_ids) goto end; @@ -625,42 +653,45 @@ static void hid_internal_get_info(const wchar_t* interface_path, struct hid_devi 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; - hid_internal_get_usb_info(dev, dev_node); + result.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; + result.bus_type = HID_API_BUS_BLUETOOTH; break; } /* Bluetooth LE devices */ if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) { - dev->bus_type = HID_API_BUS_BLUETOOTH; - hid_internal_get_ble_info(dev, dev_node); + result.bus_type = HID_API_BUS_BLUETOOTH; + result.bus_flags |= HID_API_BUS_FLAG_BLE; 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; + result.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; + result.bus_type = HID_API_BUS_SPI; break; } } + + result.dev_node = dev_node; + end: free(device_id); free(compatible_ids); + return result; } static char *hid_internal_UTF16toUTF8(const wchar_t *src) @@ -699,7 +730,10 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HIDD_ATTRIBUTES attrib; PHIDP_PREPARSED_DATA pp_data = NULL; HIDP_CAPS caps; - wchar_t string[MAX_STRING_WCHARS]; + wchar_t string[MAX_STRING_WCHARS + 1]; + ULONG len; + ULONG size; + hid_internal_detect_bus_type_result detect_bus_type_result; /* Create the record. */ dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info)); @@ -733,25 +767,46 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HidD_FreePreparsedData(pp_data); } + /* detect bus type before reading string descriptors */ + detect_bus_type_result = hid_internal_detect_bus_type(path); + dev->bus_type = detect_bus_type_result.bus_type; + + len = dev->bus_type == HID_API_BUS_USB ? MAX_STRING_WCHARS_USB : MAX_STRING_WCHARS; + string[len] = L'\0'; + size = len * sizeof(wchar_t); + /* Serial Number */ string[0] = L'\0'; - HidD_GetSerialNumberString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; + HidD_GetSerialNumberString(handle, string, size); dev->serial_number = _wcsdup(string); /* Manufacturer String */ string[0] = L'\0'; - HidD_GetManufacturerString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; + HidD_GetManufacturerString(handle, string, size); dev->manufacturer_string = _wcsdup(string); /* Product String */ string[0] = L'\0'; - HidD_GetProductString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; + HidD_GetProductString(handle, string, size); dev->product_string = _wcsdup(string); - hid_internal_get_info(path, dev); + /* now, the portion that depends on string descriptors */ + switch (dev->bus_type) { + case HID_API_BUS_USB: + hid_internal_get_usb_info(dev, detect_bus_type_result.dev_node); + break; + + case HID_API_BUS_BLUETOOTH: + if (detect_bus_type_result.bus_flags & HID_API_BUS_FLAG_BLE) + hid_internal_get_ble_info(dev, detect_bus_type_result.dev_node); + break; + + case HID_API_BUS_UNKNOWN: + case HID_API_BUS_SPI: + case HID_API_BUS_I2C: + /* shut down -Wswitch */ + break; + } return dev; } @@ -994,7 +1049,10 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) end_of_function: free(interface_path); - CloseHandle(device_handle); + + if (device_handle != INVALID_HANDLE_VALUE) { + CloseHandle(device_handle); + } if (pp_data) { HidD_FreePreparsedData(pp_data); @@ -1003,6 +1061,11 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) return dev; } +void HID_API_EXPORT_CALL hid_winapi_set_write_timeout(hid_device *dev, unsigned long timeout) +{ + dev->write_timeout_ms = timeout; +} + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) { DWORD bytes_written = 0; @@ -1029,15 +1092,22 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * /* The user passed the right number of bytes. Use the buffer as-is. */ buf = (unsigned char *) data; } else { - if (dev->write_buf == NULL) + if (dev->write_buf == NULL) { dev->write_buf = (unsigned char *) malloc(dev->output_report_length); + + if (dev->write_buf == NULL) { + register_string_error(dev, L"hid_write/malloc"); + goto end_of_function; + } + } + buf = dev->write_buf; memcpy(buf, data, length); memset(buf + length, 0, dev->output_report_length - length); length = dev->output_report_length; } - res = WriteFile(dev->device_handle, buf, (DWORD) length, NULL, &dev->write_ol); + res = WriteFile(dev->device_handle, buf, (DWORD) length, &bytes_written, &dev->write_ol); if (!res) { if (GetLastError() != ERROR_IO_PENDING) { @@ -1046,12 +1116,15 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * goto end_of_function; } overlapped = TRUE; + } else { + /* WriteFile() succeeded synchronously. */ + function_result = bytes_written; } if (overlapped) { /* Wait for the transaction to complete. This makes hid_write() synchronous. */ - res = WaitForSingleObject(dev->write_ol.hEvent, 1000); + res = WaitForSingleObject(dev->write_ol.hEvent, dev->write_timeout_ms); if (res != WAIT_OBJECT_0) { /* There was a Timeout. */ register_winapi_error(dev, L"hid_write/WaitForSingleObject"); @@ -1083,11 +1156,11 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char BOOL overlapped = FALSE; if (!data || !length) { - register_string_error(dev, L"Zero buffer/length"); + register_string_error_to_buffer(&dev->last_read_error_str, L"Zero buffer/length"); return -1; } - register_string_error(dev, NULL); + register_string_error_to_buffer(&dev->last_read_error_str, NULL); /* Copy the handle for convenience. */ HANDLE ev = dev->ol.hEvent; @@ -1103,7 +1176,7 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char if (GetLastError() != ERROR_IO_PENDING) { /* ReadFile() has failed. Clean up and return error. */ - register_winapi_error(dev, L"ReadFile"); + register_winapi_error_to_buffer(&dev->last_read_error_str, L"ReadFile"); CancelIo(dev->device_handle); dev->read_pending = FALSE; goto end_of_function; @@ -1116,20 +1189,19 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char } 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; - } + /* See if there is any data yet. */ + res = WaitForSingleObject(ev, milliseconds >= 0 ? (DWORD)milliseconds : INFINITE); + 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*/); + /* Get the number of bytes read. The actual data has been copied to the data[] + array which was passed to ReadFile(). We must not wait here because we've + already waited on our event above, and since it's auto-reset, it will have + been reset back to unsignalled by now. */ + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, FALSE/*don't wait now - already did on the prev step*/); } /* Set pending back to false, even if GetOverlappedResult() returned error. */ dev->read_pending = FALSE; @@ -1151,7 +1223,7 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char } } if (!res) { - register_winapi_error(dev, L"hid_read_timeout/GetOverlappedResult"); + register_winapi_error_to_buffer(&dev->last_read_error_str, L"hid_read_timeout/GetOverlappedResult"); } end_of_function: @@ -1167,6 +1239,13 @@ int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, s return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); } +HID_API_EXPORT const wchar_t * HID_API_CALL hid_read_error(hid_device *dev) +{ + if (dev->last_read_error_str == NULL) + return L"Success"; + return dev->last_read_error_str; +} + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) { dev->blocking = !nonblock; @@ -1195,8 +1274,15 @@ int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const u buf = (unsigned char *) data; length_to_send = length; } else { - if (dev->feature_buf == NULL) + if (dev->feature_buf == NULL) { dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length); + + if (dev->feature_buf == NULL) { + register_string_error(dev, L"hid_send_feature_report/malloc"); + return -1; + } + } + buf = dev->feature_buf; memcpy(buf, data, length); memset(buf + length, 0, dev->feature_report_length - length); @@ -1267,6 +1353,52 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length); } +int HID_API_EXPORT HID_API_CALL hid_send_output_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.OutputeportByteLength bytes passed + to HidD_SetOutputReport(), even if the report is shorter. Any less sent and + the function fails with error ERROR_INVALID_PARAMETER set. Any more + and HidD_SetOutputReport() silently truncates the data sent in the report + to caps.OutputReportByteLength. */ + if (length >= dev->output_report_length) { + buf = (unsigned char *) data; + length_to_send = length; + } else { + if (dev->write_buf == NULL) { + dev->write_buf = (unsigned char *) malloc(dev->output_report_length); + + if (dev->write_buf == NULL) { + register_string_error(dev, L"hid_send_output_report/malloc"); + return -1; + } + } + + buf = dev->write_buf; + memcpy(buf, data, length); + memset(buf + length, 0, dev->output_report_length - length); + length_to_send = dev->output_report_length; + } + + res = HidD_SetOutputReport(dev->device_handle, (PVOID)buf, (DWORD) length_to_send); + if (!res) { + register_string_error(dev, L"HidD_SetOutputReport"); + return -1; + } + + return (int) 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 */ @@ -1342,13 +1474,16 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *de return 0; } -HID_API_EXPORT struct hid_device_info * HID_API_CALL hid_get_device_info(hid_device *dev) { +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; } + register_string_error(dev, NULL); + return dev->device_info; } @@ -1356,7 +1491,12 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int { BOOL res; - res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS)); + if (dev->device_info && dev->device_info->bus_type == HID_API_BUS_USB && maxlen > MAX_STRING_WCHARS_USB) { + string[MAX_STRING_WCHARS_USB] = L'\0'; + maxlen = MAX_STRING_WCHARS_USB; + } + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)maxlen * sizeof(wchar_t)); if (!res) { register_winapi_error(dev, L"HidD_GetIndexedString"); return -1; @@ -1389,7 +1529,7 @@ int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *conta } /* Get the device id from interface path */ - device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); + device_id = (wchar_t *)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; @@ -1428,10 +1568,18 @@ int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char return -1; } + int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, buf, buf_size); HidD_FreePreparsedData(pp_data); + if (res == 0) { + register_string_error(dev, NULL); + } + else { + register_string_error(dev, L"Failed to reconstruct descriptor from PREPARSED_DATA"); + } + return res; } diff --git a/hidapi/windows/hidapi_descriptor_reconstruct.c b/hidapi/windows/hidapi_descriptor_reconstruct.c index a4efb25..04f5ee7 100644 --- a/hidapi/windows/hidapi_descriptor_reconstruct.c +++ b/hidapi/windows/hidapi_descriptor_reconstruct.c @@ -46,7 +46,7 @@ static void rd_append_byte(unsigned char byte, struct rd_buffer* rpt_desc) { * * @param[in] rd_item Enumeration identifying type (Main, Global, Local) and function (e.g Usage or Report Count) of the item. * @param[in] data Data (Size depends on rd_item 0,1,2 or 4bytes). - * @param list Chained list of report descriptor bytes. + * @param rpt_desc Pointer to report descriptor buffer struct. * * @return Returns 0 if successful, -1 for error. */ @@ -140,7 +140,7 @@ static struct rd_main_item_node * rd_append_main_item_node(int first_bit, int la list = &(*list)->next; } - new_list_node = malloc(sizeof(*new_list_node)); // Create new list entry + new_list_node = (struct rd_main_item_node *)malloc(sizeof(*new_list_node)); // Create new list entry new_list_node->FirstBit = first_bit; new_list_node->LastBit = last_bit; new_list_node->TypeOfNode = type_of_node; @@ -203,13 +203,13 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha // Allocate memory and initialize lookup table rd_bit_range ****coll_bit_range; - coll_bit_range = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range)); + coll_bit_range = (rd_bit_range ****)malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_bit_range)); for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { - coll_bit_range[collection_node_idx] = malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00) + coll_bit_range[collection_node_idx] = (rd_bit_range ***)malloc(256 * sizeof(*coll_bit_range[0])); // 256 possible report IDs (incl. 0x00) for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { - coll_bit_range[collection_node_idx][reportid_idx] = malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0])); - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { - coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = malloc(sizeof(rd_bit_range)); + coll_bit_range[collection_node_idx][reportid_idx] = (rd_bit_range **)malloc(NUM_OF_HIDP_REPORT_TYPES * sizeof(*coll_bit_range[0][0])); + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + coll_bit_range[collection_node_idx][reportid_idx][rt_idx] = (rd_bit_range *)malloc(sizeof(rd_bit_range)); coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit = -1; coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->LastBit = -1; } @@ -217,7 +217,7 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha } // Fill the lookup table where caps exist - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { for (USHORT caps_idx = pp_data->caps_info[rt_idx].FirstCap; caps_idx < pp_data->caps_info[rt_idx].LastCap; caps_idx++) { int first_bit, last_bit; first_bit = (pp_data->caps[caps_idx].BytePosition - 1) * 8 @@ -235,14 +235,14 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha } // ************************************************************************* - // -Determine hierachy levels of each collections and store it in: + // -Determine hierarchy levels of each collections and store it in: // coll_levels[COLLECTION_INDEX] // -Determine number of direct childs of each collections and store it in: // coll_number_of_direct_childs[COLLECTION_INDEX] // ************************************************************************* int max_coll_level = 0; - int *coll_levels = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0])); - int *coll_number_of_direct_childs = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0])); + int *coll_levels = (int *)malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_levels[0])); + int *coll_number_of_direct_childs = (int *)malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_number_of_direct_childs[0])); for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { coll_levels[collection_node_idx] = -1; coll_number_of_direct_childs[collection_node_idx] = 0; @@ -286,7 +286,7 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild; while (child_idx) { for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { // Merge bit range from childs if ((coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit != -1) && (coll_bit_range[collection_node_idx][reportid_idx][rt_idx]->FirstBit > coll_bit_range[child_idx][reportid_idx][rt_idx]->FirstBit)) { @@ -303,15 +303,13 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha } } - // ************************************************************************************************* - // Determine child collection order of the whole hierachy, based on previously determined bit ranges + // ************************************************************************************************** + // Determine child collection order of the whole hierarchy, based on previously determined bit ranges // and store it this index coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX] - // ************************************************************************************************* - USHORT **coll_child_order; - coll_child_order = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order)); + // ************************************************************************************************** + USHORT **coll_child_order = (USHORT **)malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_child_order)); { - BOOLEAN *coll_parsed_flag; - coll_parsed_flag = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0])); + BOOLEAN *coll_parsed_flag = (BOOLEAN *)malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_parsed_flag[0])); for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { coll_parsed_flag[collection_node_idx] = FALSE; } @@ -321,12 +319,12 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha if ((coll_number_of_direct_childs[collection_node_idx] != 0) && (coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] == FALSE)) { coll_parsed_flag[link_collection_nodes[collection_node_idx].FirstChild] = TRUE; - coll_child_order[collection_node_idx] = malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0])); + coll_child_order[collection_node_idx] = (USHORT *)malloc((coll_number_of_direct_childs[collection_node_idx]) * sizeof(*coll_child_order[0])); { // Create list of child collection indices // sorted reverse to the order returned to HidP_GetLinkCollectionNodeschild - // which seems to match teh original order, as long as no bit position needs to be considered + // which seems to match the original order, as long as no bit position needs to be considered USHORT child_idx = link_collection_nodes[collection_node_idx].FirstChild; int child_count = coll_number_of_direct_childs[collection_node_idx] - 1; coll_child_order[collection_node_idx][child_count] = child_idx; @@ -339,7 +337,7 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha if (coll_number_of_direct_childs[collection_node_idx] > 1) { // Sort child collections indices by bit positions - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { for (int child_idx = 1; child_idx < coll_number_of_direct_childs[collection_node_idx]; child_idx++) { // since the coll_bit_range array is not sorted, we need to reference the collection index in @@ -380,10 +378,10 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha // *************************************************************************************** struct rd_main_item_node *main_item_list = NULL; // List root // Lookup table to find the Collection items in the list by index - struct rd_main_item_node **coll_begin_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup)); - struct rd_main_item_node **coll_end_lookup = malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup)); + struct rd_main_item_node **coll_begin_lookup = (struct rd_main_item_node **)malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_begin_lookup)); + struct rd_main_item_node **coll_end_lookup = (struct rd_main_item_node **)malloc(pp_data->NumberLinkCollectionNodes * sizeof(*coll_end_lookup)); { - int *coll_last_written_child = malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0])); + int *coll_last_written_child = (int *)malloc(pp_data->NumberLinkCollectionNodes * sizeof(coll_last_written_child[0])); for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { coll_last_written_child[collection_node_idx] = -1; } @@ -467,7 +465,7 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha // Inserted Input/Output/Feature main items into the main_item_list // in order of reconstructed bit positions // **************************************************************** - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { // Add all value caps to node list struct rd_main_item_node *firstDelimiterNode = NULL; struct rd_main_item_node *delimiterCloseNode = NULL; @@ -530,14 +528,17 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha { int last_bit_position[NUM_OF_HIDP_REPORT_TYPES][256]; struct rd_main_item_node *last_report_item_lookup[NUM_OF_HIDP_REPORT_TYPES][256]; - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { last_bit_position[rt_idx][reportid_idx] = -1; last_report_item_lookup[rt_idx][reportid_idx] = NULL; } } + BOOLEAN devicehasReportIDs = FALSE; struct rd_main_item_node *list = main_item_list; // List root; + // Windows pp_data are per top-level collection, therefore top level coll end is unique + struct rd_main_item_node* node_before_top_level_coll_end = NULL; while (list->next != NULL) { @@ -552,24 +553,53 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha struct rd_main_item_node *list_node = rd_search_main_item_list_for_bit_position(last_bit_position[list->MainItemType][list->ReportID], list->MainItemType, list->ReportID, &last_report_item_lookup[list->MainItemType][list->ReportID]); rd_insert_main_item_node(last_bit_position[list->MainItemType][list->ReportID] + 1, list->FirstBit - 1, rd_item_node_padding, -1, 0, list->MainItemType, list->ReportID, &list_node); } + if (list->ReportID != 0) { + devicehasReportIDs = TRUE; + } last_bit_position[list->MainItemType][list->ReportID] = list->LastBit; last_report_item_lookup[list->MainItemType][list->ReportID] = list; } } + if (list->next->MainItemType == rd_collection_end) { + // Store the node before the collection end - the last occurence is the end of the top level collection + node_before_top_level_coll_end = list; + } list = list->next; } + // Add 8 bit padding at each report end - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { if (last_bit_position[rt_idx][reportid_idx] != -1) { int padding = 8 - ((last_bit_position[rt_idx][reportid_idx] + 1) % 8); if (padding < 8) { // Insert padding item after item referenced in last_report_item_lookup rd_insert_main_item_node(last_bit_position[rt_idx][reportid_idx] + 1, last_bit_position[rt_idx][reportid_idx] + padding, rd_item_node_padding, -1, 0, (rd_main_items) rt_idx, (unsigned char) reportid_idx, &last_report_item_lookup[rt_idx][reportid_idx]); + if (last_report_item_lookup[rt_idx][reportid_idx] == node_before_top_level_coll_end) { + // If this padding item is at the end of the top level collection, update node_before_top_level_coll_end + node_before_top_level_coll_end = last_report_item_lookup[rt_idx][reportid_idx]->next; + } + last_bit_position[rt_idx][reportid_idx] += padding; } } } } + + // Add full byte padding at the end of the report descriptor (only reconstructable, for devices without Report IDs) + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + if (!devicehasReportIDs && pp_data->caps_info[rt_idx].NumberOfCaps > 0 && pp_data->caps_info[rt_idx].ReportByteLength > 0) { + // ReportID 0 means this device uses not Report IDs + // => Maximum one report per type possible, so we can take the size from the buffer size for the report type + const unsigned char reportId = 0; + // ReportByteLength is the report length in bytes plus the one byte for the optional ReportID + int padding = (pp_data->caps_info[rt_idx].ReportByteLength - 1) * 8 - (last_bit_position[rt_idx][reportId] + 1); + + if (padding > 0) { + // Insert padding item after item referenced in last_report_item_lookup + rd_insert_main_item_node(last_bit_position[rt_idx][reportId] + 1, last_bit_position[rt_idx][reportId] + padding, rd_item_node_padding, -1, 0, (rd_main_items)rt_idx, reportId, &node_before_top_level_coll_end); + } + } + } } @@ -652,13 +682,25 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha else if (main_item_list->TypeOfNode == rd_item_node_padding) { // Padding // The preparsed data doesn't contain any information about padding. Therefore all undefined gaps - // in the reports are filled with the same style of constant padding. + // in the reports are filled with the same style of constant bit or byte padding. + + if ((main_item_list->LastBit - main_item_list->FirstBit + 1) % 8 == 0) { + // Padding in full bytes - // Write "Report Size" with number of padding bits - rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc); + // Write "Report Size" for padding bytes is 8 + rd_write_short_item(rd_global_report_size, 8, &rpt_desc); - // Write "Report Count" for padding always as 1 - rd_write_short_item(rd_global_report_count, 1, &rpt_desc); + // Write "Report Count" with number of padding bytes + rd_write_short_item(rd_global_report_count, (main_item_list->LastBit - main_item_list->FirstBit + 1) / 8, &rpt_desc); + } else { + // Padding in bits + + // Write "Report Size" with number of padding bits + rd_write_short_item(rd_global_report_size, (main_item_list->LastBit - main_item_list->FirstBit + 1), &rpt_desc); + + // Write "Report Count" for padding bits is 1 + rd_write_short_item(rd_global_report_count, 1, &rpt_desc); + } if (rt_idx == HidP_Input) { // Write "Input" main item - We know it's Constant - We can only guess the other bits, but they don't matter in case of const @@ -966,7 +1008,7 @@ int hid_winapi_descriptor_reconstruct_pp_data(void *preparsed_data, unsigned cha // Free multidimensionable array: coll_child_order[COLLECTION_INDEX][DIRECT_CHILD_INDEX] for (USHORT collection_node_idx = 0; collection_node_idx < pp_data->NumberLinkCollectionNodes; collection_node_idx++) { for (int reportid_idx = 0; reportid_idx < 256; reportid_idx++) { - for (HIDP_REPORT_TYPE rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { + for (int rt_idx = 0; rt_idx < NUM_OF_HIDP_REPORT_TYPES; rt_idx++) { free(coll_bit_range[collection_node_idx][reportid_idx][rt_idx]); } free(coll_bit_range[collection_node_idx][reportid_idx]); diff --git a/hidapi/windows/hidapi_descriptor_reconstruct.h b/hidapi/windows/hidapi_descriptor_reconstruct.h index 2ec8b20..4b8ca83 100644 --- a/hidapi/windows/hidapi_descriptor_reconstruct.h +++ b/hidapi/windows/hidapi_descriptor_reconstruct.h @@ -27,15 +27,17 @@ #include "hidapi_winapi.h" -#if _MSC_VER +#ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4200) #pragma warning(disable: 4201) +#pragma warning(disable: 4214) #endif #include #include "hidapi_hidsdi.h" +#include #define NUM_OF_HIDP_REPORT_TYPES 3 @@ -124,6 +126,13 @@ typedef struct hid_pp_link_collection_node_ { // Same as the public API structure HIDP_LINK_COLLECTION_NODE, but without PVOID UserContext at the end } hid_pp_link_collection_node, *phid_pp_link_collection_node; +// Note: This is risk-reduction-measure for this specific struct, as it has ULONG bit-field. +// Although very unlikely, it might still be possible that the compiler creates a memory layout that is +// not binary compatile. +// Other structs are not checked at the time of writing. +static_assert(sizeof(struct hid_pp_link_collection_node_) == 16, + "Size of struct hid_pp_link_collection_node_ not as expected. This might break binary compatibility"); + typedef struct hidp_unknown_token_ { UCHAR Token; /* Specifies the one-byte prefix of a global item. */ UCHAR Reserved[3]; @@ -212,7 +221,7 @@ typedef struct hidp_preparsed_data_ { USHORT FirstByteOfLinkCollectionArray; USHORT NumberLinkCollectionNodes; -#if defined(__MINGW32__) || defined(__CYGWIN__) +#ifndef _MSC_VER // MINGW fails with: Flexible array member in union not supported // Solution: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html union { diff --git a/hidapi/windows/hidapi_hidclass.h b/hidapi/windows/hidapi_hidclass.h index 13bd6f2..4ad98ea 100644 --- a/hidapi/windows/hidapi_hidclass.h +++ b/hidapi/windows/hidapi_hidclass.h @@ -29,6 +29,24 @@ /* This part of the header mimics hidclass.h, but only what is used by HIDAPI */ +#ifndef FILE_DEVICE_KEYBOARD +#define FILE_DEVICE_KEYBOARD 0x0000000b +#endif + +#ifndef METHOD_OUT_DIRECT +#define METHOD_OUT_DIRECT 2 +#endif + +#ifndef FILE_ANY_ACCESS +#define FILE_ANY_ACCESS 0 +#endif + +#ifndef CTL_CODE +#define CTL_CODE( DeviceType, Function, Method, Access ) ( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ +) +#endif + #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) diff --git a/hidapi/windows/hidapi_hidsdi.h b/hidapi/windows/hidapi_hidsdi.h index 453f899..ffed5b2 100644 --- a/hidapi/windows/hidapi_hidsdi.h +++ b/hidapi/windows/hidapi_hidsdi.h @@ -40,8 +40,6 @@ typedef struct _HIDD_ATTRIBUTES{ 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); @@ -49,6 +47,7 @@ typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID bu 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_SetOutputReport_)(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); diff --git a/hidapi/windows/hidapi_winapi.h b/hidapi/windows/hidapi_winapi.h index 43071f5..e5a1242 100644 --- a/hidapi/windows/hidapi_winapi.h +++ b/hidapi/windows/hidapi_winapi.h @@ -46,7 +46,7 @@ extern "C" { @ingroup API @param dev A device handle returned from hid_open(). - @param guid The device's container ID on return. + @param container_id The device's container ID on return. @returns This function returns 0 on success and -1 on error. @@ -67,6 +67,26 @@ extern "C" { */ int HID_API_EXPORT_CALL hid_winapi_descriptor_reconstruct_pp_data(void *hidp_preparsed_data, unsigned char *buf, size_t buf_size); + /** + * @brief Sets the timeout for hid_write operation. + * + * Since version 0.15.0, @ref HID_API_VERSION >= HID_API_MAKE_VERSION(0, 15, 0) + * + * The default timeout is 1sec for winapi backend. + * + * In case if 1sec is not enough, on in case of multi-platform development, + * the recommended value is 5sec, e.g. to match (unconfigurable) 5sec timeout + * set for hidraw (linux kernel) implementation. + * + * When the timeout is set to 0, hid_write function becomes non-blocking and would exit immediately. + * When the timeout is set to INFINITE ((DWORD)-1), the function will not exit, + * until the write operation is performed or an error occured. + * See dwMilliseconds parameter documentation of WaitForSingleObject function. + * + * @param timeout New timeout value in milliseconds. + */ + void HID_API_EXPORT_CALL hid_winapi_set_write_timeout(hid_device *dev, unsigned long timeout); + #ifdef __cplusplus } #endif diff --git a/libusb/AUTHORS b/libusb/AUTHORS index 8f91512..50b9c5e 100644 --- a/libusb/AUTHORS +++ b/libusb/AUTHORS @@ -13,15 +13,18 @@ Copyright © 2013-2018 Chris Dickens Other contributors: Aaron Luft Adam Korcz +Addison Crump Adrian Bunk Adrien Destugues Akshay Jaggi Alan Ott Alan Stern Aleksandr Mezin +Alexander Mot Alexander Pyhalov Alexander Schlarb Alexander Stein +Alex Feinman Alex Vatchenko Andrew Aldridge Andrew Fernandes @@ -44,6 +47,7 @@ Bence Csokas Benjamin Berg Benjamin Dobell Bohdan Tymkiv +Brad Smith Brent Rector Bruno Harbulot Carl Karsten @@ -59,7 +63,9 @@ David Moore Dmitry Fleytman Dmitry Kostjuchenko Dmitry Zakablukov +Dominik Boehi Doug Johnston +Edgar Fuß Evan Hunter Evan Miller Fabrice Fontaine @@ -68,6 +74,7 @@ Felipe Balbi Florian Albrechtskirchinger Francesco Montorsi Francisco Facioni +Francis Hart Frank Li Frederik Carlier Freek Dijkstra @@ -84,6 +91,7 @@ Ido Yariv Igor Anokhin Ihor Dutchak Ilya Konstantinov +Ingvar Stepanyan Jakub Klama James Hanko Jeffrey Nichols @@ -98,6 +106,7 @@ Joost Muller Josh Gao Joshua Blake Joshua Hou +Joshua M. Clulow Juan Cruz Viotti Julian Scheel Justin Bischoff @@ -112,11 +121,13 @@ Lars Wirzenius Lei Chen Léo Lam Liang Yunwang +Lonnie Abelbeck Luca Longinotti Luz Paz Mac Wang Marco Trevisan (Treviño) Marcus Meissner +Mario Kleiner Mark Kuo Markus Heidelberg Martin Ettl @@ -136,20 +147,26 @@ Moritz Fischer Nancy Li Nia Alarie Nicholas Corgan +Niklas Gürtler Omri Iluz +Orhan aib Kavrakoglu Orin Eman Ozkan Sezer +Pablo Prietz Patrick Stewart Paul Cercueil Paul Fertser Paul Qureshi Pekka Nikander +Petr Pazourek Philémon Favrod Pino Toscano Rob Walker Romain Vimont Roman Kalashnikov +Rosen Penev Ryan Hileman +Ryan Metcalfe Ryan Schmidt Saleem Rashid Sameeh Jubran @@ -158,6 +175,7 @@ Sebastian Pipping Sebastian von Ohr Sergey Serb Shawn Hoffman +Simon Chan Simon Haggett Simon Newton Slash Gordon @@ -165,6 +183,7 @@ Stefan Agner Stefan Tauner Steinar H. Gunderson Stephen Groat +Sylvain Fasel Theo Buehler Thomas Röfer Tim Hutt @@ -187,6 +206,7 @@ William Orr William Skellenger Xiaofan Chen Yegor Yefremov +Zeng Guang Zhiqiang Liu Zoltán Kovács Сергей Валерьевич @@ -199,4 +219,6 @@ parafin RipleyTom Seneral saur0n +SomeAlphabetGuy winterrace +xloem diff --git a/libusb/libusb/core.c b/libusb/libusb/core.c index ec429b7..ffe33b7 100644 --- a/libusb/libusb/core.c +++ b/libusb/libusb/core.c @@ -1,7 +1,7 @@ /* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* * Core functions for libusb - * Copyright © 2012-2013 Nathan Hjelm + * Copyright © 2012-2023 Nathan Hjelm * Copyright © 2007-2008 Daniel Drake * Copyright © 2001 Johannes Erdfelt * @@ -34,7 +34,7 @@ static const struct libusb_version libusb_version_internal = { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, - LIBUSB_RC, "http://libusb.info" }; + LIBUSB_RC, "https://libusb.info" }; static struct timespec timestamp_origin; #if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY) static libusb_log_cb log_handler; @@ -43,6 +43,9 @@ static libusb_log_cb log_handler; struct libusb_context *usbi_default_context; struct libusb_context *usbi_fallback_context; static int default_context_refcnt; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) +static usbi_atomic_t default_debug_level = -1; +#endif static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; @@ -57,12 +60,12 @@ struct list_head active_contexts_list; * * libusb is an open source library that allows you to communicate with USB * devices from user space. For more info, see the - * libusb homepage. + * libusb homepage. * * This documentation is aimed at application developers wishing to * communicate with USB peripherals from their own software. After reviewing * this documentation, feedback and questions can be sent to the - * libusb-devel mailing list. + * libusb-devel mailing list. * * This documentation assumes knowledge of how to operate USB devices from * a software standpoint (descriptors, configurations, interfaces, endpoints, @@ -111,17 +114,18 @@ struct list_head active_contexts_list; * libusb uses stderr for all logging. By default, logging is set to NONE, * which means that no output will be produced. However, unless the library * has been compiled with logging disabled, then any application calls to - * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or the setting of the - * environmental variable LIBUSB_DEBUG outside of the application, can result - * in logging being produced. Your application should therefore not close - * stderr, but instead direct it to the null device if its output is - * undesirable. - * - * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) function can be - * used to enable logging of certain messages. Under standard configuration, - * libusb doesn't really log much so you are advised to use this function - * to enable all error/warning/ informational messages. It will help debug - * problems with your software. + * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), + * libusb_init_context, or the setting of the environmental variable + * LIBUSB_DEBUG outside of the application, can result in logging being + * produced. Your application should therefore not close stderr, but instead + * direct it to the null device if its output is undesirable. + * + * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) or + * libusb_init_context functions can be used to enable logging of certain + * messages. With the default configuration, libusb will not log much so if + * you are advised to use one of these functions to enable all + * error/warning/informational messages. It will help debug problems with your + * software. * * The logged messages are unstructured. There is no one-to-one correspondence * between messages being logged and success or failure return codes from @@ -137,19 +141,19 @@ struct list_head active_contexts_list; * The LIBUSB_DEBUG environment variable can be used to enable message logging * at run-time. This environment variable should be set to a log level number, * which is interpreted the same as the - * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) parameter. When this - * environment variable is set, the message logging verbosity level is fixed - * and libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) effectively does - * nothing. + * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or + * libusb_init_context(&ctx, &(struct libusb_init_option){.option = LIBUSB_OPTION_LOG_LEVEL, .value = {.ival = level}}, 0). + * When the environment variable is set, the message logging verbosity level is + * fixed and setting the LIBUSB_OPTION_LOG_LEVEL option has no effect. * * libusb can be compiled without any logging functions, useful for embedded - * systems. In this case, libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) - * and the LIBUSB_DEBUG environment variable have no effects. + * systems. In this case, neither the LIBUSB_OPTION_LOG_LEVEL option, nor the + * LIBUSB_DEBUG environment variable will have any effect. * * libusb can also be compiled with verbose debugging messages always. When * the library is compiled in this way, all messages of all verbosities are - * always logged. libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) and - * the LIBUSB_DEBUG environment variable have no effects. + * always logged. Again, in this case, neither the LIBUSB_OPTION_LOG_LEVEL + * option, nor the LIBUSB_DEBUG environment variable will have any effect. * * \section remarks Other remarks * @@ -327,23 +331,23 @@ if (cfg != desired) * developed modules may both use libusb. * * libusb is written to allow for these multiple user scenarios. The two - * "instances" of libusb will not interfere: libusb_set_option() calls - * from one user will not affect the same settings for other users, other - * users can continue using libusb after one of them calls libusb_exit(), etc. + * "instances" of libusb will not interfere: an option set by one user will have + * no effect the same option for other users, other users can continue using + * libusb after one of them calls libusb_exit(), etc. * * This is made possible through libusb's context concept. When you - * call libusb_init(), you are (optionally) given a context. You can then pass + * call libusb_init_context(), you are (optionally) given a context. You can then pass * this context pointer back into future libusb functions. * * In order to keep things simple for more simplistic applications, it is * legal to pass NULL to all functions requiring a context pointer (as long as * you're sure no other code will attempt to use libusb from the same process). * When you pass NULL, the default context will be used. The default context - * is created the first time a process calls libusb_init() when no other + * is created the first time a process calls libusb_init_context() when no other * context is alive. Contexts are destroyed during libusb_exit(). * * The default context is reference-counted and can be shared. That means that - * if libusb_init(NULL) is called twice within the same process, the two + * if libusb_init_context(NULL, x, y) is called twice within the same process, the two * users end up sharing the same context. The deinitialization and freeing of * the default context will only happen when the last user calls libusb_exit(). * In other words, the default context is created and initialized when its @@ -413,6 +417,7 @@ if (cfg != desired) * - libusb_get_device_speed() * - libusb_get_iso_packet_buffer() * - libusb_get_iso_packet_buffer_simple() + * - libusb_get_max_alt_packet_size() * - libusb_get_max_iso_packet_size() * - libusb_get_max_packet_size() * - libusb_get_next_timeout() @@ -436,6 +441,7 @@ if (cfg != desired) * - libusb_hotplug_deregister_callback() * - libusb_hotplug_register_callback() * - libusb_init() + * - libusb_init_context() * - libusb_interrupt_event_handler() * - libusb_interrupt_transfer() * - libusb_kernel_driver_active() @@ -931,13 +937,13 @@ uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev) /** \ingroup libusb_dev * Get the list of all port numbers from root for the specified device * - * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * Since version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102 * \param dev a device * \param port_numbers the array that should contain the port numbers * \param port_numbers_len the maximum length of the array. As per the USB 3.0 * specs, the current maximum limit for the depth is 7. * \returns the number of elements filled - * \returns LIBUSB_ERROR_OVERFLOW if the array is too small + * \returns \ref LIBUSB_ERROR_OVERFLOW if the array is too small */ int API_EXPORTED libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len) @@ -1049,8 +1055,8 @@ static const struct libusb_endpoint_descriptor *find_endpoint( * \param dev a device * \param endpoint address of the endpoint in question * \returns the wMaxPacketSize value - * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist - * \returns LIBUSB_ERROR_OTHER on other failure + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure */ int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) @@ -1079,6 +1085,65 @@ int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev, return r; } +static const struct libusb_endpoint_descriptor *find_alt_endpoint( + struct libusb_config_descriptor *config, + int iface_idx, int altsetting_idx, unsigned char endpoint) +{ + if (iface_idx >= config->bNumInterfaces) { + return NULL; + } + + const struct libusb_interface *iface = &config->interface[iface_idx]; + + if (altsetting_idx >= iface->num_altsetting) { + return NULL; + } + + const struct libusb_interface_descriptor *altsetting + = &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) + return ep; + } + return NULL; +} + +static int get_endpoint_max_packet_size(libusb_device *dev, + const struct libusb_endpoint_descriptor *ep) +{ + struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp; + enum libusb_endpoint_transfer_type ep_type; + uint16_t val; + int r = 0; + int speed; + + speed = libusb_get_device_speed(dev); + if (speed >= LIBUSB_SPEED_SUPER) { + r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp); + if (r == LIBUSB_SUCCESS) { + r = ss_ep_cmp->wBytesPerInterval; + libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp); + } + } + + /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */ + if (speed < LIBUSB_SPEED_SUPER || r < 0) { + val = ep->wMaxPacketSize; + ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3); + + r = val & 0x07ff; + if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS + || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) + r *= (1 + ((val >> 11) & 3)); + } + + return r; +} + /** \ingroup libusb_dev * Calculate the maximum packet size which a specific endpoint is capable is * sending or receiving in the duration of 1 microframe @@ -1099,24 +1164,25 @@ int API_EXPORTED libusb_get_max_packet_size(libusb_device *dev, * libusb_set_iso_packet_lengths() in order to set the length field of every * isochronous packet in a transfer. * + * This function only considers the first alternate setting of the interface. + * If the endpoint has different maximum packet sizes for different alternate + * settings, you probably want libusb_get_max_alt_packet_size() instead. + * * Since v1.0.3. * * \param dev a device * \param endpoint address of the endpoint in question * \returns the maximum packet size which can be sent/received on this endpoint - * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist - * \returns LIBUSB_ERROR_OTHER on other failure + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure + * \see libusb_get_max_alt_packet_size */ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, unsigned char endpoint) { struct libusb_config_descriptor *config; const struct libusb_endpoint_descriptor *ep; - struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp; - enum libusb_endpoint_transfer_type ep_type; - uint16_t val; int r; - int speed; r = libusb_get_active_config_descriptor(dev, &config); if (r < 0) { @@ -1131,26 +1197,68 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, goto out; } - speed = libusb_get_device_speed(dev); - if (speed >= LIBUSB_SPEED_SUPER) { - r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp); - if (r == LIBUSB_SUCCESS) { - r = ss_ep_cmp->wBytesPerInterval; - libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp); - } - } + r = get_endpoint_max_packet_size(dev, ep); - /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */ - if (speed < LIBUSB_SPEED_SUPER || r < 0) { - val = ep->wMaxPacketSize; - ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3); +out: + libusb_free_config_descriptor(config); + return r; +} - r = val & 0x07ff; - if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS - || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) - r *= (1 + ((val >> 11) & 3)); +/** \ingroup libusb_dev + * Calculate the maximum packet size which a specific endpoint is capable of + * sending or receiving in the duration of 1 microframe + * + * Only the active configuration is examined. The calculation is based on the + * wMaxPacketSize field in the endpoint descriptor as described in section + * 9.6.6 in the USB 2.0 specifications. + * + * If acting on an isochronous or interrupt endpoint, this function will + * multiply the value found in bits 0:10 by the number of transactions per + * microframe (determined by bits 11:12). Otherwise, this function just + * returns the numeric value found in bits 0:10. For USB 3.0 device, it + * will attempts to retrieve the Endpoint Companion Descriptor to return + * wBytesPerInterval. + * + * This function is useful for setting up isochronous transfers, for example + * you might pass the return value from this function to + * libusb_set_iso_packet_lengths() in order to set the length field of every + * isochronous packet in a transfer. + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A + * + * \param dev a device + * \param interface_number the bInterfaceNumber of the interface + * the endpoint belongs to + * \param alternate_setting the bAlternateSetting of the interface + * \param endpoint address of the endpoint in question + * \returns the maximum packet size which can be sent/received on this endpoint + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_OTHER on other failure + * \see libusb_get_max_iso_packet_size + */ +int API_EXPORTED libusb_get_max_alt_packet_size(libusb_device *dev, + int interface_number, int alternate_setting, unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; + } + + ep = find_alt_endpoint(config, interface_number, + alternate_setting, endpoint); + if (!ep) { + r = LIBUSB_ERROR_NOT_FOUND; + goto out; } + r = get_endpoint_max_packet_size(dev, ep); + out: libusb_free_config_descriptor(config); return r; @@ -1209,10 +1317,10 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * handle for the underlying device. The handle allows you to use libusb to * perform I/O on the device in question. * - * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before - * libusb_init() if you want to skip enumeration of USB devices. In particular, - * this might be needed on Android if you don't have authority to access USB - * devices in general. + * Call libusb_init_context with the LIBUSB_OPTION_NO_DEVICE_DISCOVERY + * option if you want to skip enumeration of USB devices. In particular, this + * might be needed on Android if you don't have authority to access USB + * devices in general. Setting this option with libusb_set_option is deprecated. * * On Linux, the system device handle must be a valid file descriptor opened * on the device node. @@ -1233,9 +1341,9 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * \param dev_handle output location for the returned device handle pointer. Only * populated when the return code is 0. * \returns 0 on success - * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure - * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions - * \returns LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this + * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this * platform * \returns another LIBUSB_ERROR code on other failure */ @@ -1289,9 +1397,9 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, * \param dev_handle output location for the returned device handle pointer. Only * populated when the return code is 0. * \returns 0 on success - * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure - * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns \ref LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_open(libusb_device *dev, @@ -1397,20 +1505,22 @@ static void do_close(struct libusb_context *ctx, for_each_transfer_safe(ctx, itransfer, tmp) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + uint32_t state_flags; if (transfer->dev_handle != dev_handle) continue; usbi_mutex_lock(&itransfer->lock); - if (!(itransfer->state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { + state_flags = itransfer->state_flags; + usbi_mutex_unlock(&itransfer->lock); + if (!(state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) { usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know"); - if (itransfer->state_flags & USBI_TRANSFER_CANCELLING) + if (state_flags & USBI_TRANSFER_CANCELLING) usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle"); else usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing"); } - usbi_mutex_unlock(&itransfer->lock); /* remove from the list of in-flight transfers and make sure * we don't accidentally use the device handle in the future @@ -1424,7 +1534,7 @@ static void do_close(struct libusb_context *ctx, * the device handle is invalid */ usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed", - transfer, dev_handle); + (void *) transfer, (void *) dev_handle); } usbi_mutex_unlock(&ctx->flying_transfers_lock); @@ -1533,7 +1643,7 @@ libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle) * \param config output location for the bConfigurationValue of the active * configuration (only valid for return code 0) * \returns 0 on success - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, @@ -1584,7 +1694,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, * endpoint halts cleared, toggles reset). * * Not all backends support setting the configuration from user space, which - * will be indicated by the return code LIBUSB_ERROR_NOT_SUPPORTED. As this + * will be indicated by the return code \ref LIBUSB_ERROR_NOT_SUPPORTED. As this * suggests that the platform is handling the device configuration itself, * this error should generally be safe to ignore. * @@ -1615,11 +1725,11 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, * wish to activate, or -1 if you wish to put the device in an unconfigured * state * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist - * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed - * \returns LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist + * \returns \ref LIBUSB_ERROR_BUSY if interfaces are currently claimed + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration * is not supported by the backend - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure * \see libusb_set_auto_detach_kernel_driver() */ @@ -1653,10 +1763,10 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle, * \param interface_number the bInterfaceNumber of the interface you * wish to claim * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist - * \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist + * \returns \ref LIBUSB_ERROR_BUSY if another program or driver has claimed the * interface - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns a LIBUSB_ERROR code on other failure * \see libusb_set_auto_detach_kernel_driver() */ @@ -1699,8 +1809,8 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle, * \param interface_number the bInterfaceNumber of the * previously-claimed interface * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure * \see libusb_set_auto_detach_kernel_driver() */ @@ -1744,9 +1854,9 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle, * \param alternate_setting the bAlternateSetting of the alternate * setting to activate * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the * requested alternate setting does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, @@ -1760,7 +1870,6 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand return LIBUSB_ERROR_INVALID_PARAM; if (!usbi_atomic_load(&dev_handle->dev->attached)) { - usbi_mutex_unlock(&dev_handle->lock); return LIBUSB_ERROR_NO_DEVICE; } @@ -1787,8 +1896,8 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand * \param dev_handle a device handle * \param endpoint the endpoint to clear halt status * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, @@ -1809,14 +1918,14 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, * If the reset fails, the descriptors change, or the previous state cannot be * restored, the device will appear to be disconnected and reconnected. This * means that the device handle is no longer valid (you should close it) and - * rediscover the device. A return code of LIBUSB_ERROR_NOT_FOUND indicates + * rediscover the device. A return code of \ref LIBUSB_ERROR_NOT_FOUND indicates * when this is the case. * * This is a blocking function which usually incurs a noticeable delay. * * \param dev_handle a handle of the device to reset * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the + * \returns \ref LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the * device has been disconnected * \returns another LIBUSB_ERROR code on other failure */ @@ -1881,7 +1990,7 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, * \param dev_handle a device handle * \param endpoints array of endpoints to free streams on * \param num_endpoints length of the endpoints array - * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure */ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) @@ -1944,7 +2053,7 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl * \param dev_handle a device handle * \param buffer pointer to the previously allocated memory * \param length size of previously allocated memory - * \returns LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure + * \returns \ref LIBUSB_SUCCESS, or a LIBUSB_ERROR code on failure */ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, unsigned char *buffer, size_t length) @@ -1966,8 +2075,8 @@ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, * \param interface_number the interface to check * \returns 0 if no kernel driver is active * \returns 1 if a kernel driver is active - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available * \returns another LIBUSB_ERROR code on other failure * \see libusb_detach_kernel_driver() @@ -1997,15 +2106,15 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, * * Note that libusb itself also talks to the device through a special kernel * driver, if this driver is already attached to the device, this call will - * not detach it and return LIBUSB_ERROR_NOT_FOUND. + * not detach it and return \ref LIBUSB_ERROR_NOT_FOUND. * * \param dev_handle a device handle * \param interface_number the interface to detach the driver from * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active - * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available * \returns another LIBUSB_ERROR code on other failure * \see libusb_kernel_driver_active() @@ -2036,12 +2145,12 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, * \param dev_handle a device handle * \param interface_number the interface to attach the driver from * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active - * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_ERROR_NOT_FOUND if no kernel driver was active + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the interface does not exist + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available - * \returns LIBUSB_ERROR_BUSY if the driver cannot be attached because the + * \returns \ref LIBUSB_ERROR_BUSY if the driver cannot be attached because the * interface is claimed by a program or driver * \returns another LIBUSB_ERROR code on other failure * \see libusb_kernel_driver_active() @@ -2072,14 +2181,14 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle, * handles by default. * * On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER - * this function will return LIBUSB_ERROR_NOT_SUPPORTED, and libusb will + * this function will return \ref LIBUSB_ERROR_NOT_SUPPORTED, and libusb will * continue as if this function was never called. * * \param dev_handle a device handle * \param enable whether to enable or disable auto kernel driver detachment * - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality * is not available * \see libusb_claim_interface() * \see libusb_release_interface() @@ -2096,20 +2205,34 @@ int API_EXPORTED libusb_set_auto_detach_kernel_driver( } /** \ingroup libusb_lib - * \deprecated Use libusb_set_option() instead using the - * \ref LIBUSB_OPTION_LOG_LEVEL option. + * Deprecated. Use libusb_set_option() or libusb_init_context() instead, + * with the \ref LIBUSB_OPTION_LOG_LEVEL option. */ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) { -#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - ctx = usbi_get_context(ctx); - if (!ctx->debug_fixed) { - level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG); - ctx->debug = (enum libusb_log_level)level; + libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level); +} + +static void libusb_set_log_cb_internal(libusb_context *ctx, libusb_log_cb cb, + int mode) +{ +#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY)) +#if !defined(USE_SYSTEM_LOGGING_FACILITY) + if (mode & LIBUSB_LOG_CB_GLOBAL) + log_handler = cb; +#endif +#if !defined(ENABLE_DEBUG_LOGGING) + if (mode & LIBUSB_LOG_CB_CONTEXT) { + ctx = usbi_get_context(ctx); + ctx->log_handler = cb; } #else UNUSED(ctx); - UNUSED(level); +#endif +#else + UNUSED(ctx); + UNUSED(cb); + UNUSED(mode); #endif } @@ -2139,24 +2262,7 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode) { -#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY)) -#if !defined(USE_SYSTEM_LOGGING_FACILITY) - if (mode & LIBUSB_LOG_CB_GLOBAL) - log_handler = cb; -#endif -#if !defined(ENABLE_DEBUG_LOGGING) - if (mode & LIBUSB_LOG_CB_CONTEXT) { - ctx = usbi_get_context(ctx); - ctx->log_handler = cb; - } -#else - UNUSED(ctx); -#endif -#else - UNUSED(ctx); - UNUSED(cb); - UNUSED(mode); -#endif + libusb_set_log_cb_internal(ctx, cb, mode); } /** \ingroup libusb_lib @@ -2176,72 +2282,95 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, * \param option which option to set * \param ... any required arguments for the specified option * - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid - * \returns LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported * on this platform - * \returns LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available + * \returns \ref LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available */ -int API_EXPORTED libusb_set_option(libusb_context *ctx, +int API_EXPORTEDV libusb_set_option(libusb_context *ctx, enum libusb_option option, ...) { int arg = 0, r = LIBUSB_SUCCESS; + libusb_log_cb log_cb = NULL; va_list ap; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + int is_default_context = (NULL == ctx); +#endif va_start(ap, option); + if (LIBUSB_OPTION_LOG_LEVEL == option) { arg = va_arg(ap, int); if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) { r = LIBUSB_ERROR_INVALID_PARAM; } } - va_end(ap); - - if (LIBUSB_SUCCESS != r) { - return r; + if (LIBUSB_OPTION_LOG_CB == option) { + log_cb = (libusb_log_cb) va_arg(ap, libusb_log_cb); } - if (option >= LIBUSB_OPTION_MAX) { - return LIBUSB_ERROR_INVALID_PARAM; - } + do { + if (LIBUSB_SUCCESS != r) { + break; + } - if (NULL == ctx) { - usbi_mutex_static_lock(&default_context_lock); - default_context_options[option].is_set = 1; - if (LIBUSB_OPTION_LOG_LEVEL == option) { - default_context_options[option].arg.ival = arg; + if (option >= LIBUSB_OPTION_MAX) { + r = LIBUSB_ERROR_INVALID_PARAM; + break; } - usbi_mutex_static_unlock(&default_context_lock); - } - ctx = usbi_get_context(ctx); - if (NULL == ctx) { - return LIBUSB_SUCCESS; - } + if (NULL == ctx) { + usbi_mutex_static_lock(&default_context_lock); + default_context_options[option].is_set = 1; + if (LIBUSB_OPTION_LOG_LEVEL == option) { + default_context_options[option].arg.ival = arg; + } else if (LIBUSB_OPTION_LOG_CB == option) { + default_context_options[option].arg.log_cbval = log_cb; + libusb_set_log_cb_internal(NULL, log_cb, LIBUSB_LOG_CB_GLOBAL); + } + usbi_mutex_static_unlock(&default_context_lock); + } - switch (option) { - case LIBUSB_OPTION_LOG_LEVEL: + ctx = usbi_get_context(ctx); + if (NULL == ctx) + break; + + switch (option) { + case LIBUSB_OPTION_LOG_LEVEL: #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - if (!ctx->debug_fixed) - ctx->debug = (enum libusb_log_level)arg; + if (!ctx->debug_fixed) { + ctx->debug = (enum libusb_log_level)arg; + if (is_default_context) + usbi_atomic_store(&default_debug_level, CLAMP(arg, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG)); + } #endif - break; + break; - /* Handle all backend-specific options here */ - case LIBUSB_OPTION_USE_USBDK: - case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: - if (usbi_backend.set_option) - return usbi_backend.set_option(ctx, option, ap); + /* Handle all backend-specific options here */ + case LIBUSB_OPTION_USE_USBDK: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: + if (usbi_backend.set_option) { + r = usbi_backend.set_option(ctx, option, ap); + break; + } - return LIBUSB_ERROR_NOT_SUPPORTED; - break; + r = LIBUSB_ERROR_NOT_SUPPORTED; + break; - case LIBUSB_OPTION_MAX: - default: - return LIBUSB_ERROR_INVALID_PARAM; - } + case LIBUSB_OPTION_LOG_CB: + libusb_set_log_cb_internal(ctx, log_cb, LIBUSB_LOG_CB_CONTEXT); + break; - return LIBUSB_SUCCESS;; + case LIBUSB_OPTION_MAX: /* unreachable */ + default: + r = LIBUSB_ERROR_INVALID_PARAM; + } + } while (0); + + va_end(ap); + + return r; } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) @@ -2264,20 +2393,35 @@ static enum libusb_log_level get_env_debug_level(void) } #endif +/** \ingroup libusb_lib + * Deprecated initialization function. Equivalent to calling libusb_init_context with no options. + * + * \see libusb_init_context + */ +int API_EXPORTED libusb_init(libusb_context **ctx) +{ + return libusb_init_context(ctx, NULL, 0); +} + /** \ingroup libusb_lib * Initialize libusb. This function must be called before calling any other * libusb function. * * If you do not provide an output location for a context pointer, a default * context will be created. If there was already a default context, it will - * be reused (and nothing will be initialized/reinitialized). + * be reused (and nothing will be initialized/reinitialized and options will + * be ignored). If num_options is 0 then options is ignored and may be NULL. + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A * * \param ctx Optional output location for context pointer. * Only valid on return code 0. + * \param options Optional array of options to set on the new context. + * \param num_options Number of elements in the options array. * \returns 0 on success, or a LIBUSB_ERROR code on failure * \see libusb_contexts */ -int API_EXPORTED libusb_init(libusb_context **ctx) +int API_EXPORTED libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options) { size_t priv_size = usbi_backend.context_priv_size; struct libusb_context *_ctx; @@ -2293,10 +2437,12 @@ int API_EXPORTED libusb_init(libusb_context **ctx) } /* check for first init */ + usbi_mutex_static_lock(&active_contexts_lock); if (!active_contexts_list.next) { list_init(&active_contexts_list); usbi_get_monotonic_time(×tamp_origin); } + usbi_mutex_static_unlock(&active_contexts_lock); _ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size); if (!_ctx) { @@ -2305,13 +2451,13 @@ int API_EXPORTED libusb_init(libusb_context **ctx) } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) { - _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival; - } else { + _ctx->debug = LIBUSB_LOG_LEVEL_NONE; + if (getenv("LIBUSB_DEBUG")) { _ctx->debug = get_env_debug_level(); - } - if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE) _ctx->debug_fixed = 1; + } else if (default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) { + _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival; + } #endif usbi_mutex_init(&_ctx->usb_devs_lock); @@ -2324,7 +2470,29 @@ int API_EXPORTED libusb_init(libusb_context **ctx) if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) { continue; } - r = libusb_set_option(_ctx, option); + if (LIBUSB_OPTION_LOG_CB != option) { + r = libusb_set_option(_ctx, option); + } else { + r = libusb_set_option(_ctx, option, default_context_options[option].arg.log_cbval); + } + if (LIBUSB_SUCCESS != r) + goto err_free_ctx; + } + + /* apply any options provided by the user */ + for (int i = 0 ; i < num_options ; ++i) { + switch(options[i].option) { + case LIBUSB_OPTION_LOG_CB: + r = libusb_set_option(_ctx, options[i].option, options[i].value.log_cbval); + break; + + case LIBUSB_OPTION_LOG_LEVEL: + case LIBUSB_OPTION_USE_USBDK: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: + case LIBUSB_OPTION_MAX: + default: + r = libusb_set_option(_ctx, options[i].option, options[i].value.ival); + } if (LIBUSB_SUCCESS != r) goto err_free_ctx; } @@ -2333,6 +2501,9 @@ int API_EXPORTED libusb_init(libusb_context **ctx) if (!ctx) { usbi_default_context = _ctx; default_context_refcnt = 1; +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + usbi_atomic_store(&default_debug_level, _ctx->debug); +#endif usbi_dbg(usbi_default_context, "created default context"); } @@ -2360,8 +2531,12 @@ int API_EXPORTED libusb_init(libusb_context **ctx) *ctx = _ctx; if (!usbi_fallback_context) { +#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) + if (usbi_atomic_load(&default_debug_level) == -1) + usbi_atomic_store(&default_debug_level, _ctx->debug); +#endif usbi_fallback_context = _ctx; - usbi_warn(usbi_fallback_context, "installing new context as implicit default"); + usbi_dbg(usbi_fallback_context, "installing new context as implicit default"); } } @@ -2432,6 +2607,9 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) list_del(&_ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); + /* Exit hotplug before backend dependency */ + usbi_hotplug_exit(_ctx); + if (usbi_backend.exit) usbi_backend.exit(_ctx); @@ -2445,7 +2623,6 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) /* Don't bother with locking after this point because unless there is * an application bug, nobody will be accessing the context. */ - usbi_hotplug_exit(_ctx); usbi_io_exit(_ctx); for_each_device(_ctx, dev) { @@ -2465,7 +2642,7 @@ void API_EXPORTED libusb_exit(libusb_context *ctx) /** \ingroup libusb_misc * Check at runtime if the loaded library has a given capability. - * This call should be performed after \ref libusb_init(), to ensure the + * This call should be performed after \ref libusb_init_context(), to ensure the * backend has updated its capability set. * * \param capability the \ref libusb_capability to check for @@ -2584,13 +2761,14 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, UNUSED(ctx); #else enum libusb_log_level ctx_level; + long default_level_value; - ctx = ctx ? ctx : usbi_default_context; - ctx = ctx ? ctx : usbi_fallback_context; - if (ctx) + if (ctx) { ctx_level = ctx->debug; - else - ctx_level = get_env_debug_level(); + } else { + default_level_value = usbi_atomic_load(&default_debug_level); + ctx_level = default_level_value < 0 ? get_env_debug_level() : (enum libusb_log_level)default_level_value; + } if (ctx_level < level) return; diff --git a/libusb/libusb/descriptor.c b/libusb/libusb/descriptor.c index 253ef1c..4623ad1 100644 --- a/libusb/libusb/descriptor.c +++ b/libusb/libusb/descriptor.c @@ -521,7 +521,7 @@ static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx, * * This is a non-blocking function; the device descriptor is cached in memory. * - * Note since libusb-1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, this + * Note since libusb-1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, this * function always succeeds. * * \param dev the device @@ -548,7 +548,7 @@ int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() * after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state * \returns another LIBUSB_ERROR code on error * \see libusb_get_config_descriptor */ @@ -588,7 +588,7 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() * after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist * \returns another LIBUSB_ERROR code on error * \see libusb_get_active_config_descriptor() * \see libusb_get_config_descriptor_by_value() @@ -634,7 +634,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, * valid if 0 was returned. Must be freed with libusb_free_config_descriptor() * after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist * \returns another LIBUSB_ERROR code on error * \see libusb_get_active_config_descriptor() * \see libusb_get_config_descriptor() @@ -699,7 +699,7 @@ void API_EXPORTED libusb_free_config_descriptor( * descriptor. Only valid if 0 was returned. Must be freed with * libusb_free_ss_endpoint_companion_descriptor() after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the configuration does not exist * \returns another LIBUSB_ERROR code on error */ int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor( @@ -840,7 +840,7 @@ static int parse_bos(struct libusb_context *ctx, * \param bos output location for the BOS descriptor. Only valid if 0 was returned. * Must be freed with \ref libusb_free_bos_descriptor() after use. * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor * \returns another LIBUSB_ERROR code on error */ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, @@ -1070,6 +1070,70 @@ void API_EXPORTED libusb_free_container_id_descriptor( free(container_id); } +/** \ingroup libusb_desc + * Get a platform descriptor + * + * Since version 1.0.27, \ref LIBUSB_API_VERSION >= 0x0100010A + * + * \param ctx the context to operate on, or NULL for the default context + * \param dev_cap Device Capability descriptor with a bDevCapabilityType of + * \ref libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR + * LIBUSB_BT_PLATFORM_DESCRIPTOR + * \param platform_descriptor output location for the Platform descriptor. + * Only valid if 0 was returned. Must be freed with + * libusb_free_platform_descriptor() after use. + * \returns 0 on success + * \returns a LIBUSB_ERROR code on error + */ +int API_EXPORTED libusb_get_platform_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_platform_descriptor **platform_descriptor) +{ + struct libusb_platform_descriptor *_platform_descriptor; + + if (dev_cap->bDevCapabilityType != LIBUSB_BT_PLATFORM_DESCRIPTOR) { + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", + dev_cap->bDevCapabilityType, + LIBUSB_BT_PLATFORM_DESCRIPTOR); + return LIBUSB_ERROR_INVALID_PARAM; + } else if (dev_cap->bLength < LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", + dev_cap->bLength, LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE); + return LIBUSB_ERROR_IO; + } + + _platform_descriptor = malloc(dev_cap->bLength); + if (!_platform_descriptor) + return LIBUSB_ERROR_NO_MEM; + + parse_descriptor(dev_cap, "bbbbu", _platform_descriptor); + + /* Capability data is located after reserved byte and 128-bit UUID */ + uint8_t* capability_data = dev_cap->dev_capability_data + 1 + 16; + + /* Capability data length is total descriptor length minus initial fields */ + size_t capability_data_length = _platform_descriptor->bLength - (16 + 4); + + memcpy(_platform_descriptor->CapabilityData, capability_data, capability_data_length); + + *platform_descriptor = _platform_descriptor; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Free a platform descriptor obtained from + * libusb_get_platform_descriptor(). + * It is safe to call this function with a NULL platform_descriptor parameter, + * in which case the function simply returns. + * + * \param platform_descriptor the Platform descriptor to free + */ +void API_EXPORTED libusb_free_platform_descriptor( + struct libusb_platform_descriptor *platform_descriptor) +{ + free(platform_descriptor); +} + /** \ingroup libusb_desc * Retrieve a string descriptor in C style ASCII. * @@ -1086,7 +1150,7 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha uint8_t desc_index, unsigned char *data, int length) { union usbi_string_desc_buf str; - int r, si, di; + int r; uint16_t langid, wdata; /* Asking for the zero'th index is special - it returns a string @@ -1122,18 +1186,214 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha else if ((str.desc.bLength & 1) || str.desc.bLength != r) usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor (read %d)", str.desc.bLength, r); - di = 0; - for (si = 2; si < str.desc.bLength; si += 2) { - if (di >= (length - 1)) - break; + /* Stop one byte before the end to leave room for null termination. */ + int dest_max = length - 1; + + /* The descriptor has this number of wide characters */ + int src_max = (str.desc.bLength - 1 - 1) / 2; - wdata = libusb_le16_to_cpu(str.desc.wData[di]); + /* Neither read nor write more than the smallest buffer */ + int idx_max = MIN(dest_max, src_max); + + int idx; + for (idx = 0; idx < idx_max; ++idx) { + wdata = libusb_le16_to_cpu(str.desc.wData[idx]); if (wdata < 0x80) - data[di++] = (unsigned char)wdata; + data[idx] = (unsigned char)wdata; else - data[di++] = '?'; /* non-ASCII */ + data[idx] = '?'; /* non-ASCII */ + } + + data[idx] = 0; /* null-terminate string */ + return idx; +} + +static int parse_iad_array(struct libusb_context *ctx, + struct libusb_interface_association_descriptor_array *iad_array, + const uint8_t *buffer, int size) +{ + uint8_t i; + struct usbi_descriptor_header header; + int consumed = 0; + const uint8_t *buf = buffer; + struct libusb_interface_association_descriptor *iad; + + if (size < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "short config descriptor read %d/%d", + size, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } + + // First pass: Iterate through desc list, count number of IADs + iad_array->length = 0; + while (consumed < size) { + parse_descriptor(buf, "bb", &header); + if (header.bLength < 2) { + usbi_err(ctx, "invalid descriptor bLength %d", + header.bLength); + return LIBUSB_ERROR_IO; + } + if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION) + iad_array->length++; + buf += header.bLength; + consumed += header.bLength; + } + + iad_array->iad = NULL; + if (iad_array->length > 0) { + iad = calloc(iad_array->length, sizeof(*iad)); + if (!iad) + return LIBUSB_ERROR_NO_MEM; + + iad_array->iad = iad; + + // Second pass: Iterate through desc list, fill IAD structures + consumed = 0; + i = 0; + while (consumed < size) { + parse_descriptor(buffer, "bb", &header); + if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION) + parse_descriptor(buffer, "bbbbbbbb", &iad[i++]); + buffer += header.bLength; + consumed += header.bLength; + } } - data[di] = 0; - return di; + return LIBUSB_SUCCESS; +} + +static int raw_desc_to_iad_array(struct libusb_context *ctx, const uint8_t *buf, + int size, struct libusb_interface_association_descriptor_array **iad_array) +{ + struct libusb_interface_association_descriptor_array *_iad_array + = calloc(1, sizeof(*_iad_array)); + int r; + + if (!_iad_array) + return LIBUSB_ERROR_NO_MEM; + + r = parse_iad_array(ctx, _iad_array, buf, size); + if (r < 0) { + usbi_err(ctx, "parse_iad_array failed with error %d", r); + free(_iad_array); + return r; + } + + *iad_array = _iad_array; + return LIBUSB_SUCCESS; +} + +/** \ingroup libusb_desc + * Get an array of interface association descriptors (IAD) for a given + * configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param config_index the index of the configuration you wish to retrieve the + * IADs for. + * \param iad_array output location for the array of IADs. Only valid if 0 was + * returned. Must be freed with libusb_free_interface_association_descriptors() + * after use. It's possible that a given configuration contains no IADs. In this + * case the iad_array is still output, but will have 'length' field set to 0, and + * iad field set to NULL. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_active_interface_association_descriptors() + */ +int API_EXPORTED libusb_get_interface_association_descriptors(libusb_device *dev, + uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + if (!iad_array) + return LIBUSB_ERROR_INVALID_PARAM; + + usbi_dbg(DEVICE_CTX(dev), "IADs for config index %u", config_index); + if (config_index >= dev->device_descriptor.bNumConfigurations) + return LIBUSB_ERROR_NOT_FOUND; + + r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_config_descriptor(dev, config_index, buf, config_len); + if (r >= 0) + r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array); + + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Get an array of interface association descriptors (IAD) for the currently + * active configuration. + * This is a non-blocking function which does not involve any requests being + * sent to the device. + * + * \param dev a device + * \param iad_array output location for the array of IADs. Only valid if 0 was + * returned. Must be freed with libusb_free_interface_association_descriptors() + * after use. It's possible that a given configuration contains no IADs. In this + * case the iad_array is still output, but will have 'length' field set to 0, and + * iad field set to NULL. + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state + * \returns another LIBUSB_ERROR code on error + * \see libusb_get_interface_association_descriptors + */ +int API_EXPORTED libusb_get_active_interface_association_descriptors(libusb_device *dev, + struct libusb_interface_association_descriptor_array **iad_array) +{ + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; + int r; + + if (!iad_array) + return LIBUSB_ERROR_INVALID_PARAM; + + r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); + if (!buf) + return LIBUSB_ERROR_NO_MEM; + + r = get_active_config_descriptor(dev, buf, config_len); + if (r >= 0) + r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array); + free(buf); + return r; +} + +/** \ingroup libusb_desc + * Free an array of interface association descriptors (IADs) obtained from + * libusb_get_interface_association_descriptors() or + * libusb_get_active_interface_association_descriptors(). + * It is safe to call this function with a NULL iad_array parameter, in which + * case the function simply returns. + * + * \param iad_array the IAD array to free + */ +void API_EXPORTED libusb_free_interface_association_descriptors( + struct libusb_interface_association_descriptor_array *iad_array) +{ + if (!iad_array) + return; + + if (iad_array->iad) + free((void*)iad_array->iad); + free(iad_array); } diff --git a/libusb/libusb/hotplug.c b/libusb/libusb/hotplug.c index 6b743c7..3c64f69 100644 --- a/libusb/libusb/hotplug.c +++ b/libusb/libusb/hotplug.c @@ -33,7 +33,7 @@ * * \section hotplug_intro Introduction * - * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support + * Version 1.0.16, \ref LIBUSBX_API_VERSION >= 0x01000102, has added support * for hotplug events on some platforms (you should test if your platform * supports hotplug notification by calling \ref libusb_has_capability() with * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). @@ -117,7 +117,7 @@ int main (void) { libusb_hotplug_callback_handle callback_handle; int rc; - libusb_init(NULL); + libusb_init_context(NULL, NULL, 0); rc = libusb_hotplug_register_callback(NULL, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, 0x045a, 0x5005, @@ -311,7 +311,7 @@ void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_ for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) { if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) { usbi_dbg(ctx, "freeing hotplug cb %p with handle %d", - hotplug_cb, hotplug_cb->handle); + (void *) hotplug_cb, hotplug_cb->handle); list_del(&hotplug_cb->list); free(hotplug_cb); } @@ -377,7 +377,8 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, usbi_mutex_unlock(&ctx->hotplug_cbs_lock); - usbi_dbg(ctx, "new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle); + usbi_dbg(ctx, "new hotplug cb %p with handle %d", + (void *) hotplug_cb, hotplug_cb->handle); if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) { ssize_t i, len; diff --git a/libusb/libusb/hotplug.h b/libusb/libusb/hotplug.h deleted file mode 100644 index 161f7e5..0000000 --- a/libusb/libusb/hotplug.h +++ /dev/null @@ -1,105 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ -/* - * Hotplug support for libusb - * Copyright © 2012-2013 Nathan Hjelm - * Copyright © 2012-2013 Peter Stuge - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef USBI_HOTPLUG_H -#define USBI_HOTPLUG_H - -#include "libusbi.h" - -enum usbi_hotplug_flags { - /* This callback is interested in device arrivals */ - USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, - - /* This callback is interested in device removals */ - USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, - - /* IMPORTANT: The values for the below entries must start *after* - * the highest value of the above entries!!! - */ - - /* The vendor_id field is valid for matching */ - USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3), - - /* The product_id field is valid for matching */ - USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4), - - /* The dev_class field is valid for matching */ - USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5), - - /* This callback has been unregistered and needs to be freed */ - USBI_HOTPLUG_NEEDS_FREE = (1U << 6), -}; - -/** \ingroup hotplug - * The hotplug callback structure. The user populates this structure with - * libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback() - * to receive notification of hotplug events. - */ -struct libusb_hotplug_callback { - /** Flags that control how this callback behaves */ - uint8_t flags; - - /** Vendor ID to match (if flags says this is valid) */ - uint16_t vendor_id; - - /** Product ID to match (if flags says this is valid) */ - uint16_t product_id; - - /** Device class to match (if flags says this is valid) */ - uint8_t dev_class; - - /** Callback function to invoke for matching event/device */ - libusb_hotplug_callback_fn cb; - - /** Handle for this callback (used to match on deregister) */ - libusb_hotplug_callback_handle handle; - - /** User data that will be passed to the callback function */ - void *user_data; - - /** List this callback is registered in (ctx->hotplug_cbs) */ - struct list_head list; -}; - -struct libusb_hotplug_message { - /** The hotplug event that occurred */ - libusb_hotplug_event event; - - /** The device for which this hotplug event occurred */ - struct libusb_device *device; - - /** List this message is contained in (ctx->hotplug_msgs) */ - struct list_head list; -}; - -#define for_each_hotplug_cb(ctx, c) \ - for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback) - -#define for_each_hotplug_cb_safe(ctx, c, n) \ - for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback) - -void usbi_hotplug_deregister(struct libusb_context *ctx, int forced); -void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, - libusb_hotplug_event event); -void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, - libusb_hotplug_event event); - -#endif diff --git a/libusb/libusb/io.c b/libusb/libusb/io.c index 9e3146c..ab84ba6 100644 --- a/libusb/libusb/io.c +++ b/libusb/libusb/io.c @@ -3,8 +3,8 @@ * I/O functions for libusb * Copyright © 2007-2009 Daniel Drake * Copyright © 2001 Johannes Erdfelt - * Copyright © 2019 Nathan Hjelm - * Copyright © 2019 Google LLC. All rights reserved. + * Copyright © 2019-2022 Nathan Hjelm + * Copyright © 2019-2022 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -313,6 +313,10 @@ if (r == 0 && actual_length == sizeof(data)) { * be invoked, and the callback function should check the transfer status to * determine that it was cancelled. * + * On macOS and iOS it is not possible to cancel a single transfer. In this + * case cancelling one transfer on an endpoint will cause all transfers on + * that endpoint to be cancelled. + * * Freeing the transfer after it has been cancelled but before cancellation * has completed will result in undefined behaviour. * @@ -1240,8 +1244,8 @@ void usbi_io_exit(struct libusb_context *ctx) static void calculate_timeout(struct usbi_transfer *itransfer) { - unsigned int timeout = - USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout; + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + unsigned int timeout = transfer->timeout; if (!timeout) { TIMESPEC_CLEAR(&itransfer->timeout); @@ -1285,30 +1289,25 @@ DEFAULT_VISIBILITY struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( int iso_packets) { - size_t priv_size; - size_t alloc_size; - unsigned char *ptr; - struct usbi_transfer *itransfer; - struct libusb_transfer *transfer; - assert(iso_packets >= 0); if (iso_packets < 0) return NULL; - priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); - alloc_size = priv_size - + sizeof(struct usbi_transfer) - + sizeof(struct libusb_transfer) - + (sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets); - ptr = calloc(1, alloc_size); + size_t priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); + size_t usbi_transfer_size = PTR_ALIGN(sizeof(struct usbi_transfer)); + size_t libusb_transfer_size = PTR_ALIGN(sizeof(struct libusb_transfer)); + size_t iso_packets_size = sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets; + size_t alloc_size = priv_size + usbi_transfer_size + libusb_transfer_size + iso_packets_size; + unsigned char *ptr = calloc(1, alloc_size); if (!ptr) return NULL; - itransfer = (struct usbi_transfer *)(ptr + priv_size); + struct usbi_transfer *itransfer = (struct usbi_transfer *)(ptr + priv_size); itransfer->num_iso_packets = iso_packets; itransfer->priv = ptr; usbi_mutex_init(&itransfer->lock); - transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + return transfer; } @@ -1331,31 +1330,26 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( */ void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) { - struct usbi_transfer *itransfer; - size_t priv_size; - unsigned char *ptr; - if (!transfer) return; - usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer); + usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", (void *) transfer); if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) free(transfer->buffer); - itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); + struct usbi_transfer *itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); usbi_mutex_destroy(&itransfer->lock); if (itransfer->dev) libusb_unref_device(itransfer->dev); - priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); - ptr = (unsigned char *)itransfer - priv_size; + unsigned char *ptr = USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer); assert(ptr == itransfer->priv); free(ptr); } /* iterates through the flying transfers, and rearms the timer based on the * next upcoming timeout. - * must be called with flying_list locked. + * NB: flying_transfers_lock must be held when calling this. * returns 0 on success or a LIBUSB_ERROR code on failure. */ #ifdef HAVE_OS_TIMER @@ -1376,7 +1370,8 @@ static int arm_timer_for_next_timeout(struct libusb_context *ctx) /* act on first transfer that has not already been handled */ if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) { - usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + usbi_dbg(ctx, "next timeout originally %ums", transfer->timeout); return usbi_arm_timer(&ctx->timer, cur_ts); } } @@ -1394,7 +1389,8 @@ static inline int arm_timer_for_next_timeout(struct libusb_context *ctx) /* add a transfer to the (timeout-sorted) active transfers list. * This function will return non 0 if fails to update the timer, - * in which case the transfer is *not* on the flying_transfers list. */ + * in which case the transfer is *not* on the flying_transfers list. + * NB: flying_transfers_lock MUST be held when calling this. */ static int add_to_flying_list(struct usbi_transfer *itransfer) { struct usbi_transfer *cur; @@ -1438,8 +1434,9 @@ static int add_to_flying_list(struct usbi_transfer *itransfer) if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) { /* if this transfer has the lowest timeout of all active transfers, * rearm the timer with this transfer's timeout */ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)", - USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); + transfer->timeout); r = usbi_arm_timer(&ctx->timer, timeout); } #else @@ -1455,20 +1452,19 @@ static int add_to_flying_list(struct usbi_transfer *itransfer) /* remove a transfer from the active transfers list. * This function will *always* remove the transfer from the * flying_transfers list. It will return a LIBUSB_ERROR code - * if it fails to update the timer for the next timeout. */ + * if it fails to update the timer for the next timeout. + * NB: flying_transfers_lock MUST be held when calling this. */ static int remove_from_flying_list(struct usbi_transfer *itransfer) { struct libusb_context *ctx = ITRANSFER_CTX(itransfer); int rearm_timer; int r = 0; - usbi_mutex_lock(&ctx->flying_transfers_lock); rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) && list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer); list_del(&itransfer->list); if (rearm_timer) r = arm_timer_for_next_timeout(ctx); - usbi_mutex_unlock(&ctx->flying_transfers_lock); return r; } @@ -1479,11 +1475,11 @@ static int remove_from_flying_list(struct usbi_transfer *itransfer) * * \param transfer the transfer to submit * \returns 0 on success - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if the transfer has already been submitted. - * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if the transfer has already been submitted. + * \returns \ref LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported * by the operating system. - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failure */ @@ -1500,7 +1496,7 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) itransfer->dev = libusb_ref_device(transfer->dev_handle->dev); ctx = HANDLE_CTX(transfer->dev_handle); - usbi_dbg(ctx, "transfer %p", transfer); + usbi_dbg(ctx, "transfer %p", (void *) transfer); /* * Important note on locking, this function takes / releases locks @@ -1562,8 +1558,11 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) } usbi_mutex_unlock(&itransfer->lock); - if (r != LIBUSB_SUCCESS) + if (r != LIBUSB_SUCCESS) { + usbi_mutex_lock(&ctx->flying_transfers_lock); remove_from_flying_list(itransfer); + usbi_mutex_unlock(&ctx->flying_transfers_lock); + } return r; } @@ -1584,21 +1583,23 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) * \ref libusb_transfer_status::LIBUSB_TRANSFER_CANCELLED * "LIBUSB_TRANSFER_CANCELLED" for each transfer that was cancelled. - * - Calling this function also sends a \c ClearFeature(ENDPOINT_HALT) request - * for the transfer's endpoint. If the device does not handle this request - * correctly, the data toggle bits for the endpoint can be left out of sync - * between host and device, which can have unpredictable results when the - * next data is sent on the endpoint, including data being silently lost. - * A call to \ref libusb_clear_halt will not resolve this situation, since - * that function uses the same request. Therefore, if your program runs on - * Darwin and uses a device that does not correctly implement - * \c ClearFeature(ENDPOINT_HALT) requests, it may only be safe to cancel - * transfers when followed by a device reset using + * - When built for macOS versions prior to 10.5, this function sends a + * \c ClearFeature(ENDPOINT_HALT) request for the transfer's endpoint. + * (Prior to libusb 1.0.27, this request was sent on all Darwin systems.) + * If the device does not handle this request correctly, the data toggle + * bits for the endpoint can be left out of sync between host and device, + * which can have unpredictable results when the next data is sent on + * the endpoint, including data being silently lost. A call to + * \ref libusb_clear_halt will not resolve this situation, since that + * function uses the same request. Therefore, if your program runs on + * macOS < 10.5 (or libusb < 1.0.27), and uses a device that does not + * correctly implement \c ClearFeature(ENDPOINT_HALT) requests, it may + * only be safe to cancel transfers when followed by a device reset using * \ref libusb_reset_device. * * \param transfer the transfer to cancel * \returns 0 on success - * \returns LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress, + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the transfer is not in progress, * already complete, or already cancelled. * \returns a LIBUSB_ERROR code on failure */ @@ -1609,7 +1610,7 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer) struct libusb_context *ctx = ITRANSFER_CTX(itransfer); int r; - usbi_dbg(ctx, "transfer %p", transfer ); + usbi_dbg(ctx, "transfer %p", (void *) transfer ); usbi_mutex_lock(&itransfer->lock); if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT) || (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) { @@ -1689,7 +1690,9 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, uint8_t flags; int r; + usbi_mutex_lock(&ctx->flying_transfers_lock); r = remove_from_flying_list(itransfer); + usbi_mutex_unlock(&ctx->flying_transfers_lock); if (r < 0) usbi_err(ctx, "failed to set timer for next timeout"); @@ -1711,9 +1714,13 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, flags = transfer->flags; transfer->status = status; transfer->actual_length = itransfer->transferred; - usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback); - if (transfer->callback) + usbi_dbg(ctx, "transfer %p has callback %p", + (void *) transfer, transfer->callback); + if (transfer->callback) { + libusb_lock_event_waiters (ctx); transfer->callback(transfer); + libusb_unlock_event_waiters(ctx); + } /* transfer might have been freed by the above call, do not use from * this point. */ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) @@ -2013,7 +2020,7 @@ void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx) * indicates unlimited timeout. * \returns 0 after a transfer completes or another thread stops event handling * \returns 1 if the timeout expired - * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid * \ref libusb_mtasync */ int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) @@ -2037,6 +2044,7 @@ int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) return 0; } +// NB: flying_transfers_lock must be held when calling this static void handle_timeout(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = @@ -2052,6 +2060,7 @@ static void handle_timeout(struct usbi_transfer *itransfer) "async cancel failed %d", r); } +// NB: flying_transfers_lock must be held when calling this static void handle_timeouts_locked(struct libusb_context *ctx) { struct timespec systime; @@ -2332,7 +2341,7 @@ static int get_next_timeout(libusb_context *ctx, struct timeval *tv, * timeval struct for non-blocking mode * \param completed pointer to completion integer to check, or NULL * \returns 0 on success - * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid * \returns another LIBUSB_ERROR code on other failure * \ref libusb_mtasync */ @@ -2474,7 +2483,7 @@ int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx, * \param tv the maximum time to block waiting for events, or zero for * non-blocking mode * \returns 0 on success - * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if timeval is invalid * \returns another LIBUSB_ERROR code on other failure * \ref libusb_mtasync */ @@ -2558,7 +2567,7 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) * \param tv output location for a relative time against the current * clock in which libusb must be called into in order to process timeout events * \returns 0 if there are no pending timeouts, 1 if a timeout was returned, - * or LIBUSB_ERROR_OTHER on failure + * or \ref LIBUSB_ERROR_OTHER on failure */ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv) @@ -2619,11 +2628,11 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, * To remove notifiers, pass NULL values for the function pointers. * * Note that file descriptors may have been added even before you register - * these notifiers (e.g. at libusb_init() time). + * these notifiers (e.g. at libusb_init_context() time). * * Additionally, note that the removal notifier may be called during * libusb_exit() (e.g. when it is closing file descriptors that were opened - * and added to the poll set at libusb_init() time). If you don't want this, + * and added to the poll set at libusb_init_context() time). If you don't want this, * remove the notifiers immediately before calling libusb_exit(). * * \param ctx the context to operate on, or NULL for the default context @@ -2827,7 +2836,8 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) to_cancel = NULL; usbi_mutex_lock(&ctx->flying_transfers_lock); for_each_transfer(ctx, cur) { - if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) { + struct libusb_transfer *cur_transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur); + if (cur_transfer->dev_handle == dev_handle) { usbi_mutex_lock(&cur->lock); if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT) to_cancel = cur; @@ -2842,8 +2852,9 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) if (!to_cancel) break; + struct libusb_transfer *transfer_to_cancel = USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel); usbi_dbg(ctx, "cancelling transfer %p from disconnect", - USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel)); + (void *) transfer_to_cancel); usbi_mutex_lock(&to_cancel->lock); usbi_backend.clear_transfer_priv(to_cancel); diff --git a/libusb/libusb/libusb.h b/libusb/libusb/libusb.h index 2592ea7..f4e9203 100644 --- a/libusb/libusb/libusb.h +++ b/libusb/libusb/libusb.h @@ -3,9 +3,9 @@ * Copyright © 2001 Johannes Erdfelt * Copyright © 2007-2008 Daniel Drake * Copyright © 2012 Pete Batard - * Copyright © 2012-2018 Nathan Hjelm + * Copyright © 2012-2023 Nathan Hjelm * Copyright © 2014-2020 Chris Dickens - * For more information, please visit: http://libusb.info + * For more information, please visit: https://libusb.info * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,9 +50,9 @@ typedef SSIZE_T ssize_t; #include #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) -#define ZERO_SIZED_ARRAY /* [] - valid C99 code */ +#define LIBUSB_FLEXIBLE_ARRAY /* [] - valid C99 code */ #else -#define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */ +#define LIBUSB_FLEXIBLE_ARRAY 0 /* [0] - non-standard, but usually working code */ #endif /* __STDC_VERSION__ */ /* 'interface' might be defined as a macro on Windows, so we need to @@ -74,6 +74,8 @@ typedef SSIZE_T ssize_t; #define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead"))) #elif defined(__GNUC__) && (__GNUC__ >= 3) #define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated)) +#elif defined(_MSC_VER) +#define LIBUSB_DEPRECATED_FOR(f) __declspec(deprecated("Use " #f " instead")) #else #define LIBUSB_DEPRECATED_FOR(f) #endif /* __GNUC__ */ @@ -118,20 +120,25 @@ typedef SSIZE_T ssize_t; */ #if defined(_WIN32) || defined(__CYGWIN__) #define LIBUSB_CALL WINAPI +#define LIBUSB_CALLV WINAPIV #else #define LIBUSB_CALL +#define LIBUSB_CALLV #endif /* _WIN32 || __CYGWIN__ */ /** \def LIBUSB_API_VERSION * \ingroup libusb_misc * libusb's API version. * - * Since version 1.0.13, to help with feature detection, libusb defines + * Since version 1.0.18, to help with feature detection, libusb defines * a LIBUSB_API_VERSION macro that gets increased every time there is a * significant change to the API, such as the introduction of a new call, * the definition of a new macro/enum member, or any other element that * libusb applications may want to detect at compilation time. * + * Between versions 1.0.13 and 1.0.17 (inclusive) the older spelling of + * LIBUSBX_API_VERSION was used. + * * The macro is typically used in an application as follows: * \code * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) @@ -141,10 +148,34 @@ typedef SSIZE_T ssize_t; * * Internally, LIBUSB_API_VERSION is defined as follows: * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + * + * The incremental component has changed as follows: + *
    + *
  • libusbx version 1.0.13: LIBUSBX_API_VERSION = 0x01000100 + *
  • libusbx version 1.0.14: LIBUSBX_API_VERSION = 0x010000FF + *
  • libusbx version 1.0.15: LIBUSBX_API_VERSION = 0x01000101 + *
  • libusbx version 1.0.16: LIBUSBX_API_VERSION = 0x01000102 + *
  • libusbx version 1.0.17: LIBUSBX_API_VERSION = 0x01000102 + *
  • libusb version 1.0.18: LIBUSB_API_VERSION = 0x01000102 + *
  • libusb version 1.0.19: LIBUSB_API_VERSION = 0x01000103 + *
  • libusb version 1.0.20: LIBUSB_API_VERSION = 0x01000104 + *
  • libusb version 1.0.21: LIBUSB_API_VERSION = 0x01000105 + *
  • libusb version 1.0.22: LIBUSB_API_VERSION = 0x01000106 + *
  • libusb version 1.0.23: LIBUSB_API_VERSION = 0x01000107 + *
  • libusb version 1.0.24: LIBUSB_API_VERSION = 0x01000108 + *
  • libusb version 1.0.25: LIBUSB_API_VERSION = 0x01000109 + *
  • libusb version 1.0.26: LIBUSB_API_VERSION = 0x01000109 + *
  • libusb version 1.0.27: LIBUSB_API_VERSION = 0x0100010A + *
*/ -#define LIBUSB_API_VERSION 0x01000109 +#define LIBUSB_API_VERSION 0x0100010A -/* The following is kept for compatibility, but will be deprecated in the future */ +/** \def LIBUSBX_API_VERSION + * \ingroup libusb_misc + * + * This is the older spelling, kept for backwards compatibility of code + * needing to test for older library versions where the newer spelling + * did not exist. */ #define LIBUSBX_API_VERSION LIBUSB_API_VERSION #if defined(__cplusplus) @@ -265,6 +296,10 @@ enum libusb_descriptor_type { /** Endpoint descriptor. See libusb_endpoint_descriptor. */ LIBUSB_DT_ENDPOINT = 0x05, + /** Interface Association Descriptor. + * See libusb_interface_association_descriptor */ + LIBUSB_DT_INTERFACE_ASSOCIATION = 0x0b, + /** BOS descriptor */ LIBUSB_DT_BOS = 0x0f, @@ -305,6 +340,7 @@ enum libusb_descriptor_type { #define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 #define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 #define LIBUSB_BT_CONTAINER_ID_SIZE 20 +#define LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE 20 /* We unwrap the BOS => define its max size */ #define LIBUSB_DT_BOS_MAX_SIZE \ @@ -523,7 +559,10 @@ enum libusb_bos_type { LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03, /** Container ID type */ - LIBUSB_BT_CONTAINER_ID = 0x04 + LIBUSB_BT_CONTAINER_ID = 0x04, + + /** Platform descriptor */ + LIBUSB_BT_PLATFORM_DESCRIPTOR = 0x05 }; /** \ingroup libusb_desc @@ -628,6 +667,65 @@ struct libusb_endpoint_descriptor { int extra_length; }; +/** \ingroup libusb_desc + * A structure representing the standard USB interface association descriptor. + * This descriptor is documented in section 9.6.4 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_association_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE_ASSOCIATION + * LIBUSB_DT_INTERFACE_ASSOCIATION in this context. */ + uint8_t bDescriptorType; + + /** Interface number of the first interface that is associated + * with this function */ + uint8_t bFirstInterface; + + /** Number of contiguous interfaces that are associated with + * this function */ + uint8_t bInterfaceCount; + + /** USB-IF class code for this function. + * A value of zero is not allowed in this descriptor. + * If this field is 0xff, the function class is vendor-specific. + * All other values are reserved for assignment by the USB-IF. + */ + uint8_t bFunctionClass; + + /** USB-IF subclass code for this function. + * If this field is not set to 0xff, all values are reserved + * for assignment by the USB-IF + */ + uint8_t bFunctionSubClass; + + /** USB-IF protocol code for this function. + * These codes are qualified by the values of the bFunctionClass + * and bFunctionSubClass fields. + */ + uint8_t bFunctionProtocol; + + /** Index of string descriptor describing this function */ + uint8_t iFunction; +}; + +/** \ingroup libusb_desc + * Structure containing an array of 0 or more interface association + * descriptors + */ +struct libusb_interface_association_descriptor_array { + /** Array of interface association descriptors. The size of this array + * is determined by the length field. + */ + const struct libusb_interface_association_descriptor *iad; + + /** Number of interface association descriptors contained. Read-only. */ + int length; +}; + /** \ingroup libusb_desc * A structure representing the standard USB interface descriptor. This * descriptor is documented in section 9.6.5 of the USB 3.0 specification. @@ -786,7 +884,7 @@ struct libusb_bos_dev_capability_descriptor { uint8_t bDevCapabilityType; /** Device Capability data (bLength - 3 bytes) */ - uint8_t dev_capability_data[ZERO_SIZED_ARRAY]; + uint8_t dev_capability_data[LIBUSB_FLEXIBLE_ARRAY]; }; /** \ingroup libusb_desc @@ -811,7 +909,7 @@ struct libusb_bos_descriptor { uint8_t bNumDeviceCaps; /** bNumDeviceCap Device Capability Descriptors */ - struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY]; + struct libusb_bos_dev_capability_descriptor *dev_capability[LIBUSB_FLEXIBLE_ARRAY]; }; /** \ingroup libusb_desc @@ -908,6 +1006,34 @@ struct libusb_container_id_descriptor { uint8_t ContainerID[16]; }; +/** \ingroup libusb_desc + * A structure representing a Platform descriptor. + * This descriptor is documented in section 9.6.2.4 of the USB 3.2 specification. + */ +struct libusb_platform_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_PLATFORM_DESCRIPTOR + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t PlatformCapabilityUUID[16]; + + /** Capability data (bLength - 20) */ + uint8_t CapabilityData[LIBUSB_FLEXIBLE_ARRAY]; +}; + /** \ingroup libusb_asyncio * Setup packet for control transfers. */ #if defined(_MSC_VER) || defined(__WATCOMC__) @@ -982,7 +1108,7 @@ struct libusb_version { * libusb_exit() will not destroy resources that the other user is still * using. * - * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * Sessions are created by libusb_init_context() and destroyed through libusb_exit(). * If your application is guaranteed to only ever include a single libusb * user (i.e. you), you do not have to worry about contexts: pass NULL in * every function call where a context is required, and the default context @@ -1187,7 +1313,8 @@ enum libusb_transfer_flags { * * This flag is currently only supported on Linux. * On other systems, libusb_submit_transfer() will return - * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * \ref LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this + * flag is set. * * Available since libusb-1.0.9. */ @@ -1284,7 +1411,7 @@ struct libusb_transfer { int num_iso_packets; /** Isochronous packet descriptors, for isochronous transfers only. */ - struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY]; + struct libusb_iso_packet_descriptor iso_packet_desc[LIBUSB_FLEXIBLE_ARRAY]; }; /** \ingroup libusb_misc @@ -1345,6 +1472,79 @@ enum libusb_log_cb_mode { LIBUSB_LOG_CB_CONTEXT = (1 << 1) }; +/** \ingroup libusb_lib + * Available option values for libusb_set_option() and libusb_init_context(). + */ +enum libusb_option { + /** Set the log message verbosity. + * + * This option must be provided an argument of type \ref libusb_log_level. + * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever + * printed. If you choose to increase the message verbosity level, ensure + * that your application does not close the stderr file descriptor. + * + * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative + * with its message logging and most of the time, will only log messages that + * explain error conditions and other oddities. This will help you debug + * your software. + * + * If the LIBUSB_DEBUG environment variable was set when libusb was + * initialized, this option does nothing: the message verbosity is fixed + * to the value in the environment variable. + * + * If libusb was compiled without any message logging, this option does + * nothing: you'll never get any messages. + * + * If libusb was compiled with verbose debug message logging, this option + * does nothing: you'll always get messages from all levels. + */ + LIBUSB_OPTION_LOG_LEVEL = 0, + + /** Use the UsbDk backend for a specific context, if available. + * + * This option should be set at initialization with libusb_init_context() + * otherwise unspecified behavior may occur. + * + * Only valid on Windows. Ignored on all other platforms. + */ + LIBUSB_OPTION_USE_USBDK = 1, + + /** Do not scan for devices + * + * With this option set, libusb will skip scanning devices in + * libusb_init_context(). + * + * Hotplug functionality will also be deactivated. + * + * The option is useful in combination with libusb_wrap_sys_device(), + * which can access a device directly without prior device scanning. + * + * This is typically needed on Android, where access to USB devices + * is limited. + * + * This option should only be used with libusb_init_context() + * otherwise unspecified behavior may occur. + * + * Only valid on Linux. Ignored on all other platforms. + */ + LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, + +#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY + + /** Set the context log callback function. + * + * Set the log callback function either on a context or globally. This + * option must be provided an argument of type \ref libusb_log_cb. + * Using this option with a NULL context is equivalent to calling + * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_GLOBAL. + * Using it with a non-NULL context is equivalent to calling + * libusb_set_log_cb() with mode \ref LIBUSB_LOG_CB_CONTEXT. + */ + LIBUSB_OPTION_LOG_CB = 3, + + LIBUSB_OPTION_MAX = 4 +}; + /** \ingroup libusb_lib * Callback function for handling log messages. * \param ctx the context which is related to the log message, or NULL if it @@ -1359,10 +1559,25 @@ enum libusb_log_cb_mode { typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx, enum libusb_log_level level, const char *str); +/** \ingroup libusb_lib + * Structure used for setting options through \ref libusb_init_context. + * + */ +struct libusb_init_option { + /** Which option to set */ + enum libusb_option option; + /** An integer value used by the option (if applicable). */ + union { + int ival; + libusb_log_cb log_cbval; + } value; +}; + int LIBUSB_CALL libusb_init(libusb_context **ctx); +int LIBUSB_CALL libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options); void LIBUSB_CALL libusb_exit(libusb_context *ctx); -LIBUSB_DEPRECATED_FOR(libusb_set_option) void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +/* may be deprecated in the future in favor of lubusb_init_context()+libusb_set_option() */ void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode); const struct libusb_version * LIBUSB_CALL libusb_get_version(void); int LIBUSB_CALL libusb_has_capability(uint32_t capability); @@ -1415,6 +1630,11 @@ int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx, struct libusb_container_id_descriptor **container_id); void LIBUSB_CALL libusb_free_container_id_descriptor( struct libusb_container_id_descriptor *container_id); +int LIBUSB_CALL libusb_get_platform_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_platform_descriptor **platform_descriptor); +void LIBUSB_CALL libusb_free_platform_descriptor( + struct libusb_platform_descriptor *platform_descriptor); uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len); @@ -1427,6 +1647,15 @@ int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint); int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_alt_packet_size(libusb_device *dev, + int interface_number, int alternate_setting, unsigned char endpoint); + +int LIBUSB_CALL libusb_get_interface_association_descriptors(libusb_device *dev, + uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array); +int LIBUSB_CALL libusb_get_active_interface_association_descriptors(libusb_device *dev, + struct libusb_interface_association_descriptor_array **iad_array); +void LIBUSB_CALL libusb_free_interface_association_descriptors( + struct libusb_interface_association_descriptor_array *iad_array); int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle); int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); @@ -2036,7 +2265,7 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, * \param[in] cb_fn the function to be invoked on a matching event/device * \param[in] user_data user data to pass to the callback function * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL) - * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + * \returns \ref LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure */ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, int events, int flags, @@ -2069,67 +2298,7 @@ void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, libusb_hotplug_callback_handle callback_handle); -/** \ingroup libusb_lib - * Available option values for libusb_set_option(). - */ -enum libusb_option { - /** Set the log message verbosity. - * - * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever - * printed. If you choose to increase the message verbosity level, ensure - * that your application does not close the stderr file descriptor. - * - * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative - * with its message logging and most of the time, will only log messages that - * explain error conditions and other oddities. This will help you debug - * your software. - * - * If the LIBUSB_DEBUG environment variable was set when libusb was - * initialized, this function does nothing: the message verbosity is fixed - * to the value in the environment variable. - * - * If libusb was compiled without any message logging, this function does - * nothing: you'll never get any messages. - * - * If libusb was compiled with verbose debug message logging, this function - * does nothing: you'll always get messages from all levels. - */ - LIBUSB_OPTION_LOG_LEVEL = 0, - - /** Use the UsbDk backend for a specific context, if available. - * - * This option should be set immediately after calling libusb_init(), otherwise - * unspecified behavior may occur. - * - * Only valid on Windows. - */ - LIBUSB_OPTION_USE_USBDK = 1, - - /** Do not scan for devices - * - * With this option set, libusb will skip scanning devices in - * libusb_init(). Must be set before calling libusb_init(). - * - * Hotplug functionality will also be deactivated. - * - * The option is useful in combination with libusb_wrap_sys_device(), - * which can access a device directly without prior device scanning. - * - * This is typically needed on Android, where access to USB devices - * is limited. - * - * For LIBUSB_API_VERSION 0x01000108 it was called LIBUSB_OPTION_WEAK_AUTHORITY - * - * Only valid on Linux. - */ - LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, - -#define LIBUSB_OPTION_WEAK_AUTHORITY LIBUSB_OPTION_NO_DEVICE_DISCOVERY - - LIBUSB_OPTION_MAX = 3 -}; - -int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); +int LIBUSB_CALLV libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); #ifdef _MSC_VER #pragma warning(pop) diff --git a/libusb/libusb/libusbi.h b/libusb/libusb/libusbi.h index b1fc88c..3b0c610 100644 --- a/libusb/libusb/libusbi.h +++ b/libusb/libusb/libusbi.h @@ -73,7 +73,7 @@ #endif /* The following is used to silence warnings for unused variables */ -#if defined(UNREFERENCED_PARAMETER) +#if defined(UNREFERENCED_PARAMETER) && !defined(__GNUC__) #define UNUSED(var) UNREFERENCED_PARAMETER(var) #else #define UNUSED(var) do { (void)(var); } while(0) @@ -128,6 +128,7 @@ typedef atomic_long usbi_atomic_t; * return_type LIBUSB_CALL function_name(params); */ #define API_EXPORTED LIBUSB_CALL DEFAULT_VISIBILITY +#define API_EXPORTEDV LIBUSB_CALLV DEFAULT_VISIBILITY #ifdef __cplusplus extern "C" { @@ -321,10 +322,10 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, #else /* ENABLE_LOGGING */ -#define usbi_err(ctx, ...) UNUSED(ctx) -#define usbi_warn(ctx, ...) UNUSED(ctx) -#define usbi_info(ctx, ...) UNUSED(ctx) -#define usbi_dbg(ctx, ...) do {} while (0) +#define usbi_err(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_warn(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_info(ctx, ...) do { (void)(ctx); } while(0) +#define usbi_dbg(ctx, ...) do { (void)(ctx); } while(0) #endif /* ENABLE_LOGGING */ @@ -379,7 +380,7 @@ struct libusb_context { struct list_head flying_transfers; /* Note paths taking both this and usbi_transfer->lock must always * take this lock first */ - usbi_mutex_t flying_transfers_lock; + usbi_mutex_t flying_transfers_lock; /* for flying_transfers and timeout_flags */ #if !defined(PLATFORM_WINDOWS) /* user callbacks for pollfd changes */ @@ -533,7 +534,7 @@ static inline void usbi_localize_device_descriptor(struct libusb_device_descript desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice); } -#ifdef HAVE_CLOCK_GETTIME +#if defined(HAVE_CLOCK_GETTIME) && !defined(__APPLE__) static inline void usbi_get_monotonic_time(struct timespec *tp) { ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, tp), 0); @@ -562,8 +563,11 @@ void usbi_get_real_time(struct timespec *tp); * 2. struct usbi_transfer * 3. struct libusb_transfer (which includes iso packets) [variable size] * - * from a libusb_transfer, you can get the usbi_transfer by rewinding the - * appropriate number of bytes. + * You can convert between them with the macros: + * TRANSFER_PRIV_TO_USBI_TRANSFER + * USBI_TRANSFER_TO_TRANSFER_PRIV + * USBI_TRANSFER_TO_LIBUSB_TRANSFER + * LIBUSB_TRANSFER_TO_USBI_TRANSFER */ struct usbi_transfer { @@ -574,7 +578,7 @@ struct usbi_transfer { int transferred; uint32_t stream_id; uint32_t state_flags; /* Protected by usbi_transfer->lock */ - uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ + uint32_t timeout_flags; /* Protected by the flying_transfers_lock */ /* The device reference is held until destruction for logging * even after dev_handle is set to NULL. */ @@ -616,10 +620,21 @@ enum usbi_transfer_timeout_flags { USBI_TRANSFER_TIMED_OUT = 1U << 2, }; +#define TRANSFER_PRIV_TO_USBI_TRANSFER(transfer_priv) \ + ((struct usbi_transfer *) \ + ((unsigned char *)(transfer_priv) \ + + PTR_ALIGN(sizeof(*transfer_priv)))) + +#define USBI_TRANSFER_TO_TRANSFER_PRIV(itransfer) \ + ((unsigned char *) \ + ((unsigned char *)(itransfer) \ + - PTR_ALIGN(usbi_backend.transfer_priv_size))) + #define USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer) \ ((struct libusb_transfer *) \ ((unsigned char *)(itransfer) \ + PTR_ALIGN(sizeof(struct usbi_transfer)))) + #define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ ((struct usbi_transfer *) \ ((unsigned char *)(transfer) \ @@ -678,7 +693,7 @@ struct usbi_interface_descriptor { struct usbi_string_descriptor { uint8_t bLength; uint8_t bDescriptorType; - uint16_t wData[ZERO_SIZED_ARRAY]; + uint16_t wData[LIBUSB_FLEXIBLE_ARRAY]; } LIBUSB_PACKED; struct usbi_bos_descriptor { @@ -813,6 +828,7 @@ struct usbi_option { int is_set; union { int ival; + libusb_log_cb log_cbval; } arg; }; @@ -891,7 +907,7 @@ static inline void *usbi_get_transfer_priv(struct usbi_transfer *itransfer) struct discovered_devs { size_t len; size_t capacity; - struct libusb_device *devices[ZERO_SIZED_ARRAY]; + struct libusb_device *devices[LIBUSB_FLEXIBLE_ARRAY]; }; struct discovered_devs *discovered_devs_append( @@ -1180,6 +1196,8 @@ struct usbi_os_backend { * claiming, no other drivers/applications can use the interface because * we now "own" it. * + * This function gets called with dev_handle->lock locked! + * * Return: * - 0 on success * - LIBUSB_ERROR_NOT_FOUND if the interface does not exist @@ -1199,6 +1217,8 @@ struct usbi_os_backend { * You will only ever be asked to release an interface which was * successfully claimed earlier. * + * This function gets called with dev_handle->lock locked! + * * Return: * - 0 on success * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since it @@ -1335,7 +1355,7 @@ struct usbi_os_backend { * * This function must not block. * - * This function gets called with the flying_transfers_lock locked! + * This function gets called with itransfer->lock locked! * * Return: * - 0 on success @@ -1349,6 +1369,8 @@ struct usbi_os_backend { * This function must not block. The transfer cancellation must complete * later, resulting in a call to usbi_handle_transfer_cancellation() * from the context of handle_events. + * + * This function gets called with itransfer->lock locked! */ int (*cancel_transfer)(struct usbi_transfer *itransfer); diff --git a/libusb/libusb/os/events_posix.c b/libusb/libusb/os/events_posix.c index 715a2d5..4056dae 100644 --- a/libusb/libusb/os/events_posix.c +++ b/libusb/libusb/os/events_posix.c @@ -28,6 +28,31 @@ #ifdef HAVE_TIMERFD #include #endif + +#ifdef __EMSCRIPTEN__ +/* On Emscripten `pipe` does not conform to the spec and does not block + * until events are available, which makes it unusable for event system + * and often results in deadlocks when `pipe` is in a loop like it is + * in libusb. + * + * Therefore use a custom event system based on browser event emitters. */ +#include +#include +#include + +EM_ASYNC_JS(void, em_libusb_wait_async, (const _Atomic int* ptr, int expected_value, int timeout), { + await Atomics.waitAsync(HEAP32, ptr >> 2, expected_value, timeout).value; +}); + +static void em_libusb_wait(const _Atomic int *ptr, int expected_value, int timeout) +{ + if (emscripten_is_main_runtime_thread()) { + em_libusb_wait_async(ptr, expected_value, timeout); + } else { + emscripten_atomic_wait_u32((int*)ptr, expected_value, 1000000LL * timeout); + } +} +#endif #include #ifdef HAVE_EVENTFD @@ -131,6 +156,10 @@ void usbi_signal_event(usbi_event_t *event) r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy)); if (r != sizeof(dummy)) usbi_warn(NULL, "event write failed"); +#ifdef __EMSCRIPTEN__ + event->has_event = 1; + emscripten_atomic_notify(&event->has_event, EMSCRIPTEN_NOTIFY_ALL_WAITERS); +#endif } void usbi_clear_event(usbi_event_t *event) @@ -141,6 +170,9 @@ void usbi_clear_event(usbi_event_t *event) r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy)); if (r != sizeof(dummy)) usbi_warn(NULL, "event read failed"); +#ifdef __EMSCRIPTEN__ + event->has_event = 0; +#endif } #ifdef HAVE_TIMERFD @@ -223,6 +255,14 @@ int usbi_wait_for_events(struct libusb_context *ctx, int internal_fds, num_ready; usbi_dbg(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms); +#ifdef __EMSCRIPTEN__ + // Emscripten's poll doesn't actually block, so we need to use an out-of-band + // waiting signal. + em_libusb_wait(&ctx->event.has_event, 0, timeout_ms); + // Emscripten ignores timeout_ms, but set it to 0 for future-proofing in case + // they ever implement real poll. + timeout_ms = 0; +#endif num_ready = poll(fds, nfds, timeout_ms); usbi_dbg(ctx, "poll() returned %d", num_ready); if (num_ready == 0) { diff --git a/libusb/libusb/os/events_posix.h b/libusb/libusb/os/events_posix.h index d81b5c4..4bd7f0f 100644 --- a/libusb/libusb/os/events_posix.h +++ b/libusb/libusb/os/events_posix.h @@ -36,6 +36,9 @@ typedef struct usbi_event { #else typedef struct usbi_event { int pipefd[2]; +#ifdef __EMSCRIPTEN__ + _Atomic int has_event; +#endif } usbi_event_t; #define USBI_EVENT_OS_HANDLE(e) ((e)->pipefd[0]) #define USBI_EVENT_POLL_EVENTS POLLIN diff --git a/libusb/libusb/os/linux_usbfs.c b/libusb/libusb/os/linux_usbfs.c index 285d9ca..ed8597b 100644 --- a/libusb/libusb/os/linux_usbfs.c +++ b/libusb/libusb/os/linux_usbfs.c @@ -95,9 +95,6 @@ static int sysfs_available = -1; /* how many times have we initted (and not exited) ? */ static int init_count = 0; -/* have no authority to operate usb device directly */ -static int no_enumeration = 0; - /* Serialize scan-devices, event-thread, and poll */ usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER; @@ -119,6 +116,11 @@ struct config_descriptor { size_t actual_len; }; +struct linux_context_priv { + /* no enumeration or hot-plug detection */ + int no_device_discovery; +}; + struct linux_device_priv { char *sysfs_dir; void *descriptors; @@ -187,10 +189,10 @@ static int get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent) int fd; if (usbdev_names) - sprintf(path, USBDEV_PATH "/usbdev%u.%u", + snprintf(path, sizeof(path), USBDEV_PATH "/usbdev%u.%u", dev->bus_number, dev->device_address); else - sprintf(path, USB_DEVTMPFS_PATH "/%03u/%03u", + snprintf(path, sizeof(path), USB_DEVTMPFS_PATH "/%03u/%03u", dev->bus_number, dev->device_address); fd = open(path, mode | O_CLOEXEC); @@ -354,6 +356,7 @@ static int op_init(struct libusb_context *ctx) struct kernel_version kversion; const char *usbfs_path; int r; + struct linux_context_priv *cpriv = usbi_get_context_priv(ctx); if (get_kernel_version(ctx, &kversion) < 0) return LIBUSB_ERROR_OTHER; @@ -397,7 +400,7 @@ static int op_init(struct libusb_context *ctx) } } - if (no_enumeration) { + if (cpriv->no_device_discovery) { return LIBUSB_SUCCESS; } @@ -421,9 +424,9 @@ static int op_init(struct libusb_context *ctx) static void op_exit(struct libusb_context *ctx) { - UNUSED(ctx); + struct linux_context_priv *cpriv = usbi_get_context_priv(ctx); - if (no_enumeration) { + if (cpriv->no_device_discovery) { return; } @@ -436,12 +439,13 @@ static void op_exit(struct libusb_context *ctx) static int op_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap) { - UNUSED(ctx); UNUSED(ap); if (option == LIBUSB_OPTION_NO_DEVICE_DISCOVERY) { - usbi_dbg(ctx, "no enumeration will be performed"); - no_enumeration = 1; + struct linux_context_priv *cpriv = usbi_get_context_priv(ctx); + + usbi_dbg(ctx, "no device discovery will be performed"); + cpriv->no_device_discovery = 1; return LIBUSB_SUCCESS; } @@ -597,7 +601,7 @@ int linux_get_device_address(struct libusb_context *ctx, int detached, char proc_path[32]; /* try to retrieve the device node from fd */ - sprintf(proc_path, "/proc/self/fd/%d", fd); + snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd); r = readlink(proc_path, fd_path, PATH_MAX - 1); if (r > 0) { fd_path[r] = '\0'; @@ -648,7 +652,7 @@ static int seek_to_next_config(struct libusb_context *ctx, while (len > 0) { if (len < 2) { - usbi_err(ctx, "short descriptor read %zu/2", len); + usbi_err(ctx, "remaining descriptor length too small %zu/2", len); return LIBUSB_ERROR_IO; } @@ -656,6 +660,11 @@ static int seek_to_next_config(struct libusb_context *ctx, if (header->bDescriptorType == LIBUSB_DT_CONFIG) return offset; + if (header->bLength < 2) { + usbi_err(ctx, "invalid descriptor bLength %hhu", header->bLength); + return LIBUSB_ERROR_IO; + } + if (len < header->bLength) { usbi_err(ctx, "bLength overflow by %zu bytes", (size_t)header->bLength - len); @@ -1079,8 +1088,9 @@ static int linux_get_parent_info(struct libusb_device *dev, const char *sysfs_di goto retry; } - usbi_dbg(ctx, "dev %p (%s) has parent %p (%s) port %u", dev, sysfs_dir, - dev->parent_dev, parent_sysfs_dir, dev->port_number); + usbi_dbg(ctx, "dev %p (%s) has parent %p (%s) port %u", + (void *) dev, sysfs_dir, (void *) dev->parent_dev, + parent_sysfs_dir, dev->port_number); free(parent_sysfs_dir); @@ -1188,7 +1198,7 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum) struct dirent *entry; int r = LIBUSB_ERROR_IO; - sprintf(dirpath, USB_DEVTMPFS_PATH "/%03u", busnum); + snprintf(dirpath, sizeof(dirpath), USB_DEVTMPFS_PATH "/%03u", busnum); usbi_dbg(ctx, "%s", dirpath); dir = opendir(dirpath); if (!dir) { @@ -2801,6 +2811,7 @@ const struct usbi_os_backend usbi_backend = { .handle_events = op_handle_events, + .context_priv_size = sizeof(struct linux_context_priv), .device_priv_size = sizeof(struct linux_device_priv), .device_handle_priv_size = sizeof(struct linux_device_handle_priv), .transfer_priv_size = sizeof(struct linux_transfer_priv), diff --git a/libusb/libusb/os/threads_posix.c b/libusb/libusb/os/threads_posix.c index 0e0e221..0079fd5 100644 --- a/libusb/libusb/os/threads_posix.c +++ b/libusb/libusb/os/threads_posix.c @@ -32,8 +32,6 @@ #elif defined(__NetBSD__) # include #elif defined(__OpenBSD__) -# define _BSD_SOURCE -# include # include #elif defined(__sun__) # include @@ -109,9 +107,7 @@ unsigned int usbi_get_tid(void) #elif defined(__NetBSD__) tid = _lwp_self(); #elif defined(__OpenBSD__) - /* The following only works with OpenBSD > 5.1 as it requires - * real thread support. For 5.1 and earlier, -1 is returned. */ - tid = syscall(SYS_getthrid); + tid = getthrid(); #elif defined(__sun__) tid = _lwp_self(); #else diff --git a/libusb/libusb/strerror.c b/libusb/libusb/strerror.c index 9445fa9..eb35503 100644 --- a/libusb/libusb/strerror.c +++ b/libusb/libusb/strerror.c @@ -28,7 +28,7 @@ *
  • Download the latest \c strerror.c from:
    * https://raw.github.com/libusb/libusb/master/libusb/strerror.c
  • *
  • Open the file in an UTF-8 capable editor
  • - *
  • Add the 2 letter ISO 639-1 + *
  • Add the 2 letter ISO 639-1 * code for your locale at the end of \c usbi_locale_supported[]
    * Eg. for Chinese, you would add "zh" so that: * \code... usbi_locale_supported[] = { "en", "nl", "fr" };\endcode @@ -160,7 +160,7 @@ static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_loca * If libusb_setlocale() is not called, all messages will be in English. * * The following functions return translatable strings: libusb_strerror(). - * Note that the libusb log messages controlled through libusb_set_debug() + * Note that the libusb log messages controlled through LIBUSB_OPTION_LOG_LEVEL * are not translated, they are always in English. * * For POSIX UTF-8 environments if you want libusb to follow the standard @@ -169,9 +169,9 @@ static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_loca * * \param locale locale-string in the form of lang[_country_region][.codeset] * or lang[-region], where lang is a 2 letter ISO 639-1 code - * \returns LIBUSB_SUCCESS on success - * \returns LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements - * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported + * \returns \ref LIBUSB_SUCCESS on success + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the locale doesn't meet the requirements + * \returns \ref LIBUSB_ERROR_NOT_FOUND if the requested language is not supported * \returns a LIBUSB_ERROR code on other errors */ diff --git a/libusb/libusb/sync.c b/libusb/libusb/sync.c index 1fa1f0b..146cce2 100644 --- a/libusb/libusb/sync.c +++ b/libusb/libusb/sync.c @@ -34,10 +34,15 @@ static void LIBUSB_CALL sync_transfer_cb(struct libusb_transfer *transfer) { + usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length); + int *completed = transfer->user_data; *completed = 1; - usbi_dbg(TRANSFER_CTX(transfer), "actual_length=%d", transfer->actual_length); - /* caller interprets result and frees transfer */ + /* + * Right after setting 'completed', another thread might free the transfer, so don't + * access it beyond this point. The instantiating thread (not necessarily the + * current one) interprets the result and frees the transfer. + */ } static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) @@ -85,12 +90,12 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) * before giving up due to no response being received. For an unlimited * timeout, use value 0. * \returns on success, the number of bytes actually transferred - * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out - * \returns LIBUSB_ERROR_PIPE if the control request was not supported by the + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns \ref LIBUSB_ERROR_PIPE if the control request was not supported by the * device - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if called from event handling context - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failures */ @@ -260,14 +265,14 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, * timeout, use value 0. * * \returns 0 on success (and populates transferred) - * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out (and populates * transferred) - * \returns LIBUSB_ERROR_PIPE if the endpoint halted - * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted + * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see * \ref libusb_packetoverflow - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if called from event handling context - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failures */ @@ -315,13 +320,13 @@ int API_EXPORTED libusb_bulk_transfer(libusb_device_handle *dev_handle, * timeout, use value 0. * * \returns 0 on success (and populates transferred) - * \returns LIBUSB_ERROR_TIMEOUT if the transfer timed out - * \returns LIBUSB_ERROR_PIPE if the endpoint halted - * \returns LIBUSB_ERROR_OVERFLOW if the device offered more data, see + * \returns \ref LIBUSB_ERROR_TIMEOUT if the transfer timed out + * \returns \ref LIBUSB_ERROR_PIPE if the endpoint halted + * \returns \ref LIBUSB_ERROR_OVERFLOW if the device offered more data, see * \ref libusb_packetoverflow - * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected - * \returns LIBUSB_ERROR_BUSY if called from event handling context - * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * \returns \ref LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns \ref LIBUSB_ERROR_BUSY if called from event handling context + * \returns \ref LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other error */ diff --git a/libusb/libusb/version.h b/libusb/libusb/version.h index fe95d84..ca9df20 100644 --- a/libusb/libusb/version.h +++ b/libusb/libusb/version.h @@ -7,7 +7,7 @@ #define LIBUSB_MINOR 0 #endif #ifndef LIBUSB_MICRO -#define LIBUSB_MICRO 26 +#define LIBUSB_MICRO 27 #endif #ifndef LIBUSB_NANO #define LIBUSB_NANO 0 diff --git a/libusb/libusb/version_nano.h b/libusb/libusb/version_nano.h index dbd5d5f..a6165f3 100644 --- a/libusb/libusb/version_nano.h +++ b/libusb/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11724 +#define LIBUSB_NANO 11882