diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h index 57478c1787ee..1803ff637738 100644 --- a/lib/libusb/libusb.h +++ b/lib/libusb/libusb.h @@ -1,618 +1,619 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef __LIBUSB_H__ #define __LIBUSB_H__ #ifndef LIBUSB_GLOBAL_INCLUDE_FILE #include #include #include #endif #define LIBUSB_API_VERSION 0x01000102 #define LIBUSB_CALL #ifdef __cplusplus extern "C" { #endif #if 0 } /* indent fix */ #endif /* libusb enums */ enum libusb_class_code { LIBUSB_CLASS_PER_INTERFACE = 0, LIBUSB_CLASS_AUDIO = 1, LIBUSB_CLASS_COMM = 2, LIBUSB_CLASS_HID = 3, LIBUSB_CLASS_PHYSICAL = 5, LIBUSB_CLASS_PTP = 6, LIBUSB_CLASS_IMAGE = 6, LIBUSB_CLASS_PRINTER = 7, LIBUSB_CLASS_MASS_STORAGE = 8, LIBUSB_CLASS_HUB = 9, LIBUSB_CLASS_DATA = 10, LIBUSB_CLASS_SMART_CARD = 11, LIBUSB_CLASS_CONTENT_SECURITY = 13, LIBUSB_CLASS_VIDEO = 14, LIBUSB_CLASS_PERSONAL_HEALTHCARE = 15, LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, LIBUSB_CLASS_WIRELESS = 0xe0, LIBUSB_CLASS_APPLICATION = 0xfe, LIBUSB_CLASS_VENDOR_SPEC = 0xff, }; enum libusb_descriptor_type { LIBUSB_DT_DEVICE = 0x01, LIBUSB_DT_CONFIG = 0x02, LIBUSB_DT_STRING = 0x03, LIBUSB_DT_INTERFACE = 0x04, LIBUSB_DT_ENDPOINT = 0x05, LIBUSB_DT_HID = 0x21, LIBUSB_DT_REPORT = 0x22, LIBUSB_DT_PHYSICAL = 0x23, LIBUSB_DT_HUB = 0x29, LIBUSB_DT_BOS = 0x0f, LIBUSB_DT_DEVICE_CAPABILITY = 0x10, LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30, }; enum libusb_device_capability_type { LIBUSB_WIRELESS_USB_DEVICE_CAPABILITY = 0x1, LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY = 0x2, LIBUSB_SS_USB_DEVICE_CAPABILITY = 0x3, LIBUSB_CONTAINER_ID_DEVICE_CAPABILITY = 0x4, }; #define LIBUSB_DT_DEVICE_SIZE 18 #define LIBUSB_DT_CONFIG_SIZE 9 #define LIBUSB_DT_INTERFACE_SIZE 9 #define LIBUSB_DT_ENDPOINT_SIZE 7 #define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 #define LIBUSB_DT_HUB_NONVAR_SIZE 7 #define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 #define LIBUSB_DT_BOS_SIZE 5 #define LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE 7 #define LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE 10 #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_ENDPOINT_ADDRESS_MASK 0x0f #define LIBUSB_ENDPOINT_DIR_MASK 0x80 enum libusb_endpoint_direction { LIBUSB_ENDPOINT_IN = 0x80, LIBUSB_ENDPOINT_OUT = 0x00, }; #define LIBUSB_TRANSFER_TYPE_MASK 0x03 enum libusb_transfer_type { LIBUSB_TRANSFER_TYPE_CONTROL = 0, LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, LIBUSB_TRANSFER_TYPE_BULK = 2, LIBUSB_TRANSFER_TYPE_INTERRUPT = 3, + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4, }; enum libusb_standard_request { LIBUSB_REQUEST_GET_STATUS = 0x00, LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, LIBUSB_REQUEST_SET_FEATURE = 0x03, LIBUSB_REQUEST_SET_ADDRESS = 0x05, LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, LIBUSB_REQUEST_GET_INTERFACE = 0x0A, LIBUSB_REQUEST_SET_INTERFACE = 0x0B, LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, LIBUSB_REQUEST_SET_SEL = 0x30, LIBUSB_REQUEST_SET_ISOCH_DELAY = 0x31, }; enum libusb_request_type { LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5), }; enum libusb_request_recipient { LIBUSB_RECIPIENT_DEVICE = 0x00, LIBUSB_RECIPIENT_INTERFACE = 0x01, LIBUSB_RECIPIENT_ENDPOINT = 0x02, LIBUSB_RECIPIENT_OTHER = 0x03, }; #define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C enum libusb_iso_sync_type { LIBUSB_ISO_SYNC_TYPE_NONE = 0, LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, LIBUSB_ISO_SYNC_TYPE_SYNC = 3, }; #define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 enum libusb_iso_usage_type { LIBUSB_ISO_USAGE_TYPE_DATA = 0, LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, }; enum libusb_bos_type { LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, LIBUSB_BT_USB_2_0_EXTENSION = 2, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, LIBUSB_BT_CONTAINER_ID = 4, }; enum libusb_capability { /* libusb supports libusb_has_capability(). */ LIBUSB_CAP_HAS_CAPABILITY = 0, /* Hotplug support is available. */ LIBUSB_CAP_HAS_HOTPLUG, /* Can access HID devices without requiring user intervention. */ LIBUSB_CAP_HAS_HID_ACCESS, /* * Supports detaching of the default USB driver with * libusb_detach_kernel_driver(). */ LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, }; enum libusb_error { LIBUSB_SUCCESS = 0, LIBUSB_ERROR_IO = -1, LIBUSB_ERROR_INVALID_PARAM = -2, LIBUSB_ERROR_ACCESS = -3, LIBUSB_ERROR_NO_DEVICE = -4, LIBUSB_ERROR_NOT_FOUND = -5, LIBUSB_ERROR_BUSY = -6, LIBUSB_ERROR_TIMEOUT = -7, LIBUSB_ERROR_OVERFLOW = -8, LIBUSB_ERROR_PIPE = -9, LIBUSB_ERROR_INTERRUPTED = -10, LIBUSB_ERROR_NO_MEM = -11, LIBUSB_ERROR_NOT_SUPPORTED = -12, LIBUSB_ERROR_OTHER = -99, }; #define LIBUSB_ERROR_COUNT 14 enum libusb_speed { LIBUSB_SPEED_UNKNOWN = 0, LIBUSB_SPEED_LOW = 1, LIBUSB_SPEED_FULL = 2, LIBUSB_SPEED_HIGH = 3, LIBUSB_SPEED_SUPER = 4, LIBUSB_SPEED_SUPER_PLUS = 5, }; enum libusb_transfer_status { LIBUSB_TRANSFER_COMPLETED, LIBUSB_TRANSFER_ERROR, LIBUSB_TRANSFER_TIMED_OUT, LIBUSB_TRANSFER_CANCELLED, LIBUSB_TRANSFER_STALL, LIBUSB_TRANSFER_NO_DEVICE, LIBUSB_TRANSFER_OVERFLOW, }; enum libusb_transfer_flags { LIBUSB_TRANSFER_SHORT_NOT_OK = 1 << 0, LIBUSB_TRANSFER_FREE_BUFFER = 1 << 1, LIBUSB_TRANSFER_FREE_TRANSFER = 1 << 2, }; enum libusb_log_level { LIBUSB_LOG_LEVEL_NONE = 0, LIBUSB_LOG_LEVEL_ERROR, LIBUSB_LOG_LEVEL_WARNING, LIBUSB_LOG_LEVEL_INFO, LIBUSB_LOG_LEVEL_DEBUG }; #define LIBUSB_HOTPLUG_MATCH_ANY -1 typedef enum { LIBUSB_HOTPLUG_NO_FLAGS = 0, LIBUSB_HOTPLUG_ENUMERATE = 1 << 0, } libusb_hotplug_flag; typedef enum { LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 1, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 2, } libusb_hotplug_event; enum libusb_option { LIBUSB_OPTION_LOG_LEVEL = 0, LIBUSB_OPTION_USE_USBDK = 1, LIBUSB_OPTION_NO_DEVICE_DISCOVERY = 2, LIBUSB_OPTION_WEAK_AUTHORITY = 2, LIBUSB_OPTION_MAX = 3, }; /* libusb structures */ struct libusb_context; struct libusb_device; struct libusb_transfer; struct libusb_device_handle; struct libusb_hotplug_callback_handle_struct; struct libusb_pollfd { int fd; short events; }; struct libusb_version { const uint16_t major; const uint16_t minor; const uint16_t micro; const uint16_t nano; const char *rc; const char *describe; }; struct libusb_init_option { enum libusb_option option; union { int64_t ival; } value; }; typedef struct libusb_context libusb_context; typedef struct libusb_device libusb_device; typedef struct libusb_device_handle libusb_device_handle; typedef struct libusb_pollfd libusb_pollfd; typedef void (*libusb_pollfd_added_cb) (int fd, short events, void *user_data); typedef void (*libusb_pollfd_removed_cb) (int fd, void *user_data); typedef struct libusb_hotplug_callback_handle_struct *libusb_hotplug_callback_handle; typedef struct libusb_device_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint16_t bcdUSB; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bMaxPacketSize0; uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t iManufacturer; uint8_t iProduct; uint8_t iSerialNumber; uint8_t bNumConfigurations; } libusb_device_descriptor; typedef struct libusb_endpoint_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bEndpointAddress; uint8_t bmAttributes; uint16_t wMaxPacketSize; uint8_t bInterval; uint8_t bRefresh; uint8_t bSynchAddress; uint8_t *extra; int extra_length; } libusb_endpoint_descriptor __aligned(sizeof(void *)); typedef struct libusb_ss_endpoint_companion_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bMaxBurst; uint8_t bmAttributes; uint16_t wBytesPerInterval; } libusb_ss_endpoint_companion_descriptor __aligned(sizeof(void *)); typedef struct libusb_interface_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bInterfaceNumber; uint8_t bAlternateSetting; uint8_t bNumEndpoints; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t iInterface; struct libusb_endpoint_descriptor *endpoint; uint8_t *extra; int extra_length; } libusb_interface_descriptor __aligned(sizeof(void *)); typedef struct libusb_interface { struct libusb_interface_descriptor *altsetting; int num_altsetting; } libusb_interface __aligned(sizeof(void *)); typedef struct libusb_config_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint16_t wTotalLength; uint8_t bNumInterfaces; uint8_t bConfigurationValue; uint8_t iConfiguration; uint8_t bmAttributes; uint8_t MaxPower; struct libusb_interface *interface; uint8_t *extra; int extra_length; } libusb_config_descriptor __aligned(sizeof(void *)); typedef struct libusb_usb_2_0_device_capability_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDevCapabilityType; uint32_t bmAttributes; #define LIBUSB_USB_2_0_CAPABILITY_LPM_SUPPORT (1 << 1) } libusb_usb_2_0_device_capability_descriptor __aligned(sizeof(void *)); typedef struct libusb_ss_usb_device_capability_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDevCapabilityType; uint8_t bmAttributes; #define LIBUSB_SS_USB_CAPABILITY_LPM_SUPPORT (1 << 1) uint16_t wSpeedSupported; #define LIBUSB_CAPABILITY_LOW_SPEED_OPERATION (1) #define LIBUSB_CAPABILITY_FULL_SPEED_OPERATION (1 << 1) #define LIBUSB_CAPABILITY_HIGH_SPEED_OPERATION (1 << 2) #define LIBUSB_CAPABILITY_5GBPS_OPERATION (1 << 3) uint8_t bFunctionalitySupport; uint8_t bU1DevExitLat; uint16_t wU2DevExitLat; } libusb_ss_usb_device_capability_descriptor __aligned(sizeof(void *)); typedef struct libusb_bos_dev_capability_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDevCapabilityType; uint8_t dev_capability_data[0]; } libusb_bos_dev_capability_descriptor __aligned(sizeof(void *)); typedef struct libusb_bos_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint16_t wTotalLength; #ifndef bNumDeviceCapabilities #define bNumDeviceCapabilities bNumDeviceCaps #endif uint8_t bNumDeviceCaps; struct libusb_usb_2_0_device_capability_descriptor *usb_2_0_ext_cap; struct libusb_ss_usb_device_capability_descriptor *ss_usb_cap; struct libusb_bos_dev_capability_descriptor **dev_capability; } libusb_bos_descriptor __aligned(sizeof(void *)); typedef struct libusb_usb_2_0_extension_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDevCapabilityType; uint32_t bmAttributes; } libusb_usb_2_0_extension_descriptor __aligned(sizeof(void *)); typedef struct libusb_container_id_descriptor { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDevCapabilityType; uint8_t bReserved; uint8_t ContainerID[16]; } libusb_container_id_descriptor __aligned(sizeof(void *)); typedef struct libusb_control_setup { uint8_t bmRequestType; uint8_t bRequest; uint16_t wValue; uint16_t wIndex; uint16_t wLength; } libusb_control_setup; #define LIBUSB_CONTROL_SETUP_SIZE 8 /* bytes */ typedef struct libusb_iso_packet_descriptor { uint32_t length; uint32_t actual_length; enum libusb_transfer_status status; } libusb_iso_packet_descriptor __aligned(sizeof(void *)); typedef void (*libusb_transfer_cb_fn) (struct libusb_transfer *transfer); typedef struct libusb_transfer { libusb_device_handle *dev_handle; uint8_t flags; uint8_t endpoint; uint8_t type; uint32_t timeout; enum libusb_transfer_status status; int length; int actual_length; libusb_transfer_cb_fn callback; void *user_data; uint8_t *buffer; int num_iso_packets; struct libusb_iso_packet_descriptor iso_packet_desc[0]; } libusb_transfer __aligned(sizeof(void *)); /* Library initialisation */ void libusb_set_debug(libusb_context * ctx, int level); const struct libusb_version *libusb_get_version(void); const char *libusb_strerror(int code); const char *libusb_error_name(int code); int libusb_init(libusb_context ** context); int libusb_init_context(libusb_context **, const struct libusb_init_option [], int num_options); void libusb_exit(struct libusb_context *ctx); int libusb_has_capability(uint32_t capability); int libusb_setlocale(const char *locale); /* Device handling and enumeration */ ssize_t libusb_get_device_list(libusb_context * ctx, libusb_device *** list); void libusb_free_device_list(libusb_device ** list, int unref_devices); uint8_t libusb_get_bus_number(libusb_device * dev); uint8_t libusb_get_port_number(libusb_device * dev); int libusb_get_port_numbers(libusb_device *dev, uint8_t *buf, uint8_t bufsize); int libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *buf, uint8_t bufsize); uint8_t libusb_get_device_address(libusb_device * dev); enum libusb_speed libusb_get_device_speed(libusb_device * dev); int libusb_clear_halt(libusb_device_handle *devh, uint8_t endpoint); int libusb_get_max_packet_size(libusb_device * dev, uint8_t endpoint); int libusb_get_max_iso_packet_size(libusb_device * dev, uint8_t endpoint); libusb_device *libusb_ref_device(libusb_device * dev); void libusb_unref_device(libusb_device * dev); int libusb_open(libusb_device * dev, libusb_device_handle ** devh); libusb_device_handle *libusb_open_device_with_vid_pid(libusb_context * ctx, uint16_t vendor_id, uint16_t product_id); void libusb_close(libusb_device_handle * devh); libusb_device *libusb_get_device(libusb_device_handle * devh); libusb_device *libusb_get_parent(libusb_device * dev); int libusb_get_configuration(libusb_device_handle * devh, int *config); int libusb_set_configuration(libusb_device_handle * devh, int configuration); int libusb_claim_interface(libusb_device_handle * devh, int interface_number); int libusb_release_interface(libusb_device_handle * devh, int interface_number); int libusb_reset_device(libusb_device_handle * devh); int libusb_check_connected(libusb_device_handle * devh); int libusb_kernel_driver_active(libusb_device_handle * devh, int interface); int libusb_get_driver_np(libusb_device_handle * devh, int interface, char *name, int namelen); int libusb_get_driver(libusb_device_handle * devh, int interface, char *name, int namelen); int libusb_detach_kernel_driver_np(libusb_device_handle * devh, int interface); int libusb_detach_kernel_driver(libusb_device_handle * devh, int interface); int libusb_attach_kernel_driver(libusb_device_handle * devh, int interface); int libusb_set_auto_detach_kernel_driver(libusb_device_handle *dev, int enable); int libusb_set_interface_alt_setting(libusb_device_handle * devh, int interface_number, int alternate_setting); unsigned char *libusb_dev_mem_alloc(libusb_device_handle *devh); int libusb_dev_mem_free(libusb_device_handle *devh, unsigned char *buffer, size_t size); /* USB Descriptors */ int libusb_get_device_descriptor(libusb_device * dev, struct libusb_device_descriptor *desc); int libusb_get_active_config_descriptor(libusb_device * dev, struct libusb_config_descriptor **config); int libusb_get_config_descriptor(libusb_device * dev, uint8_t config_index, struct libusb_config_descriptor **config); int libusb_get_config_descriptor_by_value(libusb_device * dev, uint8_t bConfigurationValue, struct libusb_config_descriptor **config); void libusb_free_config_descriptor(struct libusb_config_descriptor *config); int libusb_get_ss_endpoint_companion_descriptor(struct libusb_context *ctx, const struct libusb_endpoint_descriptor *endpoint, struct libusb_ss_endpoint_companion_descriptor **ep_comp); void libusb_free_ss_endpoint_companion_descriptor(struct libusb_ss_endpoint_companion_descriptor *ep_comp); int libusb_get_string_descriptor(libusb_device_handle * devh, uint8_t desc_index, uint16_t langid, unsigned char *data, int length); int libusb_get_string_descriptor_ascii(libusb_device_handle * devh, uint8_t desc_index, uint8_t *data, int length); int libusb_get_descriptor(libusb_device_handle * devh, uint8_t desc_type, uint8_t desc_index, uint8_t *data, int length); int libusb_parse_ss_endpoint_comp(const void *buf, int len, struct libusb_ss_endpoint_companion_descriptor **ep_comp); void libusb_free_ss_endpoint_comp(struct libusb_ss_endpoint_companion_descriptor *ep_comp); int libusb_parse_bos_descriptor(const void *buf, int len, struct libusb_bos_descriptor **bos); void libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); int libusb_get_bos_descriptor(libusb_device_handle *handle, struct libusb_bos_descriptor **bos); int libusb_get_usb_2_0_extension_descriptor(struct libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); void libusb_free_usb_2_0_extension_descriptor(struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); int libusb_get_ss_usb_device_capability_descriptor(struct libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_capability); void libusb_free_ss_usb_device_capability_descriptor(struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_capability); int libusb_get_container_id_descriptor(struct libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_container_id_descriptor **container_id); void libusb_free_container_id_descriptor(struct libusb_container_id_descriptor *container_id); /* Asynchronous device I/O */ struct libusb_transfer *libusb_alloc_transfer(int iso_packets); void libusb_free_transfer(struct libusb_transfer *transfer); int libusb_submit_transfer(struct libusb_transfer *transfer); int libusb_cancel_transfer(struct libusb_transfer *transfer); uint8_t *libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t index); uint8_t *libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t index); void libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, uint32_t length); uint8_t *libusb_control_transfer_get_data(struct libusb_transfer *transfer); struct libusb_control_setup *libusb_control_transfer_get_setup(struct libusb_transfer *transfer); void libusb_fill_control_setup(uint8_t *buf, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength); void libusb_fill_control_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t *buf, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); void libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); void libusb_fill_iso_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, int npacket, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout); /* Polling and timing */ int libusb_try_lock_events(libusb_context * ctx); void libusb_lock_events(libusb_context * ctx); void libusb_unlock_events(libusb_context * ctx); int libusb_event_handling_ok(libusb_context * ctx); int libusb_event_handler_active(libusb_context * ctx); void libusb_interrupt_event_handler(libusb_context *ctx); void libusb_lock_event_waiters(libusb_context * ctx); void libusb_unlock_event_waiters(libusb_context * ctx); int libusb_wait_for_event(libusb_context * ctx, struct timeval *tv); int libusb_handle_events_timeout_completed(libusb_context * ctx, struct timeval *tv, int *completed); int libusb_handle_events_completed(libusb_context * ctx, int *completed); int libusb_handle_events_timeout(libusb_context * ctx, struct timeval *tv); int libusb_handle_events(libusb_context * ctx); int libusb_handle_events_locked(libusb_context * ctx, struct timeval *tv); int libusb_get_next_timeout(libusb_context * ctx, struct timeval *tv); void libusb_set_pollfd_notifiers(libusb_context * ctx, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data); const struct libusb_pollfd **libusb_get_pollfds(libusb_context *ctx); void libusb_free_pollfds(const struct libusb_pollfd **pollfds); /* Synchronous device I/O */ int libusb_control_transfer(libusb_device_handle * devh, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, uint32_t timeout); int libusb_bulk_transfer(libusb_device_handle * devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, uint32_t timeout); int libusb_interrupt_transfer(libusb_device_handle * devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, uint32_t timeout); /* Byte-order */ uint16_t libusb_cpu_to_le16(uint16_t x); uint16_t libusb_le16_to_cpu(uint16_t x); /* Hotplug support */ typedef int (*libusb_hotplug_callback_fn)(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data); int libusb_hotplug_register_callback(libusb_context *ctx, libusb_hotplug_event events, libusb_hotplug_flag flags, int vendor_id, int product_id, int dev_class, libusb_hotplug_callback_fn cb_fn, void *user_data, libusb_hotplug_callback_handle *handle); void libusb_hotplug_deregister_callback(libusb_context *ctx, libusb_hotplug_callback_handle handle); void *libusb_hotplug_get_user_data(struct libusb_context *ctx, libusb_hotplug_callback_handle callback_handle); /* Streams support */ int libusb_alloc_streams(libusb_device_handle *dev, uint32_t num_streams, unsigned char *endpoints, int num_endpoints); int libusb_free_streams(libusb_device_handle *dev, unsigned char *endpoints, int num_endpoints); void libusb_transfer_set_stream_id(struct libusb_transfer *transfer, uint32_t stream_id); uint32_t libusb_transfer_get_stream_id(struct libusb_transfer *transfer); #if 0 { /* indent fix */ #endif #ifdef __cplusplus } #endif #endif /* __LIBUSB_H__ */ diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index 6bfc01eb8c4e..5c116b39ea17 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -1,1919 +1,1920 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. * Copyright (c) 2009-2023 Hans Petter Selasky * Copyright (c) 2024 Aymeric Wibo * Copyright (c) 2025 ShengYi Hung * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef LIBUSB_GLOBAL_INCLUDE_FILE #include LIBUSB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif #define libusb_device_handle libusb20_device #define LIBUSB_LOG_BUFFER_SIZE 1024 #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" #include "libusb.h" #include "libusb10.h" #define LIBUSB_NUM_SW_ENDPOINTS (16 * 4) static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER; struct libusb_context *usbi_default_context = NULL; /* Prototypes */ static struct libusb20_transfer *libusb10_get_transfer(struct libusb20_device *, uint8_t, uint8_t); static int libusb10_get_buffsize(struct libusb20_device *, libusb_transfer *); static int libusb10_convert_error(uint8_t status); static void libusb10_complete_transfer(struct libusb20_transfer *, struct libusb_super_transfer *, int); static void libusb10_isoc_proxy(struct libusb20_transfer *); static void libusb10_bulk_intr_proxy(struct libusb20_transfer *); static void libusb10_ctrl_proxy(struct libusb20_transfer *); static void libusb10_submit_transfer_sub(struct libusb20_device *, uint8_t); /* Library initialisation / deinitialisation */ static const struct libusb_version libusb_version = { .major = 1, .minor = 0, .micro = 0, .nano = 2016, .rc = "", .describe = "https://www.freebsd.org" }; static const struct libusb_language_context libusb_language_ctx[] = { { .lang_name = "en", .err_strs = { [-LIBUSB_SUCCESS] = "Success", [-LIBUSB_ERROR_IO] = "I/O error", [-LIBUSB_ERROR_INVALID_PARAM] = "Invalid parameter", [-LIBUSB_ERROR_ACCESS] = "Permissions error", [-LIBUSB_ERROR_NO_DEVICE] = "No device", [-LIBUSB_ERROR_NOT_FOUND] = "Not found", [-LIBUSB_ERROR_BUSY] = "Device busy", [-LIBUSB_ERROR_TIMEOUT] = "Timeout", [-LIBUSB_ERROR_OVERFLOW] = "Overflow", [-LIBUSB_ERROR_PIPE] = "Pipe error", [-LIBUSB_ERROR_INTERRUPTED] = "Interrupted", [-LIBUSB_ERROR_NO_MEM] = "Out of memory", [-LIBUSB_ERROR_NOT_SUPPORTED] ="Not supported", [LIBUSB_ERROR_COUNT - 1] = "Other error", [LIBUSB_ERROR_COUNT] = "Unknown error", } }, { .lang_name = "zh", .err_strs = { [-LIBUSB_SUCCESS] = "成功", [-LIBUSB_ERROR_IO] = "I/O 錯誤", [-LIBUSB_ERROR_INVALID_PARAM] = "不合法的參數", [-LIBUSB_ERROR_ACCESS] = "權限錯誤", [-LIBUSB_ERROR_NO_DEVICE] = "裝置不存在", [-LIBUSB_ERROR_NOT_FOUND] = "不存在", [-LIBUSB_ERROR_BUSY] = "裝置忙碌中", [-LIBUSB_ERROR_TIMEOUT] = "逾時", [-LIBUSB_ERROR_OVERFLOW] = "溢位", [-LIBUSB_ERROR_PIPE] = "管道錯誤", [-LIBUSB_ERROR_INTERRUPTED] = "被中斷", [-LIBUSB_ERROR_NO_MEM] = "記憶體不足", [-LIBUSB_ERROR_NOT_SUPPORTED] ="不支援", [LIBUSB_ERROR_COUNT - 1] = "其他錯誤", [LIBUSB_ERROR_COUNT] = "未知錯誤", } }, }; static const struct libusb_language_context *default_language_context = &libusb_language_ctx[0]; const struct libusb_version * libusb_get_version(void) { return (&libusb_version); } void libusb_set_debug(libusb_context *ctx, int level) { ctx = GET_CONTEXT(ctx); /* debug_fixed is set when the environment overrides libusb_set_debug */ if (ctx && ctx->debug_fixed == 0) ctx->debug = level; } static void libusb_set_nonblocking(int f) { int flags; /* * We ignore any failures in this function, hence the * non-blocking flag is not critical to the operation of * libUSB. We use F_GETFL and F_SETFL to be compatible with * Linux. */ flags = fcntl(f, F_GETFL, NULL); if (flags == -1) return; flags |= O_NONBLOCK; fcntl(f, F_SETFL, flags); } void libusb_interrupt_event_handler(libusb_context *ctx) { int err; if (ctx == NULL) return; err = eventfd_write(ctx->event, 1); if (err < 0) { /* ignore error, if any */ DPRINTF(ctx, LIBUSB_LOG_LEVEL_ERROR, "Waking up event loop failed!"); } } int libusb_init(libusb_context **context) { return (libusb_init_context(context, NULL, 0)); } int libusb_init_context(libusb_context **context, const struct libusb_init_option option[], int num_options) { struct libusb_context *ctx; pthread_condattr_t attr; char *debug, *ep; if (num_options < 0) return (LIBUSB_ERROR_INVALID_PARAM); ctx = malloc(sizeof(*ctx)); if (!ctx) return (LIBUSB_ERROR_INVALID_PARAM); memset(ctx, 0, sizeof(*ctx)); ctx->devd_pipe = -1; debug = getenv("LIBUSB_DEBUG"); if (debug != NULL) { /* * If LIBUSB_DEBUG is set, we'll honor that first and * use it to override any future libusb_set_debug() * calls or init options. */ errno = 0; ctx->debug = strtol(debug, &ep, 10); if (errno == 0 && *ep == '\0') { ctx->debug_fixed = 1; } else { /* * LIBUSB_DEBUG conversion failed for some reason, but * we don't care about the specifics all that much. We * can't use it either way. Force it to the default, * 0, in case we had a partial number. */ ctx->debug = 0; } } else { /* * If the LIBUSB_OPTION_LOG_LEVEL is set, honor that. */ for (int i = 0; i != num_options; i++) { if (option[i].option != LIBUSB_OPTION_LOG_LEVEL) continue; ctx->debug = (int)option[i].value.ival; if ((int64_t)ctx->debug == option[i].value.ival) { ctx->debug_fixed = 1; } else { free(ctx); return (LIBUSB_ERROR_INVALID_PARAM); } } } TAILQ_INIT(&ctx->pollfds); TAILQ_INIT(&ctx->tr_done); TAILQ_INIT(&ctx->hotplug_cbh); TAILQ_INIT(&ctx->hotplug_devs); if (pthread_mutex_init(&ctx->ctx_lock, NULL) != 0) { free(ctx); return (LIBUSB_ERROR_NO_MEM); } if (pthread_mutex_init(&ctx->hotplug_lock, NULL) != 0) { pthread_mutex_destroy(&ctx->ctx_lock); free(ctx); return (LIBUSB_ERROR_NO_MEM); } if (pthread_condattr_init(&attr) != 0) { pthread_mutex_destroy(&ctx->ctx_lock); pthread_mutex_destroy(&ctx->hotplug_lock); free(ctx); return (LIBUSB_ERROR_NO_MEM); } if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) != 0) { pthread_mutex_destroy(&ctx->ctx_lock); pthread_mutex_destroy(&ctx->hotplug_lock); pthread_condattr_destroy(&attr); free(ctx); return (LIBUSB_ERROR_OTHER); } if (pthread_cond_init(&ctx->ctx_cond, &attr) != 0) { pthread_mutex_destroy(&ctx->ctx_lock); pthread_mutex_destroy(&ctx->hotplug_lock); pthread_condattr_destroy(&attr); free(ctx); return (LIBUSB_ERROR_NO_MEM); } pthread_condattr_destroy(&attr); ctx->ctx_handler = NO_THREAD; ctx->hotplug_handler = NO_THREAD; ctx->event = eventfd(0, EFD_NONBLOCK); if (ctx->event < 0) { pthread_mutex_destroy(&ctx->ctx_lock); pthread_mutex_destroy(&ctx->hotplug_lock); pthread_cond_destroy(&ctx->ctx_cond); free(ctx); return (LIBUSB_ERROR_OTHER); } libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->event, POLLIN); pthread_mutex_lock(&default_context_lock); if (usbi_default_context == NULL) { usbi_default_context = ctx; } pthread_mutex_unlock(&default_context_lock); if (context) *context = ctx; DPRINTF(ctx, LIBUSB_LOG_LEVEL_INFO, "libusb_init complete"); signal(SIGPIPE, SIG_IGN); return (0); } void libusb_exit(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); if (ctx == NULL) return; /* stop hotplug thread, if any */ if (ctx->hotplug_handler != NO_THREAD) { pthread_t td; void *ptr; HOTPLUG_LOCK(ctx); td = ctx->hotplug_handler; ctx->hotplug_handler = NO_THREAD; if (ctx->usb_event_mode == usb_event_devd) { close(ctx->devd_pipe); ctx->devd_pipe = -1; } else if (ctx->usb_event_mode == usb_event_netlink) { close(ctx->ss.fd); ctx->ss.fd = -1; } HOTPLUG_UNLOCK(ctx); pthread_join(td, &ptr); } /* XXX cleanup devices */ libusb10_remove_pollfd(ctx, &ctx->ctx_poll); close(ctx->event); pthread_mutex_destroy(&ctx->ctx_lock); pthread_mutex_destroy(&ctx->hotplug_lock); pthread_cond_destroy(&ctx->ctx_cond); pthread_mutex_lock(&default_context_lock); if (ctx == usbi_default_context) { usbi_default_context = NULL; } pthread_mutex_unlock(&default_context_lock); free(ctx); } /* Device handling and initialisation. */ ssize_t libusb_get_device_list(libusb_context *ctx, libusb_device ***list) { struct libusb20_backend *usb_backend; struct libusb20_device *pdev, *parent_dev; struct libusb_device *dev; int i, j, k; ctx = GET_CONTEXT(ctx); if (ctx == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (list == NULL) return (LIBUSB_ERROR_INVALID_PARAM); usb_backend = libusb20_be_alloc_default(); if (usb_backend == NULL) return (LIBUSB_ERROR_NO_MEM); /* figure out how many USB devices are present */ pdev = NULL; i = 0; while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) i++; /* allocate device pointer list */ *list = malloc((i + 1) * sizeof(void *)); if (*list == NULL) { libusb20_be_free(usb_backend); return (LIBUSB_ERROR_NO_MEM); } /* create libusb v1.0 compliant devices */ i = 0; while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { dev = malloc(sizeof(*dev)); if (dev == NULL) { while (i != 0) { libusb_unref_device((*list)[i - 1]); i--; } free(*list); *list = NULL; libusb20_be_free(usb_backend); return (LIBUSB_ERROR_NO_MEM); } /* get device into libUSB v1.0 list */ libusb20_be_dequeue_device(usb_backend, pdev); memset(dev, 0, sizeof(*dev)); /* init transfer queues */ TAILQ_INIT(&dev->tr_head); /* set context we belong to */ dev->ctx = ctx; /* assume we have no parent by default */ dev->parent_dev = NULL; /* link together the two structures */ dev->os_priv = pdev; pdev->privLuData = dev; (*list)[i] = libusb_ref_device(dev); i++; } (*list)[i] = NULL; /* for each device, find its parent */ for (j = 0; j < i; j++) { pdev = (*list)[j]->os_priv; for (k = 0; k < i; k++) { if (k == j) continue; parent_dev = (*list)[k]->os_priv; if (parent_dev->bus_number != pdev->bus_number) continue; if (parent_dev->device_address == pdev->parent_address) { (*list)[j]->parent_dev = libusb_ref_device((*list)[k]); break; } } } libusb20_be_free(usb_backend); return (i); } void libusb_free_device_list(libusb_device **list, int unref_devices) { int i; if (list == NULL) return; /* be NULL safe */ if (unref_devices) { for (i = 0; list[i] != NULL; i++) libusb_unref_device(list[i]); } free(list); } uint8_t libusb_get_bus_number(libusb_device *dev) { if (dev == NULL) return (0); /* should not happen */ return (libusb20_dev_get_bus_number(dev->os_priv)); } uint8_t libusb_get_port_number(libusb_device *dev) { if (dev == NULL) return (0); /* should not happen */ return (libusb20_dev_get_parent_port(dev->os_priv)); } int libusb_get_port_numbers(libusb_device *dev, uint8_t *buf, uint8_t bufsize) { return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize)); } int libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *buf, uint8_t bufsize) { return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize)); } uint8_t libusb_get_device_address(libusb_device *dev) { if (dev == NULL) return (0); /* should not happen */ return (libusb20_dev_get_address(dev->os_priv)); } enum libusb_speed libusb_get_device_speed(libusb_device *dev) { if (dev == NULL) return (LIBUSB_SPEED_UNKNOWN); /* should not happen */ switch (libusb20_dev_get_speed(dev->os_priv)) { case LIBUSB20_SPEED_LOW: return (LIBUSB_SPEED_LOW); case LIBUSB20_SPEED_FULL: return (LIBUSB_SPEED_FULL); case LIBUSB20_SPEED_HIGH: return (LIBUSB_SPEED_HIGH); case LIBUSB20_SPEED_SUPER: return (LIBUSB_SPEED_SUPER); case LIBUSB20_SPEED_SUPER_PLUS: return (LIBUSB_SPEED_SUPER_PLUS); default: break; } return (LIBUSB_SPEED_UNKNOWN); } int libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint) { struct libusb_config_descriptor *pdconf; struct libusb_interface *pinf; struct libusb_interface_descriptor *pdinf; struct libusb_endpoint_descriptor *pdend; int i; int j; int k; int ret; if (dev == NULL) return (LIBUSB_ERROR_NO_DEVICE); ret = libusb_get_active_config_descriptor(dev, &pdconf); if (ret < 0) return (ret); ret = LIBUSB_ERROR_NOT_FOUND; for (i = 0; i < pdconf->bNumInterfaces; i++) { pinf = &pdconf->interface[i]; for (j = 0; j < pinf->num_altsetting; j++) { pdinf = &pinf->altsetting[j]; for (k = 0; k < pdinf->bNumEndpoints; k++) { pdend = &pdinf->endpoint[k]; if (pdend->bEndpointAddress == endpoint) { ret = pdend->wMaxPacketSize; goto out; } } } } out: libusb_free_config_descriptor(pdconf); return (ret); } int libusb_get_max_iso_packet_size(libusb_device *dev, uint8_t endpoint) { int multiplier; int ret; ret = libusb_get_max_packet_size(dev, endpoint); switch (libusb20_dev_get_speed(dev->os_priv)) { case LIBUSB20_SPEED_LOW: case LIBUSB20_SPEED_FULL: break; default: if (ret > -1) { multiplier = (1 + ((ret >> 11) & 3)); if (multiplier > 3) multiplier = 3; ret = (ret & 0x7FF) * multiplier; } break; } return (ret); } libusb_device * libusb_ref_device(libusb_device *dev) { if (dev == NULL) return (NULL); /* be NULL safe */ CTX_LOCK(dev->ctx); dev->refcnt++; CTX_UNLOCK(dev->ctx); return (dev); } void libusb_unref_device(libusb_device *dev) { if (dev == NULL) return; /* be NULL safe */ CTX_LOCK(dev->ctx); dev->refcnt--; CTX_UNLOCK(dev->ctx); if (dev->refcnt == 0) { libusb_unref_device(dev->parent_dev); libusb20_dev_free(dev->os_priv); free(dev); } } int libusb_open(libusb_device *dev, libusb_device_handle **devh) { libusb_context *ctx = dev->ctx; struct libusb20_device *pdev = dev->os_priv; int err; if (devh == NULL) return (LIBUSB_ERROR_INVALID_PARAM); /* set default device handle value */ *devh = NULL; dev = libusb_ref_device(dev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); err = libusb20_dev_open(pdev, LIBUSB_NUM_SW_ENDPOINTS); if (err) { libusb_unref_device(dev); return (LIBUSB_ERROR_NO_MEM); } /* * Clear the device gone flag, in case the device was opened * after a re-attach, to allow new transaction: */ CTX_LOCK(ctx); dev->device_is_gone = 0; CTX_UNLOCK(ctx); libusb10_add_pollfd(ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); /* make sure our event loop detects the new device */ libusb_interrupt_event_handler(ctx); *devh = pdev; return (0); } libusb_device_handle * libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vendor_id, uint16_t product_id) { struct libusb_device **devs; struct libusb20_device *pdev; struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; int i; int j; ctx = GET_CONTEXT(ctx); if (ctx == NULL) return (NULL); /* be NULL safe */ DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_open_device_with_vid_pid enter"); if ((i = libusb_get_device_list(ctx, &devs)) < 0) return (NULL); pdev = NULL; for (j = 0; j < i; j++) { struct libusb20_device *tdev; tdev = devs[j]->os_priv; pdesc = libusb20_dev_get_device_desc(tdev); /* * NOTE: The USB library will automatically swap the * fields in the device descriptor to be of host * endian type! */ if (pdesc->idVendor == vendor_id && pdesc->idProduct == product_id) { libusb_open(devs[j], &pdev); break; } } libusb_free_device_list(devs, 1); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_open_device_with_vid_pid leave"); return (pdev); } void libusb_close(struct libusb20_device *pdev) { libusb_context *ctx; struct libusb_device *dev; if (pdev == NULL) return; /* be NULL safe */ dev = libusb_get_device(pdev); ctx = dev->ctx; libusb10_remove_pollfd(ctx, &dev->dev_poll); libusb20_dev_close(pdev); /* unref will free the "pdev" when the refcount reaches zero */ libusb_unref_device(dev); /* make sure our event loop detects the closed device */ libusb_interrupt_event_handler(ctx); } libusb_device * libusb_get_device(struct libusb20_device *pdev) { if (pdev == NULL) return (NULL); return ((libusb_device *)pdev->privLuData); } int libusb_get_configuration(struct libusb20_device *pdev, int *config) { struct libusb20_config *pconf; if (pdev == NULL || config == NULL) return (LIBUSB_ERROR_INVALID_PARAM); pconf = libusb20_dev_alloc_config(pdev, libusb20_dev_get_config_index(pdev)); if (pconf == NULL) return (LIBUSB_ERROR_NO_MEM); *config = pconf->desc.bConfigurationValue; free(pconf); return (0); } int libusb_set_configuration(struct libusb20_device *pdev, int configuration) { struct libusb20_config *pconf; struct libusb_device *dev; int err; uint8_t i; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (configuration < 1) { /* unconfigure */ i = 255; } else { for (i = 0; i != 255; i++) { uint8_t found; pconf = libusb20_dev_alloc_config(pdev, i); if (pconf == NULL) return (LIBUSB_ERROR_INVALID_PARAM); found = (pconf->desc.bConfigurationValue == configuration); free(pconf); if (found) goto set_config; } return (LIBUSB_ERROR_INVALID_PARAM); } set_config: libusb10_cancel_all_transfer(dev); libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); err = libusb20_dev_set_config_index(pdev, i); libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); return (err ? LIBUSB_ERROR_INVALID_PARAM : 0); } int libusb_claim_interface(struct libusb20_device *pdev, int interface_number) { libusb_device *dev; int err = 0; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); if (pdev->auto_detach != 0) { err = libusb_detach_kernel_driver(pdev, interface_number); if (err != 0) goto done; } CTX_LOCK(dev->ctx); dev->claimed_interfaces |= (1 << interface_number); CTX_UNLOCK(dev->ctx); done: return (err); } int libusb_release_interface(struct libusb20_device *pdev, int interface_number) { libusb_device *dev; int err = 0; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); if (pdev->auto_detach != 0) { err = libusb_attach_kernel_driver(pdev, interface_number); if (err != 0) goto done; } CTX_LOCK(dev->ctx); if (!(dev->claimed_interfaces & (1 << interface_number))) err = LIBUSB_ERROR_NOT_FOUND; else dev->claimed_interfaces &= ~(1 << interface_number); CTX_UNLOCK(dev->ctx); done: return (err); } int libusb_set_interface_alt_setting(struct libusb20_device *pdev, int interface_number, int alternate_setting) { libusb_device *dev; int err = 0; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); CTX_LOCK(dev->ctx); if (!(dev->claimed_interfaces & (1 << interface_number))) err = LIBUSB_ERROR_NOT_FOUND; CTX_UNLOCK(dev->ctx); if (err) return (err); libusb10_cancel_all_transfer(dev); libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); err = libusb20_dev_set_alt_index(pdev, interface_number, alternate_setting); libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); return (err ? LIBUSB_ERROR_OTHER : 0); } libusb_device * libusb_get_parent(libusb_device *dev) { return (dev->parent_dev); } static struct libusb20_transfer * libusb10_get_transfer(struct libusb20_device *pdev, uint8_t endpoint, uint8_t xfer_index) { xfer_index &= 1; /* double buffering */ xfer_index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4; if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) { /* this is an IN endpoint */ xfer_index |= 2; } return (libusb20_tr_get_pointer(pdev, xfer_index)); } int libusb_clear_halt(struct libusb20_device *pdev, uint8_t endpoint) { struct libusb20_transfer *xfer; struct libusb_device *dev; int err; xfer = libusb10_get_transfer(pdev, endpoint, 0); if (xfer == NULL) return (LIBUSB_ERROR_INVALID_PARAM); dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); CTX_LOCK(dev->ctx); err = libusb20_tr_open(xfer, 0, 1, endpoint); CTX_UNLOCK(dev->ctx); if (err != 0 && err != LIBUSB20_ERROR_BUSY) return (LIBUSB_ERROR_OTHER); libusb20_tr_clear_stall_sync(xfer); /* check if we opened the transfer */ if (err == 0) { CTX_LOCK(dev->ctx); libusb20_tr_close(xfer); CTX_UNLOCK(dev->ctx); } return (0); /* success */ } int libusb_reset_device(struct libusb20_device *pdev) { libusb_device *dev; int err; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); libusb10_cancel_all_transfer(dev); libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); err = libusb20_dev_reset(pdev); libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); return (err ? LIBUSB_ERROR_OTHER : 0); } int libusb_check_connected(struct libusb20_device *pdev) { libusb_device *dev; int err; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); err = libusb20_dev_check_connected(pdev); return (err ? LIBUSB_ERROR_NO_DEVICE : 0); } int libusb_kernel_driver_active(struct libusb20_device *pdev, int interface) { if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (libusb20_dev_kernel_driver_active(pdev, interface)) return (0); /* no kernel driver is active */ else return (1); /* kernel driver is active */ } int libusb_get_driver_np(struct libusb20_device *pdev, int interface, char *name, int namelen) { return (libusb_get_driver(pdev, interface, name, namelen)); } int libusb_get_driver(struct libusb20_device *pdev, int interface, char *name, int namelen) { char *ptr; int err; if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (namelen < 1) return (LIBUSB_ERROR_INVALID_PARAM); if (namelen > 255) namelen = 255; err = libusb20_dev_get_iface_desc( pdev, interface, name, namelen); if (err != 0) return (LIBUSB_ERROR_OTHER); /* we only want the driver name */ ptr = strstr(name, ":"); if (ptr != NULL) *ptr = 0; return (0); } int libusb_detach_kernel_driver_np(struct libusb20_device *pdev, int interface) { return (libusb_detach_kernel_driver(pdev, interface)); } int libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface) { int err; if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); err = libusb20_dev_detach_kernel_driver( pdev, interface); return (err ? LIBUSB_ERROR_OTHER : 0); } int libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface) { if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); /* stub - currently not supported by libusb20 */ return (0); } int libusb_set_auto_detach_kernel_driver(libusb_device_handle *dev, int enable) { dev->auto_detach = (enable ? 1 : 0); return (0); } /* Asynchronous device I/O */ struct libusb_transfer * libusb_alloc_transfer(int iso_packets) { struct libusb_transfer *uxfer; struct libusb_super_transfer *sxfer; int len; len = sizeof(struct libusb_transfer) + sizeof(struct libusb_super_transfer) + (iso_packets * sizeof(libusb_iso_packet_descriptor)); sxfer = malloc(len); if (sxfer == NULL) return (NULL); memset(sxfer, 0, len); uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); /* set default value */ uxfer->num_iso_packets = iso_packets; return (uxfer); } void libusb_free_transfer(struct libusb_transfer *uxfer) { struct libusb_super_transfer *sxfer; if (uxfer == NULL) return; /* be NULL safe */ /* check if we should free the transfer buffer */ if (uxfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) free(uxfer->buffer); sxfer = (struct libusb_super_transfer *)( (uint8_t *)uxfer - sizeof(*sxfer)); free(sxfer); } static uint32_t libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) { uint32_t ret; switch (xfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: ret = 60 | LIBUSB20_MAX_FRAME_PRE_SCALE; /* 60ms */ break; case LIBUSB_TRANSFER_TYPE_CONTROL: ret = 2; break; default: ret = 1; break; } return (ret); } static int libusb10_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer) { int ret; int usb_speed; usb_speed = libusb20_dev_get_speed(pdev); switch (xfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: ret = 0; /* kernel will auto-select */ break; case LIBUSB_TRANSFER_TYPE_CONTROL: ret = 1024; break; default: switch (usb_speed) { case LIBUSB20_SPEED_LOW: ret = 256; break; case LIBUSB20_SPEED_FULL: ret = 4096; break; case LIBUSB20_SPEED_SUPER: ret = 65536; break; case LIBUSB20_SPEED_SUPER_PLUS: ret = 131072; break; default: ret = 16384; break; } break; } return (ret); } static int libusb10_convert_error(uint8_t status) { ; /* indent fix */ switch (status) { case LIBUSB20_TRANSFER_START: case LIBUSB20_TRANSFER_COMPLETED: return (LIBUSB_TRANSFER_COMPLETED); case LIBUSB20_TRANSFER_OVERFLOW: return (LIBUSB_TRANSFER_OVERFLOW); case LIBUSB20_TRANSFER_NO_DEVICE: return (LIBUSB_TRANSFER_NO_DEVICE); case LIBUSB20_TRANSFER_STALL: return (LIBUSB_TRANSFER_STALL); case LIBUSB20_TRANSFER_CANCELLED: return (LIBUSB_TRANSFER_CANCELLED); case LIBUSB20_TRANSFER_TIMED_OUT: return (LIBUSB_TRANSFER_TIMED_OUT); default: return (LIBUSB_TRANSFER_ERROR); } } /* This function must be called locked */ static void libusb10_complete_transfer(struct libusb20_transfer *pxfer, struct libusb_super_transfer *sxfer, int status) { struct libusb_transfer *uxfer; struct libusb_device *dev; uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); if (pxfer != NULL) libusb20_tr_set_priv_sc1(pxfer, NULL); /* set transfer status */ uxfer->status = status; /* update super transfer state */ sxfer->state = LIBUSB_SUPER_XFER_ST_NONE; dev = libusb_get_device(uxfer->dev_handle); TAILQ_INSERT_TAIL(&dev->ctx->tr_done, sxfer, entry); } /* This function must be called locked */ static void libusb10_isoc_proxy(struct libusb20_transfer *pxfer) { struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; uint32_t actlen; uint16_t iso_packets; uint16_t i; uint8_t status; status = libusb20_tr_get_status(pxfer); sxfer = libusb20_tr_get_priv_sc1(pxfer); actlen = libusb20_tr_get_actual_length(pxfer); iso_packets = libusb20_tr_get_max_frames(pxfer); if (sxfer == NULL) return; /* cancelled - nothing to do */ uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); if (iso_packets > uxfer->num_iso_packets) iso_packets = uxfer->num_iso_packets; if (iso_packets == 0) return; /* nothing to do */ /* make sure that the number of ISOCHRONOUS packets is valid */ uxfer->num_iso_packets = iso_packets; switch (status) { case LIBUSB20_TRANSFER_COMPLETED: /* update actual length */ uxfer->actual_length = actlen; for (i = 0; i != iso_packets; i++) { uxfer->iso_packet_desc[i].actual_length = libusb20_tr_get_length(pxfer, i); } libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); break; case LIBUSB20_TRANSFER_START: /* setup length(s) */ actlen = 0; for (i = 0; i != iso_packets; i++) { libusb20_tr_setup_isoc(pxfer, &uxfer->buffer[actlen], uxfer->iso_packet_desc[i].length, i); actlen += uxfer->iso_packet_desc[i].length; } /* no remainder */ sxfer->rem_len = 0; libusb20_tr_set_total_frames(pxfer, iso_packets); libusb20_tr_submit(pxfer); /* fork another USB transfer, if any */ libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); break; } } /* This function must be called locked */ static void libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer) { struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; uint32_t max_bulk; uint32_t actlen; uint8_t status; uint8_t flags; status = libusb20_tr_get_status(pxfer); sxfer = libusb20_tr_get_priv_sc1(pxfer); max_bulk = libusb20_tr_get_max_total_length(pxfer); actlen = libusb20_tr_get_actual_length(pxfer); if (sxfer == NULL) return; /* cancelled - nothing to do */ uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); flags = uxfer->flags; switch (status) { case LIBUSB20_TRANSFER_COMPLETED: uxfer->actual_length += actlen; /* check for short packet */ if (sxfer->last_len != actlen) { if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR); } else { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); } break; } /* check for end of data */ if (sxfer->rem_len == 0) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); break; } /* FALLTHROUGH */ case LIBUSB20_TRANSFER_START: if (max_bulk > sxfer->rem_len) { max_bulk = sxfer->rem_len; } /* setup new BULK or INTERRUPT transaction */ libusb20_tr_setup_bulk(pxfer, sxfer->curr_data, max_bulk, uxfer->timeout); /* update counters */ sxfer->last_len = max_bulk; sxfer->curr_data += max_bulk; sxfer->rem_len -= max_bulk; libusb20_tr_submit(pxfer); /* check if we can fork another USB transfer */ if (sxfer->rem_len == 0) libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); break; } } /* This function must be called locked */ static void libusb10_ctrl_proxy(struct libusb20_transfer *pxfer) { struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; uint32_t max_bulk; uint32_t actlen; uint8_t status; uint8_t flags; status = libusb20_tr_get_status(pxfer); sxfer = libusb20_tr_get_priv_sc1(pxfer); max_bulk = libusb20_tr_get_max_total_length(pxfer); actlen = libusb20_tr_get_actual_length(pxfer); if (sxfer == NULL) return; /* cancelled - nothing to do */ uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); flags = uxfer->flags; switch (status) { case LIBUSB20_TRANSFER_COMPLETED: uxfer->actual_length += actlen; /* subtract length of SETUP packet, if any */ actlen -= libusb20_tr_get_length(pxfer, 0); /* check for short packet */ if (sxfer->last_len != actlen) { if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR); } else { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); } break; } /* check for end of data */ if (sxfer->rem_len == 0) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); break; } /* FALLTHROUGH */ case LIBUSB20_TRANSFER_START: if (max_bulk > sxfer->rem_len) { max_bulk = sxfer->rem_len; } /* setup new CONTROL transaction */ if (status == LIBUSB20_TRANSFER_COMPLETED) { /* next fragment - don't send SETUP packet */ libusb20_tr_set_length(pxfer, 0, 0); } else { /* first fragment - send SETUP packet */ libusb20_tr_set_length(pxfer, 8, 0); libusb20_tr_set_buffer(pxfer, uxfer->buffer, 0); } if (max_bulk != 0) { libusb20_tr_set_length(pxfer, max_bulk, 1); libusb20_tr_set_buffer(pxfer, sxfer->curr_data, 1); libusb20_tr_set_total_frames(pxfer, 2); } else { libusb20_tr_set_total_frames(pxfer, 1); } /* update counters */ sxfer->last_len = max_bulk; sxfer->curr_data += max_bulk; sxfer->rem_len -= max_bulk; libusb20_tr_submit(pxfer); /* check if we can fork another USB transfer */ if (sxfer->rem_len == 0) libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); break; } } /* The following function must be called locked */ static void libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint) { struct libusb20_transfer *pxfer0; struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; struct libusb_device *dev; int err; int buffsize; int maxframe; int temp; dev = libusb_get_device(pdev); pxfer0 = libusb10_get_transfer(pdev, endpoint, 0); pxfer1 = libusb10_get_transfer(pdev, endpoint, 1); if (pxfer0 == NULL || pxfer1 == NULL) return; /* shouldn't happen */ temp = 0; if (libusb20_tr_pending(pxfer0)) temp |= 1; if (libusb20_tr_pending(pxfer1)) temp |= 2; switch (temp) { case 3: /* wait till one of the transfers complete */ return; case 2: sxfer = libusb20_tr_get_priv_sc1(pxfer1); if (sxfer == NULL) return; /* cancelling */ if (sxfer->rem_len) return; /* cannot queue another one */ /* swap transfers */ pxfer1 = pxfer0; break; case 1: sxfer = libusb20_tr_get_priv_sc1(pxfer0); if (sxfer == NULL) return; /* cancelling */ if (sxfer->rem_len) return; /* cannot queue another one */ /* swap transfers */ pxfer0 = pxfer1; break; default: break; } /* find next transfer on same endpoint */ TAILQ_FOREACH(sxfer, &dev->tr_head, entry) { uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); if (uxfer->endpoint == endpoint) { TAILQ_REMOVE(&dev->tr_head, sxfer, entry); sxfer->entry.tqe_prev = NULL; goto found; } } return; /* success */ found: libusb20_tr_set_priv_sc0(pxfer0, pdev); libusb20_tr_set_priv_sc1(pxfer0, sxfer); /* reset super transfer state */ sxfer->rem_len = uxfer->length; sxfer->curr_data = uxfer->buffer; uxfer->actual_length = 0; switch (uxfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: libusb20_tr_set_callback(pxfer0, libusb10_isoc_proxy); break; case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_BULK_STREAM: case LIBUSB_TRANSFER_TYPE_INTERRUPT: libusb20_tr_set_callback(pxfer0, libusb10_bulk_intr_proxy); break; case LIBUSB_TRANSFER_TYPE_CONTROL: libusb20_tr_set_callback(pxfer0, libusb10_ctrl_proxy); if (sxfer->rem_len < 8) goto failure; /* remove SETUP packet from data */ sxfer->rem_len -= 8; sxfer->curr_data += 8; break; default: goto failure; } buffsize = libusb10_get_buffsize(pdev, uxfer); maxframe = libusb10_get_maxframe(pdev, uxfer); /* make sure the transfer is opened */ err = libusb20_tr_open_stream(pxfer0, buffsize, maxframe, endpoint, sxfer->stream_id); if (err && (err != LIBUSB20_ERROR_BUSY)) { goto failure; } libusb20_tr_start(pxfer0); return; failure: libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_ERROR); /* make sure our event loop spins the done handler */ libusb_interrupt_event_handler(dev->ctx); } /* The following function must be called unlocked */ int libusb_submit_transfer(struct libusb_transfer *uxfer) { struct libusb20_transfer *pxfer0; struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_device *dev; uint8_t endpoint; int err; if (uxfer == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (uxfer->dev_handle == NULL) return (LIBUSB_ERROR_INVALID_PARAM); endpoint = uxfer->endpoint; dev = libusb_get_device(uxfer->dev_handle); DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_submit_transfer enter"); sxfer = (struct libusb_super_transfer *)( (uint8_t *)uxfer - sizeof(*sxfer)); CTX_LOCK(dev->ctx); pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0); pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1); if (pxfer0 == NULL || pxfer1 == NULL) { err = LIBUSB_ERROR_OTHER; } else if ((sxfer->entry.tqe_prev != NULL) || (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) || (libusb20_tr_get_priv_sc1(pxfer1) == sxfer)) { err = LIBUSB_ERROR_BUSY; } else if (dev->device_is_gone != 0) { err = LIBUSB_ERROR_NO_DEVICE; } else { /* set pending state */ sxfer->state = LIBUSB_SUPER_XFER_ST_PEND; /* insert transfer into transfer head list */ TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry); /* start work transfers */ libusb10_submit_transfer_sub( uxfer->dev_handle, endpoint); err = 0; /* success */ } CTX_UNLOCK(dev->ctx); DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_submit_transfer leave %d", err); return (err); } /* Asynchronous transfer cancel */ int libusb_cancel_transfer(struct libusb_transfer *uxfer) { struct libusb20_transfer *pxfer0; struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_device *dev; struct libusb_device_handle *devh; uint8_t endpoint; int retval; if (uxfer == NULL) return (LIBUSB_ERROR_INVALID_PARAM); /* check if not initialised */ if ((devh = uxfer->dev_handle) == NULL) return (LIBUSB_ERROR_NOT_FOUND); endpoint = uxfer->endpoint; dev = libusb_get_device(devh); DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_cancel_transfer enter"); sxfer = (struct libusb_super_transfer *)( (uint8_t *)uxfer - sizeof(*sxfer)); retval = 0; CTX_LOCK(dev->ctx); pxfer0 = libusb10_get_transfer(devh, endpoint, 0); pxfer1 = libusb10_get_transfer(devh, endpoint, 1); if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) { /* only update the transfer status */ uxfer->status = LIBUSB_TRANSFER_CANCELLED; retval = LIBUSB_ERROR_NOT_FOUND; } else if (sxfer->entry.tqe_prev != NULL) { /* we are lucky - transfer is on a queue */ TAILQ_REMOVE(&dev->tr_head, sxfer, entry); sxfer->entry.tqe_prev = NULL; libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_CANCELLED); /* make sure our event loop spins the done handler */ libusb_interrupt_event_handler(dev->ctx); } else if (pxfer0 == NULL || pxfer1 == NULL) { /* not started */ retval = LIBUSB_ERROR_NOT_FOUND; } else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) { libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_CANCELLED); if (dev->device_is_gone != 0) { /* clear transfer pointer */ libusb20_tr_set_priv_sc1(pxfer0, NULL); /* make sure our event loop spins the done handler */ libusb_interrupt_event_handler(dev->ctx); } else { libusb20_tr_stop(pxfer0); /* make sure the queue doesn't stall */ libusb10_submit_transfer_sub(devh, endpoint); } } else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) { libusb10_complete_transfer(pxfer1, sxfer, LIBUSB_TRANSFER_CANCELLED); /* check if handle is still active */ if (dev->device_is_gone != 0) { /* clear transfer pointer */ libusb20_tr_set_priv_sc1(pxfer1, NULL); /* make sure our event loop spins the done handler */ libusb_interrupt_event_handler(dev->ctx); } else { libusb20_tr_stop(pxfer1); /* make sure the queue doesn't stall */ libusb10_submit_transfer_sub(devh, endpoint); } } else { /* not started */ retval = LIBUSB_ERROR_NOT_FOUND; } CTX_UNLOCK(dev->ctx); DPRINTF(dev->ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_cancel_transfer leave"); return (retval); } UNEXPORTED void libusb10_cancel_all_transfer(libusb_device *dev) { struct libusb20_device *pdev = dev->os_priv; unsigned x; for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) { struct libusb20_transfer *xfer; xfer = libusb20_tr_get_pointer(pdev, x); if (xfer == NULL) continue; libusb20_tr_close(xfer); } } UNEXPORTED void libusb10_cancel_all_transfer_locked(struct libusb20_device *pdev, struct libusb_device *dev) { struct libusb_super_transfer *sxfer; unsigned x; for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) { struct libusb20_transfer *xfer; xfer = libusb20_tr_get_pointer(pdev, x); if (xfer == NULL) continue; if (libusb20_tr_pending(xfer) == 0) continue; sxfer = libusb20_tr_get_priv_sc1(xfer); if (sxfer == NULL) continue; /* complete pending transfer */ libusb10_complete_transfer(xfer, sxfer, LIBUSB_TRANSFER_ERROR); } while ((sxfer = TAILQ_FIRST(&dev->tr_head))) { TAILQ_REMOVE(&dev->tr_head, sxfer, entry); /* complete pending transfer */ libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_ERROR); } } uint16_t libusb_cpu_to_le16(uint16_t x) { return (htole16(x)); } uint16_t libusb_le16_to_cpu(uint16_t x) { return (le16toh(x)); } const char * libusb_strerror(int code) { int entry = -code; if (code == LIBUSB_ERROR_OTHER) entry = LIBUSB_ERROR_COUNT - 1; /* * The libusb upstream considers all code out of range a * LIBUSB_ERROR_OTHER. In FreeBSD, it is a special unknown error. We * preserve the FreeBSD implementation as I think it make sense. */ if (entry < 0 || entry >= LIBUSB_ERROR_COUNT) entry = LIBUSB_ERROR_COUNT; /* * Fall back to English one as the translation may be unimplemented * when adding new error code. */ if (default_language_context->err_strs[entry] == NULL) return (libusb_language_ctx[0].err_strs[entry]); return (default_language_context->err_strs[entry]); } const char * libusb_error_name(int code) { switch (code) { case LIBUSB_SUCCESS: return ("LIBUSB_SUCCESS"); case LIBUSB_ERROR_IO: return ("LIBUSB_ERROR_IO"); case LIBUSB_ERROR_INVALID_PARAM: return ("LIBUSB_ERROR_INVALID_PARAM"); case LIBUSB_ERROR_ACCESS: return ("LIBUSB_ERROR_ACCESS"); case LIBUSB_ERROR_NO_DEVICE: return ("LIBUSB_ERROR_NO_DEVICE"); case LIBUSB_ERROR_NOT_FOUND: return ("LIBUSB_ERROR_NOT_FOUND"); case LIBUSB_ERROR_BUSY: return ("LIBUSB_ERROR_BUSY"); case LIBUSB_ERROR_TIMEOUT: return ("LIBUSB_ERROR_TIMEOUT"); case LIBUSB_ERROR_OVERFLOW: return ("LIBUSB_ERROR_OVERFLOW"); case LIBUSB_ERROR_PIPE: return ("LIBUSB_ERROR_PIPE"); case LIBUSB_ERROR_INTERRUPTED: return ("LIBUSB_ERROR_INTERRUPTED"); case LIBUSB_ERROR_NO_MEM: return ("LIBUSB_ERROR_NO_MEM"); case LIBUSB_ERROR_NOT_SUPPORTED: return ("LIBUSB_ERROR_NOT_SUPPORTED"); case LIBUSB_ERROR_OTHER: return ("LIBUSB_ERROR_OTHER"); default: return ("LIBUSB_ERROR_UNKNOWN"); } } int libusb_has_capability(uint32_t capability) { switch (capability) { case LIBUSB_CAP_HAS_CAPABILITY: case LIBUSB_CAP_HAS_HOTPLUG: case LIBUSB_CAP_HAS_HID_ACCESS: case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER: return (1); default: return (0); } } void libusb_log_va_args(struct libusb_context *ctx, enum libusb_log_level level, const char *fmt, ...) { static const char *log_prefix[5] = { [LIBUSB_LOG_LEVEL_ERROR] = "LIBUSB_ERROR", [LIBUSB_LOG_LEVEL_WARNING] = "LIBUSB_WARN", [LIBUSB_LOG_LEVEL_INFO] = "LIBUSB_INFO", [LIBUSB_LOG_LEVEL_DEBUG] = "LIBUSB_DEBUG", }; char buffer[LIBUSB_LOG_BUFFER_SIZE]; char new_fmt[LIBUSB_LOG_BUFFER_SIZE]; va_list args; ctx = GET_CONTEXT(ctx); if (ctx->debug < level) return; va_start(args, fmt); snprintf(new_fmt, sizeof(new_fmt), "%s: %s\n", log_prefix[level], fmt); vsnprintf(buffer, sizeof(buffer), new_fmt, args); fputs(buffer, stdout); va_end(args); } /* * Upstream code actually recognizes the first two characters to identify a * language. We do so to provide API compatibility with setlocale. */ int libusb_setlocale(const char *locale) { size_t idx; const char *lang; if (locale == NULL || strlen(locale) < 2 || (locale[2] != '\0' && strchr("-_.", locale[2]) == NULL)) return (LIBUSB_ERROR_INVALID_PARAM); for (idx = 0; idx < nitems(libusb_language_ctx); ++idx) { lang = libusb_language_ctx[idx].lang_name; if (tolower(locale[0]) == lang[0] && tolower(locale[1]) == lang[1]) { default_language_context = &libusb_language_ctx[idx]; return (LIBUSB_SUCCESS); } } return (LIBUSB_ERROR_INVALID_PARAM); } unsigned char * libusb_dev_mem_alloc(libusb_device_handle *devh) { return (NULL); } int libusb_dev_mem_free(libusb_device_handle *devh, unsigned char *buffer, size_t size) { return (LIBUSB_ERROR_NOT_SUPPORTED); } diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c index ea365162df5a..c99586ff650d 100644 --- a/lib/libusb/libusb10_io.c +++ b/lib/libusb/libusb10_io.c @@ -1,853 +1,866 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef LIBUSB_GLOBAL_INCLUDE_FILE #include LIBUSB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #endif #define libusb_device_handle libusb20_device #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" #include "libusb.h" #include "libusb10.h" UNEXPORTED void libusb10_add_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd, struct libusb20_device *pdev, int fd, short events) { if (ctx == NULL) return; /* invalid */ if (pollfd->entry.tqe_prev != NULL) return; /* already queued */ if (fd < 0) return; /* invalid */ pollfd->pdev = pdev; pollfd->pollfd.fd = fd; pollfd->pollfd.events = events; CTX_LOCK(ctx); TAILQ_INSERT_TAIL(&ctx->pollfds, pollfd, entry); CTX_UNLOCK(ctx); if (ctx->fd_added_cb) ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data); } UNEXPORTED void libusb10_remove_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd) { if (ctx == NULL) return; /* invalid */ if (pollfd->entry.tqe_prev == NULL) return; /* already dequeued */ CTX_LOCK(ctx); TAILQ_REMOVE(&ctx->pollfds, pollfd, entry); pollfd->entry.tqe_prev = NULL; CTX_UNLOCK(ctx); if (ctx->fd_removed_cb) ctx->fd_removed_cb(pollfd->pollfd.fd, ctx->fd_cb_user_data); } /* This function must be called locked */ static int libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) { struct libusb_device *dev; struct libusb20_device **ppdev; struct libusb_super_pollfd *pfd; struct pollfd *fds; struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; nfds_t nfds; int timeout; int i; int err; DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb10_handle_events_sub enter"); nfds = 0; i = 0; TAILQ_FOREACH(pfd, &ctx->pollfds, entry) nfds++; fds = alloca(sizeof(*fds) * nfds); if (fds == NULL) return (LIBUSB_ERROR_NO_MEM); ppdev = alloca(sizeof(*ppdev) * nfds); if (ppdev == NULL) return (LIBUSB_ERROR_NO_MEM); TAILQ_FOREACH(pfd, &ctx->pollfds, entry) { fds[i].fd = pfd->pollfd.fd; fds[i].events = pfd->pollfd.events; fds[i].revents = 0; ppdev[i] = pfd->pdev; if (pfd->pdev != NULL) libusb_get_device(pfd->pdev)->refcnt++; i++; } if (tv == NULL) timeout = -1; else timeout = (tv->tv_sec * 1000) + ((tv->tv_usec + 999) / 1000); CTX_UNLOCK(ctx); err = poll(fds, nfds, timeout); CTX_LOCK(ctx); if ((err == -1) && (errno == EINTR)) err = LIBUSB_ERROR_INTERRUPTED; else if (err < 0) err = LIBUSB_ERROR_IO; if (err < 1) { for (i = 0; i != (int)nfds; i++) { if (ppdev[i] != NULL) { CTX_UNLOCK(ctx); libusb_unref_device(libusb_get_device(ppdev[i])); CTX_LOCK(ctx); } } goto do_done; } for (i = 0; i != (int)nfds; i++) { if (ppdev[i] != NULL) { dev = libusb_get_device(ppdev[i]); if (fds[i].revents != 0) { err = libusb20_dev_process(ppdev[i]); if (err) { /* * When the device is opened * set the "device_is_gone" * flag. This prevents the * client from submitting new * USB transfers to a detached * device. */ if (ppdev[i]->is_opened) dev->device_is_gone = 1; /* remove USB device from polling loop */ libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); /* cancel all pending transfers */ libusb10_cancel_all_transfer_locked(ppdev[i], dev); } } CTX_UNLOCK(ctx); libusb_unref_device(dev); CTX_LOCK(ctx); } else { eventfd_read(fds[i].fd, &(eventfd_t){0}); } } err = 0; do_done: /* Do all done callbacks */ while ((sxfer = TAILQ_FIRST(&ctx->tr_done))) { uint8_t flags; TAILQ_REMOVE(&ctx->tr_done, sxfer, entry); sxfer->entry.tqe_prev = NULL; ctx->tr_done_ref++; CTX_UNLOCK(ctx); uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); /* Allow the callback to free the transfer itself. */ flags = uxfer->flags; if (uxfer->callback != NULL) (uxfer->callback) (uxfer); /* Check if the USB transfer should be automatically freed. */ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) libusb_free_transfer(uxfer); CTX_LOCK(ctx); ctx->tr_done_ref--; ctx->tr_done_gen++; } /* Wakeup other waiters */ pthread_cond_broadcast(&ctx->ctx_cond); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb10_handle_events_sub complete"); return (err); } /* Polling and timing */ int libusb_try_lock_events(libusb_context *ctx) { int err; ctx = GET_CONTEXT(ctx); if (ctx == NULL) return (1); err = CTX_TRYLOCK(ctx); if (err) return (1); err = (ctx->ctx_handler != NO_THREAD); if (err) CTX_UNLOCK(ctx); else ctx->ctx_handler = pthread_self(); return (err); } void libusb_lock_events(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); CTX_LOCK(ctx); if (ctx->ctx_handler == NO_THREAD) ctx->ctx_handler = pthread_self(); } void libusb_unlock_events(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); if (ctx->ctx_handler == pthread_self()) { ctx->ctx_handler = NO_THREAD; pthread_cond_broadcast(&ctx->ctx_cond); } CTX_UNLOCK(ctx); } int libusb_event_handling_ok(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); return (ctx->ctx_handler == pthread_self()); } int libusb_event_handler_active(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); return (ctx->ctx_handler != NO_THREAD); } void libusb_lock_event_waiters(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); CTX_LOCK(ctx); } void libusb_unlock_event_waiters(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); CTX_UNLOCK(ctx); } int libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) { struct timespec ts; int err; ctx = GET_CONTEXT(ctx); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_wait_for_event enter"); if (tv == NULL) { pthread_cond_wait(&ctx->ctx_cond, &ctx->ctx_lock); /* try to grab polling of actual events, if any */ if (ctx->ctx_handler == NO_THREAD) ctx->ctx_handler = pthread_self(); return (0); } err = clock_gettime(CLOCK_MONOTONIC, &ts); if (err < 0) return (LIBUSB_ERROR_OTHER); /* * The "tv" arguments points to a relative time structure and * not an absolute time structure. */ ts.tv_sec += tv->tv_sec; ts.tv_nsec += tv->tv_usec * 1000; if (ts.tv_nsec >= 1000000000) { ts.tv_nsec -= 1000000000; ts.tv_sec++; } err = pthread_cond_timedwait(&ctx->ctx_cond, &ctx->ctx_lock, &ts); /* try to grab polling of actual events, if any */ if (ctx->ctx_handler == NO_THREAD) ctx->ctx_handler = pthread_self(); if (err == ETIMEDOUT) return (1); return (0); } int libusb_handle_events_timeout_completed(libusb_context *ctx, struct timeval *tv, int *completed) { int err = 0; ctx = GET_CONTEXT(ctx); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_handle_events_timeout_completed enter"); libusb_lock_events(ctx); while (1) { if (completed != NULL) { if (*completed != 0 || err != 0) break; } err = libusb_handle_events_locked(ctx, tv); if (completed == NULL) break; } libusb_unlock_events(ctx); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_handle_events_timeout_completed exit"); return (err); } int libusb_handle_events_completed(libusb_context *ctx, int *completed) { return (libusb_handle_events_timeout_completed(ctx, NULL, completed)); } int libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv) { return (libusb_handle_events_timeout_completed(ctx, tv, NULL)); } int libusb_handle_events(libusb_context *ctx) { return (libusb_handle_events_timeout_completed(ctx, NULL, NULL)); } int libusb_handle_events_locked(libusb_context *ctx, struct timeval *tv) { int err; ctx = GET_CONTEXT(ctx); if (libusb_event_handling_ok(ctx)) { err = libusb10_handle_events_sub(ctx, tv); } else { err = libusb_wait_for_event(ctx, tv); if (err != 0) err = LIBUSB_ERROR_TIMEOUT; } return (err); } int libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv) { /* all timeouts are currently being done by the kernel */ timerclear(tv); return (0); } void libusb_set_pollfd_notifiers(libusb_context *ctx, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data) { ctx = GET_CONTEXT(ctx); ctx->fd_added_cb = added_cb; ctx->fd_removed_cb = removed_cb; ctx->fd_cb_user_data = user_data; } const struct libusb_pollfd ** libusb_get_pollfds(libusb_context *ctx) { struct libusb_super_pollfd *pollfd; libusb_pollfd **ret; int i; ctx = GET_CONTEXT(ctx); CTX_LOCK(ctx); i = 0; TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) i++; ret = calloc(i + 1, sizeof(struct libusb_pollfd *)); if (ret == NULL) goto done; i = 0; TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) ret[i++] = &pollfd->pollfd; ret[i] = NULL; done: CTX_UNLOCK(ctx); return ((const struct libusb_pollfd **)ret); } /* Synchronous device I/O */ int libusb_control_transfer(libusb_device_handle *devh, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, unsigned int timeout) { struct LIBUSB20_CONTROL_SETUP_DECODED req; int err; uint16_t actlen; if (devh == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if ((wLength != 0) && (data == NULL)) return (LIBUSB_ERROR_INVALID_PARAM); LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); req.bmRequestType = bmRequestType; req.bRequest = bRequest; req.wValue = wValue; req.wIndex = wIndex; req.wLength = wLength; err = libusb20_dev_request_sync(devh, &req, data, &actlen, timeout, 0); if (err == LIBUSB20_ERROR_PIPE) return (LIBUSB_ERROR_PIPE); else if (err == LIBUSB20_ERROR_TIMEOUT) return (LIBUSB_ERROR_TIMEOUT); else if (err) return (LIBUSB_ERROR_NO_DEVICE); return (actlen); } static libusb_context * libusb10_get_context_by_device_handle(libusb_device_handle *devh) { libusb_context *ctx; if (devh != NULL) ctx = libusb_get_device(devh)->ctx; else ctx = NULL; return (GET_CONTEXT(ctx)); } static void libusb10_do_transfer_cb(struct libusb_transfer *transfer) { libusb_context *ctx; int *pdone; ctx = libusb10_get_context_by_device_handle(transfer->dev_handle); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "sync I/O done"); pdone = transfer->user_data; *pdone = 1; } /* * TODO: Replace the following function. Allocating and freeing on a * per-transfer basis is slow. --HPS */ static int libusb10_do_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout, int type) { libusb_context *ctx; struct libusb_transfer *xfer; int done; int ret; if (devh == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if ((length != 0) && (data == NULL)) return (LIBUSB_ERROR_INVALID_PARAM); xfer = libusb_alloc_transfer(0); if (xfer == NULL) return (LIBUSB_ERROR_NO_MEM); ctx = libusb_get_device(devh)->ctx; xfer->dev_handle = devh; xfer->endpoint = endpoint; xfer->type = type; xfer->timeout = timeout; xfer->buffer = data; xfer->length = length; xfer->user_data = (void *)&done; xfer->callback = libusb10_do_transfer_cb; done = 0; if ((ret = libusb_submit_transfer(xfer)) < 0) { libusb_free_transfer(xfer); return (ret); } while (done == 0) { if ((ret = libusb_handle_events(ctx)) < 0) { libusb_cancel_transfer(xfer); usleep(1000); /* nice it */ } } *transferred = xfer->actual_length; switch (xfer->status) { case LIBUSB_TRANSFER_COMPLETED: ret = 0; break; case LIBUSB_TRANSFER_TIMED_OUT: ret = LIBUSB_ERROR_TIMEOUT; break; case LIBUSB_TRANSFER_OVERFLOW: ret = LIBUSB_ERROR_OVERFLOW; break; case LIBUSB_TRANSFER_STALL: ret = LIBUSB_ERROR_PIPE; break; case LIBUSB_TRANSFER_NO_DEVICE: ret = LIBUSB_ERROR_NO_DEVICE; break; default: ret = LIBUSB_ERROR_OTHER; break; } libusb_free_transfer(xfer); return (ret); } int libusb_bulk_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout) { libusb_context *ctx; int ret; ctx = libusb10_get_context_by_device_handle(devh); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_bulk_transfer enter"); ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_bulk_transfer leave"); return (ret); } int libusb_interrupt_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout) { libusb_context *ctx; int ret; ctx = libusb10_get_context_by_device_handle(devh); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_interrupt_transfer enter"); ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); DPRINTF(ctx, LIBUSB_LOG_LEVEL_DEBUG, "libusb_interrupt_transfer leave"); return (ret); } uint8_t * libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t off) { uint8_t *ptr; uint32_t n; if (transfer->num_iso_packets < 0) return (NULL); if (off >= (uint32_t)transfer->num_iso_packets) return (NULL); ptr = transfer->buffer; if (ptr == NULL) return (NULL); for (n = 0; n != off; n++) { ptr += transfer->iso_packet_desc[n].length; } return (ptr); } uint8_t * libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t off) { uint8_t *ptr; if (transfer->num_iso_packets < 0) return (NULL); if (off >= (uint32_t)transfer->num_iso_packets) return (NULL); ptr = transfer->buffer; if (ptr == NULL) return (NULL); ptr += transfer->iso_packet_desc[0].length * off; return (ptr); } void libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, uint32_t length) { int n; if (transfer->num_iso_packets < 0) return; for (n = 0; n != transfer->num_iso_packets; n++) transfer->iso_packet_desc[n].length = length; } uint8_t * libusb_control_transfer_get_data(struct libusb_transfer *transfer) { if (transfer->buffer == NULL) return (NULL); return (transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE); } struct libusb_control_setup * libusb_control_transfer_get_setup(struct libusb_transfer *transfer) { return ((struct libusb_control_setup *)transfer->buffer); } void libusb_fill_control_setup(uint8_t *buf, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) { struct libusb_control_setup *req = (struct libusb_control_setup *)buf; /* The alignment is OK for all fields below. */ req->bmRequestType = bmRequestType; req->bRequest = bRequest; req->wValue = htole16(wValue); req->wIndex = htole16(wIndex); req->wLength = htole16(wLength); } void libusb_fill_control_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t *buf, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { struct libusb_control_setup *setup = (struct libusb_control_setup *)buf; transfer->dev_handle = devh; transfer->endpoint = 0; transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; transfer->timeout = timeout; transfer->buffer = buf; if (setup != NULL) transfer->length = LIBUSB_CONTROL_SETUP_SIZE + le16toh(setup->wLength); else transfer->length = 0; transfer->user_data = user_data; transfer->callback = callback; } void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { transfer->dev_handle = devh; transfer->endpoint = endpoint; transfer->type = LIBUSB_TRANSFER_TYPE_BULK; transfer->timeout = timeout; transfer->buffer = buf; transfer->length = length; transfer->user_data = user_data; transfer->callback = callback; } void libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { transfer->dev_handle = devh; transfer->endpoint = endpoint; transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; transfer->timeout = timeout; transfer->buffer = buf; transfer->length = length; transfer->user_data = user_data; transfer->callback = callback; } +void +libusb_fill_bulk_stream_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + uint32_t stream_id, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + + libusb_transfer_set_stream_id(transfer, stream_id); +} + void libusb_fill_iso_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, int npacket, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { transfer->dev_handle = devh; transfer->endpoint = endpoint; transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; transfer->timeout = timeout; transfer->buffer = buf; transfer->length = length; transfer->num_iso_packets = npacket; transfer->user_data = user_data; transfer->callback = callback; } int libusb_alloc_streams(libusb_device_handle *dev, uint32_t num_streams, unsigned char *endpoints, int num_endpoints) { if (num_streams > 1) return (LIBUSB_ERROR_INVALID_PARAM); return (0); } int libusb_free_streams(libusb_device_handle *dev, unsigned char *endpoints, int num_endpoints) { return (0); } void libusb_transfer_set_stream_id(struct libusb_transfer *transfer, uint32_t stream_id) { struct libusb_super_transfer *sxfer; if (transfer == NULL) return; sxfer = (struct libusb_super_transfer *)( ((uint8_t *)transfer) - sizeof(*sxfer)); /* set stream ID */ sxfer->stream_id = stream_id; } uint32_t libusb_transfer_get_stream_id(struct libusb_transfer *transfer) { struct libusb_super_transfer *sxfer; if (transfer == NULL) return (0); sxfer = (struct libusb_super_transfer *)( ((uint8_t *)transfer) - sizeof(*sxfer)); /* get stream ID */ return (sxfer->stream_id); } void libusb_free_pollfds(const struct libusb_pollfd **pollfds) { if (pollfds == NULL) return; free(pollfds); }