diff -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb.3 src/lib/libusb/libusb.3 --- src/lib/libusb/libusb.3 2009-06-30 15:48:20.000000000 +0200 +++ src/lib/libusb/libusb.3 2009-07-17 09:01:36.000000000 +0200 @@ -294,7 +294,7 @@ . .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 -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb.h src/lib/libusb/libusb.h --- src/lib/libusb/libusb.h 2009-07-26 15:52:07.000000000 +0200 +++ src/lib/libusb/libusb.h 2009-07-16 15:07:11.000000000 +0200 @@ -30,7 +29,6 @@ #include #include #include -#include #include #include @@ -181,95 +179,25 @@ 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 +224,7 @@ 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 +239,7 @@ 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 +258,7 @@ 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 +276,20 @@ 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 +307,8 @@ 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 +319,7 @@ 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 +332,15 @@ 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 +361,9 @@ /* 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 -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb10.c src/lib/libusb/libusb10.c --- src/lib/libusb/libusb10.c 2009-07-26 15:52:07.000000000 +0200 +++ src/lib/libusb/libusb10.c 2009-07-16 15:07:11.000000000 +0200 @@ -1,6 +1,6 @@ -/* $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 +24,6 @@ * SUCH DAMAGE. */ -#include #include #include #include @@ -32,6 +31,9 @@ #include #include #include +#include +#include +#include #include "libusb20.h" #include "libusb20_desc.h" @@ -41,23 +43,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 +85,28 @@ 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 +117,26 @@ 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); + + if (ctx == NULL) + return; - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_exit enter"); - usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + /* 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 +145,48 @@ 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 +194,21 @@ 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 +216,65 @@ (*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 +286,401 @@ 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 ; + if (err < sizeof(dummy)) { + /* ignore error, if any */ + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close write failed!"); } - 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); - } - 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; - - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_configuration enter"); + struct libusb20_config *pconf; - 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); - libusb20_dev_set_config_index(pdev, configuration); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_configuration leave"); - return (0); + if (found) + goto set_config; + } + return (LIBUSB_ERROR_INVALID_PARAM); + } + +set_config: + + libusb10_cancel_all_transfer(dev); + + libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); + + err = libusb20_dev_set_config_index(pdev, i); + + libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | + POLLOUT | POLLRDNORM | POLLWRNORM); + + return (err ? LIBUSB_ERROR_INVALID_PARAM : 0); } int -libusb_claim_interface(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; + libusb_device *dev; + int err = 0; - ctx = NULL; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_release_interface enter"); - - 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; + struct libusb_device *dev; + int err; - 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)); + xfer = libusb10_get_transfer(pdev, endpoint, 0); if (xfer == NULL) - return (LIBUSB_ERROR_NO_MEM); + return (LIBUSB_ERROR_INVALID_PARAM); + + dev = libusb_get_device(pdev); - 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); + 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 +689,45 @@ 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 +740,24 @@ 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 +766,548 @@ 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 */ - 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); + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); + + if (iso_packets > uxfer->num_iso_packets) + iso_packets = uxfer->num_iso_packets; + + if (iso_packets == 0) + return; /* nothing to do */ + + /* make sure that the number of ISOCHRONOUS packets is valid */ + uxfer->num_iso_packets = iso_packets; + + 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 */ + + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); - 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++; + 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; + + 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; - xfer->os_priv = usb20_xfer; + default: + libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); + break; + } +} - pthread_mutex_lock(&libusb20_lock); +/* The following function must be called locked */ - 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++; - } - 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); +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); - } - - 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]); +found: - pthread_mutex_unlock(&libusb20_lock); + libusb20_tr_set_priv_sc0(pxfer0, pdev); + libusb20_tr_set_priv_sc1(pxfer0, sxfer); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer leave"); - return (0); + /* reset super transfer state */ + sxfer->rem_len = uxfer->length; + sxfer->curr_data = uxfer->buffer; + uxfer->actual_length = 0; + + switch (uxfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + libusb20_tr_set_callback(pxfer0, libusb10_isoc_proxy); + break; + case LIBUSB_TRANSFER_TYPE_BULK: + case LIBUSB_TRANSFER_TYPE_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); + + 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; - pthread_mutex_lock(&libusb20_lock); - libusb20_tr_stop(xfer->os_priv); - pthread_mutex_unlock(&libusb20_lock); + 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 -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb10.h src/lib/libusb/libusb10.h --- src/lib/libusb/libusb10.h 2009-07-26 15:52:07.000000000 +0200 +++ src/lib/libusb/libusb10.h 2009-07-16 15:07:11.000000000 +0200 @@ -25,46 +24,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; +#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; \ + } \ + } \ +} while(0) -/* if ctx is NULL use default context*/ +/* internal structures */ -#define GET_CONTEXT(ctx) \ - if (ctx == NULL) ctx = usbi_default_context; +struct libusb_super_pollfd { + TAILQ_ENTRY(libusb_super_pollfd) entry; + struct libusb20_device *pdev; + struct libusb_pollfd pollfd; +}; -#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 ; \ - } \ - printf("\n"); \ -} +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 -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb10_desc.c src/lib/libusb/libusb10_desc.c --- src/lib/libusb/libusb10_desc.c 2009-07-26 15:52:07.000000000 +0200 +++ src/lib/libusb/libusb10_desc.c 2009-07-16 15:07:11.000000000 +0200 @@ -24,10 +23,10 @@ * SUCH DAMAGE. */ -#include #include #include #include +#include #include "libusb20.h" #include "libusb20_desc.h" @@ -38,16 +37,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 +64,47 @@ 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 +112,101 @@ 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; + /* fill in all interface and endpoint data */ - for (i = 0 ; i < nif ; i++) { + 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 +216,13 @@ 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 +233,71 @@ 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 -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb10_io.c src/lib/libusb/libusb10_io.c --- src/lib/libusb/libusb10_io.c 2009-07-26 15:52:07.000000000 +0200 +++ src/lib/libusb/libusb10_io.c 2009-07-16 15:07:11.000000000 +0200 @@ -24,7 +23,6 @@ * SUCH DAMAGE. */ -#include #include #include #include @@ -32,6 +30,7 @@ #include #include #include +#include #include "libusb20.h" #include "libusb20_desc.h" @@ -39,739 +38,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; - - found = 0; - pthread_mutex_lock(&ctx->pollfds_lock); - - TAILQ_FOREACH(pollfd, &ctx->pollfds, list) { - if (pollfd->pollfd.fd == fd) { - found = 1; - break ; - } - } + if (ctx == NULL) + return; /* invalid */ - if (found == 0) { - pthread_mutex_unlock(&ctx->pollfds_lock); - return ; - } + if (pollfd->entry.tqe_prev == NULL) + return; /* already dequeued */ - 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); + ctx->fd_removed_cb(pollfd->pollfd.fd, ctx->fd_cb_user_data); } -UNEXPORTED void -usb_handle_transfer_completion(struct usb_transfer *uxfer, - enum libusb_transfer_status status) +/* This function must be called locked */ + +static int +libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) { - libusb_transfer *xfer; - libusb_context *ctx; - int len; + struct libusb_device *dev; + struct libusb20_device **ppdev; + struct libusb_super_pollfd *pfd; + struct pollfd *fds; + struct libusb_super_transfer *sxfer; + struct libusb_transfer *uxfer; + nfds_t nfds; + int timeout; + int i; + int err; - 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; - } - } + DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb10_handle_events_sub enter"); - xfer->status = status; - xfer->actual_length = uxfer->transferred; + nfds = 0; + i = 0; + TAILQ_FOREACH(pfd, &ctx->pollfds, entry) + nfds++; - if (xfer->callback) - xfer->callback(xfer); - if (xfer->flags & LIBUSB_TRANSFER_FREE_TRANSFER) - libusb_free_transfer(xfer); + fds = alloca(sizeof(*fds) * nfds); + if (fds == NULL) + return (LIBUSB_ERROR_NO_MEM); - pthread_mutex_lock(&ctx->event_waiters_lock); - pthread_cond_broadcast(&ctx->event_waiters_cond); - pthread_mutex_unlock(&ctx->event_waiters_lock); -} + ppdev = alloca(sizeof(*ppdev) * nfds); + if (ppdev == NULL) + return (LIBUSB_ERROR_NO_MEM); -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; + 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++; + } - ctx = devh->dev->ctx; + if (tv == NULL) + timeout = -1; + else + timeout = (tv->tv_sec * 1000) + ((tv->tv_usec + 999) / 1000); - 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 ; + 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); } } - pthread_mutex_unlock(&ctx->flying_transfers_lock); - - if (to_cancel == NULL) - break ; - - usb_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE); + goto do_done; } - return ; -} - -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; - } - - return (0); -} + for (i = 0; i != nfds; i++) { + if (fds[i].revents == 0) + continue; + if (ppdev[i] != NULL) { + dev = libusb_get_device(ppdev[i]); -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; + 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); - GET_CONTEXT(ctx); - ret = 0; + } else { + uint8_t dummy; - 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); + while (1) { + if (read(fds[i].fd, &dummy, 1) != 1) + break; + } + } } -out: - pthread_mutex_unlock(&ctx->flying_transfers_lock); - return (ret); -} -UNEXPORTED int -handle_events(struct libusb_context *ctx, struct timeval *tv) -{ - struct libusb_pollfd *tmppollfd; - struct libusb_device_handle *devh; - struct usb_pollfd *ipollfd; - struct pollfd *fds; - struct pollfd *tfds; - nfds_t nfds; - int tmpfd; - int ret; - int timeout; - int i; - - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "handle_events enter"); + err = 0; - nfds = 0; - i = -1; +do_done: - pthread_mutex_lock(&ctx->pollfds_lock); - TAILQ_FOREACH(ipollfd, &ctx->pollfds, list) - nfds++; + /* Do all done callbacks */ - fds = alloca(sizeof(*fds) * nfds); - if (fds == NULL) - return (LIBUSB_ERROR_NO_MEM); + while ((sxfer = TAILQ_FIRST(&ctx->tr_done))) { + TAILQ_REMOVE(&ctx->tr_done, sxfer, entry); + sxfer->entry.tqe_prev = NULL; - TAILQ_FOREACH(ipollfd, &ctx->pollfds, list) { - tmppollfd = &ipollfd->pollfd; - tmpfd = tmppollfd->fd; - i++; - fds[i].fd = tmpfd; - fds[i].events = tmppollfd->events; - fds[i].revents = 0; - } + ctx->tr_done_ref++; - pthread_mutex_unlock(&ctx->pollfds_lock); + CTX_UNLOCK(ctx); - 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 (fds[0].revents) { - if (ret == 1){ - ret = 0; - goto handled; - } else { - fds[0].revents = 0; - ret--; - } - } + uxfer = (struct libusb_transfer *)( + ((uint8_t *)sxfer) + sizeof(*sxfer)); - pthread_mutex_lock(&ctx->open_devs_lock); - for (i = 0, devh = NULL ; i < nfds && ret > 0 ; i++) { + if (uxfer->callback != NULL) + (uxfer->callback) (uxfer); - tfds = &fds[i]; - if (!tfds->revents) - continue; + if (uxfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) + free(uxfer->buffer); - ret--; - TAILQ_FOREACH(devh, &ctx->open_devs, list) { - if (libusb20_dev_get_fd(devh->os_priv) == tfds->fd) - break ; - } + if (uxfer->flags & LIBUSB_TRANSFER_FREE_TRANSFER) + libusb_free_transfer(uxfer); - if (tfds->revents & POLLERR) { - usb_remove_pollfd(ctx, libusb20_dev_get_fd(devh->os_priv)); - if (devh != NULL) - usb_handle_disconnect(devh); - continue ; - } + CTX_LOCK(ctx); + + ctx->tr_done_ref--; + ctx->tr_done_gen++; + } + /* Wakeup other waiters */ + pthread_cond_broadcast(&ctx->ctx_cond); - 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 (ret == 0 || ret == LIBUSB20_ERROR_NO_DEVICE) - continue; - else if (ret < 0) - goto out; - } - - ret = 0; -out: - pthread_mutex_unlock(&ctx->open_devs_lock); - -handled: - 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"); - - pthread_mutex_lock(&ctx->pollfd_modify_lock); - ret = ctx->pollfd_modify; - pthread_mutex_unlock(&ctx->pollfd_modify_lock); + int err; - 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_event_waiters(ctx); - if (libusb_event_handler_active(ctx) == 0) { - libusb_unlock_event_waiters(ctx); - goto retry; - } - - ret = libusb_wait_for_event(ctx, &poll_timeout); - libusb_unlock_event_waiters(ctx); - - if (ret < 0) - return ret; - else if (ret == 1) - return (handle_timeouts(ctx)); + libusb_lock_events(ctx); + + err = libusb_handle_events_locked(ctx, tv); + + 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; + struct LIBUSB20_CONTROL_SETUP_DECODED req; + int err; + uint16_t actlen; - ctx = devh->dev->ctx; - GET_CONTEXT(ctx); - DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_control_transfer enter"); + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); - if (devh == NULL || data == NULL) - return (LIBUSB_ERROR_NO_MEM); + if ((wLength != 0) && (data == NULL)) + return (LIBUSB_ERROR_INVALID_PARAM); - xfer = libusb_alloc_transfer(0); - if (xfer == NULL) - return (LIBUSB_ERROR_NO_MEM); + LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); - buff = malloc(sizeof(libusb_control_setup) + wLength); - if (buff == NULL) { - libusb_free_transfer(xfer); - return (LIBUSB_ERROR_NO_MEM); - } + req.bmRequestType = bmRequestType; + req.bRequest = bRequest; + req.wValue = wValue; + req.wIndex = wIndex; + req.wLength = wLength; - 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); + err = libusb20_dev_request_sync(devh, &req, data, + &actlen, timeout, 0); - xfer->dev_handle = devh; - xfer->endpoint = 0; - xfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; - xfer->timeout = timeout; - xfer->buffer = buff; - xfer->length = sizeof(libusb_control_setup) + wLength; - xfer->user_data = &complet; - xfer->callback = ctrl_tr_cb; - xfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER; - complet = 0; + if (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); - if ((ret = libusb_submit_transfer(xfer)) < 0) { - libusb_free_transfer(xfer); - return (ret); - } - - while (complet == 0) - if ((ret = libusb_handle_events(ctx)) < 0) { - libusb_cancel_transfer(xfer); - while (complet == 0) - if (libusb_handle_events(ctx) < 0) { - break; - } - libusb_free_transfer(xfer); - return (ret); - } + 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 +495,42 @@ 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 +538,35 @@ } 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 -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb20.3 src/lib/libusb/libusb20.3 --- src/lib/libusb/libusb20.3 2009-06-30 15:48:20.000000000 +0200 +++ src/lib/libusb/libusb20.3 2009-07-13 11:18:13.000000000 +0200 @@ -310,8 +310,13 @@ .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 -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb20_compat01.c src/lib/libusb/libusb20_compat01.c --- src/lib/libusb/libusb20_compat01.c 2009-06-30 15:48:21.000000000 +0200 +++ src/lib/libusb/libusb20_compat01.c 2009-07-13 23:52:17.000000000 +0200 @@ -835,7 +835,7 @@ /* 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 @@ } /* 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 @@ pdev = (void *)dev; - return (pdev->priv01Data); + return (pdev->privLuData); } struct usb_bus * diff -exclude=compile -u -r -I'$\FreeBSD' src/lib/libusb/libusb20_int.h src/lib/libusb/libusb20_int.h --- src/lib/libusb/libusb20_int.h 2009-06-30 15:48:20.000000000 +0200 +++ src/lib/libusb/libusb20_int.h 2009-07-13 23:52:17.000000000 +0200 @@ -191,8 +191,8 @@ /* 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; diff -exclude=compile -u -r -I'$\FreeBSD' src/share/man/man4/u3g.4 src/share/man/man4/u3g.4 --- src/share/man/man4/u3g.4 2009-05-04 19:55:18.000000000 +0200 +++ src/share/man/man4/u3g.4 2009-07-15 23:02:29.000000000 +0200 @@ -64,7 +64,7 @@ .It Qualcomm Inc. CDMA MSM .It -Huawei B190, E220 ('') +Huawei B190, E180v, E220 ('') .It Novatel U740, MC950D, X950D, etc. .It diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/compat/linsysfs/linsysfs.c src/sys/compat/linsysfs/linsysfs.c --- src/sys/compat/linsysfs/linsysfs.c 2008-12-29 18:22:21.000000000 +0100 +++ src/sys/compat/linsysfs/linsysfs.c 2008-05-09 17:40:46.000000000 +0200 @@ -214,10 +214,12 @@ } } - device_get_children(dev, &children, &nchildren); - for (i = 0; i < nchildren; i++) { + if (!device_get_children(dev, &children, &nchildren)) { + for (i = 0; i < nchildren; i++) { if (children[i]) linsysfs_run_bus(children[i], dir, scsi, new_path, prefix); + } + free(children, M_TEMP); } if (new_path != path) free(new_path, M_TEMP); diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/sound/usb/uaudio.c src/sys/dev/sound/usb/uaudio.c --- src/sys/dev/sound/usb/uaudio.c 2009-07-26 15:52:39.000000000 +0200 +++ src/sys/dev/sound/usb/uaudio.c 2009-07-16 15:07:18.000000000 +0200 @@ -108,6 +108,7 @@ #define UAUDIO_MINFRAMES 16 /* must be factor of 8 due HS-USB */ #define UAUDIO_NCHANBUFS 2 /* number of outstanding request */ #define UAUDIO_RECURSE_LIMIT 24 /* rounds */ +#define UAUDIO_MINFRAMES_ALIGN(x) ((x) & ~(UAUDIO_MINFRAMES - 1)) #define MAKE_WORD(h,l) (((h) << 8) | (l)) #define BIT_TEST(bm,bno) (((bm)[(bno) / 8] >> (7 - ((bno) % 8))) & 1) @@ -1113,6 +1114,41 @@ } } +/* + * The following function sets up data size and block count for the + * next audio transfer. + */ +static void +uaudio_setup_blockcount(struct uaudio_chan *ch, usb_frcount_t max_frames, + uint32_t *total, uint32_t *blockcount) +{ + uint32_t temp; + uint32_t isiz; + + /* allow dynamic sizing of play buffer */ + isiz = ch->intr_size; + + /* allow dynamic sizing of play buffer */ + temp = isiz / ch->bytes_per_frame; + + /* align units */ + temp = UAUDIO_MINFRAMES_ALIGN(temp); + + /* range check - min */ + if (temp == 0) + temp = UAUDIO_MINFRAMES; + + /* range check - max */ + if (temp > max_frames) + temp = max_frames; + + /* store blockcount */ + *blockcount = temp; + + /* compute the total length */ + *total = temp * ch->bytes_per_frame; +} + static void uaudio_chan_play_callback(struct usb_xfer *xfer, usb_error_t error) { @@ -1126,25 +1162,8 @@ usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); - /* allow dynamic sizing of play buffer */ - total = ch->intr_size; - - /* allow dynamic sizing of play buffer */ - blockcount = total / ch->bytes_per_frame; - - /* align units */ - blockcount -= (blockcount % UAUDIO_MINFRAMES); - - /* range check - min */ - if (blockcount == 0) { - blockcount = UAUDIO_MINFRAMES; - } - /* range check - max */ - if (blockcount > usbd_xfer_max_frames(xfer)) { - blockcount = usbd_xfer_max_frames(xfer); - } - /* compute the total length */ - total = blockcount * ch->bytes_per_frame; + uaudio_setup_blockcount(ch, usbd_xfer_max_frames(xfer), + &total, &blockcount); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: @@ -1221,25 +1240,8 @@ usbd_xfer_status(xfer, &actlen, NULL, NULL, &nframes); - /* allow dynamic sizing of play buffer */ - total = ch->intr_size; - - /* allow dynamic sizing of play buffer */ - blockcount = total / ch->bytes_per_frame; - - /* align units */ - blockcount -= (blockcount % UAUDIO_MINFRAMES); - - /* range check - min */ - if (blockcount == 0) { - blockcount = UAUDIO_MINFRAMES; - } - /* range check - max */ - if (blockcount > usbd_xfer_max_frames(xfer)) { - blockcount = usbd_xfer_max_frames(xfer); - } - /* compute the total length */ - total = blockcount * ch->bytes_per_frame; + uaudio_setup_blockcount(ch, usbd_xfer_max_frames(xfer), + &total, &blockcount); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/at91dci.c src/sys/dev/usb/controller/at91dci.c --- src/sys/dev/usb/controller/at91dci.c 2009-06-30 15:50:34.000000000 +0200 +++ src/sys/dev/usb/controller/at91dci.c 2009-07-15 22:31:31.000000000 +0200 @@ -2326,4 +2326,5 @@ .set_stall = &at91dci_set_stall, .clear_stall = &at91dci_clear_stall, .roothub_exec = &at91dci_roothub_exec, + .xfer_poll = &at91dci_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/atmegadci.c src/sys/dev/usb/controller/atmegadci.c --- src/sys/dev/usb/controller/atmegadci.c 2009-06-30 15:50:35.000000000 +0200 +++ src/sys/dev/usb/controller/atmegadci.c 2009-07-15 22:31:32.000000000 +0200 @@ -2143,4 +2143,5 @@ .set_stall = &atmegadci_set_stall, .clear_stall = &atmegadci_clear_stall, .roothub_exec = &atmegadci_roothub_exec, + .xfer_poll = &atmegadci_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/avr32dci.c src/sys/dev/usb/controller/avr32dci.c --- src/sys/dev/usb/controller/avr32dci.c 2009-06-30 15:50:35.000000000 +0200 +++ src/sys/dev/usb/controller/avr32dci.c 2009-07-15 22:31:32.000000000 +0200 @@ -2081,4 +2081,5 @@ .set_stall = &avr32dci_set_stall, .clear_stall = &avr32dci_clear_stall, .roothub_exec = &avr32dci_roothub_exec, + .xfer_poll = &avr32dci_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/ehci.c src/sys/dev/usb/controller/ehci.c --- src/sys/dev/usb/controller/ehci.c 2009-06-30 15:50:35.000000000 +0200 +++ src/sys/dev/usb/controller/ehci.c 2009-07-15 22:31:32.000000000 +0200 @@ -3828,4 +3828,5 @@ .device_suspend = ehci_device_suspend, .set_hw_power = ehci_set_hw_power, .roothub_exec = ehci_roothub_exec, + .xfer_poll = ehci_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/ehci_pci.c src/sys/dev/usb/controller/ehci_pci.c --- src/sys/dev/usb/controller/ehci_pci.c 2009-06-30 15:50:35.000000000 +0200 +++ src/sys/dev/usb/controller/ehci_pci.c 2009-07-08 10:59:33.000000000 +0200 @@ -206,6 +206,8 @@ return "NVIDIA nForce3 250 USB 2.0 controller"; case 0x005b10de: return "NVIDIA nForce4 USB 2.0 controller"; + case 0x03f210de: + return "NVIDIA nForce MCP61 USB 2.0 controller"; case 0x15621131: return "Philips ISP156x USB 2.0 controller"; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/musb_otg.c src/sys/dev/usb/controller/musb_otg.c --- src/sys/dev/usb/controller/musb_otg.c 2009-06-30 15:50:35.000000000 +0200 +++ src/sys/dev/usb/controller/musb_otg.c 2009-07-15 22:31:32.000000000 +0200 @@ -2736,4 +2736,5 @@ .set_stall = &musbotg_set_stall, .clear_stall = &musbotg_clear_stall, .roothub_exec = &musbotg_roothub_exec, + .xfer_poll = &musbotg_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/ohci.c src/sys/dev/usb/controller/ohci.c --- src/sys/dev/usb/controller/ohci.c 2009-06-30 15:50:36.000000000 +0200 +++ src/sys/dev/usb/controller/ohci.c 2009-07-15 22:31:32.000000000 +0200 @@ -2756,4 +2756,5 @@ .device_suspend = ohci_device_suspend, .set_hw_power = ohci_set_hw_power, .roothub_exec = ohci_roothub_exec, + .xfer_poll = ohci_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/ohci_pci.c src/sys/dev/usb/controller/ohci_pci.c --- src/sys/dev/usb/controller/ohci_pci.c 2009-06-30 15:50:35.000000000 +0200 +++ src/sys/dev/usb/controller/ohci_pci.c 2009-07-08 10:59:33.000000000 +0200 @@ -172,6 +172,9 @@ case 0x00d710de: return ("nVidia nForce3 USB Controller"); + case 0x03f110de: + return ("nVidia nForce MCP61 USB Controller"); + case 0x70011039: return ("SiS 5571 USB controller"); diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/uhci.c src/sys/dev/usb/controller/uhci.c --- src/sys/dev/usb/controller/uhci.c 2009-06-30 15:50:36.000000000 +0200 +++ src/sys/dev/usb/controller/uhci.c 2009-07-15 22:31:32.000000000 +0200 @@ -3251,4 +3251,5 @@ .device_suspend = uhci_device_suspend, .set_hw_power = uhci_set_hw_power, .roothub_exec = uhci_roothub_exec, + .xfer_poll = uhci_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/uhci_pci.c src/sys/dev/usb/controller/uhci_pci.c --- src/sys/dev/usb/controller/uhci_pci.c 2009-06-30 15:50:36.000000000 +0200 +++ src/sys/dev/usb/controller/uhci_pci.c 2009-07-08 10:59:33.000000000 +0200 @@ -200,6 +200,15 @@ case 0x265b8086: return ("Intel 82801FB/FR/FW/FRW (ICH6) USB controller USB-D"); + case 0x27c88086: + return ("Intel 82801G (ICH7) USB controller USB-A"); + case 0x27c98086: + return ("Intel 82801G (ICH7) USB controller USB-B"); + case 0x27ca8086: + return ("Intel 82801G (ICH7) USB controller USB-C"); + case 0x27cb8086: + return ("Intel 82801G (ICH7) USB controller USB-D"); + case 0x28308086: return ("Intel 82801H (ICH8) USB controller USB-A"); case 0x28318086: diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/usb_controller.c src/sys/dev/usb/controller/usb_controller.c --- src/sys/dev/usb/controller/usb_controller.c 2009-06-30 15:50:36.000000000 +0200 +++ src/sys/dev/usb/controller/usb_controller.c 2009-07-28 16:30:49.000000000 +0200 @@ -79,6 +79,11 @@ "Debug level"); #endif +static int usb_no_boot_wait = 0; +TUNABLE_INT("hw.usb.no_boot_wait", &usb_no_boot_wait); +SYSCTL_INT(_hw_usb, OID_AUTO, no_boot_wait, CTLFLAG_RDTUN, &usb_no_boot_wait, 0, + "No device enumerate waiting at boot."); + static uint8_t usb_post_init_called = 0; static devclass_t usb_devclass; @@ -132,8 +137,10 @@ return (ENXIO); } - /* delay vfs_mountroot until the bus is explored */ - bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); + if (usb_no_boot_wait == 0) { + /* delay vfs_mountroot until the bus is explored */ + bus->bus_roothold = root_mount_hold(device_get_nameunit(dev)); + } if (usb_post_init_called) { mtx_lock(&Giant); diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/controller/uss820dci.c src/sys/dev/usb/controller/uss820dci.c --- src/sys/dev/usb/controller/uss820dci.c 2009-06-30 15:50:36.000000000 +0200 +++ src/sys/dev/usb/controller/uss820dci.c 2009-07-15 22:31:32.000000000 +0200 @@ -2360,4 +2360,5 @@ .set_stall = &uss820dci_set_stall, .clear_stall = &uss820dci_clear_stall, .roothub_exec = &uss820dci_roothub_exec, + .xfer_poll = &uss820dci_do_poll, }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/input/ukbd.c src/sys/dev/usb/input/ukbd.c --- src/sys/dev/usb/input/ukbd.c 2009-06-30 15:50:37.000000000 +0200 +++ src/sys/dev/usb/input/ukbd.c 2009-07-26 17:49:41.000000000 +0200 @@ -299,6 +299,28 @@ } } +static void +ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait) +{ + DPRINTFN(2, "polling\n"); + + while (sc->sc_inputs == 0) { + + usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER); + + DELAY(1000); /* delay 1 ms */ + + sc->sc_time_ms++; + + /* support repetition of keys: */ + + ukbd_interrupt(sc); + + if (!wait) + break; + } +} + static int32_t ukbd_get_key(struct ukbd_softc *sc, uint8_t wait) { @@ -311,24 +333,7 @@ usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT]); } if (sc->sc_flags & UKBD_FLAG_POLLING) { - DPRINTFN(2, "polling\n"); - - while (sc->sc_inputs == 0) { - - usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER); - - DELAY(1000); /* delay 1 ms */ - - sc->sc_time_ms++; - - /* support repetition of keys: */ - - ukbd_interrupt(sc); - - if (!wait) { - break; - } - } + ukbd_do_poll(sc, wait); } if (sc->sc_inputs == 0) { c = -1; @@ -706,7 +711,15 @@ if (error) return (ENXIO); + /* + * NOTE: we currently don't support USB mouse and USB keyboard + * on the same USB endpoint. + */ if (hid_is_collection(d_ptr, d_len, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) { + /* most likely a mouse */ + error = ENXIO; + } else if (hid_is_collection(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) { if (usb_test_quirk(uaa, UQ_KBD_IGNORE)) error = ENXIO; @@ -983,6 +996,14 @@ static int ukbd_enable(keyboard_t *kbd) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_enable(kbd); + mtx_unlock(&Giant); + return (retval); + } mtx_assert(&Giant, MA_OWNED); KBD_ACTIVATE(kbd); return (0); @@ -992,6 +1013,14 @@ static int ukbd_disable(keyboard_t *kbd) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_disable(kbd); + mtx_unlock(&Giant); + return (retval); + } mtx_assert(&Giant, MA_OWNED); KBD_DEACTIVATE(kbd); return (0); @@ -1003,14 +1032,26 @@ { struct ukbd_softc *sc = kbd->kb_data; - if (!mtx_owned(&Giant)) { - return (0); /* XXX */ + if (!KBD_IS_ACTIVE(kbd)) + return (0); + + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_check(kbd); + mtx_unlock(&Giant); + return (retval); + } + ukbd_do_poll(sc, 0); + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (0); } mtx_assert(&Giant, MA_OWNED); - if (!KBD_IS_ACTIVE(kbd)) { - return (0); - } #ifdef UKBD_EMULATE_ATSCANCODE if (sc->sc_buffered_char[0]) { return (1); @@ -1028,14 +1069,25 @@ { struct ukbd_softc *sc = kbd->kb_data; - if (!mtx_owned(&Giant)) { - return (0); /* XXX */ + if (!KBD_IS_ACTIVE(kbd)) + return (0); + + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_check_char(kbd); + mtx_unlock(&Giant); + return (retval); + } + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (0); } mtx_assert(&Giant, MA_OWNED); - if (!KBD_IS_ACTIVE(kbd)) { - return (0); - } if ((sc->sc_composed_char > 0) && (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { return (1); @@ -1056,9 +1108,22 @@ uint32_t scancode; #endif + if (!KBD_IS_ACTIVE(kbd)) + return (-1); - if (!mtx_owned(&Giant)) { - return -1; /* XXX */ + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_read(kbd, wait); + mtx_unlock(&Giant); + return (retval); + } + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (-1); } mtx_assert(&Giant, MA_OWNED); @@ -1077,9 +1142,9 @@ /* XXX */ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1); - if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) { - return -1; - } + if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) + return (-1); + ++(kbd->kb_count); #ifdef UKBD_EMULATE_ATSCANCODE @@ -1107,8 +1172,23 @@ uint32_t scancode; #endif - if (!mtx_owned(&Giant)) { - return (NOKEY); /* XXX */ + + if (!KBD_IS_ACTIVE(kbd)) + return (NOKEY); + + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_read_char(kbd, wait); + mtx_unlock(&Giant); + return (retval); + } + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (NOKEY); } mtx_assert(&Giant, MA_OWNED); @@ -1485,7 +1565,12 @@ struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { - return (0); /* XXX */ + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_poll(kbd, on); + mtx_unlock(&Giant); + return (retval); } mtx_assert(&Giant, MA_OWNED); diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/input/ums.c src/sys/dev/usb/input/ums.c --- src/sys/dev/usb/input/ums.c 2009-06-30 15:50:37.000000000 +0200 +++ src/sys/dev/usb/input/ums.c 2009-07-26 20:49:12.000000000 +0200 @@ -140,6 +140,8 @@ struct usb_xfer *sc_xfer[UMS_N_TRANSFER]; + int sc_pollrate; + uint8_t sc_buttons; uint8_t sc_iid; uint8_t sc_temp[64]; @@ -188,6 +190,7 @@ struct usb_page_cache *pc; uint8_t *buf = sc->sc_temp; int32_t buttons = 0; + int32_t buttons_found = 0; int32_t dw = 0; int32_t dx = 0; int32_t dy = 0; @@ -263,16 +266,24 @@ dt -= hid_get_data(buf, len, &info->sc_loc_t); for (i = 0; i < info->sc_buttons; i++) { + uint32_t mask; + mask = 1UL << UMS_BUT(i); + /* check for correct button ID */ if (id != info->sc_iid_btn[i]) continue; - if (hid_get_data(buf, len, &info->sc_loc_btn[i])) { - buttons |= (1 << UMS_BUT(i)); - } + /* check for button pressed */ + if (hid_get_data(buf, len, &info->sc_loc_btn[i])) + buttons |= mask; + /* register button mask */ + buttons_found |= mask; } if (++info != &sc->sc_info[UMS_INFO_MAX]) goto repeat; + /* keep old button value(s) for non-detected buttons */ + buttons |= sc->sc_status.button & ~buttons_found; + if (dx || dy || dz || dt || dw || (buttons != sc->sc_status.button)) { @@ -514,6 +525,8 @@ DPRINTF("error=%s\n", usbd_errstr(err)); goto detach; } + + /* Get HID descriptor */ err = usbd_req_get_hid_desc(uaa->device, NULL, &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex); @@ -531,6 +544,9 @@ * it has two addional buttons and a tilt wheel. */ if (usb_test_quirk(uaa, UQ_MS_BAD_CLASS)) { + + sc->sc_iid = 0; + info = &sc->sc_info[0]; info->sc_flags = (UMS_FLAG_X_AXIS | UMS_FLAG_Y_AXIS | @@ -540,11 +556,17 @@ isize = 5; /* 1st byte of descriptor report contains garbage */ info->sc_loc_x.pos = 16; + info->sc_loc_x.size = 8; info->sc_loc_y.pos = 24; + info->sc_loc_y.size = 8; info->sc_loc_z.pos = 32; + info->sc_loc_z.size = 8; info->sc_loc_btn[0].pos = 8; + info->sc_loc_btn[0].size = 1; info->sc_loc_btn[1].pos = 9; + info->sc_loc_btn[1].size = 1; info->sc_loc_btn[2].pos = 10; + info->sc_loc_btn[2].size = 1; /* Announce device */ device_printf(dev, "3 buttons and [XYZ] " @@ -653,6 +675,23 @@ ums_start_read(struct usb_fifo *fifo) { struct ums_softc *sc = usb_fifo_softc(fifo); + int rate; + + /* Check if we should override the default polling interval */ + rate = sc->sc_pollrate; + /* Range check rate */ + if (rate > 1000) + rate = 1000; + /* Check for set rate */ + if ((rate > 0) && (sc->sc_xfer[UMS_INTR_DT] != NULL)) { + DPRINTF("Setting pollrate = %d\n", rate); + /* Stop current transfer, if any */ + usbd_transfer_stop(sc->sc_xfer[UMS_INTR_DT]); + /* Set new interval */ + usbd_xfer_set_interval(sc->sc_xfer[UMS_INTR_DT], 1000 / rate); + /* Only set pollrate once */ + sc->sc_pollrate = 0; + } usbd_transfer_start(sc->sc_xfer[UMS_INTR_DT]); } @@ -791,6 +830,9 @@ sc->sc_mode.level = mode.level; } + /* store polling rate */ + sc->sc_pollrate = mode.rate; + if (sc->sc_mode.level == 0) { if (sc->sc_buttons > MOUSE_MSC_MAXBUTTON) sc->sc_hw.buttons = MOUSE_MSC_MAXBUTTON; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/serial/u3g.c src/sys/dev/usb/serial/u3g.c --- src/sys/dev/usb/serial/u3g.c 2009-06-30 15:50:32.000000000 +0200 +++ src/sys/dev/usb/serial/u3g.c 2009-07-15 23:02:29.000000000 +0200 @@ -186,6 +186,7 @@ U3G_DEV(QUALCOMMINC, CDMA_MSM, U3GFL_SCSI_EJECT), /* OEM: Huawei */ U3G_DEV(HUAWEI, MOBILE, U3GFL_HUAWEI_INIT), + U3G_DEV(HUAWEI, E180V, U3GFL_HUAWEI_INIT), U3G_DEV(HUAWEI, E220, U3GFL_HUAWEI_INIT), /* OEM: Novatel */ U3G_DEV(NOVATEL, CDMA_MODEM, 0), diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/serial/ulpt.c src/sys/dev/usb/serial/ulpt.c --- src/sys/dev/usb/serial/ulpt.c 2009-06-30 15:50:33.000000000 +0200 +++ src/sys/dev/usb/serial/ulpt.c 2009-07-16 22:14:15.000000000 +0200 @@ -426,6 +426,9 @@ /* we assume that open is a serial process */ if (sc->sc_fflags == 0) { + + /* reset USB paralell port */ + ulpt_reset(sc); } return (unlpt_open(fifo, fflags)); @@ -720,7 +723,12 @@ mtx_assert(&sc->sc_mtx, MA_OWNED); - usbd_transfer_start(sc->sc_xfer[ULPT_INTR_DT_RD]); + /* + * Only read status while the device is not opened, due to + * possible hardware or firmware bug in some printers. + */ + if (sc->sc_fflags == 0) + usbd_transfer_start(sc->sc_xfer[ULPT_INTR_DT_RD]); usb_callout_reset(&sc->sc_watchdog, hz, &ulpt_watchdog, sc); diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_compat_linux.c src/sys/dev/usb/usb_compat_linux.c --- src/sys/dev/usb/usb_compat_linux.c 2009-06-30 15:50:41.000000000 +0200 +++ src/sys/dev/usb/usb_compat_linux.c 2009-07-25 18:04:33.000000000 +0200 @@ -398,15 +398,32 @@ usb_submit_urb(struct urb *urb, uint16_t mem_flags) { struct usb_host_endpoint *uhe; + uint8_t do_unlock; + int err; - if (urb == NULL) { + if (urb == NULL) return (-EINVAL); - } - mtx_assert(&Giant, MA_OWNED); + + do_unlock = mtx_owned(&Giant) ? 0 : 1; + if (do_unlock) + mtx_lock(&Giant); if (urb->endpoint == NULL) { - return (-EINVAL); + err = -EINVAL; + goto done; } + + /* + * Check to see if the urb is in the process of being killed + * and stop a urb that is in the process of being killed from + * being re-submitted (e.g. from its completion callback + * function). + */ + if (urb->kill_count != 0) { + err = -EPERM; + goto done; + } + uhe = urb->endpoint; /* @@ -424,12 +441,16 @@ usbd_transfer_start(uhe->bsd_xfer[0]); usbd_transfer_start(uhe->bsd_xfer[1]); + err = 0; } else { /* no pipes have been setup yet! */ urb->status = -EINVAL; - return (-EINVAL); + err = -EINVAL; } - return (0); +done: + if (do_unlock) + mtx_unlock(&Giant); + return (err); } /*------------------------------------------------------------------------* @@ -448,9 +469,11 @@ usb_unlink_bsd(struct usb_xfer *xfer, struct urb *urb, uint8_t drain) { - if (xfer && - usbd_transfer_pending(xfer) && - (xfer->priv_fifo == (void *)urb)) { + if (xfer == NULL) + return; + if (!usbd_transfer_pending(xfer)) + return; + if (xfer->priv_fifo == (void *)urb) { if (drain) { mtx_unlock(&Giant); usbd_transfer_drain(xfer); @@ -467,14 +490,21 @@ { struct usb_host_endpoint *uhe; uint16_t x; + uint8_t do_unlock; + int err; - if (urb == NULL) { + if (urb == NULL) return (-EINVAL); - } - mtx_assert(&Giant, MA_OWNED); + + do_unlock = mtx_owned(&Giant) ? 0 : 1; + if (do_unlock) + mtx_lock(&Giant); + if (drain) + urb->kill_count++; if (urb->endpoint == NULL) { - return (-EINVAL); + err = -EINVAL; + goto done; } uhe = urb->endpoint; @@ -504,7 +534,13 @@ usb_unlink_bsd(uhe->bsd_xfer[0], urb, drain); usb_unlink_bsd(uhe->bsd_xfer[1], urb, drain); } - return (0); + err = 0; +done: + if (drain) + urb->kill_count--; + if (do_unlock) + mtx_unlock(&Giant); + return (err); } /*------------------------------------------------------------------------* @@ -555,6 +591,7 @@ usb_start_wait_urb(struct urb *urb, usb_timeout_t timeout, uint16_t *p_actlen) { int err; + uint8_t do_unlock; /* you must have a timeout! */ if (timeout == 0) { @@ -565,6 +602,9 @@ urb->transfer_flags |= URB_WAIT_WAKEUP; urb->transfer_flags &= ~URB_IS_SLEEPING; + do_unlock = mtx_owned(&Giant) ? 0 : 1; + if (do_unlock) + mtx_lock(&Giant); err = usb_submit_urb(urb, 0); if (err) goto done; @@ -582,6 +622,8 @@ err = urb->status; done: + if (do_unlock) + mtx_unlock(&Giant); if (err) { *p_actlen = 0; } else { @@ -638,7 +680,7 @@ * transfers on control endpoint zero: */ err = usbd_do_request_flags(dev, - &Giant, &req, data, USB_SHORT_XFER_OK, + NULL, &req, data, USB_SHORT_XFER_OK, &actlen, timeout); if (err) { err = -EPIPE; @@ -1216,9 +1258,7 @@ void usb_kill_urb(struct urb *urb) { - if (usb_unlink_urb_sub(urb, 1)) { - /* ignore */ - } + usb_unlink_urb_sub(urb, 1); } /*------------------------------------------------------------------------* diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_compat_linux.h src/sys/dev/usb/usb_compat_linux.h --- src/sys/dev/usb/usb_compat_linux.h 2009-06-30 15:50:41.000000000 +0200 +++ src/sys/dev/usb/usb_compat_linux.h 2009-07-25 18:04:33.000000000 +0200 @@ -262,6 +262,7 @@ uint8_t setup_dma; /* (in) not used on FreeBSD */ uint8_t transfer_dma; /* (in) not used on FreeBSD */ uint8_t bsd_isread; + uint8_t kill_count; /* FreeBSD specific */ struct usb_iso_packet_descriptor iso_frame_desc[]; /* (in) ISO ONLY */ }; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_controller.h src/sys/dev/usb/usb_controller.h --- src/sys/dev/usb/usb_controller.h 2009-06-30 15:50:39.000000000 +0200 +++ src/sys/dev/usb/usb_controller.h 2009-07-15 22:31:32.000000000 +0200 @@ -99,6 +99,9 @@ void (*set_stall) (struct usb_device *udev, struct usb_xfer *xfer, struct usb_endpoint *ep, uint8_t *did_stall); void (*clear_stall) (struct usb_device *udev, struct usb_endpoint *ep); + /* Optional transfer polling support */ + + void (*xfer_poll) (struct usb_bus *); }; /* diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_dev.c src/sys/dev/usb/usb_dev.c --- src/sys/dev/usb/usb_dev.c 2009-06-30 15:50:38.000000000 +0200 +++ src/sys/dev/usb/usb_dev.c 2009-07-13 08:42:39.000000000 +0200 @@ -740,6 +740,8 @@ break; } } + /* reset have fragment flag */ + f->flag_have_fragment = 0; } /*------------------------------------------------------------------------* @@ -783,6 +785,16 @@ /* set flushing flag */ f->flag_flushing = 1; + /* get the last packet in */ + if (f->flag_have_fragment) { + struct usb_mbuf *m; + f->flag_have_fragment = 0; + USB_IF_DEQUEUE(&f->free_q, m); + if (m) { + USB_IF_ENQUEUE(&f->used_q, m); + } + } + /* start write transfer, if not already started */ (f->methods->f_start_write) (f); @@ -1303,6 +1315,7 @@ struct usb_cdev_privdata* cpd; struct usb_fifo *f; struct usb_mbuf *m; + uint8_t *pdata; int fflags; int resid; int io_len; @@ -1373,33 +1386,59 @@ } tr_data = 1; - USB_MBUF_RESET(m); - - io_len = MIN(m->cur_data_len, uio->uio_resid); - - m->cur_data_len = io_len; + if (f->flag_have_fragment == 0) { + USB_MBUF_RESET(m); + io_len = m->cur_data_len; + pdata = m->cur_data_ptr; + if (io_len > uio->uio_resid) + io_len = uio->uio_resid; + m->cur_data_len = io_len; + } else { + io_len = m->max_data_len - m->cur_data_len; + pdata = m->cur_data_ptr + m->cur_data_len; + if (io_len > uio->uio_resid) + io_len = uio->uio_resid; + m->cur_data_len += io_len; + } DPRINTFN(2, "transfer %d bytes to %p\n", - io_len, m->cur_data_ptr); + io_len, pdata); - err = usb_fifo_uiomove(f, - m->cur_data_ptr, io_len, uio); + err = usb_fifo_uiomove(f, pdata, io_len, uio); if (err) { + f->flag_have_fragment = 0; USB_IF_ENQUEUE(&f->free_q, m); break; } - if (f->methods->f_filter_write) { + + /* check if the buffer is ready to be transmitted */ + + if ((f->flag_write_defrag == 0) || + (m->cur_data_len == m->max_data_len)) { + f->flag_have_fragment = 0; + /* - * Sometimes it is convenient to process data at the - * expense of a userland process instead of a kernel - * process. + * Check for write filter: + * + * Sometimes it is convenient to process data + * at the expense of a userland process + * instead of a kernel process. */ - (f->methods->f_filter_write) (f, m); - } - USB_IF_ENQUEUE(&f->used_q, m); + if (f->methods->f_filter_write) { + (f->methods->f_filter_write) (f, m); + } - (f->methods->f_start_write) (f); + /* Put USB mbuf in the used queue */ + USB_IF_ENQUEUE(&f->used_q, m); + + /* Start writing data, if not already started */ + (f->methods->f_start_write) (f); + } else { + /* Wait for more data or close */ + f->flag_have_fragment = 1; + USB_IF_PREPEND(&f->free_q, m); + } } while (uio->uio_resid > 0); done: @@ -2220,6 +2259,18 @@ f->flag_short = onoff; } +void +usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff) +{ + if (f == NULL) + return; + + /* defrag written data */ + f->flag_write_defrag = onoff; + /* reset defrag state */ + f->flag_have_fragment = 0; +} + void * usb_fifo_softc(struct usb_fifo *f) { diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_dev.h src/sys/dev/usb/usb_dev.h --- src/sys/dev/usb/usb_dev.h 2009-06-30 15:50:39.000000000 +0200 +++ src/sys/dev/usb/usb_dev.h 2009-07-07 20:35:08.000000000 +0200 @@ -130,6 +130,8 @@ uint8_t flag_short; /* set if short_ok or force_short * transfer flags should be set */ uint8_t flag_stall; /* set if clear stall should be run */ + uint8_t flag_write_defrag; /* set to defrag written data */ + uint8_t flag_have_fragment; /* set if defragging */ uint8_t iface_index; /* set to the interface we belong to */ uint8_t fifo_index; /* set to the FIFO index in "struct * usb_device" */ @@ -144,11 +146,9 @@ int usb_fifo_wait(struct usb_fifo *fifo); void usb_fifo_signal(struct usb_fifo *fifo); uint8_t usb_fifo_opened(struct usb_fifo *fifo); -void usb_fifo_free(struct usb_fifo *f); struct usb_symlink *usb_alloc_symlink(const char *target); void usb_free_symlink(struct usb_symlink *ps); int usb_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len); -void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t); #endif /* _USB_DEV_H_ */ diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_device.c src/sys/dev/usb/usb_device.c --- src/sys/dev/usb/usb_device.c 2009-06-30 15:50:41.000000000 +0200 +++ src/sys/dev/usb/usb_device.c 2009-07-26 17:49:41.000000000 +0200 @@ -833,18 +833,13 @@ err = USB_ERR_INVAL; goto done; } - if (udev->flags.usb_mode == USB_MODE_DEVICE) { - usb_detach_device(udev, iface_index, - USB_UNCFG_FLAG_FREE_SUBDEV); - } else { - if (iface->alt_index == alt_index) { - /* - * Optimise away duplicate setting of - * alternate setting in USB Host Mode! - */ - err = 0; - goto done; - } + if (iface->alt_index == alt_index) { + /* + * Optimise away duplicate setting of + * alternate setting in USB Host Mode! + */ + err = 0; + goto done; } #if USB_HAVE_UGEN /* @@ -858,6 +853,12 @@ if (err) { goto done; } + if (iface->alt_index != alt_index) { + /* the alternate setting does not exist */ + err = USB_ERR_INVAL; + goto done; + } + err = usbd_req_set_alt_interface_no(udev, NULL, iface_index, iface->idesc->bAlternateSetting); @@ -959,7 +960,6 @@ { struct usb_endpoint *ep; struct usb_endpoint *ep_end; - usb_error_t err; ep = udev->endpoints; ep_end = udev->endpoints + udev->endpoints_max; @@ -971,10 +971,7 @@ continue; } /* simulate a clear stall from the peer */ - err = usbd_set_endpoint_stall(udev, ep, 0); - if (err) { - /* just ignore */ - } + usbd_set_endpoint_stall(udev, ep, 0); } return (0); } @@ -1286,6 +1283,7 @@ uaa.info.bIfaceNum = iface->idesc->bInterfaceNumber; uaa.use_generic = 0; + uaa.driver_info = 0; /* reset driver_info */ DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n", uaa.info.bInterfaceClass, @@ -1302,6 +1300,7 @@ /* try generic interface drivers last */ uaa.use_generic = 1; + uaa.driver_info = 0; /* reset driver_info */ if (usb_probe_and_attach_sub(udev, &uaa)) { /* ignore */ @@ -2334,6 +2333,7 @@ "devclass=0x%02x " "devsubclass=0x%02x " "sernum=\"%s\" " + "release=0x%04x " "at " "port=%u " "on " @@ -2345,6 +2345,7 @@ udev->ddesc.bDeviceClass, udev->ddesc.bDeviceSubClass, udev->serial, + UGETW(udev->ddesc.bcdDevice), udev->port_no, udev->parent_hub != NULL ? udev->parent_hub->ugen_name : diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_handle_request.c src/sys/dev/usb/usb_handle_request.c --- src/sys/dev/usb/usb_handle_request.c 2009-06-30 15:50:41.000000000 +0200 +++ src/sys/dev/usb/usb_handle_request.c 2009-07-16 10:27:29.000000000 +0200 @@ -46,6 +46,7 @@ #include #include +#include #include "usb_if.h" #define USB_DEBUG_VAR usb_debug @@ -181,6 +182,30 @@ return (err); } +static usb_error_t +usb_check_alt_setting(struct usb_device *udev, + struct usb_interface *iface, uint8_t alt_index) +{ + uint8_t do_unlock; + usb_error_t err = 0; + + /* automatic locking */ + if (sx_xlocked(udev->default_sx + 1)) { + do_unlock = 0; + } else { + do_unlock = 1; + sx_xlock(udev->default_sx + 1); + } + + if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) + err = USB_ERR_INVAL; + + if (do_unlock) { + sx_unlock(udev->default_sx + 1); + } + return (err); +} + /*------------------------------------------------------------------------* * usb_handle_iface_request * @@ -285,42 +310,29 @@ switch (req.bRequest) { case UR_SET_INTERFACE: /* - * Handle special case. If we have parent interface - * we just reset the endpoints, because this is a - * multi interface device and re-attaching only a - * part of the device is not possible. Also if the - * alternate setting is the same like before we just - * reset the interface endoints. - */ - if ((iface_parent != NULL) || - (iface->alt_index == req.wValue[0])) { - error = usb_reset_iface_endpoints(udev, - iface_index); - if (error) { - DPRINTF("alt setting failed %s\n", - usbd_errstr(error)); - goto tr_stalled; - } - break; - } - /* - * Doing the alternate setting will detach the - * interface aswell: + * We assume that the endpoints are the same + * accross the alternate settings. + * + * Reset the endpoints, because re-attaching + * only a part of the device is not possible. */ - error = usbd_set_alt_interface_index(udev, - iface_index, req.wValue[0]); + error = usb_check_alt_setting(udev, + iface, req.wValue[0]); if (error) { - DPRINTF("alt setting failed %s\n", + DPRINTF("alt setting does not exist %s\n", usbd_errstr(error)); goto tr_stalled; } - error = usb_probe_and_attach(udev, - iface_index); + error = usb_reset_iface_endpoints(udev, iface_index); if (error) { - DPRINTF("alt setting probe failed\n"); + DPRINTF("alt setting failed %s\n", + usbd_errstr(error)); goto tr_stalled; } + /* update the current alternate setting */ + iface->alt_index = req.wValue[0]; break; + default: goto tr_stalled; } diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_hid.c src/sys/dev/usb/usb_hid.c --- src/sys/dev/usb/usb_hid.c 2009-06-30 15:50:37.000000000 +0200 +++ src/sys/dev/usb/usb_hid.c 2009-07-27 22:33:16.000000000 +0200 @@ -78,11 +78,19 @@ #define MAXUSAGE 64 #define MAXPUSH 4 +#define MAXID 16 + +struct hid_pos_data { + int32_t rid; + uint32_t pos; +}; + struct hid_data { const uint8_t *start; const uint8_t *end; const uint8_t *p; struct hid_item cur[MAXPUSH]; + struct hid_pos_data last_pos[MAXID]; int32_t usages_min[MAXUSAGE]; int32_t usages_max[MAXUSAGE]; int32_t usage_last; /* last seen usage */ @@ -119,6 +127,58 @@ c->set_delimiter = 0; } +static void +hid_switch_rid(struct hid_data *s, struct hid_item *c, int32_t next_rID) +{ + uint8_t i; + + /* check for same report ID - optimise */ + + if (c->report_ID == next_rID) + return; + + /* save current position for current rID */ + + if (c->report_ID == 0) { + i = 0; + } else { + for (i = 1; i != MAXID; i++) { + if (s->last_pos[i].rid == c->report_ID) + break; + if (s->last_pos[i].rid == 0) + break; + } + } + if (i != MAXID) { + s->last_pos[i].rid = c->report_ID; + s->last_pos[i].pos = c->loc.pos; + } + + /* store next report ID */ + + c->report_ID = next_rID; + + /* lookup last position for next rID */ + + if (next_rID == 0) { + i = 0; + } else { + for (i = 1; i != MAXID; i++) { + if (s->last_pos[i].rid == next_rID) + break; + if (s->last_pos[i].rid == 0) + break; + } + } + if (i != MAXID) { + s->last_pos[i].rid = next_rID; + c->loc.pos = s->last_pos[i].pos; + } else { + DPRINTF("Out of RID entries, position is set to zero!\n"); + c->loc.pos = 0; + } +} + /*------------------------------------------------------------------------* * hid_start_parse *------------------------------------------------------------------------*/ @@ -373,9 +433,7 @@ s->loc_size = dval & mask; break; case 8: - c->report_ID = dval; - /* new report - reset position */ - c->loc.pos = 0; + hid_switch_rid(s, c, dval); break; case 9: /* mask because value is unsigned */ diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_hub.c src/sys/dev/usb/usb_hub.c --- src/sys/dev/usb/usb_hub.c 2009-06-30 15:50:38.000000000 +0200 +++ src/sys/dev/usb/usb_hub.c 2009-07-18 12:20:38.000000000 +0200 @@ -1020,12 +1020,14 @@ snprintf(buf, buflen, "vendor=0x%04x product=0x%04x " "devclass=0x%02x devsubclass=0x%02x " "sernum=\"%s\" " + "release=0x%04x " "intclass=0x%02x intsubclass=0x%02x", UGETW(res.udev->ddesc.idVendor), UGETW(res.udev->ddesc.idProduct), res.udev->ddesc.bDeviceClass, res.udev->ddesc.bDeviceSubClass, res.udev->serial, + UGETW(res.udev->ddesc.bcdDevice), iface->idesc->bInterfaceClass, iface->idesc->bInterfaceSubClass); } else { diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_parse.c src/sys/dev/usb/usb_parse.c --- src/sys/dev/usb/usb_parse.c 2009-06-30 15:50:37.000000000 +0200 +++ src/sys/dev/usb/usb_parse.c 2009-07-16 20:20:34.000000000 +0200 @@ -215,20 +215,29 @@ * usbd_get_no_alts * * Return value: - * Number of alternate settings for the given interface descriptor pointer. + * Number of alternate settings for the given interface descriptor + * pointer. If the USB descriptor is corrupt, the returned value can + * be greater than the actual number of alternate settings. *------------------------------------------------------------------------*/ uint8_t usbd_get_no_alts(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id) { struct usb_descriptor *desc; - uint8_t n = 0; + uint8_t n; uint8_t ifaceno; + /* Reset interface count */ + + n = 0; + + /* Get the interface number */ + ifaceno = id->bInterfaceNumber; - desc = (struct usb_descriptor *)id; + /* Iterate all the USB descriptors */ + desc = NULL; while ((desc = usb_desc_foreach(cd, desc))) { if ((desc->bDescriptorType == UDESC_INTERFACE) && (desc->bLength >= sizeof(*id))) { @@ -237,8 +246,7 @@ n++; if (n == 0xFF) break; /* crazy */ - } else - break; /* end */ + } } } return (n); diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_request.c src/sys/dev/usb/usb_request.c --- src/sys/dev/usb/usb_request.c 2009-06-30 15:50:40.000000000 +0200 +++ src/sys/dev/usb/usb_request.c 2009-07-22 22:01:50.000000000 +0200 @@ -1059,9 +1059,9 @@ struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; - if ((iface == NULL) || (iface->idesc == NULL)) { + if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); - } + req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); @@ -1085,9 +1085,9 @@ struct usb_interface *iface = usbd_get_iface(udev, iface_index); struct usb_device_request req; - if ((iface == NULL) || (iface->idesc == NULL)) { + if ((iface == NULL) || (iface->idesc == NULL)) return (USB_ERR_INVAL); - } + req.bmRequestType = UT_WRITE_INTERFACE; req.bRequest = UR_SET_INTERFACE; req.wValue[0] = alt_no; diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usb_transfer.c src/sys/dev/usb/usb_transfer.c --- src/sys/dev/usb/usb_transfer.c 2009-06-30 15:50:39.000000000 +0200 +++ src/sys/dev/usb/usb_transfer.c 2009-07-25 11:15:46.000000000 +0200 @@ -2853,15 +2853,99 @@ return (1); /* Clear Stall Finished */ } +/*------------------------------------------------------------------------* + * usbd_transfer_poll + * + * The following function gets called from the USB keyboard driver and + * UMASS when the system has paniced. + * + * NOTE: It is currently not possible to resume normal operation on + * the USB controller which has been polled, due to clearing of the + * "up_dsleep" and "up_msleep" flags. + *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { - static uint8_t once = 0; - /* polling is currently not supported */ - if (!once) { - once = 1; - printf("usbd_transfer_poll: USB polling is " - "not supported!\n"); + struct usb_xfer *xfer; + struct usb_xfer_root *xroot; + struct usb_device *udev; + struct usb_proc_msg *pm; + uint16_t n; + uint16_t drop_bus; + uint16_t drop_xfer; + + for (n = 0; n != max; n++) { + /* Extra checks to avoid panic */ + xfer = ppxfer[n]; + if (xfer == NULL) + continue; /* no USB transfer */ + xroot = xfer->xroot; + if (xroot == NULL) + continue; /* no USB root */ + udev = xroot->udev; + if (udev == NULL) + continue; /* no USB device */ + if (udev->bus == NULL) + continue; /* no BUS structure */ + if (udev->bus->methods == NULL) + continue; /* no BUS methods */ + if (udev->bus->methods->xfer_poll == NULL) + continue; /* no poll method */ + + /* make sure that the BUS mutex is not locked */ + drop_bus = 0; + while (mtx_owned(&xroot->udev->bus->bus_mtx)) { + mtx_unlock(&xroot->udev->bus->bus_mtx); + drop_bus++; + } + + /* make sure that the transfer mutex is not locked */ + drop_xfer = 0; + while (mtx_owned(xroot->xfer_mtx)) { + mtx_unlock(xroot->xfer_mtx); + drop_xfer++; + } + + /* Make sure cv_signal() and cv_broadcast() is not called */ + udev->bus->control_xfer_proc.up_dsleep = 0; + udev->bus->control_xfer_proc.up_msleep = 0; + udev->bus->explore_proc.up_dsleep = 0; + udev->bus->explore_proc.up_msleep = 0; + udev->bus->giant_callback_proc.up_dsleep = 0; + udev->bus->giant_callback_proc.up_msleep = 0; + udev->bus->non_giant_callback_proc.up_dsleep = 0; + udev->bus->non_giant_callback_proc.up_msleep = 0; + + /* poll USB hardware */ + (udev->bus->methods->xfer_poll) (udev->bus); + + USB_BUS_LOCK(xroot->bus); + + /* check for clear stall */ + if (udev->default_xfer[1] != NULL) { + + /* poll clear stall start */ + pm = &udev->cs_msg[0].hdr; + (pm->pm_callback) (pm); + /* poll clear stall done thread */ + pm = &udev->default_xfer[1]-> + xroot->done_m[0].hdr; + (pm->pm_callback) (pm); + } + + /* poll done thread */ + pm = &xroot->done_m[0].hdr; + (pm->pm_callback) (pm); + + USB_BUS_UNLOCK(xroot->bus); + + /* restore transfer mutex */ + while (drop_xfer--) + mtx_lock(xroot->xfer_mtx); + + /* restore BUS mutex */ + while (drop_bus--) + mtx_lock(&xroot->udev->bus->bus_mtx); } } diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usbdevs src/sys/dev/usb/usbdevs --- src/sys/dev/usb/usbdevs 2009-07-28 10:07:02.000000000 +0200 +++ src/sys/dev/usb/usbdevs 2009-07-28 10:12:46.000000000 +0200 @@ -1115,6 +1115,8 @@ product CYPRESS WISPY1A 0x0bad MetaGeek Wi-Spy product CYPRESS KBDHUB 0x0101 Keyboard/Hub product CYPRESS FMRADIO 0x1002 FM Radio +product CYPRESS IKARILASER 0x121f Ikari Laser SteelSeries ApS + product CYPRESS USBRS232 0x5500 USB-RS232 Interface product CYPRESS SLIM_HUB 0x6560 Slim Hub product CYPRESS XX6830XX 0x6830 PATA Storage Device @@ -1475,6 +1477,7 @@ /* HUAWEI products */ product HUAWEI MOBILE 0x1001 Huawei Mobile product HUAWEI E220 0x1003 Huawei HSDPA modem +product HUAWEI E180V 0x140c Huawei Mobile E180V /* HUAWEI 3com products */ product HUAWEI3COM WUB320G 0x0009 Aolynk WUB320g diff -exclude=compile -u -r -I'$\FreeBSD' src/sys/dev/usb/usbdi.h src/sys/dev/usb/usbdi.h --- src/sys/dev/usb/usbdi.h 2009-06-30 15:50:37.000000000 +0200 +++ src/sys/dev/usb/usbdi.h 2009-07-07 20:35:08.000000000 +0200 @@ -531,5 +531,8 @@ void usb_fifo_wakeup(struct usb_fifo *f); void usb_fifo_get_data_error(struct usb_fifo *fifo); void *usb_fifo_softc(struct usb_fifo *fifo); +void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t); +void usb_fifo_set_write_defrag(struct usb_fifo *, uint8_t); +void usb_fifo_free(struct usb_fifo *f); #endif /* _KERNEL */ #endif /* _USB_USBDI_H_ */