From 6c89a5f102e50c4c64fe2243108d70c38997ccec Mon Sep 17 00:00:00 2001 From: alfred Date: Thu, 30 Jul 2009 00:11:41 +0000 Subject: LibUSB v1.0: - Significantly improve libusb10 support. - Many minor issues fixed. - P4 ID: 166189, 165853, 165991, 166052, 166069 Submitted by: hps Approved by: re --- lib/libusb/libusb.3 | 2 +- lib/libusb/libusb.h | 122 +--- lib/libusb/libusb10.c | 1504 ++++++++++++++++++++++------------------ lib/libusb/libusb10.h | 119 +++- lib/libusb/libusb10_desc.c | 264 +++---- lib/libusb/libusb10_io.c | 857 ++++++++--------------- lib/libusb/libusb20.3 | 9 +- lib/libusb/libusb20_compat01.c | 6 +- lib/libusb/libusb20_int.h | 4 +- 9 files changed, 1356 insertions(+), 1531 deletions(-) diff --git a/lib/libusb/libusb.3 b/lib/libusb/libusb.3 index c19a13c..4066852 100644 --- a/lib/libusb/libusb.3 +++ b/lib/libusb/libusb.3 @@ -294,7 +294,7 @@ LIBUSB_ERROR code on failure. . .Pp .Ft void -.Fn libusb_free_config_descriptor "libusb_config_descriptor *config`" +.Fn libusb_free_config_descriptor "libusb_config_descriptor *config" Free a configuration descriptor. . .Pp diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h index 58a8918..f65b57a 100644 --- a/lib/libusb/libusb.h +++ b/lib/libusb/libusb.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -181,95 +180,25 @@ enum libusb_debug_level { LIBUSB_DEBUG_TRANSFER=2, }; -/* internal structures */ +/* libusb structures */ + +struct libusb_context; +struct libusb_device; +struct libusb_transfer; +struct libusb20_device; -typedef struct libusb_pollfd { +struct libusb_pollfd { int fd; short events; -} libusb_pollfd; - -struct usb_pollfd { - TAILQ_ENTRY(usb_pollfd) list; - struct libusb_pollfd pollfd; -}; - -struct usb_transfer { - TAILQ_ENTRY(usb_transfer) list; - int num_iso_packets; - struct timeval timeout; - int transferred; - uint8_t flags; -}; - -struct usb_ep_tr { - TAILQ_ENTRY(usb_ep_tr) list; - uint8_t addr; - uint8_t idx; - uint8_t flags; - void *os_priv; }; -/* libusb structures */ +typedef struct libusb_context libusb_context; +typedef struct libusb_device libusb_device; +typedef struct libusb20_device 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_context { - int debug; - int debug_fixed; - - int ctrl_pipe[2]; - - TAILQ_HEAD(usb_devs_list, libusb_device) usb_devs; - pthread_mutex_t usb_devs_lock; - - TAILQ_HEAD(open_devs_list, libusb_device_handle) open_devs; - pthread_mutex_t open_devs_lock; - - TAILQ_HEAD(flying_transfers_list, usb_transfer) flying_transfers; - pthread_mutex_t flying_transfers_lock; - - TAILQ_HEAD(pollfds_list, usb_pollfd) 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; - - TAILQ_ENTRY(libusb_device) list; - unsigned long session_data; - void *os_priv; -} libusb_device; - -typedef struct libusb_device_handle { - pthread_mutex_t lock; - unsigned long claimed_interfaces; - - TAILQ_ENTRY(libusb_device_handle) list; - struct libusb_device *dev; - void *os_priv; - TAILQ_HEAD(ep_list, usb_ep_tr) ep_list; -} libusb_device_handle; - typedef struct libusb_device_descriptor { uint8_t bLength; uint8_t bDescriptorType; @@ -296,7 +225,7 @@ typedef struct libusb_endpoint_descriptor { uint8_t bInterval; uint8_t bRefresh; uint8_t bSynchAddress; - unsigned char *extra; + uint8_t *extra; int extra_length; } libusb_endpoint_descriptor __aligned(sizeof(void *)); @@ -311,7 +240,7 @@ typedef struct libusb_interface_descriptor { uint8_t bInterfaceProtocol; uint8_t iInterface; struct libusb_endpoint_descriptor *endpoint; - unsigned char *extra; + uint8_t *extra; int extra_length; } libusb_interface_descriptor __aligned(sizeof(void *)); @@ -330,7 +259,7 @@ typedef struct libusb_config_descriptor { uint8_t bmAttributes; uint8_t MaxPower; struct libusb_interface *interface; - unsigned char *extra; + uint8_t *extra; int extra_length; } libusb_config_descriptor __aligned(sizeof(void *)); @@ -348,22 +277,20 @@ typedef struct libusb_iso_packet_descriptor { 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; + uint8_t 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; + uint8_t *buffer; void *os_priv; int num_iso_packets; struct libusb_iso_packet_descriptor iso_packet_desc[0]; @@ -381,8 +308,8 @@ 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_clear_halt(libusb_device_handle *devh, unsigned char endpoint); -int libusb_get_max_packet_size(libusb_device * dev, unsigned char endpoint); +int libusb_clear_halt(libusb_device_handle *devh, uint8_t endpoint); +int libusb_get_max_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); @@ -393,6 +320,7 @@ 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 * dev); 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); @@ -405,15 +333,15 @@ int libusb_get_active_config_descriptor(libusb_device * dev, struct libusb_confi 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); +int libusb_get_string_descriptor_ascii(libusb_device_handle * dev, uint8_t desc_index, uint8_t *data, int length); -/* Asynchronous device I/O*/ +/* 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); +uint8_t *libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, unsigned int packet); /* Polling and timing */ @@ -434,9 +362,9 @@ 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); +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); +int libusb_bulk_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout); +int libusb_interrupt_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout); #if 0 { /* indent fix */ diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index 638f0c7..fa9130d 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -1,6 +1,7 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. + * Copyright (c) 2009 Hans Petter Selasky. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,7 +25,6 @@ * SUCH DAMAGE. */ -#include #include #include #include @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include "libusb20.h" #include "libusb20_desc.h" @@ -41,23 +44,34 @@ 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; + +/* Prototypes */ + +static struct libusb20_transfer *libusb10_get_transfer(struct libusb20_device *, uint8_t, uint8_t); +static int libusb10_get_maxframe(struct libusb20_device *, libusb_transfer *); +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 */ void -libusb_set_debug(libusb_context * ctx, int level) +libusb_set_debug(libusb_context *ctx, int level) { - GET_CONTEXT(ctx); + ctx = GET_CONTEXT(ctx); if (ctx) ctx->debug = level; } int -libusb_init(libusb_context ** context) +libusb_init(libusb_context **context) { struct libusb_context *ctx; - char * debug; + char *debug; int ret; ctx = malloc(sizeof(*ctx)); @@ -72,39 +86,28 @@ libusb_init(libusb_context ** context) if (ctx->debug != 0) ctx->debug_fixed = 1; } + TAILQ_INIT(&ctx->pollfds); + TAILQ_INIT(&ctx->tr_done); - pthread_mutex_init(&ctx->usb_devs_lock, NULL); - pthread_mutex_init(&ctx->open_devs_lock, NULL); - TAILQ_INIT(&ctx->usb_devs); - TAILQ_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); + pthread_mutex_init(&ctx->ctx_lock, NULL); + pthread_cond_init(&ctx->ctx_cond, NULL); - TAILQ_INIT(&ctx->flying_transfers); - TAILQ_INIT(&ctx->pollfds); + ctx->ctx_handler = NO_THREAD; 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]); + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_cond_destroy(&ctx->ctx_cond); free(ctx); return (LIBUSB_ERROR_OTHER); } + /* set non-blocking mode on the control pipe to avoid deadlock */ + ret = 1; + ioctl(ctx->ctrl_pipe[0], FIONBIO, &ret); + ret = 1; + ioctl(ctx->ctrl_pipe[1], FIONBIO, &ret); - 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; - } + libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->ctrl_pipe[0], POLLIN); pthread_mutex_lock(&default_context_lock); if (usbi_default_context == NULL) { @@ -115,18 +118,26 @@ libusb_init(libusb_context ** context) if (context) *context = ctx; + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_init complete"); + return (0); } void -libusb_exit(libusb_context * ctx) +libusb_exit(libusb_context *ctx) { - GET_CONTEXT(ctx); + ctx = GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_exit enter"); - usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + if (ctx == NULL) + return; + + /* XXX cleanup devices */ + + libusb10_remove_pollfd(ctx, &ctx->ctx_poll); close(ctx->ctrl_pipe[0]); close(ctx->ctrl_pipe[1]); + pthread_mutex_destroy(&ctx->ctx_lock); + pthread_cond_destroy(&ctx->ctx_cond); pthread_mutex_lock(&default_context_lock); if (ctx == usbi_default_context) { @@ -135,47 +146,48 @@ libusb_exit(libusb_context * ctx) 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) +libusb_get_device_list(libusb_context *ctx, libusb_device ***list) { + struct libusb20_backend *usb_backend; 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"); + 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 (-1); + 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++; - if (list == NULL) { - libusb20_be_free(usb_backend); - return (LIBUSB_ERROR_INVALID_PARAM); - } + /* 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))) { /* 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) { while (i != 0) { @@ -183,23 +195,21 @@ libusb_get_device_list(libusb_context * ctx, libusb_device *** list) i--; } free(*list); + *list = NULL; libusb20_be_free(usb_backend); return (LIBUSB_ERROR_NO_MEM); } memset(dev, 0, sizeof(*dev)); - pthread_mutex_init(&dev->lock, NULL); + /* init transfer queues */ + TAILQ_INIT(&dev->tr_head); + + /* set context we belong to */ 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); - TAILQ_INSERT_HEAD(&ctx->usb_devs, dev, list); - pthread_mutex_unlock(&ctx->usb_devs_lock); + pdev->privLuData = dev; (*list)[i] = libusb_ref_device(dev); i++; @@ -207,91 +217,65 @@ libusb_get_device_list(libusb_context * ctx, libusb_device *** list) (*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 ; + return; /* be NULL safe */ 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_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); + return (0); /* should not happen */ + return (libusb20_dev_get_bus_number(dev->os_priv)); } uint8_t -libusb_get_device_address(libusb_device * dev) +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); + return (0); /* should not happen */ + return (libusb20_dev_get_address(dev->os_priv)); } int -libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) +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; - libusb_context *ctx; - int i, j, k, ret; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_max_packet_size enter"); + int i; + int j; + int k; + int ret; if (dev == NULL) return (LIBUSB_ERROR_NO_DEVICE); - if (libusb_get_active_config_descriptor(dev, &pdconf) < 0) - return (LIBUSB_ERROR_OTHER); - + 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++) { + for (i = 0; i < pdconf->bNumInterfaces; i++) { pinf = &pdconf->interface[i]; - for (j = 0 ; j < pinf->num_altsetting ; j++) { + for (j = 0; j < pinf->num_altsetting; j++) { pdinf = &pinf->altsetting[j]; - for (k = 0 ; k < pdinf->bNumEndpoints ; k++) { + for (k = 0; k < pdinf->bNumEndpoints; k++) { pdend = &pdinf->endpoint[k]; if (pdend->bEndpointAddress == endpoint) { ret = pdend->wMaxPacketSize; @@ -303,485 +287,401 @@ libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) 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_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); + return (NULL); /* be NULL safe */ - pthread_mutex_lock(&dev->lock); + CTX_LOCK(dev->ctx); dev->refcnt++; - pthread_mutex_unlock(&dev->lock); + CTX_UNLOCK(dev->ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_ref_device leave"); return (dev); } void -libusb_unref_device(libusb_device * dev) +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; + return; /* be NULL safe */ - pthread_mutex_lock(&dev->lock); + CTX_LOCK(dev->ctx); dev->refcnt--; - pthread_mutex_unlock(&dev->lock); + CTX_UNLOCK(dev->ctx); if (dev->refcnt == 0) { - pthread_mutex_lock(&dev->ctx->usb_devs_lock); - TAILQ_REMOVE(&ctx->usb_devs, 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_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; + uint8_t 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); + /* 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, 16 * 4 /* number of endpoints */ ); if (err) { - free(hdl); + libusb_unref_device(dev); return (LIBUSB_ERROR_NO_MEM); } - memset(hdl, 0, sizeof(*hdl)); - pthread_mutex_init(&hdl->lock, NULL); - - TAILQ_INIT(&hdl->ep_list); - 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 | + libusb10_add_pollfd(ctx, &dev->dev_poll, pdev, 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); - TAILQ_INSERT_HEAD(&ctx->open_devs, hdl, list); - 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); + /* make sure our event loop detects the new device */ + dummy = 0; 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; + if (err < sizeof(dummy)) { + /* ignore error, if any */ + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open write failed!"); } + *devh = pdev; - 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, +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; + int i; + int j; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid enter"); + ctx = GET_CONTEXT(ctx); + if (ctx == NULL) + return (NULL); /* be NULL safe */ - devh = NULL; + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid enter"); if ((i = libusb_get_device_list(ctx, &devs)) < 0) return (NULL); + pdev = NULL; + for (j = 0; j < i; j++) { - pdev = (struct libusb20_device *)devs[j]->os_priv; + pdev = devs[j]->os_priv; pdesc = libusb20_dev_get_device_desc(pdev); + /* + * 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) { - if (libusb_open(devs[j], &devh) < 0) - devh = NULL; - break ; + if (libusb_open(devs[j], &pdev) < 0) + pdev = NULL; + break; } } libusb_free_device_list(devs, 1); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid leave"); - return (devh); + return (pdev); } void -libusb_close(libusb_device_handle * devh) +libusb_close(struct libusb20_device *pdev) { libusb_context *ctx; - struct libusb20_device *pdev; - struct usb_ep_tr *eptr; - unsigned char dummy = 1; + struct libusb_device *dev; + uint8_t dummy; int err; - if (devh == NULL) - return ; + if (pdev == NULL) + return; /* be NULL safe */ - ctx = devh->dev->ctx; - pdev = devh->os_priv; + dev = libusb_get_device(pdev); + ctx = dev->ctx; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close enter"); + libusb10_remove_pollfd(ctx, &dev->dev_poll); - pthread_mutex_lock(&ctx->pollfd_modify_lock); - ctx->pollfd_modify++; - pthread_mutex_unlock(&ctx->pollfd_modify_lock); + libusb20_dev_close(pdev); + libusb_unref_device(dev); + /* make sure our event loop detects the closed device */ + dummy = 0; err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); - - if (err <= 0) { - pthread_mutex_lock(&ctx->open_devs_lock); - TAILQ_REMOVE(&ctx->open_devs, devh, list); - pthread_mutex_unlock(&ctx->open_devs_lock); - - usb_remove_pollfd(ctx, libusb20_dev_get_fd(pdev)); - libusb20_dev_close(pdev); - libusb_unref_device(devh->dev); - TAILQ_FOREACH(eptr, &devh->ep_list, list) { - TAILQ_REMOVE(&devh->ep_list, eptr, list); - libusb20_tr_close(((struct libusb20_transfer **) - eptr->os_priv)[0]); - if (eptr->flags) - libusb20_tr_close(((struct libusb20_transfer **) - eptr->os_priv)[1]); - free((struct libusb20_transfer **)eptr->os_priv); - } - 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); - TAILQ_REMOVE(&ctx->open_devs, devh, list); - pthread_mutex_unlock(&ctx->open_devs_lock); - - usb_remove_pollfd(ctx, libusb20_dev_get_fd(pdev)); - libusb20_dev_close(pdev); - libusb_unref_device(devh->dev); - TAILQ_FOREACH(eptr, &devh->ep_list, list) { - TAILQ_REMOVE(&devh->ep_list, eptr, list); - libusb20_tr_close(((struct libusb20_transfer **) - eptr->os_priv)[0]); - if (eptr->flags) - libusb20_tr_close(((struct libusb20_transfer **) - eptr->os_priv)[1]); - free((struct libusb20_transfer **)eptr->os_priv); + if (err < sizeof(dummy)) { + /* ignore error, if any */ + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close write failed!"); } - 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_get_device(struct libusb20_device *pdev) { - libusb_context *ctx; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device enter"); - - if (devh == NULL) + if (pdev == NULL) return (NULL); - - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device leave"); - return (devh->dev); + return ((libusb_device *)pdev->privLuData); } int -libusb_get_configuration(libusb_device_handle * devh, int *config) +libusb_get_configuration(struct libusb20_device *pdev, int *config) { - libusb_context *ctx; + struct libusb20_config *pconf; - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_configuration enter"); - - if (devh == NULL || config == NULL) + if (pdev == NULL || config == NULL) return (LIBUSB_ERROR_INVALID_PARAM); - *config = libusb20_dev_get_config_index((struct libusb20_device *) - devh->dev->os_priv); + 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); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_configuration leave"); return (0); } int -libusb_set_configuration(libusb_device_handle * devh, int configuration) +libusb_set_configuration(struct libusb20_device *pdev, int configuration) { - struct libusb20_device *pdev; - libusb_context *ctx; + struct libusb20_config *pconf; + struct libusb_device *dev; + int err; + uint8_t i; - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_configuration enter"); + dev = libusb_get_device(pdev); - if (devh == NULL) + if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); - pdev = (struct libusb20_device *)devh->dev->os_priv; + 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); + } - libusb20_dev_set_config_index(pdev, configuration); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_configuration leave"); - return (0); +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(libusb_device_handle * dev, int interface_number) +libusb_claim_interface(struct libusb20_device *pdev, int interface_number) { - libusb_context *ctx; - int ret = 0; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_claim_interface enter"); + libusb_device *dev; + int err = 0; + dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); - if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); - pthread_mutex_lock(&(dev->lock)); + CTX_LOCK(dev->ctx); if (dev->claimed_interfaces & (1 << interface_number)) - ret = LIBUSB_ERROR_BUSY; + err = LIBUSB_ERROR_BUSY; - if (!ret) + if (!err) dev->claimed_interfaces |= (1 << interface_number); - pthread_mutex_unlock(&(dev->lock)); - - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_claim_interface leave"); - return (ret); + CTX_UNLOCK(dev->ctx); + return (err); } int -libusb_release_interface(libusb_device_handle * dev, int interface_number) +libusb_release_interface(struct libusb20_device *pdev, int interface_number) { - libusb_context *ctx; - int ret; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_release_interface enter"); + libusb_device *dev; + int err = 0; - ret = 0; + dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); - if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); - pthread_mutex_lock(&(dev->lock)); + CTX_LOCK(dev->ctx); if (!(dev->claimed_interfaces & (1 << interface_number))) - ret = LIBUSB_ERROR_NOT_FOUND; + err = LIBUSB_ERROR_NOT_FOUND; - if (!ret) + if (!err) dev->claimed_interfaces &= ~(1 << interface_number); - pthread_mutex_unlock(&(dev->lock)); - - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_release_interface leave"); - return (ret); + CTX_UNLOCK(dev->ctx); + return (err); } int -libusb_set_interface_alt_setting(libusb_device_handle * dev, +libusb_set_interface_alt_setting(struct libusb20_device *pdev, int interface_number, int alternate_setting) { - libusb_context *ctx; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_interface_alt_setting enter"); + libusb_device *dev; + int err = 0; + dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); - if (interface_number >= sizeof(dev->claimed_interfaces) *8) + if (interface_number < 0 || interface_number > 31) 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); + CTX_LOCK(dev->ctx); + if (!(dev->claimed_interfaces & (1 << interface_number))) + err = LIBUSB_ERROR_NOT_FOUND; + CTX_UNLOCK(dev->ctx); - 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); + 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); +} + +static struct libusb20_transfer * +libusb10_get_transfer(struct libusb20_device *pdev, + uint8_t endpoint, uint8_t index) +{ + index &= 1; /* double buffering */ + + index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4; + + if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) { + /* this is an IN endpoint */ + index |= 2; + } + return (libusb20_tr_get_pointer(pdev, index)); } int -libusb_clear_halt(libusb_device_handle * devh, unsigned char endpoint) +libusb_clear_halt(struct libusb20_device *pdev, uint8_t endpoint) { struct libusb20_transfer *xfer; - struct libusb20_device *pdev; - libusb_context *ctx; - int ret; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_clear_halt enter"); - - pdev = devh->os_priv; - xfer = libusb20_tr_get_pointer(pdev, - ((endpoint / 0x40) | (endpoint * 4)) % (16 * 4)); + struct libusb_device *dev; + int err; + + xfer = libusb10_get_transfer(pdev, endpoint, 0); if (xfer == NULL) - return (LIBUSB_ERROR_NO_MEM); + return (LIBUSB_ERROR_INVALID_PARAM); - 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); + dev = libusb_get_device(pdev); + + CTX_LOCK(dev->ctx); + err = libusb20_tr_open(xfer, 0, 0, endpoint); + CTX_UNLOCK(dev->ctx); + + if (err != 0 && err != LIBUSB20_ERROR_BUSY) 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); + /* 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(libusb_device_handle * dev) +libusb_reset_device(struct libusb20_device *pdev) { - libusb_context *ctx; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_reset_device enter"); + libusb_device *dev; + int err; + dev = libusb_get_device(pdev); 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); + 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_kernel_driver_active(libusb_device_handle * devh, int interface) +libusb_kernel_driver_active(struct libusb20_device *pdev, int interface) { - libusb_context *ctx; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_kernel_driver_active enter"); - - if (devh == NULL) + if (pdev == 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)); + return (libusb20_dev_kernel_driver_active( + pdev, interface)); } int -libusb_detach_kernel_driver(libusb_device_handle * devh, int interface) +libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface) { - struct libusb20_device *pdev; - libusb_context *ctx; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_detach_kernel_driver enter"); + int err; - if (devh == NULL) + if (pdev == 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); + err = libusb20_dev_detach_kernel_driver( + pdev, interface); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_detach_kernel_driver leave"); - return (0); + return (err ? LIBUSB20_ERROR_OTHER : 0); } -/* - * stub function. - * libusb20 doesn't support this feature. - */ int -libusb_attach_kernel_driver(libusb_device_handle * devh, int interface) +libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface) { - libusb_context *ctx; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_attach_kernel_driver enter"); - - if (devh == NULL) + if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); - - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_attach_kernel_driver leave"); + /* stub - currently not supported by libusb20 */ return (0); } @@ -790,56 +690,45 @@ libusb_attach_kernel_driver(libusb_device_handle * devh, int interface) struct libusb_transfer * libusb_alloc_transfer(int iso_packets) { - struct libusb_transfer *xfer; - struct usb_transfer *bxfer; - libusb_context *ctx; + struct libusb_transfer *uxfer; + struct libusb_super_transfer *sxfer; 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) + + sizeof(struct libusb_super_transfer) + (iso_packets * sizeof(libusb_iso_packet_descriptor)); - bxfer = malloc(len); - if (bxfer == NULL) + sxfer = malloc(len); + if (sxfer == NULL) return (NULL); - memset(bxfer, 0, len); - bxfer->num_iso_packets = iso_packets; + memset(sxfer, 0, len); - xfer = (struct libusb_transfer *) ((uint8_t *)bxfer + - sizeof(struct usb_transfer)); + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_alloc_transfer leave"); - return (xfer); + /* set default value */ + uxfer->num_iso_packets = iso_packets; + + return (uxfer); } void -libusb_free_transfer(struct libusb_transfer *xfer) +libusb_free_transfer(struct libusb_transfer *uxfer) { - struct usb_transfer *bxfer; - libusb_context *ctx; + struct libusb_super_transfer *sxfer; - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_transfer enter"); + if (uxfer == NULL) + return; /* be NULL safe */ - if (xfer == NULL) - return ; + sxfer = (struct libusb_super_transfer *)( + (uint8_t *)uxfer - sizeof(*sxfer)); - bxfer = (struct usb_transfer *) ((uint8_t *)xfer - - sizeof(struct usb_transfer)); - - free(bxfer); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_transfer leave"); - return; + free(sxfer); } static int -libusb_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) +libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) { int ret; int usb_speed; @@ -852,25 +741,24 @@ libusb_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) case LIBUSB20_SPEED_LOW: case LIBUSB20_SPEED_FULL: ret = 60 * 1; - break ; - default : + break; + default: ret = 60 * 8; - break ; + break; } - break ; + break; case LIBUSB_TRANSFER_TYPE_CONTROL: ret = 2; - break ; + break; default: ret = 1; - break ; + break; } - - return ret; + return (ret); } static int -libusb_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer) +libusb10_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer) { int ret; int usb_speed; @@ -879,304 +767,548 @@ libusb_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer) switch (xfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: - ret = 0; - break ; + ret = 0; /* kernel will auto-select */ + break; case LIBUSB_TRANSFER_TYPE_CONTROL: + ret = 1024; + break; + default: switch (usb_speed) { - case LIBUSB20_SPEED_LOW: - ret = 8; - break ; - case LIBUSB20_SPEED_FULL: - ret = 64; - break ; - default: - ret = 64; - break ; - } - 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 ; + case LIBUSB20_SPEED_LOW: + ret = 256; + break; + case LIBUSB20_SPEED_FULL: + ret = 4096; + break; + default: + ret = 16384; + break; } - 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); } - - return ret; } +/* This function must be called locked */ + static void -libusb10_proxy(struct libusb20_transfer *xfer) +libusb10_complete_transfer(struct libusb20_transfer *pxfer, + struct libusb_super_transfer *sxfer, int status) { - struct usb_transfer *usb_backend; - struct libusb20_device *pdev; - libusb_transfer *usb_xfer; - libusb_context *ctx; - uint32_t pos; - uint32_t max; - uint32_t size; + 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); + + uxfer->status = status; + + 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; - uint32_t iso_packets; - int i; + uint8_t flags; + + 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)); - 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); + 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; + + flags = uxfer->flags; switch (status) { case LIBUSB20_TRANSFER_COMPLETED: - usb_backend->transferred += libusb20_tr_get_actual_length(xfer); - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 TRANSFER %i bytes", - usb_backend->transferred); - if (usb_backend->transferred != usb_xfer->length) - goto tr_start; - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 TRANSFER COMPLETE"); - usb_handle_transfer_completion(usb_backend, LIBUSB_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; - break ; case LIBUSB20_TRANSFER_START: -tr_start: - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 START"); - max = libusb_get_buffsize(pdev, usb_xfer); - pos = usb_backend->transferred; - size = (usb_xfer->length - pos); - size = (size > max) ? max : size; - 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[pos]) + - 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[pos], size, 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[pos], - size, usb_xfer->timeout); - break ; - case LIBUSB_TRANSFER_TYPE_INTERRUPT: - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE INTR"); - libusb20_tr_setup_intr(xfer, &usb_xfer->buffer[pos], - size, usb_xfer->timeout); - break ; + + /* 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; } - libusb20_tr_submit(xfer); - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 SUBMITED"); - break ; + + /* 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: - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "TRANSFER DEFAULT 0x%x\n", - status); - usb_backend->transferred = 0; - usb_handle_transfer_completion(usb_backend, LIBUSB_TRANSFER_CANCELLED); - break ; + 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: - 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 ; + + 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; } } -int -libusb_submit_transfer(struct libusb_transfer *xfer) +/* This function must be called locked */ + +static void +libusb10_ctrl_proxy(struct libusb20_transfer *pxfer) { - struct libusb20_transfer **usb20_xfer; - struct usb_transfer *usb_backend; - struct usb_transfer *usb_node; - struct libusb20_device *pdev; - struct usb_ep_tr *eptr; - struct timespec cur_ts; - struct timeval *cur_tv; - libusb_device_handle *devh; - libusb_context *ctx; - int maxframe; - int buffsize; - int ep_idx; - int ret; + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; + uint32_t max_bulk; + uint32_t actlen; + uint8_t status; + uint8_t flags; - if (xfer == NULL) - return (LIBUSB_ERROR_NO_MEM); + 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); - usb20_xfer = malloc(2 * sizeof(struct libusb20_transfer *)); - if (usb20_xfer == NULL) - return (LIBUSB_ERROR_NO_MEM); + if (sxfer == NULL) + return; /* cancelled - nothing to do */ - ctx = xfer->dev_handle->dev->ctx; - pdev = xfer->dev_handle->os_priv; - devh = xfer->dev_handle; - - 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++; + 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; } - - TIMESPEC_TO_TIMEVAL(&usb_backend->timeout, &cur_ts); - } - - /*Add to flying list*/ - pthread_mutex_lock(&ctx->flying_transfers_lock); - if (TAILQ_EMPTY(&ctx->flying_transfers)) { - TAILQ_INSERT_HEAD(&ctx->flying_transfers, usb_backend, list); - goto out; - } - if (timerisset(&usb_backend->timeout) == 0) { - TAILQ_INSERT_HEAD(&ctx->flying_transfers, usb_backend, list); - goto out; - } - TAILQ_FOREACH(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)) { - TAILQ_INSERT_TAIL(&ctx->flying_transfers, usb_backend, list); - goto out; + /* check for end of data */ + if (sxfer->rem_len == 0) { + libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); + break; } - } - TAILQ_INSERT_TAIL(&ctx->flying_transfers, usb_backend, list); + /* FALLTHROUGH */ -out: - pthread_mutex_unlock(&ctx->flying_transfers_lock); + 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); + } - ep_idx = (xfer->endpoint / 0x40) | (xfer->endpoint * 4) % (16 * 4); - usb20_xfer[0] = libusb20_tr_get_pointer(pdev, ep_idx); - usb20_xfer[1] = libusb20_tr_get_pointer(pdev, ep_idx + 1); - - if (usb20_xfer[0] == NULL) - return (LIBUSB_ERROR_OTHER); + 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; - xfer->os_priv = usb20_xfer; + libusb20_tr_submit(pxfer); - pthread_mutex_lock(&libusb20_lock); + /* 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; - buffsize = libusb_get_buffsize(pdev, xfer); - maxframe = libusb_get_maxframe(pdev, xfer); - - ret = 0; - TAILQ_FOREACH(eptr, &devh->ep_list, list) { - if (xfer->endpoint == eptr->addr) - ret++; + default: + libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + break; } - if (ret == 0) { - eptr = malloc(sizeof(struct usb_ep_tr)); - eptr->addr = xfer->endpoint; - eptr->idx = ep_idx; - eptr->os_priv = usb20_xfer; - eptr->flags = (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)?1:0; - TAILQ_INSERT_HEAD(&devh->ep_list, eptr, list); - 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); - TAILQ_REMOVE(&ctx->flying_transfers, usb_backend, list); - pthread_mutex_unlock(&ctx->flying_transfers_lock); - return (LIBUSB_ERROR_OTHER); +} + +/* 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; + uint8_t dummy; + + 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->rem_len) + return; /* cannot queue another one */ + /* swap transfers */ + pxfer1 = pxfer0; + break; + case 1: + sxfer = libusb20_tr_get_priv_sc1(pxfer0); + 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 */ - 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); - } +found: - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20_TR_START"); - libusb20_tr_start(usb20_xfer[0]); - if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) - libusb20_tr_start(usb20_xfer[1]); + libusb20_tr_set_priv_sc0(pxfer0, pdev); + libusb20_tr_set_priv_sc1(pxfer0, sxfer); - pthread_mutex_unlock(&libusb20_lock); + /* reset super transfer state */ + sxfer->rem_len = uxfer->length; + sxfer->curr_data = uxfer->buffer; + uxfer->actual_length = 0; - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer leave"); - return (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_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(pxfer0, buffsize, maxframe, endpoint); + 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 */ + dummy = 0; + write(dev->ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); } +/* The following function must be called unlocked */ + int -libusb_cancel_transfer(struct libusb_transfer *xfer) +libusb_submit_transfer(struct libusb_transfer *uxfer) { - libusb_context *ctx; + struct libusb20_transfer *pxfer0; + struct libusb20_transfer *pxfer1; + struct libusb_super_transfer *sxfer; + struct libusb_device *dev; + unsigned int endpoint; + int err; - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter"); + if (uxfer == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); - if (xfer == NULL) - return (LIBUSB_ERROR_NO_MEM); + if (uxfer->dev_handle == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); - pthread_mutex_lock(&libusb20_lock); - libusb20_tr_stop(xfer->os_priv); - pthread_mutex_unlock(&libusb20_lock); + endpoint = uxfer->endpoint; + + if (endpoint > 255) + return (LIBUSB_ERROR_INVALID_PARAM); + + dev = libusb_get_device(uxfer->dev_handle); + + DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "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 { + TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry); + + libusb10_submit_transfer_sub( + uxfer->dev_handle, endpoint); + + err = 0; /* success */ + } + + CTX_UNLOCK(dev->ctx); + + DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "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; + unsigned int endpoint; + + if (uxfer == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (uxfer->dev_handle == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + endpoint = uxfer->endpoint; + + if (endpoint > 255) + return (LIBUSB_ERROR_INVALID_PARAM); + + dev = libusb_get_device(uxfer->dev_handle); + + DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_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 (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); + } else if (pxfer0 == NULL || pxfer1 == NULL) { + /* not started */ + } else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) { + libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_CANCELLED); + libusb20_tr_stop(pxfer0); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub( + uxfer->dev_handle, endpoint); + } else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) { + libusb10_complete_transfer(pxfer1, sxfer, LIBUSB_TRANSFER_CANCELLED); + libusb20_tr_stop(pxfer1); + /* make sure the queue doesn't stall */ + libusb10_submit_transfer_sub( + uxfer->dev_handle, endpoint); + } else { + /* not started */ + } + + CTX_UNLOCK(dev->ctx); + + DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave"); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave"); return (0); } +UNEXPORTED void +libusb10_cancel_all_transfer(libusb_device *dev) +{ + /* TODO */ +} diff --git a/lib/libusb/libusb10.h b/lib/libusb/libusb10.h index 3557ec0..338c2aa 100644 --- a/lib/libusb/libusb10.h +++ b/lib/libusb/libusb10.h @@ -25,46 +25,89 @@ */ #ifndef __LIBUSB10_H__ -#define __LIBUSB10_H__ +#define __LIBUSB10_H__ -/* - * The two following macros were taken from the original LibUSB v1.0 - * for sake of compatibility: - */ +#include -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) -#define UNEXPORTED __attribute__((__visibility__("hidden"))) - -#define DPRINTF(ctx, dbg, format, args...) \ -if (ctx->debug == dbg) { \ - printf("LIBUSB_%s : ", (ctx->debug == LIBUSB_DEBUG_FUNCTION) ? "FUNCTION" : "TRANSFER"); \ - switch(ctx->debug) { \ - case LIBUSB_DEBUG_FUNCTION: \ - printf(format, ## args);\ - break ; \ - case LIBUSB_DEBUG_TRANSFER: \ - printf(format, ## args);\ - break ; \ +#define GET_CONTEXT(ctx) (((ctx) == NULL) ? usbi_default_context : (ctx)) +#define UNEXPORTED __attribute__((__visibility__("hidden"))) +#define CTX_LOCK(ctx) pthread_mutex_lock(&(ctx)->ctx_lock) +#define CTX_TRYLOCK(ctx) pthread_mutex_trylock(&(ctx)->ctx_lock) +#define CTX_UNLOCK(ctx) pthread_mutex_unlock(&(ctx)->ctx_lock) + +#define DPRINTF(ctx, dbg, format, args...) do { \ + if ((ctx)->debug == dbg) { \ + switch (dbg) { \ + case LIBUSB_DEBUG_FUNCTION: \ + printf("LIBUSB_FUNCTION: " \ + format "\n", ## args); \ + break; \ + case LIBUSB_DEBUG_TRANSFER: \ + printf("LIBUSB_TRANSFER: " \ + format "\n", ## args); \ + break; \ + default: \ + break; \ } \ - printf("\n"); \ -} + } \ +} while(0) + +/* internal structures */ + +struct libusb_super_pollfd { + TAILQ_ENTRY(libusb_super_pollfd) entry; + struct libusb20_device *pdev; + struct libusb_pollfd pollfd; +}; + +struct libusb_super_transfer { + TAILQ_ENTRY(libusb_super_transfer) entry; + uint8_t *curr_data; + uint32_t rem_len; + uint32_t last_len; + uint8_t flags; +}; + +struct libusb_context { + int debug; + int debug_fixed; + int ctrl_pipe[2]; + int tr_done_ref; + int tr_done_gen; + + pthread_mutex_t ctx_lock; + pthread_cond_t ctx_cond; + pthread_t ctx_handler; +#define NO_THREAD ((pthread_t)-1) + + TAILQ_HEAD(, libusb_super_pollfd) pollfds; + TAILQ_HEAD(, libusb_super_transfer) tr_done; + + struct libusb_super_pollfd ctx_poll; + + libusb_pollfd_added_cb fd_added_cb; + libusb_pollfd_removed_cb fd_removed_cb; + void *fd_cb_user_data; +}; + +struct libusb_device { + int refcnt; + + uint32_t claimed_interfaces; + + struct libusb_super_pollfd dev_poll; + + struct libusb_context *ctx; + + TAILQ_HEAD(, libusb_super_transfer) tr_head; + + struct libusb20_device *os_priv; +}; + +extern struct libusb_context *usbi_default_context; -UNEXPORTED int usb_add_pollfd(libusb_context *ctx, int fd, short events); -UNEXPORTED void usb_remove_pollfd(libusb_context *ctx, int fd); -UNEXPORTED void usb_handle_transfer_completion(struct usb_transfer *uxfer, - enum libusb_transfer_status status); -UNEXPORTED void usb_handle_disconnect(struct libusb_device_handle *devh); +void libusb10_add_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd, struct libusb20_device *pdev, int fd, short events); +void libusb10_remove_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd); +void libusb10_cancel_all_transfer(libusb_device *dev); -#endif /*__LIBUSB10_H__*/ +#endif /* __LIBUSB10_H__ */ diff --git a/lib/libusb/libusb10_desc.c b/lib/libusb/libusb10_desc.c index b218c2e..c43443a 100644 --- a/lib/libusb/libusb10_desc.c +++ b/lib/libusb/libusb10_desc.c @@ -24,10 +24,10 @@ * SUCH DAMAGE. */ -#include #include #include #include +#include #include "libusb20.h" #include "libusb20_desc.h" @@ -38,16 +38,11 @@ /* USB descriptors */ int -libusb_get_device_descriptor(libusb_device * dev, +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); @@ -70,54 +65,47 @@ libusb_get_device_descriptor(libusb_device * dev, 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, +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"); + uint8_t config_index; pdev = dev->os_priv; - idx = libusb20_dev_get_config_index(pdev); + config_index = 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)); + return (libusb_get_config_descriptor(dev, config_index, 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, +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"); + struct libusb_config_descriptor *pconfd; + struct libusb_interface_descriptor *ifd; + struct libusb_endpoint_descriptor *endd; + uint8_t *pextra; + uint16_t nextra; + uint8_t nif; + uint8_t nep; + uint8_t nalt; + uint8_t i; + uint8_t j; + uint8_t k; if (dev == NULL || config == NULL) return (LIBUSB_ERROR_INVALID_PARAM); + *config = NULL; + pdev = dev->os_priv; pconf = libusb20_dev_alloc_config(pdev, config_index); @@ -125,75 +113,101 @@ libusb_get_config_descriptor(libusb_device * dev, uint8_t config_index, 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; + nep = 0; + nextra = pconf->extra.len; + + for (i = 0; i < nif; i++) { + + pinf = pconf->interface + i; + nextra += pinf->extra.len; + nep += pinf->num_endpoints; + k = pinf->num_endpoints; + pend = pinf->endpoints; + while (k--) { + nextra += pend->extra.len; + pend++; + } + + j = pinf->num_altsetting; + nalt += pinf->num_altsetting; + pinf = pinf->altsetting; + while (j--) { + nextra += pinf->extra.len; + nep += pinf->num_endpoints; + k = pinf->num_endpoints; + pend = pinf->endpoints; + while (k--) { + nextra += pend->extra.len; + pend++; } + pinf++; } - nend += pconf->interface[i].num_endpoints; } - *config = malloc(sizeof(libusb_config_descriptor) + + nextra = nextra + + (1 * sizeof(libusb_config_descriptor)) + (nif * sizeof(libusb_interface)) + (nalt * sizeof(libusb_interface_descriptor)) + - (nend * sizeof(libusb_endpoint_descriptor))); - if (*config == NULL) { + (nep * sizeof(libusb_endpoint_descriptor)); + + pconfd = malloc(nextra); + + if (pconfd == NULL) { free(pconf); return (LIBUSB_ERROR_NO_MEM); } + /* make sure memory is clean */ + memset(pconfd, 0, nextra); - (*config)->interface = (libusb_interface *)(*config + + pconfd->interface = (libusb_interface *) (pconfd + 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; - } + + ifd = (libusb_interface_descriptor *) (pconfd->interface + nif); + endd = (libusb_endpoint_descriptor *) (ifd + nalt); + pextra = (uint8_t *)(endd + nep); + + /* fill in config descriptor */ + + pconfd->bLength = pconf->desc.bLength; + pconfd->bDescriptorType = pconf->desc.bDescriptorType; + pconfd->wTotalLength = pconf->desc.wTotalLength; + pconfd->bNumInterfaces = pconf->desc.bNumInterfaces; + pconfd->bConfigurationValue = pconf->desc.bConfigurationValue; + pconfd->iConfiguration = pconf->desc.iConfiguration; + pconfd->bmAttributes = pconf->desc.bmAttributes; + pconfd->MaxPower = pconf->desc.bMaxPower; + + if (pconf->extra.len != 0) { + pconfd->extra_length = pconf->extra.len; + pconfd->extra = pextra; + memcpy(pextra, pconf->extra.ptr, pconfd->extra_length); + pextra += pconfd->extra_length; + } + /* setup all interface and endpoint pointers */ + + for (i = 0; i < nif; i++) { + + pconfd->interface[i].altsetting = ifd; + ifd->endpoint = endd; + endd += pconf->interface[i].num_endpoints; + ifd++; + + for (j = 0; j < pconf->interface[i].num_altsetting; j++) { + ifd->endpoint = endd; + endd += pconf->interface[i].altsetting[j].num_endpoints; + ifd++; } } - (*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++) { + /* fill in all interface and endpoint data */ + + 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++) { + pconfd->interface[i].num_altsetting = pinf->num_altsetting + 1; + for (j = 0; j < pconfd->interface[i].num_altsetting; j++) { if (j != 0) pinf = &pconf->interface[i].altsetting[j - 1]; - ifd = &(*config)->interface[i].altsetting[j]; + ifd = &pconfd->interface[i].altsetting[j]; ifd->bLength = pinf->desc.bLength; ifd->bDescriptorType = pinf->desc.bDescriptorType; ifd->bInterfaceNumber = pinf->desc.bInterfaceNumber; @@ -203,10 +217,13 @@ libusb_get_config_descriptor(libusb_device * dev, uint8_t config_index, 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++) { + if (pinf->extra.len != 0) { + ifd->extra_length = pinf->extra.len; + ifd->extra = pextra; + memcpy(pextra, pinf->extra.ptr, pinf->extra.len); + pextra += pinf->extra.len; + } + for (k = 0; k < pinf->num_endpoints; k++) { pend = &pinf->endpoints[k]; endd = &ifd->endpoint[k]; endd->bLength = pend->desc.bLength; @@ -217,82 +234,71 @@ libusb_get_config_descriptor(libusb_device * dev, uint8_t config_index, 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; + if (pend->extra.len != 0) { + endd->extra_length = pend->extra.len; + endd->extra = pextra; + memcpy(pextra, pend->extra.ptr, pend->extra.len); + pextra += pend->extra.len; + } } - } + } } free(pconf); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_config_descriptor leave"); - return (0); + + *config = pconfd; + + return (0); /* success */ } int -libusb_get_config_descriptor_by_value(libusb_device * dev, +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"); + int err; 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); + for (i = 0; i < pdesc->bNumConfigurations; i++) { + err = libusb_get_config_descriptor(dev, i, config); + if (err) + return (err); - } - free(pconf); + if ((*config)->bConfigurationValue == bConfigurationValue) + return (0); /* success */ + + libusb_free_config_descriptor(*config); } - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_config_descriptor_by_value leave"); + *config = NULL; + 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, +libusb_get_string_descriptor_ascii(libusb_device_handle *pdev, uint8_t desc_index, unsigned char *data, int length) { - struct libusb20_device *pdev; - libusb_context *ctx; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_string_descriptor_ascii enter"); - - if (dev == NULL || data == NULL) + if (pdev == NULL || data == NULL || length < 1) 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, + /* put some default data into the destination buffer */ + data[0] = 0; + + if (libusb20_dev_req_string_simple_sync(pdev, desc_index, data, length) == 0) return (strlen(data)); diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c index b1cf75fa..a5bb85f 100644 --- a/lib/libusb/libusb10_io.c +++ b/lib/libusb/libusb10_io.c @@ -24,7 +24,6 @@ * SUCH DAMAGE. */ -#include #include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include "libusb20.h" #include "libusb20_desc.h" @@ -39,739 +39,456 @@ #include "libusb.h" #include "libusb10.h" -UNEXPORTED int -usb_add_pollfd(libusb_context *ctx, int fd, short events) +UNEXPORTED void +libusb10_add_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd, + struct libusb20_device *pdev, 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); + 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; - pthread_mutex_lock(&ctx->pollfds_lock); - TAILQ_INSERT_TAIL(&ctx->pollfds, pollfd, list); - pthread_mutex_unlock(&ctx->pollfds_lock); + 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); - return (0); } UNEXPORTED void -usb_remove_pollfd(libusb_context *ctx, int fd) +libusb10_remove_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd) { - struct usb_pollfd *pollfd; - int found; + if (ctx == NULL) + return; /* invalid */ - found = 0; - pthread_mutex_lock(&ctx->pollfds_lock); + if (pollfd->entry.tqe_prev == NULL) + return; /* already dequeued */ - TAILQ_FOREACH(pollfd, &ctx->pollfds, list) { - if (pollfd->pollfd.fd == fd) { - found = 1; - break ; - } - } - - if (found == 0) { - pthread_mutex_unlock(&ctx->pollfds_lock); - return ; - } - - TAILQ_REMOVE(&ctx->pollfds, pollfd, list); - pthread_mutex_unlock(&ctx->pollfds_lock); - free(pollfd); + 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(fd, ctx->fd_cb_user_data); -} - -UNEXPORTED 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); - TAILQ_REMOVE(&ctx->flying_transfers, 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); -} - -UNEXPORTED 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; - TAILQ_FOREACH(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 ; + ctx->fd_removed_cb(pollfd->pollfd.fd, ctx->fd_cb_user_data); } -UNEXPORTED int -get_next_timeout(libusb_context *ctx, struct timeval *tv, struct timeval *out) -{ - struct timeval timeout; - - if (libusb_get_next_timeout(ctx, &timeout)) { - if (timerisset(&timeout) == 0) - return 1; - if (timercmp(&timeout, tv, <) != 0) - *out = timeout; - else - *out = *tv; - } else { - *out = *tv; - } +/* This function must be called locked */ - return (0); -} - -UNEXPORTED 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 (TAILQ_EMPTY(&ctx->flying_transfers)) - goto out; - - ret = clock_gettime(CLOCK_MONOTONIC, &sys_ts); - TIMESPEC_TO_TIMEVAL(&sys_tv, &sys_ts); - - TAILQ_FOREACH(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); -} - -UNEXPORTED int -handle_events(struct libusb_context *ctx, struct timeval *tv) +static int +libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) { - struct libusb_pollfd *tmppollfd; - struct libusb_device_handle *devh; - struct usb_pollfd *ipollfd; + struct libusb_device *dev; + struct libusb20_device **ppdev; + struct libusb_super_pollfd *pfd; struct pollfd *fds; - struct pollfd *tfds; + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; nfds_t nfds; - int tmpfd; - int ret; int timeout; int i; - - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "handle_events enter"); + int err; - nfds = 0; - i = -1; + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb10_handle_events_sub enter"); - pthread_mutex_lock(&ctx->pollfds_lock); - TAILQ_FOREACH(ipollfd, &ctx->pollfds, list) - nfds++; + nfds = 0; + i = 0; + TAILQ_FOREACH(pfd, &ctx->pollfds, entry) + nfds++; fds = alloca(sizeof(*fds) * nfds); if (fds == NULL) return (LIBUSB_ERROR_NO_MEM); - TAILQ_FOREACH(ipollfd, &ctx->pollfds, list) { - tmppollfd = &ipollfd->pollfd; - tmpfd = tmppollfd->fd; - i++; - fds[i].fd = tmpfd; - fds[i].events = tmppollfd->events; + 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++; } - 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) - return (handle_timeouts(ctx)); - else if (ret == -1 && errno == EINTR) - return (LIBUSB_ERROR_INTERRUPTED); - else if (ret < 0) - return (LIBUSB_ERROR_IO); + 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 != 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 != nfds; i++) { + if (fds[i].revents == 0) + continue; + if (ppdev[i] != NULL) { + dev = libusb_get_device(ppdev[i]); + + err = libusb20_dev_process(ppdev[i]); + if (err) { + /* cancel all transfers - device is gone */ + libusb10_cancel_all_transfer(dev); + /* + * make sure we don't go into an infinite + * loop + */ + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + } + CTX_UNLOCK(ctx); + libusb_unref_device(dev); + CTX_LOCK(ctx); - if (fds[0].revents) { - if (ret == 1){ - ret = 0; - goto handled; } else { - fds[0].revents = 0; - ret--; + uint8_t dummy; + + while (1) { + if (read(fds[i].fd, &dummy, 1) != 1) + break; + } } } - pthread_mutex_lock(&ctx->open_devs_lock); - for (i = 0, devh = NULL ; i < nfds && ret > 0 ; i++) { + err = 0; - tfds = &fds[i]; - if (!tfds->revents) - continue; +do_done: - ret--; - TAILQ_FOREACH(devh, &ctx->open_devs, list) { - if (libusb20_dev_get_fd(devh->os_priv) == tfds->fd) - break ; - } + /* Do all done callbacks */ - if (tfds->revents & POLLERR) { - usb_remove_pollfd(ctx, libusb20_dev_get_fd(devh->os_priv)); - if (devh != NULL) - usb_handle_disconnect(devh); - continue ; - } + while ((sxfer = TAILQ_FIRST(&ctx->tr_done))) { + 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)); + + if (uxfer->callback != NULL) + (uxfer->callback) (uxfer); + if (uxfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) + free(uxfer->buffer); - pthread_mutex_lock(&libusb20_lock); - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20_PROCESS"); - if (devh != NULL) - ret = libusb20_dev_process(devh->os_priv); - pthread_mutex_unlock(&libusb20_lock); + if (uxfer->flags & LIBUSB_TRANSFER_FREE_TRANSFER) + libusb_free_transfer(uxfer); + CTX_LOCK(ctx); - if (ret == 0 || ret == LIBUSB20_ERROR_NO_DEVICE) - continue; - else if (ret < 0) - goto out; + ctx->tr_done_ref--; + ctx->tr_done_gen++; } - ret = 0; -out: - pthread_mutex_unlock(&ctx->open_devs_lock); + /* Wakeup other waiters */ + pthread_cond_broadcast(&ctx->ctx_cond); -handled: - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "handle_events leave"); - return ret; + return (err); } /* Polling and timing */ int -libusb_try_lock_events(libusb_context * ctx) +libusb_try_lock_events(libusb_context *ctx) { - int ret; - - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_try_lock_events enter"); + int err; - pthread_mutex_lock(&ctx->pollfd_modify_lock); - ret = ctx->pollfd_modify; - pthread_mutex_unlock(&ctx->pollfd_modify_lock); - - if (ret != 0) + ctx = GET_CONTEXT(ctx); + if (ctx == NULL) return (1); - ret = pthread_mutex_trylock(&ctx->events_lock); - - if (ret != 0) + err = CTX_TRYLOCK(ctx); + if (err) return (1); - - ctx->event_handler_active = 1; - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_try_lock_events leave"); - return (0); + 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) +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"); + 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) +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"); + 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) +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); + ctx = GET_CONTEXT(ctx); + return (ctx->ctx_handler == pthread_self()); } int -libusb_event_handler_active(libusb_context * ctx) +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); + ctx = GET_CONTEXT(ctx); + return (ctx->ctx_handler != NO_THREAD); } void -libusb_lock_event_waiters(libusb_context * ctx) +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"); + ctx = GET_CONTEXT(ctx); + CTX_LOCK(ctx); } void -libusb_unlock_event_waiters(libusb_context * ctx) +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"); + ctx = GET_CONTEXT(ctx); + CTX_UNLOCK(ctx); } int -libusb_wait_for_event(libusb_context * ctx, struct timeval *tv) +libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) { - int ret; struct timespec ts; + int err; - GET_CONTEXT(ctx); + ctx = 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); + pthread_cond_wait(&ctx->ctx_cond, + &ctx->ctx_lock); return (0); } - - ret = clock_gettime(CLOCK_REALTIME, &ts); - if (ret < 0) + err = clock_gettime(CLOCK_REALTIME, &ts); + if (err < 0) return (LIBUSB_ERROR_OTHER); ts.tv_sec = tv->tv_sec; ts.tv_nsec = tv->tv_usec * 1000; - if (ts.tv_nsec > 1000000000) { + if (ts.tv_nsec >= 1000000000) { ts.tv_nsec -= 1000000000; ts.tv_sec++; } + err = pthread_cond_timedwait(&ctx->ctx_cond, + &ctx->ctx_lock, &ts); - ret = pthread_cond_timedwait(&ctx->event_waiters_cond, - &ctx->event_waiters_lock, &ts); - - if (ret == ETIMEDOUT) + if (err == 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) +libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv) { - struct timeval poll_timeout; - int ret; - - GET_CONTEXT(ctx); + int err; + + ctx = 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_events(ctx); - 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); + err = libusb_handle_events_locked(ctx, tv); - if (ret < 0) - return ret; - else if (ret == 1) - return (handle_timeouts(ctx)); + libusb_unlock_events(ctx); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout leave"); - return (0); + + return (err); } int -libusb_handle_events(libusb_context * ctx) +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); + return (libusb_handle_events_timeout(ctx, NULL)); } int -libusb_handle_events_locked(libusb_context * ctx, struct timeval *tv) +libusb_handle_events_locked(libusb_context *ctx, struct timeval *tv) { - int ret; - struct timeval poll_tv; + int err; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_locked enter"); + ctx = GET_CONTEXT(ctx); - ret = get_next_timeout(ctx, tv, &poll_tv); - if (ret != 0) { - return handle_timeouts(ctx); + if (libusb_event_handling_ok(ctx)) { + err = libusb10_handle_events_sub(ctx, tv); + } else { + libusb_wait_for_event(ctx, tv); + err = 0; } - - ret = handle_events(ctx, &poll_tv); - - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_locked leave"); - return (ret); + return (err); } int -libusb_get_next_timeout(libusb_context * ctx, struct timeval *tv) +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 (TAILQ_EMPTY(&ctx->flying_transfers)) { - pthread_mutex_unlock(&ctx->flying_transfers_lock); - return (0); - } - - TAILQ_FOREACH(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); + /* all timeouts are currently being done by the kernel */ + timerclear(tv); + return (0); } void -libusb_set_pollfd_notifiers(libusb_context * ctx, +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 = GET_CONTEXT(ctx); 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) +libusb_get_pollfds(libusb_context *ctx) { - struct usb_pollfd *pollfd; + struct libusb_super_pollfd *pollfd; libusb_pollfd **ret; int i; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_pollfds enter"); + ctx = GET_CONTEXT(ctx); + + CTX_LOCK(ctx); i = 0; - pthread_mutex_lock(&ctx->pollfds_lock); - TAILQ_FOREACH(pollfd, &ctx->pollfds, list) - i++; + TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) + i++; - ret = calloc(i + 1 , sizeof(struct libusb_pollfd *)); - if (ret == NULL) { - pthread_mutex_unlock(&ctx->pollfds_lock); - return (ret); - } + ret = calloc(i + 1, sizeof(struct libusb_pollfd *)); + if (ret == NULL) + goto done; i = 0; - TAILQ_FOREACH(pollfd, &ctx->pollfds, list) - ret[i++] = (struct libusb_pollfd *) pollfd; + TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) + ret[i++] = &pollfd->pollfd; ret[i] = NULL; - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_pollfds leave"); +done: + CTX_UNLOCK(ctx); 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, +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) + uint8_t *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"); + struct LIBUSB20_CONTROL_SETUP_DECODED req; + int err; + uint16_t actlen; - if (devh == NULL || data == NULL) - return (LIBUSB_ERROR_NO_MEM); + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); - xfer = libusb_alloc_transfer(0); - if (xfer == NULL) - return (LIBUSB_ERROR_NO_MEM); + if ((wLength != 0) && (data == NULL)) + return (LIBUSB_ERROR_INVALID_PARAM); - buff = malloc(sizeof(libusb_control_setup) + wLength); - if (buff == NULL) { - libusb_free_transfer(xfer); - return (LIBUSB_ERROR_NO_MEM); - } + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); - 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); + req.bmRequestType = bmRequestType; + req.bRequest = bRequest; + req.wValue = wValue; + req.wIndex = wIndex; + req.wLength = 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; + err = libusb20_dev_request_sync(devh, &req, data, + &actlen, timeout, 0); - if ((ret = libusb_submit_transfer(xfer)) < 0) { - libusb_free_transfer(xfer); - return (ret); - } + 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); - 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); - } + return (actlen); +} +static void +libusb10_do_transfer_cb(struct libusb_transfer *transfer) +{ + libusb_context *ctx; + int *pdone; - if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) - memcpy(data, buff + sizeof(libusb_control_setup), wLength); + ctx = GET_CONTEXT(NULL); - 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_TRANSFER, "sync I/O done"); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_control_transfer leave"); - return (ret); + pdone = transfer->user_data; + *pdone = 1; } +/* + * TODO: Replace the following function. Allocating and freeing on a + * per-transfer basis is slow. --HPS + */ static int -do_transfer(struct libusb_device_handle *devh, - unsigned char endpoint, unsigned char *data, int length, +libusb10_do_transfer(libusb_device_handle *devh, + uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout, int type) { - struct libusb_transfer *xfer; libusb_context *ctx; - int complet; + struct libusb_transfer *xfer; + volatile int complet; int ret; - if (devh == NULL || data == NULL) - return (LIBUSB_ERROR_NO_MEM); + 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 = devh->dev->ctx; - GET_CONTEXT(ctx); + ctx = libusb_get_device(devh)->ctx; xfer->dev_handle = devh; xfer->endpoint = endpoint; @@ -779,43 +496,42 @@ do_transfer(struct libusb_device_handle *devh, xfer->timeout = timeout; xfer->buffer = data; xfer->length = length; - xfer->user_data = &complet; - xfer->callback = ctrl_tr_cb; + xfer->user_data = (void *)&complet; + xfer->callback = libusb10_do_transfer_cb; complet = 0; - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "SUBMIT_TRANSFER"); if ((ret = libusb_submit_transfer(xfer)) < 0) { libusb_free_transfer(xfer); - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "SUBMIT_TRANSFER FAILED %i", ret); 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); + usleep(1000); /* nice it */ } } *transferred = xfer->actual_length; - DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "xfer->status %i", xfer->status); + switch (xfer->status) { case LIBUSB_TRANSFER_COMPLETED: - ret = xfer->actual_length; + 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 = xfer->status; + ret = LIBUSB_ERROR_NO_DEVICE; break; default: ret = LIBUSB_ERROR_OTHER; + break; } libusb_free_transfer(xfer); @@ -823,40 +539,35 @@ do_transfer(struct libusb_device_handle *devh, } int -libusb_bulk_transfer(struct libusb_device_handle *devh, - unsigned char endpoint, unsigned char *data, int length, +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 = NULL; - GET_CONTEXT(ctx); + + ctx = GET_CONTEXT(NULL); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_bulk_transfer enter"); - ret = do_transfer(devh, endpoint, data, length, transferred, + ret = libusb10_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, +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 = NULL; - GET_CONTEXT(ctx); + ctx = GET_CONTEXT(NULL); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer enter"); - ret = do_transfer(devh, endpoint, data, length, transferred, + ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer leave"); diff --git a/lib/libusb/libusb20.3 b/lib/libusb/libusb20.3 index 403ddba..f902883 100644 --- a/lib/libusb/libusb20.3 +++ b/lib/libusb/libusb20.3 @@ -310,8 +310,13 @@ packets are avoided for proxy buffers. .Pp . .Fn libusb20_tr_get_max_total_length -function will return the maximum value for the length sum of all -USB frames associated with an USB transfer. +function will return the maximum value for the data length sum of all USB +frames associated with an USB transfer. +In case of control transfers the value returned does not include the +length of the SETUP packet, 8 bytes, which is part of frame zero. +The returned value of this function is always aligned to the maximum +packet size, wMaxPacketSize, of the endpoint which the USB transfer is +bound to. . .Pp . diff --git a/lib/libusb/libusb20_compat01.c b/lib/libusb/libusb20_compat01.c index f97ff4d..48a7dd5 100644 --- a/lib/libusb/libusb20_compat01.c +++ b/lib/libusb/libusb20_compat01.c @@ -835,7 +835,7 @@ usb_find_devices(void) /* close all opened devices, if any */ while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { - udev = pdev->priv01Data; + udev = pdev->privLuData; libusb20_be_dequeue_device(usb_backend, pdev); libusb20_dev_free(pdev); if (udev != NULL) { @@ -893,7 +893,7 @@ usb_find_devices(void) } /* link together the two structures */ udev->dev = pdev; - pdev->priv01Data = udev; + pdev->privLuData = udev; err = libusb20_dev_open(pdev, 0); if (err == 0) { @@ -914,7 +914,7 @@ usb_device(usb_dev_handle * dev) pdev = (void *)dev; - return (pdev->priv01Data); + return (pdev->privLuData); } struct usb_bus * diff --git a/lib/libusb/libusb20_int.h b/lib/libusb/libusb20_int.h index aa89611..004b0cf 100644 --- a/lib/libusb/libusb20_int.h +++ b/lib/libusb/libusb20_int.h @@ -191,8 +191,8 @@ struct libusb20_device { /* private backend data */ void *privBeData; - /* libUSB v0.1 compat data */ - void *priv01Data; + /* libUSB v0.1 and v1.0 compat data */ + void *privLuData; /* claimed interface */ uint8_t claimed_interface; -- cgit v1.1