diff options
Diffstat (limited to 'lib/libusb')
-rw-r--r-- | lib/libusb/libusb.h | 427 | ||||
-rw-r--r-- | lib/libusb/libusb10.c | 1140 | ||||
-rw-r--r-- | lib/libusb/libusb10.h | 245 | ||||
-rw-r--r-- | lib/libusb/libusb10_desc.c | 301 | ||||
-rw-r--r-- | lib/libusb/libusb10_io.c | 748 |
5 files changed, 2861 insertions, 0 deletions
diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h new file mode 100644 index 000000000000..a07c35589f42 --- /dev/null +++ b/lib/libusb/libusb.h @@ -0,0 +1,427 @@ +/* $FreeBSD$ */ +/*- + * 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__ + +#include <stdint.h> +#include <time.h> +#include <string.h> +#include <pthread.h> + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/endian.h> + +#ifdef __cplusplus +extern "C" { +#endif +#if 0 +} /* indent fix */ + +#endif + +struct list_head { + struct list_head *prev, *next; +}; + +/* 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_PTP = 6, + LIBUSB_CLASS_PRINTER = 7, + LIBUSB_CLASS_MASS_STORAGE = 8, + LIBUSB_CLASS_HUB = 9, + LIBUSB_CLASS_DATA = 10, + 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, +}; + +#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_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, +}; + +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, +}; + +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_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, +}; + +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_debug_level { + LIBUSB_DEBUG_NO=0, + LIBUSB_DEBUG_FUNCTION=1, + LIBUSB_DEBUG_TRANSFER=2, +}; + +/* libusb structures */ + +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_context { + int debug; + int debug_fixed; + + int ctrl_pipe[2]; + + struct list_head usb_devs; + pthread_mutex_t usb_devs_lock; + + struct list_head open_devs; + pthread_mutex_t open_devs_lock; + + struct list_head flying_transfers; + pthread_mutex_t flying_transfers_lock; + + struct list_head pollfds; + pthread_mutex_t pollfds_lock; + + unsigned int pollfd_modify; + pthread_mutex_t pollfd_modify_lock; + + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; + + pthread_mutex_t events_lock; + int event_handler_active; + + pthread_mutex_t event_waiters_lock; + pthread_cond_t event_waiters_cond; +} libusb_context; + +typedef struct libusb_device { + pthread_mutex_t lock; + int refcnt; + + struct libusb_context *ctx; + + uint8_t bus_number; + uint8_t device_address; + uint8_t num_configurations; + + struct list_head list; + unsigned long session_data; + void *os_priv; +} libusb_device; + +typedef struct libusb_device_handle { + pthread_mutex_t lock; + unsigned long claimed_interfaces; + + struct list_head list; + struct libusb_device *dev; + void *os_priv; +} libusb_device_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; + unsigned char *extra; + int extra_length; +} libusb_endpoint_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; + unsigned char *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; + unsigned char *extra; + int extra_length; +} libusb_config_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; + +typedef struct libusb_iso_packet_descriptor { + unsigned int length; + unsigned int actual_length; + enum libusb_transfer_status status; +} libusb_iso_packet_descriptor __aligned(sizeof(void *)); + +struct libusb_transfer; + +typedef void (*libusb_transfer_cb_fn) (struct libusb_transfer *transfer); + +typedef struct libusb_transfer { + libusb_device_handle *dev_handle; + uint8_t flags; + unsigned int endpoint; + unsigned char type; + unsigned int timeout; + enum libusb_transfer_status status; + int length; + int actual_length; + libusb_transfer_cb_fn callback; + void *user_data; + unsigned char *buffer; + void *os_priv; + int num_iso_packets; + struct libusb_iso_packet_descriptor iso_packet_desc[0]; +} libusb_transfer __aligned(sizeof(void *)); + +typedef struct libusb_pollfd { + int fd; + short events; +} libusb_pollfd; + +/* Library initialisation */ + +void libusb_set_debug(libusb_context * ctx, int level); +int libusb_init(libusb_context ** context); +void libusb_exit(struct libusb_context *ctx); + +/* 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_device_address(libusb_device * dev); +int libusb_get_max_packet_size(libusb_device * dev, unsigned char 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); +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_kernel_driver_active(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_interface_alt_setting(libusb_device_handle * devh, int interface_number, int alternate_setting); + +/* 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_string_descriptor_ascii(libusb_device_handle * dev, uint8_t desc_index, unsigned char *data, int length); + +/* 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); +unsigned char *libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, unsigned int packet); + +/* 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_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(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); +struct libusb_pollfd **libusb_get_pollfds(libusb_context * ctx); + +/* Synchronous device I/O */ + +int libusb_control_transfer(libusb_device_handle * devh, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout); +int libusb_bulk_transfer(struct libusb_device_handle *devh, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); +int libusb_interrupt_transfer(struct libusb_device_handle *devh, unsigned char endpoint, unsigned char *data, int length, int *transferred, unsigned int timeout); + +#if 0 +{ /* indent fix */ +#endif +#ifdef __cplusplus +} + +#endif + +#endif /* __LIBUSB_H__ */ diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c new file mode 100644 index 000000000000..223f18a2bb3f --- /dev/null +++ b/lib/libusb/libusb10.c @@ -0,0 +1,1140 @@ +/* $FreeBSD$ */ +/*- + * 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. + */ + +#include <sys/queue.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <poll.h> +#include <pthread.h> +#include <time.h> +#include <errno.h> + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER; +struct libusb_context *usbi_default_context = NULL; +pthread_mutex_t libusb20_lock = PTHREAD_MUTEX_INITIALIZER; + +/* Library initialisation / deinitialisation */ + +void +libusb_set_debug(libusb_context * ctx, int level) +{ + GET_CONTEXT(ctx); + if (ctx) + ctx->debug = level; +} + +int +libusb_init(libusb_context ** context) +{ + struct libusb_context *ctx; + char * debug; + int ret; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return (LIBUSB_ERROR_INVALID_PARAM); + + memset(ctx, 0, sizeof(*ctx)); + + debug = getenv("LIBUSB_DEBUG"); + if (debug != NULL) { + ctx->debug = atoi(debug); + if (ctx->debug != 0) + ctx->debug_fixed = 1; + } + + pthread_mutex_init(&ctx->usb_devs_lock, NULL); + pthread_mutex_init(&ctx->open_devs_lock, NULL); + USB_LIST_INIT(&ctx->usb_devs); + USB_LIST_INIT(&ctx->open_devs); + + pthread_mutex_init(&ctx->flying_transfers_lock, NULL); + pthread_mutex_init(&ctx->pollfds_lock, NULL); + pthread_mutex_init(&ctx->pollfd_modify_lock, NULL); + pthread_mutex_init(&ctx->events_lock, NULL); + pthread_mutex_init(&ctx->event_waiters_lock, NULL); + pthread_cond_init(&ctx->event_waiters_cond, NULL); + + USB_LIST_INIT(&ctx->flying_transfers); + USB_LIST_INIT(&ctx->pollfds); + + ret = pipe(ctx->ctrl_pipe); + if (ret < 0) { + usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[1]); + free(ctx); + return (LIBUSB_ERROR_OTHER); + } + + ret = usb_add_pollfd(ctx, ctx->ctrl_pipe[0], POLLIN); + if (ret < 0) { + usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[1]); + free(ctx); + return ret; + } + + 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; + + return (0); +} + +void +libusb_exit(libusb_context * ctx) +{ + GET_CONTEXT(ctx); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_exit enter"); + usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[1]); + + pthread_mutex_lock(&default_context_lock); + if (ctx == usbi_default_context) { + usbi_default_context = NULL; + } + pthread_mutex_unlock(&default_context_lock); + + free(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_exit leave"); +} + +/* Device handling and initialisation. */ + +ssize_t +libusb_get_device_list(libusb_context * ctx, libusb_device *** list) +{ + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + struct libusb_device *dev; + struct libusb20_backend *usb_backend; + int i; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_list enter"); + + usb_backend = libusb20_be_alloc_default(); + if (usb_backend == NULL) + return (-1); + + pdev = NULL; + i = 0; + while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) + i++; + + if (list == NULL) { + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_INVALID_PARAM); + } + *list = malloc((i + 1) * sizeof(void *)); + if (*list == NULL) { + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_NO_MEM); + } + i = 0; + while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { + /* get device into libUSB v1.0 list */ + libusb20_be_dequeue_device(usb_backend, pdev); + + ddesc = libusb20_dev_get_device_desc(pdev); + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + free(*list); + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_NO_MEM); + } + memset(dev, 0, sizeof(*dev)); + + pthread_mutex_init(&dev->lock, NULL); + dev->ctx = ctx; + dev->bus_number = pdev->bus_number; + dev->device_address = pdev->device_address; + dev->num_configurations = ddesc->bNumConfigurations; + + /* link together the two structures */ + dev->os_priv = pdev; + + pthread_mutex_lock(&ctx->usb_devs_lock); + LIST_ADD(&dev->list, &ctx->usb_devs); + pthread_mutex_unlock(&ctx->usb_devs_lock); + + (*list)[i] = libusb_ref_device(dev); + i++; + } + (*list)[i] = NULL; + + libusb20_be_free(usb_backend); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_list leave"); + return (i); +} + +/* + * In this function we cant free all the device contained into list because + * open_with_pid_vid use some node of list after the free_device_list. + */ +void +libusb_free_device_list(libusb_device **list, int unref_devices) +{ + int i; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_device_list enter"); + + if (list == NULL) + return ; + + if (unref_devices) { + for (i = 0; list[i] != NULL; i++) + libusb_unref_device(list[i]); + } + free(list); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_device_list leave"); +} + +uint8_t +libusb_get_bus_number(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_bus_number enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_NO_DEVICE); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_bus_number leave"); + return (dev->bus_number); +} + +uint8_t +libusb_get_device_address(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_address enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_NO_DEVICE); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_address leave"); + return (dev->device_address); +} + +int +libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) +{ + struct libusb_config_descriptor *pdconf; + struct libusb_interface *pinf; + struct libusb_interface_descriptor *pdinf; + struct libusb_endpoint_descriptor *pdend; + libusb_context *ctx; + int i, j, k, ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_max_packet_size enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_NO_DEVICE); + + if (libusb_get_active_config_descriptor(dev, &pdconf) < 0) + return (LIBUSB_ERROR_OTHER); + + 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); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_max_packet_size leave"); + return (ret); +} + +libusb_device * +libusb_ref_device(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_ref_device enter"); + + if (dev == NULL) + return (NULL); + + pthread_mutex_lock(&dev->lock); + dev->refcnt++; + pthread_mutex_unlock(&dev->lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_ref_device leave"); + return (dev); +} + +void +libusb_unref_device(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unref_device enter"); + + if (dev == NULL) + return; + + pthread_mutex_lock(&dev->lock); + dev->refcnt--; + pthread_mutex_unlock(&dev->lock); + + if (dev->refcnt == 0) { + pthread_mutex_lock(&dev->ctx->usb_devs_lock); + LIST_DEL(&dev->list); + pthread_mutex_unlock(&dev->ctx->usb_devs_lock); + + libusb20_dev_free(dev->os_priv); + free(dev); + } + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unref_device leave"); +} + +int +libusb_open(libusb_device * dev, libusb_device_handle **devh) +{ + libusb_context *ctx = dev->ctx; + struct libusb20_device *pdev = dev->os_priv; + libusb_device_handle *hdl; + unsigned char dummy; + int err; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open enter"); + + dummy = 1; + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + hdl = malloc(sizeof(*hdl)); + if (hdl == NULL) + return (LIBUSB_ERROR_NO_MEM); + + err = libusb20_dev_open(pdev, 16 * 4 /* number of endpoints */ ); + if (err) { + free(hdl); + return (LIBUSB_ERROR_NO_MEM); + } + memset(hdl, 0, sizeof(*hdl)); + pthread_mutex_init(&hdl->lock, NULL); + + hdl->dev = libusb_ref_device(dev); + hdl->claimed_interfaces = 0; + hdl->os_priv = dev->os_priv; + err = usb_add_pollfd(ctx, libusb20_dev_get_fd(pdev), POLLIN | + POLLOUT | POLLRDNORM | POLLWRNORM); + if (err < 0) { + libusb_unref_device(dev); + free(hdl); + return (err); + } + + pthread_mutex_lock(&ctx->open_devs_lock); + LIST_ADD(&hdl->list, &ctx->open_devs); + pthread_mutex_unlock(&ctx->open_devs_lock); + + *devh = hdl; + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify++; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + if (err <= 0) { + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + return 0; + } + + libusb_lock_events(ctx); + read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy)); + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + libusb_unlock_events(ctx); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open leave"); + 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 libusb_device_handle *devh; + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + int i, j; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid enter"); + + devh = NULL; + + if ((i = libusb_get_device_list(ctx, &devs)) < 0) + return (NULL); + + for (j = 0; j < i; j++) { + pdev = (struct libusb20_device *)devs[j]->os_priv; + pdesc = libusb20_dev_get_device_desc(pdev); + if (pdesc->idVendor == vendor_id && + pdesc->idProduct == product_id) + if (libusb_open(devs[j], &devh) < 0) + devh = NULL; + } + + libusb_free_device_list(devs, 1); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid leave"); + return (devh); +} + +void +libusb_close(libusb_device_handle * devh) +{ + libusb_context *ctx; + struct libusb20_device *pdev; + unsigned char dummy = 1; + int err; + + if (devh == NULL) + return ; + + ctx = devh->dev->ctx; + pdev = devh->os_priv; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close enter"); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify++; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + + if (err <= 0) { + pthread_mutex_lock(&ctx->open_devs_lock); + LIST_DEL(&devh->list); + pthread_mutex_unlock(&ctx->open_devs_lock); + + usb_remove_pollfd(ctx, libusb20_dev_get_fd(pdev)); + libusb_unref_device(devh->dev); + libusb20_dev_close(pdev); + free(devh); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + return ; + } + libusb_lock_events(ctx); + + read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy)); + pthread_mutex_lock(&ctx->open_devs_lock); + LIST_DEL(&devh->list); + pthread_mutex_unlock(&ctx->open_devs_lock); + + usb_remove_pollfd(ctx, libusb20_dev_get_fd(pdev)); + libusb_unref_device(devh->dev); + libusb20_dev_close(pdev); + free(devh); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + libusb_unlock_events(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close leave"); +} + +libusb_device * +libusb_get_device(libusb_device_handle * devh) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device enter"); + + if (devh == NULL) + return (NULL); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device leave"); + return (devh->dev); +} + +int +libusb_get_configuration(libusb_device_handle * devh, int *config) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_configuration enter"); + + if (devh == NULL || config == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + *config = libusb20_dev_get_config_index((struct libusb20_device *) + devh->dev->os_priv); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_configuration leave"); + return (0); +} + +/* + * XXX this code is wrong. need update. + */ + +int +libusb_set_configuration(libusb_device_handle * devh, int configuration) +{ + struct libusb20_device *pdev; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_configuration enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = (struct libusb20_device *)devh->dev->os_priv; + + libusb20_dev_set_alt_index(pdev, libusb20_dev_get_config_index(pdev), + configuration); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_configuration leave"); + return (0); +} + +int +libusb_claim_interface(libusb_device_handle * dev, int interface_number) +{ + libusb_context *ctx; + int ret = 0; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_claim_interface enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + return (LIBUSB_ERROR_INVALID_PARAM); + + pthread_mutex_lock(&(dev->lock)); + if (dev->claimed_interfaces & (1 << interface_number)) + ret = LIBUSB_ERROR_BUSY; + + if (!ret) + dev->claimed_interfaces |= (1 << interface_number); + pthread_mutex_unlock(&(dev->lock)); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_claim_interface leave"); + return (ret); +} + +int +libusb_release_interface(libusb_device_handle * dev, int interface_number) +{ + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_release_interface enter"); + + ret = 0; + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + return (LIBUSB_ERROR_INVALID_PARAM); + + pthread_mutex_lock(&(dev->lock)); + if (!(dev->claimed_interfaces & (1 << interface_number))) + ret = LIBUSB_ERROR_NOT_FOUND; + + if (!ret) + dev->claimed_interfaces &= ~(1 << interface_number); + pthread_mutex_unlock(&(dev->lock)); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_release_interface leave"); + return (ret); +} + +int +libusb_set_interface_alt_setting(libusb_device_handle * dev, + int interface_number, int alternate_setting) +{ + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_interface_alt_setting enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number >= sizeof(dev->claimed_interfaces) *8) + return (LIBUSB_ERROR_INVALID_PARAM); + + pthread_mutex_lock(&dev->lock); + if (!(dev->claimed_interfaces & (1 << interface_number))) { + pthread_mutex_unlock(&dev->lock); + return (LIBUSB_ERROR_NOT_FOUND); + } + pthread_mutex_unlock(&dev->lock); + + if (libusb20_dev_set_alt_index(dev->os_priv, interface_number, + alternate_setting) != 0) + return (LIBUSB_ERROR_OTHER); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_interface_alt_setting leave"); + return (0); +} + +int +libusb_clear_halt(libusb_device_handle * devh, unsigned char endpoint) +{ + struct libusb20_transfer *xfer; + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_clear_halt enter"); + + GET_XFER(xfer, endpoint, devh->os_priv); + + pthread_mutex_lock(&libusb20_lock); + ret = libusb20_tr_open(xfer, 0, 0, endpoint); + if (ret != 0 && ret != LIBUSB20_ERROR_BUSY) { + pthread_mutex_unlock(&libusb20_lock); + return (LIBUSB_ERROR_OTHER); + } + + libusb20_tr_clear_stall_sync(xfer); + if (ret == 0) /* check if we have open the device */ + libusb20_tr_close(xfer); + pthread_mutex_unlock(&libusb20_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_clear_halt leave"); + return (0); +} + +int +libusb_reset_device(libusb_device_handle * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_reset_device enter"); + + if (dev == NULL) + return (LIBUSB20_ERROR_INVALID_PARAM); + + libusb20_dev_reset(dev->os_priv); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_reset_device leave"); + return (0); +} + +int +libusb_kernel_driver_active(libusb_device_handle * devh, int interface) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_kernel_driver_active enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_kernel_driver_active leave"); + return (libusb20_dev_kernel_driver_active(devh->os_priv, interface)); +} + +int +libusb_detach_kernel_driver(libusb_device_handle * devh, int interface) +{ + struct libusb20_device *pdev; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_detach_kernel_driver enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = (struct libusb20_device *)devh->dev->os_priv; + if (libusb20_dev_detach_kernel_driver(pdev, interface) == LIBUSB20_ERROR_OTHER) + return (LIBUSB_ERROR_OTHER); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_detach_kernel_driver leave"); + return (0); +} + +/* + * stub function. + * libusb20 doesn't support this feature. + */ +int +libusb_attach_kernel_driver(libusb_device_handle * devh, int interface) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_attach_kernel_driver enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_attach_kernel_driver leave"); + return (0); +} + +/* Asynchronous device I/O */ + +struct libusb_transfer * +libusb_alloc_transfer(int iso_packets) +{ + struct libusb_transfer *xfer; + struct usb_transfer *bxfer; + libusb_context *ctx; + int len; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_alloc_transfer enter"); + + len = sizeof(struct libusb_transfer) + + sizeof(struct usb_transfer) + + (iso_packets * sizeof(libusb_iso_packet_descriptor)); + + bxfer = malloc(len); + if (bxfer == NULL) + return (NULL); + + memset(bxfer, 0, len); + bxfer->num_iso_packets = iso_packets; + + xfer = (struct libusb_transfer *) ((uint8_t *)bxfer + + sizeof(struct usb_transfer)); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_alloc_transfer leave"); + return (xfer); +} + +void +libusb_free_transfer(struct libusb_transfer *xfer) +{ + struct usb_transfer *bxfer; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_transfer enter"); + + if (xfer == NULL) + return ; + + bxfer = (struct usb_transfer *) ((uint8_t *)xfer - + sizeof(struct usb_transfer)); + + free(bxfer); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_transfer leave"); + return; +} + +static void +libusb10_proxy(struct libusb20_transfer *xfer) +{ + struct usb_transfer *usb_backend; + struct libusb20_device *pdev; + libusb_transfer *usb_xfer; + libusb_context *ctx; + uint8_t status; + uint32_t iso_packets; + int i; + + status = libusb20_tr_get_status(xfer); + usb_xfer = libusb20_tr_get_priv_sc0(xfer); + usb_backend = (struct usb_transfer *) ((uint8_t *)usb_xfer - + sizeof(struct usb_transfer)); + pdev = usb_xfer->dev_handle->dev->os_priv; + ctx = usb_xfer->dev_handle->dev->ctx; + GET_CONTEXT(ctx); + + switch (status) { + case LIBUSB20_TRANSFER_COMPLETED: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 SUBMIT"); + usb_xfer->actual_length += libusb20_tr_get_actual_length(xfer); + usb_xfer->callback(usb_xfer); + + pthread_mutex_lock(&ctx->flying_transfers_lock); + LIST_DEL(&usb_backend->list); + pthread_mutex_unlock(&ctx->flying_transfers_lock); + break ; + case LIBUSB20_TRANSFER_START: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 START"); + usb_xfer->actual_length = 0; + switch (usb_xfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE CTR"); + libusb20_tr_setup_control(xfer, usb_xfer->buffer, + (void *)(((uint8_t *) usb_xfer->buffer) + + sizeof(libusb_control_setup)), + usb_xfer->timeout); + break ; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE ISO"); + iso_packets = libusb20_tr_get_max_frames(xfer); + if (usb_xfer->num_iso_packets > iso_packets) + usb_xfer->num_iso_packets = iso_packets; + for (i = 0 ; i < usb_xfer->num_iso_packets ; i++) { + libusb20_tr_setup_isoc(xfer, + usb_xfer->buffer, usb_xfer->length, i); + } + libusb20_tr_set_total_frames(xfer, i); + break ; + case LIBUSB_TRANSFER_TYPE_BULK: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE BULK"); + libusb20_tr_setup_bulk(xfer, usb_xfer->buffer, + usb_xfer->length, usb_xfer->timeout); + break ; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE INTR"); + libusb20_tr_setup_intr(xfer, usb_xfer->buffer, + usb_xfer->length, usb_xfer->timeout); + break ; + } + libusb20_tr_submit(xfer); + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 SUBMITED"); + break ; + default: + if (ctx->debug == LIBUSB_DEBUG_TRANSFER) + printf("LIBUSB TRANSFER DEFAULT 0x%x\n", status); + usb_xfer->actual_length = 0; + usb_xfer->status = LIBUSB_TRANSFER_CANCELLED; + + pthread_mutex_lock(&ctx->flying_transfers_lock); + LIST_DEL(&usb_backend->list); + pthread_mutex_unlock(&ctx->flying_transfers_lock); + usb_xfer->callback(usb_xfer); + + break ; + } + + switch (status) { + case LIBUSB20_TRANSFER_COMPLETED: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS COMPLETED"); + usb_xfer->status = LIBUSB_TRANSFER_COMPLETED; + break ; + case LIBUSB20_TRANSFER_OVERFLOW: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR OVERFLOW"); + usb_xfer->status = LIBUSB_TRANSFER_OVERFLOW; + break ; + case LIBUSB20_TRANSFER_NO_DEVICE: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR NO DEVICE"); + usb_xfer->status = LIBUSB_TRANSFER_NO_DEVICE; + break ; + case LIBUSB20_TRANSFER_STALL: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR STALL"); + usb_xfer->status = LIBUSB_TRANSFER_STALL; + break ; + case LIBUSB20_TRANSFER_CANCELLED: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR CANCELLED"); + usb_xfer->status = LIBUSB_TRANSFER_CANCELLED; + break ; + case LIBUSB20_TRANSFER_TIMED_OUT: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR TIMEOUT"); + usb_xfer->status = LIBUSB_TRANSFER_TIMED_OUT; + break ; + case LIBUSB20_TRANSFER_ERROR: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "ERROR"); + usb_xfer->status = LIBUSB_TRANSFER_ERROR; + break ; + } +} + +static int +libusb_get_maxframe(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: + switch (usb_speed) { + case LIBUSB20_SPEED_LOW: + case LIBUSB20_SPEED_FULL: + ret = 60 * 1; + break ; + default : + ret = 60 * 8; + break ; + } + break ; + case LIBUSB_TRANSFER_TYPE_CONTROL: + ret = 2; + break ; + default: + ret = 1; + break ; + } + + return ret; +} + +static int +libusb_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; + break ; + case LIBUSB_TRANSFER_TYPE_CONTROL: + switch (usb_speed) { + case LIBUSB20_SPEED_LOW: + ret = 8; + break ; + case LIBUSB20_SPEED_FULL: + ret = 64; + break ; + case LIBUSB20_SPEED_HIGH: + ret = 64; + break ; + } + /*add */ + ret += 8; + break ; + default : + switch (usb_speed) { + case LIBUSB20_SPEED_LOW: + ret = 256; + break ; + case LIBUSB20_SPEED_FULL: + ret = 4096; + break ; + default: + ret = 16384; + break ; + } + break ; + } + + return ret; +} + +int +libusb_submit_transfer(struct libusb_transfer *xfer) +{ + struct libusb20_transfer **usb20_xfer; + struct usb_transfer *usb_backend; + struct usb_transfer *usb_node; + struct libusb20_device *pdev; + struct libusb_context *ctx; + struct timespec cur_ts; + struct timeval *cur_tv; + int maxframe; + int buffsize; + int num_frame; + int ep_idx; + int ret; + int i; + + if (xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + usb20_xfer = malloc(2 * sizeof(struct libusb20_transfer *)); + if (usb20_xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + ctx = xfer->dev_handle->dev->ctx; + pdev = xfer->dev_handle->os_priv; + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer enter"); + + usb_backend = (struct usb_transfer *) ((uint8_t *)xfer - + sizeof(struct usb_transfer)); + usb_backend->transferred = 0; + usb_backend->flags = 0; + + if (xfer->timeout != 0) { + clock_gettime(CLOCK_MONOTONIC, &cur_ts); + cur_ts.tv_sec += xfer->timeout / 1000; + cur_ts.tv_nsec += (xfer->timeout % 1000) * 1000000; + + if (cur_ts.tv_nsec > 1000000000) { + cur_ts.tv_nsec -= 1000000000; + cur_ts.tv_sec++; + } + + TIMESPEC_TO_TIMEVAL(&usb_backend->timeout, &cur_ts); + } + + /*Add to flying list*/ + pthread_mutex_lock(&ctx->flying_transfers_lock); + if (USB_LIST_EMPTY(&ctx->flying_transfers)) { + LIST_ADD(&usb_backend->list, &ctx->flying_transfers); + goto out; + } + if (timerisset(&usb_backend->timeout) == 0) { + LIST_ADD_TAIL(&usb_backend->list, &ctx->flying_transfers); + goto out; + } + LIST_FOREACH_ENTRY(usb_node, &ctx->flying_transfers, list) { + cur_tv = &usb_node->timeout; + if (timerisset(cur_tv) == 0 || + (cur_tv->tv_sec > usb_backend->timeout.tv_sec) || + (cur_tv->tv_sec == usb_backend->timeout.tv_sec && + cur_tv->tv_usec > usb_backend->timeout.tv_usec)) { + LIST_ADD_TAIL(&usb_backend->list, &usb_node->list); + goto out; + } + } + LIST_ADD_TAIL(&usb_backend->list, &ctx->flying_transfers); + +out: + pthread_mutex_unlock(&ctx->flying_transfers_lock); + + usb20_xfer[0] = libusb20_tr_get_pointer(pdev, + ((xfer->endpoint / 0x40) | (xfer->endpoint * 4)) % (16 * 4)); + usb20_xfer[1] = libusb20_tr_get_pointer(pdev, + (((xfer->endpoint / 0x40) | (xfer->endpoint * 4)) % (16 * 4)) + 1); + + if (usb20_xfer[0] == NULL) + return (LIBUSB_ERROR_OTHER); + + xfer->os_priv = usb20_xfer; + + pthread_mutex_lock(&libusb20_lock); + + buffsize = libusb_get_buffsize(pdev, xfer); + maxframe = libusb_get_maxframe(pdev, xfer); + + ret = libusb20_tr_open(usb20_xfer[0], buffsize, + maxframe, xfer->endpoint); + if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) + ret |= libusb20_tr_open(usb20_xfer[1], buffsize, + maxframe, xfer->endpoint); + + if (ret != 0) { + pthread_mutex_unlock(&libusb20_lock); + pthread_mutex_lock(&ctx->flying_transfers_lock); + LIST_DEL(&usb_backend->list); + pthread_mutex_unlock(&ctx->flying_transfers_lock); + return (LIBUSB_ERROR_OTHER); + } + + libusb20_tr_set_priv_sc0(usb20_xfer[0], xfer); + libusb20_tr_set_callback(usb20_xfer[0], libusb10_proxy); + if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + libusb20_tr_set_priv_sc0(usb20_xfer[1], xfer); + libusb20_tr_set_callback(usb20_xfer[1], libusb10_proxy); + } + + libusb20_tr_start(usb20_xfer[0]); + if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) + libusb20_tr_start(usb20_xfer[1]); + + pthread_mutex_unlock(&libusb20_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer leave"); + return (0); +} + +int +libusb_cancel_transfer(struct libusb_transfer *xfer) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter"); + + if (xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + pthread_mutex_lock(&libusb20_lock); + libusb20_tr_stop(xfer->os_priv); + pthread_mutex_unlock(&libusb20_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave"); + return (0); +} + diff --git a/lib/libusb/libusb10.h b/lib/libusb/libusb10.h new file mode 100644 index 000000000000..60a13da5be41 --- /dev/null +++ b/lib/libusb/libusb10.h @@ -0,0 +1,245 @@ +/* $FreeBSD$ */ +/*- + * 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 __LIBUSB10_H__ +#define __LIBUSB10_H__ + +/* + * The two following macros were taken from the original LibUSB v1.0 + * for sake of compatibility: + */ +#define USB_LIST_INIT(entry) \ + (entry)->prev = (entry)->next = entry; +#define USB_LIST_EMPTY(entry) \ + ((entry)->next = (entry)) + +#define LIST_ADD(entry, head) \ + (entry)->next = (head)->next; \ + (entry)->prev = (head); \ + (head)->next->prev = (entry); \ + (head)->next = (entry); +#define LIST_ADD_TAIL(entry, head) \ + (entry)->next = (head); \ + (entry)->prev = (head)->prev; \ + (head)->prev->next = (entry); \ + (head)->prev = (entry); +#define LIST_DEL(entry) \ + (entry)->next->prev = (entry)->prev; \ + (entry)->prev->next = (entry)->next; + +#define LIST_ENT(ptr, type, member) \ + ((type *)((char *)(ptr) - (unsigned long) (&((type*)0L)->member))) +#define LIST_FOREACH_ENTRY(pos, head, member) \ + for (pos = LIST_ENT((head)->next, typeof(*pos), member) ; \ + &pos->member != head ; \ + pos = LIST_ENT(pos->member.next, typeof(*pos), member)) +#define LIST_FOREACH_ENTRY_SAFE(pos, n, head, member) \ + for (pos = LIST_ENT((head)->next, typeof(*pos), member), \ + n = LIST_ENT(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = LIST_ENT(n->member.next, typeof(*n), member)) + +/* fetch libusb20_transfer from libusb20_device */ +#define GET_XFER(xfer, endpoint, pdev)\ + xfer = libusb20_tr_get_pointer(pdev, \ + (2 *endpoint)|(endpoint/0x80)); \ + if (xfer == NULL) \ + return (LIBUSB_ERROR_OTHER); + + +static int get_next_timeout(libusb_context *ctx, struct timeval *tv, struct timeval *out); +static int handle_timeouts(struct libusb_context *ctx); +static int handle_events(struct libusb_context *ctx, struct timeval *tv); +extern struct libusb_context *usbi_default_context; +extern pthread_mutex_t libusb20_lock; + +/* if ctx is NULL use default context*/ + +#define GET_CONTEXT(ctx) \ + if (ctx == NULL) ctx = usbi_default_context; + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define USB_TIMED_OUT (1<<0) + +static inline void +dprintf(libusb_context *ctx, int debug, char *str) +{ + if (ctx->debug != debug) + return ; + + switch (ctx->debug) { + case LIBUSB_DEBUG_NO: + break ; + case LIBUSB_DEBUG_FUNCTION: + printf("LIBUSB FUNCTION : %s\n", str); + break ; + case LIBUSB_DEBUG_TRANSFER: + printf("LIBUSB TRANSFER : %s\n", str); + break ; + default: + printf("LIBUSB UNKNOW DEBUG\n"); + break ; + } + return ; +} + +struct usb_pollfd { + struct libusb_pollfd pollfd; + struct list_head list; +}; + +struct usb_transfer { + int num_iso_packets; + struct list_head list; + struct timeval timeout; + int transferred; + uint8_t flags; +}; + +static inline int +usb_add_pollfd(libusb_context *ctx, int fd, short events) +{ + struct usb_pollfd *pollfd; + + if (ctx == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pollfd = malloc(sizeof(*pollfd)); + if (pollfd == NULL) + return (LIBUSB_ERROR_NO_MEM); + + pollfd->pollfd.fd = fd; + pollfd->pollfd.events = events; + + pthread_mutex_lock(&ctx->pollfds_lock); + LIST_ADD_TAIL(&pollfd->list, &ctx->pollfds); + pthread_mutex_unlock(&ctx->pollfds_lock); + + if (ctx->fd_added_cb) + ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data); + return (0); +} + +static inline void +usb_remove_pollfd(libusb_context *ctx, int fd) +{ + struct usb_pollfd *pollfd; + int found; + + found = 0; + pthread_mutex_lock(&ctx->pollfds_lock); + + LIST_FOREACH_ENTRY(pollfd, &ctx->pollfds, list) { + if (pollfd->pollfd.fd == fd) { + found = 1; + break ; + } + } + + if (found == 0) { + pthread_mutex_unlock(&ctx->pollfds_lock); + return ; + } + + LIST_DEL(&pollfd->list); + pthread_mutex_unlock(&ctx->pollfds_lock); + free(pollfd); + + if (ctx->fd_removed_cb) + ctx->fd_removed_cb(fd, ctx->fd_cb_user_data); +} + +static inline void +usb_handle_transfer_completion(struct usb_transfer *uxfer, + enum libusb_transfer_status status) +{ + libusb_transfer *xfer; + libusb_context *ctx; + int len; + + xfer = (struct libusb_transfer *) ((uint8_t *)uxfer + + sizeof(struct usb_transfer)); + ctx = xfer->dev_handle->dev->ctx; + + pthread_mutex_lock(&ctx->flying_transfers_lock); + LIST_DEL(&uxfer->list); + pthread_mutex_unlock(&ctx->flying_transfers_lock); + + if (status == LIBUSB_TRANSFER_COMPLETED && xfer->flags & + LIBUSB_TRANSFER_SHORT_NOT_OK) { + len = xfer->length; + if (xfer->type == LIBUSB_TRANSFER_TYPE_CONTROL) + len -= sizeof(libusb_control_setup); + if (len != uxfer->transferred) { + status = LIBUSB_TRANSFER_ERROR; + } + } + + xfer->status = status; + xfer->actual_length = uxfer->transferred; + + if (xfer->callback) + xfer->callback(xfer); + if (xfer->flags & LIBUSB_TRANSFER_FREE_TRANSFER) + libusb_free_transfer(xfer); + + pthread_mutex_lock(&ctx->event_waiters_lock); + pthread_cond_broadcast(&ctx->event_waiters_cond); + pthread_mutex_unlock(&ctx->event_waiters_lock); +} + +static inline void +usb_handle_disconnect(struct libusb_device_handle *devh) +{ + struct libusb_context *ctx; + struct libusb_transfer *xfer; + struct usb_transfer *cur; + struct usb_transfer *to_cancel; + + ctx = devh->dev->ctx; + + while (1) { + pthread_mutex_lock(&ctx->flying_transfers_lock); + to_cancel = NULL; + LIST_FOREACH_ENTRY(cur, &ctx->flying_transfers, list) { + xfer = (struct libusb_transfer *) ((uint8_t *)cur + + sizeof(struct usb_transfer)); + if (xfer->dev_handle == devh) { + to_cancel = cur; + break ; + } + } + pthread_mutex_unlock(&ctx->flying_transfers_lock); + + if (to_cancel == NULL) + break ; + + usb_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE); + } + return ; +} + +#endif /*__LIBUSB10_H__*/ diff --git a/lib/libusb/libusb10_desc.c b/lib/libusb/libusb10_desc.c new file mode 100644 index 000000000000..a150f4d568be --- /dev/null +++ b/lib/libusb/libusb10_desc.c @@ -0,0 +1,301 @@ +/* $FreeBSD$ */ +/*- + * 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. + */ + +#include <sys/queue.h> +#include <stdlib.h> +#include <stdio.h> +#include <pthread.h> + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +/* USB descriptors */ + +int +libusb_get_device_descriptor(libusb_device * dev, + struct libusb_device_descriptor *desc) +{ + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + struct libusb20_device *pdev; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_descriptor enter"); + + if ((dev == NULL) || (desc == NULL)) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = dev->os_priv; + pdesc = libusb20_dev_get_device_desc(pdev); + + desc->bLength = pdesc->bLength; + desc->bDescriptorType = pdesc->bDescriptorType; + desc->bcdUSB = pdesc->bcdUSB; + desc->bDeviceClass = pdesc->bDeviceClass; + desc->bDeviceSubClass = pdesc->bDeviceSubClass; + desc->bDeviceProtocol = pdesc->bDeviceProtocol; + desc->bMaxPacketSize0 = pdesc->bMaxPacketSize0; + desc->idVendor = pdesc->idVendor; + desc->idProduct = pdesc->idProduct; + desc->bcdDevice = pdesc->bcdDevice; + desc->iManufacturer = pdesc->iManufacturer; + desc->iProduct = pdesc->iProduct; + desc->iSerialNumber = pdesc->iSerialNumber; + desc->bNumConfigurations = pdesc->bNumConfigurations; + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_descriptor leave"); + return (0); +} + +int +libusb_get_active_config_descriptor(libusb_device * dev, + struct libusb_config_descriptor **config) +{ + struct libusb20_device *pdev; + libusb_context *ctx; + uint8_t idx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_active_config_descriptor enter"); + + pdev = dev->os_priv; + idx = libusb20_dev_get_config_index(pdev); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_active_config_descriptor leave"); + return (libusb_get_config_descriptor(dev, idx, config)); +} + +/* + * XXX Need to check if extra need a dup because + * XXX free pconf could free this char * + */ +int +libusb_get_config_descriptor(libusb_device * dev, uint8_t config_index, + struct libusb_config_descriptor **config) +{ + struct libusb20_device *pdev; + struct libusb20_config *pconf; + struct libusb20_interface *pinf; + struct libusb20_endpoint *pend; + libusb_interface_descriptor *ifd; + libusb_endpoint_descriptor *endd; + libusb_context *ctx; + uint8_t nif, nend, nalt, i, j, k; + uint32_t if_idx, endp_idx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_config_descriptor enter"); + + if (dev == NULL || config == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = dev->os_priv; + pconf = libusb20_dev_alloc_config(pdev, config_index); + + if (pconf == NULL) + return (LIBUSB_ERROR_NOT_FOUND); + + nalt = nif = pconf->num_interface; + nend = 0; + for (i = 0 ; i < nif ; i++) { + if (pconf->interface[i].num_altsetting > 0) + { + nalt += pconf->interface[i].num_altsetting; + for (j = 0 ; j < nalt ; j++) { + nend += pconf->interface[i].altsetting[j].num_endpoints; + } + } + nend += pconf->interface[i].num_endpoints; + } + + *config = malloc(sizeof(libusb_config_descriptor) + + (nif * sizeof(libusb_interface)) + + (nalt * sizeof(libusb_interface_descriptor)) + + (nend * sizeof(libusb_endpoint_descriptor))); + if (*config == NULL) { + free(pconf); + return (LIBUSB_ERROR_NO_MEM); + } + + (*config)->interface = (libusb_interface *)(*config + + sizeof(libusb_config_descriptor)); + for (i = if_idx = endp_idx = 0 ; i < nif ; if_idx, i++) { + (*config)->interface[i].altsetting = (libusb_interface_descriptor *) + (*config + sizeof(libusb_config_descriptor) + + (nif * sizeof(libusb_interface)) + + (if_idx * sizeof(libusb_interface_descriptor))); + (*config)->interface[i].altsetting[0].endpoint = + (libusb_endpoint_descriptor *) (*config + + sizeof(libusb_config_descriptor) + + (nif * sizeof(libusb_interface)) + + (nalt * sizeof(libusb_interface_descriptor)) + + (endp_idx * sizeof(libusb_endpoint_descriptor))); + endp_idx += pconf->interface[i].num_endpoints; + + if (pconf->interface[i].num_altsetting > 0) + { + for (j = 0 ; j < pconf->interface[i].num_altsetting ; j++, if_idx++) { + (*config)->interface[i].altsetting[j + 1].endpoint = + (libusb_endpoint_descriptor *) (*config + + sizeof(libusb_config_descriptor) + + (nif * sizeof(libusb_interface)) + + (nalt * sizeof(libusb_interface_descriptor)) + + (endp_idx * sizeof(libusb_endpoint_descriptor))); + endp_idx += pconf->interface[i].altsetting[j].num_endpoints; + } + } + } + + (*config)->bLength = pconf->desc.bLength; + (*config)->bDescriptorType = pconf->desc.bDescriptorType; + (*config)->wTotalLength = pconf->desc.wTotalLength; + (*config)->bNumInterfaces = pconf->desc.bNumInterfaces; + (*config)->bConfigurationValue = pconf->desc.bConfigurationValue; + (*config)->iConfiguration = pconf->desc.iConfiguration; + (*config)->bmAttributes = pconf->desc.bmAttributes; + (*config)->MaxPower = pconf->desc.bMaxPower; + (*config)->extra_length = pconf->extra.len; + if ((*config)->extra_length != 0) + (*config)->extra = pconf->extra.ptr; + + for (i = 0 ; i < nif ; i++) { + pinf = &pconf->interface[i]; + (*config)->interface[i].num_altsetting = pinf->num_altsetting + 1; + for (j = 0 ; j < (*config)->interface[i].num_altsetting ; j++) { + if (j != 0) + pinf = &pconf->interface[i].altsetting[j - 1]; + ifd = &(*config)->interface[i].altsetting[j]; + ifd->bLength = pinf->desc.bLength; + ifd->bDescriptorType = pinf->desc.bDescriptorType; + ifd->bInterfaceNumber = pinf->desc.bInterfaceNumber; + ifd->bAlternateSetting = pinf->desc.bAlternateSetting; + ifd->bNumEndpoints = pinf->desc.bNumEndpoints; + ifd->bInterfaceClass = pinf->desc.bInterfaceClass; + ifd->bInterfaceSubClass = pinf->desc.bInterfaceSubClass; + ifd->bInterfaceProtocol = pinf->desc.bInterfaceProtocol; + ifd->iInterface = pinf->desc.iInterface; + ifd->extra_length = pinf->extra.len; + if (ifd->extra_length != 0) + ifd->extra = pinf->extra.ptr; + for (k = 0 ; k < pinf->num_endpoints ; k++) { + pend = &pinf->endpoints[k]; + endd = &ifd->endpoint[k]; + endd->bLength = pend->desc.bLength; + endd->bDescriptorType = pend->desc.bDescriptorType; + endd->bEndpointAddress = pend->desc.bEndpointAddress; + endd->bmAttributes = pend->desc.bmAttributes; + endd->wMaxPacketSize = pend->desc.wMaxPacketSize; + endd->bInterval = pend->desc.bInterval; + endd->bRefresh = pend->desc.bRefresh; + endd->bSynchAddress = pend->desc.bSynchAddress; + endd->extra_length = pend->extra.len; + if (endd->extra_length != 0) + endd->extra = pend->extra.ptr; + } + } + } + + free(pconf); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_config_descriptor leave"); + return (0); +} + +int +libusb_get_config_descriptor_by_value(libusb_device * dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config) +{ + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + struct libusb20_device *pdev; + struct libusb20_config *pconf; + libusb_context *ctx; + int i; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_config_descriptor_by_value enter"); + + if (dev == NULL || config == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = dev->os_priv; + pdesc = libusb20_dev_get_device_desc(pdev); + + for (i = 0 ; i < pdesc->bNumConfigurations ; i++) { + pconf = libusb20_dev_alloc_config(pdev, i); + if (pconf->desc.bConfigurationValue == bConfigurationValue) { + free(pconf); + return libusb_get_config_descriptor(dev, i, config); + + } + free(pconf); + } + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_config_descriptor_by_value leave"); + return (LIBUSB_ERROR_NOT_FOUND); +} + +void +libusb_free_config_descriptor(struct libusb_config_descriptor *config) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_config_descriptor enter"); + + free(config); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_config_descriptor leave"); +} + +int +libusb_get_string_descriptor_ascii(libusb_device_handle * dev, + uint8_t desc_index, unsigned char *data, int length) +{ + struct libusb20_device *pdev; + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_string_descriptor_ascii enter"); + + if (dev == NULL || data == NULL) + return (LIBUSB20_ERROR_INVALID_PARAM); + + pdev = dev->os_priv; + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_string_descriptor_ascii leave"); + if (libusb20_dev_req_string_simple_sync(pdev, desc_index, + data, length) == 0) + return (strlen(data)); + + return (LIBUSB_ERROR_OTHER); +} diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c new file mode 100644 index 000000000000..4eb30c442380 --- /dev/null +++ b/lib/libusb/libusb10_io.c @@ -0,0 +1,748 @@ +/* $FreeBSD$ */ +/*- + * 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. + */ + +#include <sys/queue.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <poll.h> +#include <pthread.h> +#include <time.h> +#include <errno.h> + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +static int +get_next_timeout(libusb_context *ctx, struct timeval *tv, struct timeval *out) +{ + struct timeval timeout; + int ret; + + ret = libusb_get_next_timeout(ctx, &timeout); + + if (ret) { + if (timerisset(&timeout) == 0) + return 1; + if (timercmp(&timeout, tv, <) != 0) + *out = timeout; + else + *out = *tv; + } else { + *out = *tv; + } + + return (0); +} + +static int +handle_timeouts(struct libusb_context *ctx) +{ + struct timespec sys_ts; + struct timeval sys_tv; + struct timeval *cur_tv; + struct usb_transfer *xfer; + struct libusb_transfer *uxfer; + int ret; + + GET_CONTEXT(ctx); + ret = 0; + + pthread_mutex_lock(&ctx->flying_transfers_lock); + if (USB_LIST_EMPTY(&ctx->flying_transfers)) + goto out; + + ret = clock_gettime(CLOCK_MONOTONIC, &sys_ts); + TIMESPEC_TO_TIMEVAL(&sys_tv, &sys_ts); + + LIST_FOREACH_ENTRY(xfer, &ctx->flying_transfers, list) { + cur_tv = &xfer->timeout; + + if (timerisset(cur_tv) == 0) + goto out; + + if (xfer->flags & USB_TIMED_OUT) + continue; + + if ((cur_tv->tv_sec > sys_tv.tv_sec) || (cur_tv->tv_sec == sys_tv.tv_sec && + cur_tv->tv_usec > sys_tv.tv_usec)) + goto out; + + xfer->flags |= USB_TIMED_OUT; + uxfer = (libusb_transfer *) ((uint8_t *)xfer + + sizeof(struct usb_transfer)); + ret = libusb_cancel_transfer(uxfer); + } +out: + pthread_mutex_unlock(&ctx->flying_transfers_lock); + return (ret); +} + +static int +handle_events(struct libusb_context *ctx, struct timeval *tv) +{ + struct libusb_pollfd *tmppollfd; + struct libusb_device_handle *devh; + struct usb_pollfd *ipollfd; + struct usb_transfer *cur; + struct usb_transfer *cancel; + struct libusb_transfer *xfer; + struct pollfd *fds; + struct pollfd *tfds; + nfds_t nfds; + int tmpfd; + int tmp; + int ret; + int timeout; + int i; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "handle_events enter"); + + nfds = 0; + i = -1; + + pthread_mutex_lock(&ctx->pollfds_lock); + LIST_FOREACH_ENTRY(ipollfd, &ctx->pollfds, list) + nfds++; + + fds = malloc(sizeof(*fds) * nfds); + if (fds == NULL) + return (LIBUSB_ERROR_NO_MEM); + + LIST_FOREACH_ENTRY(ipollfd, &ctx->pollfds, list) { + tmppollfd = &ipollfd->pollfd; + tmpfd = tmppollfd->fd; + i++; + fds[i].fd = tmpfd; + fds[i].events = tmppollfd->events; + fds[i].revents = 0; + } + + pthread_mutex_unlock(&ctx->pollfds_lock); + + timeout = (tv->tv_sec * 1000) + (tv->tv_usec / 1000); + if (tv->tv_usec % 1000) + timeout++; + + ret = poll(fds, nfds, timeout); + if (ret == 0) { + free(fds); + return (handle_timeouts(ctx)); + } else if (ret == -1 && errno == EINTR) { + free(fds); + return (LIBUSB_ERROR_INTERRUPTED); + } else if (ret < 0) { + free(fds); + return (LIBUSB_ERROR_IO); + } + + if (fds[0].revents) { + if (ret == 1){ + ret = 0; + goto handled; + } else { + fds[0].revents = 0; + ret--; + } + } + + pthread_mutex_lock(&ctx->open_devs_lock); + for (i = 0 ; i < nfds && ret > 0 ; i++) { + + tfds = &fds[i]; + if (!tfds->revents) + continue; + + ret--; + LIST_FOREACH_ENTRY(devh, &ctx->open_devs, list) { + if (libusb20_dev_get_fd(devh->os_priv) == tfds->fd) + break ; + } + + if (tfds->revents & POLLERR) { + usb_remove_pollfd(ctx, libusb20_dev_get_fd(devh->os_priv)); + usb_handle_disconnect(devh); + continue ; + } + + + pthread_mutex_lock(&libusb20_lock); + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20_PROCESS"); + ret = libusb20_dev_process(devh->os_priv); + pthread_mutex_unlock(&libusb20_lock); + + + if (ret == 0 || ret == LIBUSB20_ERROR_NO_DEVICE) + continue; + else if (ret < 0) + goto out; + } + + ret = 0; +out: + pthread_mutex_unlock(&ctx->open_devs_lock); + +handled: + free(fds); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "handle_events leave"); + return ret; +} + +/* Polling and timing */ + +int +libusb_try_lock_events(libusb_context * ctx) +{ + int ret; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_try_lock_events enter"); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ret = ctx->pollfd_modify; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + if (ret != 0) + return (1); + + ret = pthread_mutex_trylock(&ctx->events_lock); + + if (ret != 0) + return (1); + + ctx->event_handler_active = 1; + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_try_lock_events leave"); + return (0); +} + +void +libusb_lock_events(libusb_context * ctx) +{ + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_events enter"); + + pthread_mutex_lock(&ctx->events_lock); + ctx->event_handler_active = 1; + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_events leave"); +} + +void +libusb_unlock_events(libusb_context * ctx) +{ + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_events enter"); + + ctx->event_handler_active = 0; + pthread_mutex_unlock(&ctx->events_lock); + + pthread_mutex_lock(&ctx->event_waiters_lock); + pthread_cond_broadcast(&ctx->event_waiters_cond); + pthread_mutex_unlock(&ctx->event_waiters_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_events leave"); +} + +int +libusb_event_handling_ok(libusb_context * ctx) +{ + int ret; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handling_ok enter"); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ret = ctx->pollfd_modify; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + if (ret != 0) + return (0); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handling_ok leave"); + return (1); +} + +int +libusb_event_handler_active(libusb_context * ctx) +{ + int ret; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handler_active enter"); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ret = ctx->pollfd_modify; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + if (ret != 0) + return (1); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handler_active leave"); + return (ctx->event_handler_active); +} + +void +libusb_lock_event_waiters(libusb_context * ctx) +{ + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_event_waiters enter"); + + pthread_mutex_lock(&ctx->event_waiters_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_event_waiters leave"); +} + +void +libusb_unlock_event_waiters(libusb_context * ctx) +{ + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_event_waiters enter"); + + pthread_mutex_unlock(&ctx->event_waiters_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_event_waiters leave"); +} + +int +libusb_wait_for_event(libusb_context * ctx, struct timeval *tv) +{ + int ret; + struct timespec ts; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_wait_for_event enter"); + + if (tv == NULL) { + pthread_cond_wait(&ctx->event_waiters_cond, + &ctx->event_waiters_lock); + return (0); + } + + ret = clock_gettime(CLOCK_REALTIME, &ts); + if (ret < 0) + return (LIBUSB_ERROR_OTHER); + + 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++; + } + + ret = pthread_cond_timedwait(&ctx->event_waiters_cond, + &ctx->event_waiters_lock, &ts); + + if (ret == ETIMEDOUT) + return (1); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_wait_for_event leave"); + return (0); +} + +int +libusb_handle_events_timeout(libusb_context * ctx, struct timeval *tv) +{ + struct timeval timeout; + struct timeval poll_timeout; + int ret; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout enter"); + + ret = get_next_timeout(ctx, tv, &poll_timeout); + if (ret != 0) { + return handle_timeouts(ctx); + } +retry: + if (libusb_try_lock_events(ctx) == 0) { + ret = handle_events(ctx, &poll_timeout); + libusb_unlock_events(ctx); + return ret; + } + + libusb_lock_event_waiters(ctx); + if (libusb_event_handler_active(ctx) == 0) { + libusb_unlock_event_waiters(ctx); + goto retry; + } + + ret = libusb_wait_for_event(ctx, &poll_timeout); + libusb_unlock_event_waiters(ctx); + + if (ret < 0) + return ret; + else if (ret == 1) + return (handle_timeouts(ctx)); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout leave"); + return (0); +} + +int +libusb_handle_events(libusb_context * ctx) +{ + struct timeval tv; + int ret; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events enter"); + + tv.tv_sec = 2; + tv.tv_usec = 0; + ret = libusb_handle_events_timeout(ctx, &tv); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events leave"); + return (ret); +} + +int +libusb_handle_events_locked(libusb_context * ctx, struct timeval *tv) +{ + int ret; + struct timeval poll_tv; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_locked enter"); + + ret = get_next_timeout(ctx, tv, &poll_tv); + if (ret != 0) { + return handle_timeouts(ctx); + } + + ret = handle_events(ctx, &poll_tv); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_locked leave"); + return (ret); +} + +int +libusb_get_next_timeout(libusb_context * ctx, struct timeval *tv) +{ + struct usb_transfer *xfer; + struct timeval *next_tv; + struct timeval cur_tv; + struct timespec cur_ts; + int found; + int ret; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_next_timeout enter"); + + found = 0; + pthread_mutex_lock(&ctx->flying_transfers_lock); + if (USB_LIST_EMPTY(&ctx->flying_transfers)) { + pthread_mutex_unlock(&ctx->flying_transfers_lock); + return (0); + } + + LIST_FOREACH_ENTRY(xfer, &ctx->flying_transfers, list) { + if (!(xfer->flags & USB_TIMED_OUT)) { + found = 1; + break ; + } + } + pthread_mutex_unlock(&ctx->flying_transfers_lock); + + if (found == 0) { + return 0; + } + + next_tv = &xfer->timeout; + if (timerisset(next_tv) == 0) + return (0); + + ret = clock_gettime(CLOCK_MONOTONIC, &cur_ts); + if (ret < 0) + return (LIBUSB_ERROR_OTHER); + TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts); + + if (timercmp(&cur_tv, next_tv, >=) != 0) + timerclear(tv); + else + timersub(next_tv, &cur_tv, tv); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_next_timeout leave"); + return (1); +} + +void +libusb_set_pollfd_notifiers(libusb_context * ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data) +{ + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_pollfd_notifiers enter"); + + ctx->fd_added_cb = added_cb; + ctx->fd_removed_cb = removed_cb; + ctx->fd_cb_user_data = user_data; + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_pollfd_notifiers leave"); +} + +struct libusb_pollfd ** +libusb_get_pollfds(libusb_context * ctx) +{ + struct usb_pollfd *pollfd; + libusb_pollfd **ret; + int i; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_pollfds enter"); + + i = 0; + pthread_mutex_lock(&ctx->pollfds_lock); + LIST_FOREACH_ENTRY(pollfd, &ctx->pollfds, list) + i++; + + ret = calloc(i + 1 , sizeof(struct libusb_pollfd *)); + if (ret == NULL) { + pthread_mutex_unlock(&ctx->pollfds_lock); + return (ret); + } + + i = 0; + LIST_FOREACH_ENTRY(pollfd, &ctx->pollfds, list) + ret[i++] = (struct libusb_pollfd *) pollfd; + ret[i] = NULL; + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_pollfds leave"); + return (ret); +} + + +/* Synchronous device I/O */ + +static void ctrl_tr_cb(struct libusb_transfer *transfer) +{ + libusb_context *ctx; + int *complet; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "CALLBACK ENTER"); + + complet = transfer->user_data; + *complet = 1; +} + +int +libusb_control_transfer(libusb_device_handle * devh, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout) +{ + struct libusb_transfer *xfer; + struct libusb_control_setup *ctr; + libusb_context *ctx; + unsigned char *buff; + int complet; + int ret; + + ctx = devh->dev->ctx; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_control_transfer enter"); + + if (devh == NULL || data == NULL) + return (LIBUSB_ERROR_NO_MEM); + + xfer = libusb_alloc_transfer(0); + if (xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + buff = malloc(sizeof(libusb_control_setup) + wLength); + if (buff == NULL) { + libusb_free_transfer(xfer); + return (LIBUSB_ERROR_NO_MEM); + } + + ctr = (libusb_control_setup *)buff; + ctr->bmRequestType = bmRequestType; + ctr->bRequest = bRequest; + ctr->wValue = wValue; + ctr->wIndex = wIndex; + ctr->wLength = wLength; + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT) + memcpy(buff + sizeof(libusb_control_setup), data, wLength); + + xfer->dev_handle = devh; + xfer->endpoint = 0; + xfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + xfer->timeout = timeout; + xfer->buffer = buff; + xfer->length = sizeof(libusb_control_setup) + wLength; + xfer->user_data = &complet; + xfer->callback = ctrl_tr_cb; + xfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; + complet = 0; + + if ((ret = libusb_submit_transfer(xfer)) < 0) { + libusb_free_transfer(xfer); + return (ret); + } + + while (complet == 0) + if ((ret = libusb_handle_events(ctx)) < 0) { + libusb_cancel_transfer(xfer); + while (complet == 0) + if (libusb_handle_events(ctx) < 0) { + break; + } + libusb_free_transfer(xfer); + return (ret); + } + + + if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) + memcpy(data, buff + sizeof(libusb_control_setup), wLength); + + switch (xfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + ret = xfer->actual_length; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + case LIBUSB_TRANSFER_STALL: + case LIBUSB_TRANSFER_NO_DEVICE: + ret = xfer->status; + break; + default: + ret = LIBUSB_ERROR_OTHER; + } + libusb_free_transfer(xfer); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_control_transfer leave"); + return (ret); +} + +static int +do_transfer(struct libusb_device_handle *devh, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout, int type) +{ + struct libusb_transfer *xfer; + libusb_context *ctx; + int complet; + int ret; + + if (devh == NULL || data == NULL) + return (LIBUSB_ERROR_NO_MEM); + + xfer = libusb_alloc_transfer(0); + if (xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + ctx = devh->dev->ctx; + + xfer->dev_handle = devh; + xfer->endpoint = endpoint; + xfer->type = type; + xfer->timeout = timeout; + xfer->buffer = data; + xfer->length = length; + xfer->user_data = &complet; + xfer->callback = ctrl_tr_cb; + complet = 0; + + if ((ret = libusb_submit_transfer(xfer)) < 0) { + libusb_free_transfer(xfer); + return (ret); + } + + while (complet == 0) { + if ((ret = libusb_handle_events(ctx)) < 0) { + libusb_cancel_transfer(xfer); + libusb_free_transfer(xfer); + while (complet == 0) { + if (libusb_handle_events(ctx) < 0) + break ; + } + return (ret); + } + } + + *transferred = xfer->actual_length; + switch (xfer->status) { + case LIBUSB_TRANSFER_COMPLETED: + ret = xfer->actual_length; + break; + case LIBUSB_TRANSFER_TIMED_OUT: + case LIBUSB_TRANSFER_OVERFLOW: + case LIBUSB_TRANSFER_STALL: + case LIBUSB_TRANSFER_NO_DEVICE: + ret = xfer->status; + break; + default: + ret = LIBUSB_ERROR_OTHER; + } + + libusb_free_transfer(xfer); + return (ret); +} + +int +libusb_bulk_transfer(struct libusb_device_handle *devh, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout) +{ + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_bulk_transfer enter"); + + ret = do_transfer(devh, endpoint, data, length, transferred, + timeout, LIBUSB_TRANSFER_TYPE_BULK); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_bulk_transfer leave"); + return (ret); +} + +/* + * Need to fix xfer->type + */ +int +libusb_interrupt_transfer(struct libusb_device_handle *devh, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout) +{ + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer enter"); + + ret = do_transfer(devh, endpoint, data, length, transferred, + timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer leave"); + return (ret); +} |