Index: head/lib/libusb/libusb10.c =================================================================== --- head/lib/libusb/libusb10.c (revision 234490) +++ head/lib/libusb/libusb10.c (revision 234491) @@ -1,1546 +1,1546 @@ /* $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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #define libusb_device_handle libusb20_device #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" #include "libusb.h" #include "libusb10.h" static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER; struct libusb_context *usbi_default_context = NULL; /* Prototypes */ static struct libusb20_transfer *libusb10_get_transfer(struct libusb20_device *, uint8_t, uint8_t); static int libusb10_get_buffsize(struct libusb20_device *, libusb_transfer *); static int libusb10_convert_error(uint8_t status); static void libusb10_complete_transfer(struct libusb20_transfer *, struct libusb_super_transfer *, int); static void libusb10_isoc_proxy(struct libusb20_transfer *); static void libusb10_bulk_intr_proxy(struct libusb20_transfer *); static void libusb10_ctrl_proxy(struct libusb20_transfer *); static void libusb10_submit_transfer_sub(struct libusb20_device *, uint8_t); /* Library initialisation / deinitialisation */ void libusb_set_debug(libusb_context *ctx, int level) { ctx = GET_CONTEXT(ctx); if (ctx) ctx->debug = level; } static void libusb_set_nonblocking(int f) { int flags; /* * We ignore any failures in this function, hence the * non-blocking flag is not critical to the operation of * libUSB. We use F_GETFL and F_SETFL to be compatible with * Linux. */ flags = fcntl(f, F_GETFL, NULL); if (flags == -1) return; flags |= O_NONBLOCK; fcntl(f, F_SETFL, flags); } int libusb_init(libusb_context **context) { struct libusb_context *ctx; char *debug; int ret; ctx = malloc(sizeof(*ctx)); if (!ctx) return (LIBUSB_ERROR_INVALID_PARAM); memset(ctx, 0, sizeof(*ctx)); debug = getenv("LIBUSB_DEBUG"); if (debug != NULL) { ctx->debug = atoi(debug); if (ctx->debug != 0) ctx->debug_fixed = 1; } TAILQ_INIT(&ctx->pollfds); TAILQ_INIT(&ctx->tr_done); pthread_mutex_init(&ctx->ctx_lock, NULL); pthread_cond_init(&ctx->ctx_cond, NULL); ctx->ctx_handler = NO_THREAD; ret = pipe(ctx->ctrl_pipe); if (ret < 0) { 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 */ libusb_set_nonblocking(ctx->ctrl_pipe[0]); libusb_set_nonblocking(ctx->ctrl_pipe[1]); libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->ctrl_pipe[0], POLLIN); pthread_mutex_lock(&default_context_lock); if (usbi_default_context == NULL) { usbi_default_context = ctx; } pthread_mutex_unlock(&default_context_lock); if (context) *context = ctx; DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_init complete"); return (0); } void libusb_exit(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); if (ctx == NULL) return; /* XXX cleanup devices */ libusb10_remove_pollfd(ctx, &ctx->ctx_poll); close(ctx->ctrl_pipe[0]); close(ctx->ctrl_pipe[1]); pthread_mutex_destroy(&ctx->ctx_lock); pthread_cond_destroy(&ctx->ctx_cond); pthread_mutex_lock(&default_context_lock); if (ctx == usbi_default_context) { usbi_default_context = NULL; } pthread_mutex_unlock(&default_context_lock); free(ctx); } /* Device handling and initialisation. */ ssize_t libusb_get_device_list(libusb_context *ctx, libusb_device ***list) { struct libusb20_backend *usb_backend; struct libusb20_device *pdev; struct libusb_device *dev; int i; ctx = GET_CONTEXT(ctx); if (ctx == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (list == NULL) return (LIBUSB_ERROR_INVALID_PARAM); usb_backend = libusb20_be_alloc_default(); if (usb_backend == NULL) return (LIBUSB_ERROR_NO_MEM); /* figure out how many USB devices are present */ pdev = NULL; i = 0; while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) i++; /* allocate device pointer list */ *list = malloc((i + 1) * sizeof(void *)); if (*list == NULL) { libusb20_be_free(usb_backend); return (LIBUSB_ERROR_NO_MEM); } /* create libusb v1.0 compliant devices */ i = 0; while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { dev = malloc(sizeof(*dev)); if (dev == NULL) { while (i != 0) { libusb_unref_device((*list)[i - 1]); i--; } free(*list); *list = NULL; libusb20_be_free(usb_backend); return (LIBUSB_ERROR_NO_MEM); } /* get device into libUSB v1.0 list */ libusb20_be_dequeue_device(usb_backend, pdev); memset(dev, 0, sizeof(*dev)); /* init transfer queues */ TAILQ_INIT(&dev->tr_head); /* set context we belong to */ dev->ctx = ctx; /* link together the two structures */ dev->os_priv = pdev; pdev->privLuData = dev; (*list)[i] = libusb_ref_device(dev); i++; } (*list)[i] = NULL; libusb20_be_free(usb_backend); return (i); } void libusb_free_device_list(libusb_device **list, int unref_devices) { int i; if (list == NULL) return; /* be NULL safe */ if (unref_devices) { for (i = 0; list[i] != NULL; i++) libusb_unref_device(list[i]); } free(list); } uint8_t libusb_get_bus_number(libusb_device *dev) { if (dev == NULL) return (0); /* should not happen */ return (libusb20_dev_get_bus_number(dev->os_priv)); } uint8_t libusb_get_device_address(libusb_device *dev) { if (dev == NULL) return (0); /* should not happen */ return (libusb20_dev_get_address(dev->os_priv)); } enum libusb_speed libusb_get_device_speed(libusb_device *dev) { if (dev == NULL) return (LIBUSB_SPEED_UNKNOWN); /* should not happen */ switch (libusb20_dev_get_speed(dev->os_priv)) { case LIBUSB20_SPEED_LOW: return (LIBUSB_SPEED_LOW); case LIBUSB20_SPEED_FULL: return (LIBUSB_SPEED_FULL); case LIBUSB20_SPEED_HIGH: return (LIBUSB_SPEED_HIGH); case LIBUSB20_SPEED_SUPER: return (LIBUSB_SPEED_SUPER); default: break; } return (LIBUSB_SPEED_UNKNOWN); } int libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint) { struct libusb_config_descriptor *pdconf; struct libusb_interface *pinf; struct libusb_interface_descriptor *pdinf; struct libusb_endpoint_descriptor *pdend; int i; int j; int k; int ret; if (dev == NULL) return (LIBUSB_ERROR_NO_DEVICE); ret = libusb_get_active_config_descriptor(dev, &pdconf); if (ret < 0) return (ret); ret = LIBUSB_ERROR_NOT_FOUND; for (i = 0; i < pdconf->bNumInterfaces; i++) { pinf = &pdconf->interface[i]; for (j = 0; j < pinf->num_altsetting; j++) { pdinf = &pinf->altsetting[j]; for (k = 0; k < pdinf->bNumEndpoints; k++) { pdend = &pdinf->endpoint[k]; if (pdend->bEndpointAddress == endpoint) { ret = pdend->wMaxPacketSize; goto out; } } } } out: libusb_free_config_descriptor(pdconf); return (ret); } int libusb_get_max_iso_packet_size(libusb_device *dev, uint8_t endpoint) { int multiplier; int ret; ret = libusb_get_max_packet_size(dev, endpoint); switch (libusb20_dev_get_speed(dev->os_priv)) { case LIBUSB20_SPEED_LOW: case LIBUSB20_SPEED_FULL: break; default: if (ret > -1) { multiplier = (1 + ((ret >> 11) & 3)); if (multiplier > 3) multiplier = 3; ret = (ret & 0x7FF) * multiplier; } break; } return (ret); } libusb_device * libusb_ref_device(libusb_device *dev) { if (dev == NULL) return (NULL); /* be NULL safe */ CTX_LOCK(dev->ctx); dev->refcnt++; CTX_UNLOCK(dev->ctx); return (dev); } void libusb_unref_device(libusb_device *dev) { if (dev == NULL) return; /* be NULL safe */ CTX_LOCK(dev->ctx); dev->refcnt--; CTX_UNLOCK(dev->ctx); if (dev->refcnt == 0) { libusb20_dev_free(dev->os_priv); free(dev); } } int libusb_open(libusb_device *dev, libusb_device_handle **devh) { libusb_context *ctx = dev->ctx; struct libusb20_device *pdev = dev->os_priv; uint8_t dummy; int err; if (devh == NULL) return (LIBUSB_ERROR_INVALID_PARAM); /* set default device handle value */ *devh = NULL; dev = libusb_ref_device(dev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); err = libusb20_dev_open(pdev, 16 * 4 /* number of endpoints */ ); if (err) { libusb_unref_device(dev); return (LIBUSB_ERROR_NO_MEM); } libusb10_add_pollfd(ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); /* make sure our event loop detects the new device */ dummy = 0; err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); if (err < (int)sizeof(dummy)) { /* ignore error, if any */ DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open write failed!"); } *devh = pdev; return (0); } libusb_device_handle * libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vendor_id, uint16_t product_id) { struct libusb_device **devs; struct libusb20_device *pdev; struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; int i; int j; ctx = GET_CONTEXT(ctx); if (ctx == NULL) return (NULL); /* be NULL safe */ DPRINTF(ctx, LIBUSB_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++) { struct libusb20_device *tdev; tdev = devs[j]->os_priv; pdesc = libusb20_dev_get_device_desc(tdev); /* * NOTE: The USB library will automatically swap the * fields in the device descriptor to be of host * endian type! */ if (pdesc->idVendor == vendor_id && pdesc->idProduct == product_id) { libusb_open(devs[j], &pdev); break; } } libusb_free_device_list(devs, 1); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid leave"); return (pdev); } void libusb_close(struct libusb20_device *pdev) { libusb_context *ctx; struct libusb_device *dev; uint8_t dummy; int err; if (pdev == NULL) return; /* be NULL safe */ dev = libusb_get_device(pdev); ctx = dev->ctx; libusb10_remove_pollfd(ctx, &dev->dev_poll); libusb20_dev_close(pdev); /* unref will free the "pdev" when the refcount reaches zero */ libusb_unref_device(dev); /* make sure our event loop detects the closed device */ dummy = 0; err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); if (err < (int)sizeof(dummy)) { /* ignore error, if any */ DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close write failed!"); } } libusb_device * libusb_get_device(struct libusb20_device *pdev) { if (pdev == NULL) return (NULL); return ((libusb_device *)pdev->privLuData); } int libusb_get_configuration(struct libusb20_device *pdev, int *config) { struct libusb20_config *pconf; if (pdev == NULL || config == NULL) return (LIBUSB_ERROR_INVALID_PARAM); pconf = libusb20_dev_alloc_config(pdev, libusb20_dev_get_config_index(pdev)); if (pconf == NULL) return (LIBUSB_ERROR_NO_MEM); *config = pconf->desc.bConfigurationValue; free(pconf); return (0); } int libusb_set_configuration(struct libusb20_device *pdev, int configuration) { struct libusb20_config *pconf; struct libusb_device *dev; int err; uint8_t i; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (configuration < 1) { /* unconfigure */ i = 255; } else { for (i = 0; i != 255; i++) { uint8_t found; pconf = libusb20_dev_alloc_config(pdev, i); if (pconf == NULL) return (LIBUSB_ERROR_INVALID_PARAM); found = (pconf->desc.bConfigurationValue == configuration); free(pconf); if (found) goto set_config; } return (LIBUSB_ERROR_INVALID_PARAM); } set_config: libusb10_cancel_all_transfer(dev); libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); err = libusb20_dev_set_config_index(pdev, i); libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); return (err ? LIBUSB_ERROR_INVALID_PARAM : 0); } int libusb_claim_interface(struct libusb20_device *pdev, int interface_number) { libusb_device *dev; int err = 0; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); CTX_LOCK(dev->ctx); if (dev->claimed_interfaces & (1 << interface_number)) err = LIBUSB_ERROR_BUSY; if (!err) dev->claimed_interfaces |= (1 << interface_number); CTX_UNLOCK(dev->ctx); return (err); } int libusb_release_interface(struct libusb20_device *pdev, int interface_number) { libusb_device *dev; int err = 0; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); CTX_LOCK(dev->ctx); if (!(dev->claimed_interfaces & (1 << interface_number))) err = LIBUSB_ERROR_NOT_FOUND; if (!err) dev->claimed_interfaces &= ~(1 << interface_number); CTX_UNLOCK(dev->ctx); return (err); } int libusb_set_interface_alt_setting(struct libusb20_device *pdev, int interface_number, int alternate_setting) { libusb_device *dev; int err = 0; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (interface_number < 0 || interface_number > 31) return (LIBUSB_ERROR_INVALID_PARAM); CTX_LOCK(dev->ctx); if (!(dev->claimed_interfaces & (1 << interface_number))) err = LIBUSB_ERROR_NOT_FOUND; CTX_UNLOCK(dev->ctx); if (err) return (err); libusb10_cancel_all_transfer(dev); libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); err = libusb20_dev_set_alt_index(pdev, interface_number, alternate_setting); libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); return (err ? LIBUSB_ERROR_OTHER : 0); } static struct libusb20_transfer * libusb10_get_transfer(struct libusb20_device *pdev, - uint8_t endpoint, uint8_t index) + uint8_t endpoint, uint8_t xfer_index) { - index &= 1; /* double buffering */ + xfer_index &= 1; /* double buffering */ - index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4; + xfer_index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4; if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) { /* this is an IN endpoint */ - index |= 2; + xfer_index |= 2; } - return (libusb20_tr_get_pointer(pdev, index)); + return (libusb20_tr_get_pointer(pdev, xfer_index)); } int libusb_clear_halt(struct libusb20_device *pdev, uint8_t endpoint) { struct libusb20_transfer *xfer; struct libusb_device *dev; int err; xfer = libusb10_get_transfer(pdev, endpoint, 0); if (xfer == NULL) return (LIBUSB_ERROR_INVALID_PARAM); dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); CTX_LOCK(dev->ctx); err = libusb20_tr_open(xfer, 0, 1, endpoint); CTX_UNLOCK(dev->ctx); if (err != 0 && err != LIBUSB20_ERROR_BUSY) return (LIBUSB_ERROR_OTHER); libusb20_tr_clear_stall_sync(xfer); /* check if we opened the transfer */ if (err == 0) { CTX_LOCK(dev->ctx); libusb20_tr_close(xfer); CTX_UNLOCK(dev->ctx); } return (0); /* success */ } int libusb_reset_device(struct libusb20_device *pdev) { libusb_device *dev; int err; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); libusb10_cancel_all_transfer(dev); libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); err = libusb20_dev_reset(pdev); libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); return (err ? LIBUSB_ERROR_OTHER : 0); } int libusb_check_connected(struct libusb20_device *pdev) { libusb_device *dev; int err; dev = libusb_get_device(pdev); if (dev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); err = libusb20_dev_check_connected(pdev); return (err ? LIBUSB_ERROR_NO_DEVICE : 0); } int libusb_kernel_driver_active(struct libusb20_device *pdev, int interface) { if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (libusb20_dev_kernel_driver_active(pdev, interface)) return (0); /* no kernel driver is active */ else return (1); /* kernel driver is active */ } int libusb_get_driver_np(struct libusb20_device *pdev, int interface, char *name, int namelen) { return (libusb_get_driver(pdev, interface, name, namelen)); } int libusb_get_driver(struct libusb20_device *pdev, int interface, char *name, int namelen) { char *ptr; int err; if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (namelen < 1) return (LIBUSB_ERROR_INVALID_PARAM); if (namelen > 255) namelen = 255; err = libusb20_dev_get_iface_desc( pdev, interface, name, namelen); if (err != 0) return (LIBUSB_ERROR_OTHER); /* we only want the driver name */ ptr = strstr(name, ":"); if (ptr != NULL) *ptr = 0; return (0); } int libusb_detach_kernel_driver_np(struct libusb20_device *pdev, int interface) { return (libusb_detach_kernel_driver(pdev, interface)); } int libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface) { int err; if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); err = libusb20_dev_detach_kernel_driver( pdev, interface); return (err ? LIBUSB_ERROR_OTHER : 0); } int libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface) { if (pdev == NULL) return (LIBUSB_ERROR_INVALID_PARAM); /* stub - currently not supported by libusb20 */ return (0); } /* Asynchronous device I/O */ struct libusb_transfer * libusb_alloc_transfer(int iso_packets) { struct libusb_transfer *uxfer; struct libusb_super_transfer *sxfer; int len; len = sizeof(struct libusb_transfer) + sizeof(struct libusb_super_transfer) + (iso_packets * sizeof(libusb_iso_packet_descriptor)); sxfer = malloc(len); if (sxfer == NULL) return (NULL); memset(sxfer, 0, len); uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); /* set default value */ uxfer->num_iso_packets = iso_packets; return (uxfer); } void libusb_free_transfer(struct libusb_transfer *uxfer) { struct libusb_super_transfer *sxfer; if (uxfer == NULL) return; /* be NULL safe */ /* check if we should free the transfer buffer */ if (uxfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) free(uxfer->buffer); sxfer = (struct libusb_super_transfer *)( (uint8_t *)uxfer - sizeof(*sxfer)); free(sxfer); } static uint32_t libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) { uint32_t ret; switch (xfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: ret = 60 | LIBUSB20_MAX_FRAME_PRE_SCALE; /* 60ms */ break; case LIBUSB_TRANSFER_TYPE_CONTROL: ret = 2; break; default: ret = 1; break; } return (ret); } static int libusb10_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer) { int ret; int usb_speed; usb_speed = libusb20_dev_get_speed(pdev); switch (xfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: ret = 0; /* kernel will auto-select */ break; case LIBUSB_TRANSFER_TYPE_CONTROL: ret = 1024; break; default: switch (usb_speed) { case LIBUSB20_SPEED_LOW: ret = 256; break; case LIBUSB20_SPEED_FULL: ret = 4096; break; default: ret = 16384; break; } break; } return (ret); } static int libusb10_convert_error(uint8_t status) { ; /* indent fix */ switch (status) { case LIBUSB20_TRANSFER_START: case LIBUSB20_TRANSFER_COMPLETED: return (LIBUSB_TRANSFER_COMPLETED); case LIBUSB20_TRANSFER_OVERFLOW: return (LIBUSB_TRANSFER_OVERFLOW); case LIBUSB20_TRANSFER_NO_DEVICE: return (LIBUSB_TRANSFER_NO_DEVICE); case LIBUSB20_TRANSFER_STALL: return (LIBUSB_TRANSFER_STALL); case LIBUSB20_TRANSFER_CANCELLED: return (LIBUSB_TRANSFER_CANCELLED); case LIBUSB20_TRANSFER_TIMED_OUT: return (LIBUSB_TRANSFER_TIMED_OUT); default: return (LIBUSB_TRANSFER_ERROR); } } /* This function must be called locked */ static void libusb10_complete_transfer(struct libusb20_transfer *pxfer, struct libusb_super_transfer *sxfer, int status) { struct libusb_transfer *uxfer; struct libusb_device *dev; uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); if (pxfer != NULL) libusb20_tr_set_priv_sc1(pxfer, NULL); /* set transfer status */ uxfer->status = status; /* update super transfer state */ sxfer->state = LIBUSB_SUPER_XFER_ST_NONE; dev = libusb_get_device(uxfer->dev_handle); TAILQ_INSERT_TAIL(&dev->ctx->tr_done, sxfer, entry); } /* This function must be called locked */ static void libusb10_isoc_proxy(struct libusb20_transfer *pxfer) { struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; uint32_t actlen; uint16_t iso_packets; uint16_t i; uint8_t status; uint8_t flags; status = libusb20_tr_get_status(pxfer); sxfer = libusb20_tr_get_priv_sc1(pxfer); actlen = libusb20_tr_get_actual_length(pxfer); iso_packets = libusb20_tr_get_max_frames(pxfer); if (sxfer == NULL) return; /* cancelled - nothing to do */ uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); 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: /* update actual length */ uxfer->actual_length = actlen; for (i = 0; i != iso_packets; i++) { uxfer->iso_packet_desc[i].actual_length = libusb20_tr_get_length(pxfer, i); } libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); break; case LIBUSB20_TRANSFER_START: /* setup length(s) */ actlen = 0; for (i = 0; i != iso_packets; i++) { libusb20_tr_setup_isoc(pxfer, &uxfer->buffer[actlen], uxfer->iso_packet_desc[i].length, i); actlen += uxfer->iso_packet_desc[i].length; } /* no remainder */ sxfer->rem_len = 0; libusb20_tr_set_total_frames(pxfer, iso_packets); libusb20_tr_submit(pxfer); /* fork another USB transfer, if any */ libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); break; } } /* This function must be called locked */ static void libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer) { struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; uint32_t max_bulk; uint32_t actlen; uint8_t status; uint8_t flags; status = libusb20_tr_get_status(pxfer); sxfer = libusb20_tr_get_priv_sc1(pxfer); max_bulk = libusb20_tr_get_max_total_length(pxfer); actlen = libusb20_tr_get_actual_length(pxfer); if (sxfer == NULL) return; /* cancelled - nothing to do */ uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); flags = uxfer->flags; switch (status) { case LIBUSB20_TRANSFER_COMPLETED: uxfer->actual_length += actlen; /* check for short packet */ if (sxfer->last_len != actlen) { if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR); } else { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); } break; } /* check for end of data */ if (sxfer->rem_len == 0) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); break; } /* FALLTHROUGH */ case LIBUSB20_TRANSFER_START: if (max_bulk > sxfer->rem_len) { max_bulk = sxfer->rem_len; } /* setup new BULK or INTERRUPT transaction */ libusb20_tr_setup_bulk(pxfer, sxfer->curr_data, max_bulk, uxfer->timeout); /* update counters */ sxfer->last_len = max_bulk; sxfer->curr_data += max_bulk; sxfer->rem_len -= max_bulk; libusb20_tr_submit(pxfer); /* check if we can fork another USB transfer */ if (sxfer->rem_len == 0) libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); break; } } /* This function must be called locked */ static void libusb10_ctrl_proxy(struct libusb20_transfer *pxfer) { struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; uint32_t max_bulk; uint32_t actlen; uint8_t status; uint8_t flags; status = libusb20_tr_get_status(pxfer); sxfer = libusb20_tr_get_priv_sc1(pxfer); max_bulk = libusb20_tr_get_max_total_length(pxfer); actlen = libusb20_tr_get_actual_length(pxfer); if (sxfer == NULL) return; /* cancelled - nothing to do */ uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); flags = uxfer->flags; switch (status) { case LIBUSB20_TRANSFER_COMPLETED: uxfer->actual_length += actlen; /* subtract length of SETUP packet, if any */ actlen -= libusb20_tr_get_length(pxfer, 0); /* check for short packet */ if (sxfer->last_len != actlen) { if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR); } else { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); } break; } /* check for end of data */ if (sxfer->rem_len == 0) { libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED); break; } /* FALLTHROUGH */ case LIBUSB20_TRANSFER_START: if (max_bulk > sxfer->rem_len) { max_bulk = sxfer->rem_len; } /* setup new CONTROL transaction */ if (status == LIBUSB20_TRANSFER_COMPLETED) { /* next fragment - don't send SETUP packet */ libusb20_tr_set_length(pxfer, 0, 0); } else { /* first fragment - send SETUP packet */ libusb20_tr_set_length(pxfer, 8, 0); libusb20_tr_set_buffer(pxfer, uxfer->buffer, 0); } if (max_bulk != 0) { libusb20_tr_set_length(pxfer, max_bulk, 1); libusb20_tr_set_buffer(pxfer, sxfer->curr_data, 1); libusb20_tr_set_total_frames(pxfer, 2); } else { libusb20_tr_set_total_frames(pxfer, 1); } /* update counters */ sxfer->last_len = max_bulk; sxfer->curr_data += max_bulk; sxfer->rem_len -= max_bulk; libusb20_tr_submit(pxfer); /* check if we can fork another USB transfer */ if (sxfer->rem_len == 0) libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint); break; default: libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status)); break; } } /* The following function must be called locked */ static void libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint) { struct libusb20_transfer *pxfer0; struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; struct libusb_device *dev; int err; int buffsize; int maxframe; int temp; 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 == NULL) return; /* cancelling */ if (sxfer->rem_len) return; /* cannot queue another one */ /* swap transfers */ pxfer1 = pxfer0; break; case 1: sxfer = libusb20_tr_get_priv_sc1(pxfer0); if (sxfer == NULL) return; /* cancelling */ if (sxfer->rem_len) return; /* cannot queue another one */ /* swap transfers */ pxfer0 = pxfer1; break; default: break; } /* find next transfer on same endpoint */ TAILQ_FOREACH(sxfer, &dev->tr_head, entry) { uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); if (uxfer->endpoint == endpoint) { TAILQ_REMOVE(&dev->tr_head, sxfer, entry); sxfer->entry.tqe_prev = NULL; goto found; } } return; /* success */ found: libusb20_tr_set_priv_sc0(pxfer0, pdev); libusb20_tr_set_priv_sc1(pxfer0, sxfer); /* reset super transfer state */ sxfer->rem_len = uxfer->length; sxfer->curr_data = uxfer->buffer; uxfer->actual_length = 0; switch (uxfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: libusb20_tr_set_callback(pxfer0, libusb10_isoc_proxy); break; case LIBUSB_TRANSFER_TYPE_BULK: case LIBUSB_TRANSFER_TYPE_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_submit_transfer(struct libusb_transfer *uxfer) { struct libusb20_transfer *pxfer0; struct libusb20_transfer *pxfer1; struct libusb_super_transfer *sxfer; struct libusb_device *dev; uint32_t endpoint; int err; if (uxfer == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if (uxfer->dev_handle == NULL) return (LIBUSB_ERROR_INVALID_PARAM); endpoint = uxfer->endpoint; 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 { /* set pending state */ sxfer->state = LIBUSB_SUPER_XFER_ST_PEND; /* insert transfer into transfer head list */ TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry); /* start work transfers */ libusb10_submit_transfer_sub( uxfer->dev_handle, endpoint); err = 0; /* success */ } CTX_UNLOCK(dev->ctx); DPRINTF(dev->ctx, LIBUSB_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; uint32_t endpoint; int retval; if (uxfer == NULL) return (LIBUSB_ERROR_INVALID_PARAM); /* check if not initialised */ if (uxfer->dev_handle == NULL) return (LIBUSB_ERROR_NOT_FOUND); endpoint = uxfer->endpoint; if (endpoint > 255) return (LIBUSB_ERROR_INVALID_PARAM); dev = libusb_get_device(uxfer->dev_handle); DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter"); sxfer = (struct libusb_super_transfer *)( (uint8_t *)uxfer - sizeof(*sxfer)); retval = 0; CTX_LOCK(dev->ctx); pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0); pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1); if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) { /* only update the transfer status */ uxfer->status = LIBUSB_TRANSFER_CANCELLED; retval = LIBUSB_ERROR_NOT_FOUND; } else if (sxfer->entry.tqe_prev != NULL) { /* we are lucky - transfer is on a queue */ TAILQ_REMOVE(&dev->tr_head, sxfer, entry); sxfer->entry.tqe_prev = NULL; libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_CANCELLED); } else if (pxfer0 == NULL || pxfer1 == NULL) { /* not started */ retval = LIBUSB_ERROR_NOT_FOUND; } else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) { libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_CANCELLED); 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 */ retval = LIBUSB_ERROR_NOT_FOUND; } CTX_UNLOCK(dev->ctx); DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave"); return (retval); } UNEXPORTED void libusb10_cancel_all_transfer(libusb_device *dev) { /* TODO */ } uint16_t libusb_cpu_to_le16(uint16_t x) { return (htole16(x)); } uint16_t libusb_le16_to_cpu(uint16_t x) { return (le16toh(x)); } const char * libusb_strerror(int code) { switch (code) { case LIBUSB_SUCCESS: return ("Success"); case LIBUSB_ERROR_IO: return ("I/O error"); case LIBUSB_ERROR_INVALID_PARAM: return ("Invalid parameter"); case LIBUSB_ERROR_ACCESS: return ("Permissions error"); case LIBUSB_ERROR_NO_DEVICE: return ("No device"); case LIBUSB_ERROR_NOT_FOUND: return ("Not found"); case LIBUSB_ERROR_BUSY: return ("Device busy"); case LIBUSB_ERROR_TIMEOUT: return ("Timeout"); case LIBUSB_ERROR_OVERFLOW: return ("Overflow"); case LIBUSB_ERROR_PIPE: return ("Pipe error"); case LIBUSB_ERROR_INTERRUPTED: return ("Interrupted"); case LIBUSB_ERROR_NO_MEM: return ("Out of memory"); case LIBUSB_ERROR_NOT_SUPPORTED: return ("Not supported"); case LIBUSB_ERROR_OTHER: return ("Other error"); default: return ("Unknown error"); } } const char * libusb_error_name(int code) { switch (code) { case LIBUSB_SUCCESS: return ("LIBUSB_SUCCESS"); case LIBUSB_ERROR_IO: return ("LIBUSB_ERROR_IO"); case LIBUSB_ERROR_INVALID_PARAM: return ("LIBUSB_ERROR_INVALID_PARAM"); case LIBUSB_ERROR_ACCESS: return ("LIBUSB_ERROR_ACCESS"); case LIBUSB_ERROR_NO_DEVICE: return ("LIBUSB_ERROR_NO_DEVICE"); case LIBUSB_ERROR_NOT_FOUND: return ("LIBUSB_ERROR_NOT_FOUND"); case LIBUSB_ERROR_BUSY: return ("LIBUSB_ERROR_BUSY"); case LIBUSB_ERROR_TIMEOUT: return ("LIBUSB_ERROR_TIMEOUT"); case LIBUSB_ERROR_OVERFLOW: return ("LIBUSB_ERROR_OVERFLOW"); case LIBUSB_ERROR_PIPE: return ("LIBUSB_ERROR_PIPE"); case LIBUSB_ERROR_INTERRUPTED: return ("LIBUSB_ERROR_INTERRUPTED"); case LIBUSB_ERROR_NO_MEM: return ("LIBUSB_ERROR_NO_MEM"); case LIBUSB_ERROR_NOT_SUPPORTED: return ("LIBUSB_ERROR_NOT_SUPPORTED"); case LIBUSB_ERROR_OTHER: return ("LIBUSB_ERROR_OTHER"); default: return ("LIBUSB_ERROR_UNKNOWN"); } } Index: head/lib/libusb/libusb10_desc.c =================================================================== --- head/lib/libusb/libusb10_desc.c (revision 234490) +++ head/lib/libusb/libusb10_desc.c (revision 234491) @@ -1,498 +1,498 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #define libusb_device_handle libusb20_device #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" #include "libusb.h" #include "libusb10.h" #define N_ALIGN(n) (-((-(n)) & (-8UL))) /* USB descriptors */ int libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) { struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; struct libusb20_device *pdev; if ((dev == NULL) || (desc == NULL)) return (LIBUSB_ERROR_INVALID_PARAM); pdev = dev->os_priv; pdesc = libusb20_dev_get_device_desc(pdev); desc->bLength = pdesc->bLength; desc->bDescriptorType = pdesc->bDescriptorType; desc->bcdUSB = pdesc->bcdUSB; desc->bDeviceClass = pdesc->bDeviceClass; desc->bDeviceSubClass = pdesc->bDeviceSubClass; desc->bDeviceProtocol = pdesc->bDeviceProtocol; desc->bMaxPacketSize0 = pdesc->bMaxPacketSize0; desc->idVendor = pdesc->idVendor; desc->idProduct = pdesc->idProduct; desc->bcdDevice = pdesc->bcdDevice; desc->iManufacturer = pdesc->iManufacturer; desc->iProduct = pdesc->iProduct; desc->iSerialNumber = pdesc->iSerialNumber; desc->bNumConfigurations = pdesc->bNumConfigurations; return (0); } int libusb_get_active_config_descriptor(libusb_device *dev, struct libusb_config_descriptor **config) { struct libusb20_device *pdev; uint8_t config_index; pdev = dev->os_priv; config_index = libusb20_dev_get_config_index(pdev); return (libusb_get_config_descriptor(dev, config_index, config)); } int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config) { struct libusb20_device *pdev; struct libusb20_config *pconf; struct libusb20_interface *pinf; struct libusb20_endpoint *pend; 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); if (pconf == NULL) return (LIBUSB_ERROR_NOT_FOUND); nalt = nif = pconf->num_interface; nep = 0; nextra = N_ALIGN(pconf->extra.len); for (i = 0; i < nif; i++) { pinf = pconf->interface + i; nextra += N_ALIGN(pinf->extra.len); nep += pinf->num_endpoints; k = pinf->num_endpoints; pend = pinf->endpoints; while (k--) { nextra += N_ALIGN(pend->extra.len); pend++; } j = pinf->num_altsetting; nalt += pinf->num_altsetting; pinf = pinf->altsetting; while (j--) { nextra += N_ALIGN(pinf->extra.len); nep += pinf->num_endpoints; k = pinf->num_endpoints; pend = pinf->endpoints; while (k--) { nextra += N_ALIGN(pend->extra.len); pend++; } pinf++; } } nextra = nextra + (1 * sizeof(libusb_config_descriptor)) + (nif * sizeof(libusb_interface)) + (nalt * sizeof(libusb_interface_descriptor)) + (nep * sizeof(libusb_endpoint_descriptor)); nextra = N_ALIGN(nextra); pconfd = malloc(nextra); if (pconfd == NULL) { free(pconf); return (LIBUSB_ERROR_NO_MEM); } /* make sure memory is initialised */ memset(pconfd, 0, nextra); pconfd->interface = (libusb_interface *) (pconfd + 1); 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 += N_ALIGN(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++; } } /* fill in all interface and endpoint data */ for (i = 0; i < nif; i++) { pinf = &pconf->interface[i]; 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 = &pconfd->interface[i].altsetting[j]; ifd->bLength = pinf->desc.bLength; ifd->bDescriptorType = pinf->desc.bDescriptorType; ifd->bInterfaceNumber = pinf->desc.bInterfaceNumber; ifd->bAlternateSetting = pinf->desc.bAlternateSetting; ifd->bNumEndpoints = pinf->desc.bNumEndpoints; ifd->bInterfaceClass = pinf->desc.bInterfaceClass; ifd->bInterfaceSubClass = pinf->desc.bInterfaceSubClass; ifd->bInterfaceProtocol = pinf->desc.bInterfaceProtocol; ifd->iInterface = pinf->desc.iInterface; if (pinf->extra.len != 0) { ifd->extra_length = pinf->extra.len; ifd->extra = pextra; memcpy(pextra, pinf->extra.ptr, pinf->extra.len); pextra += N_ALIGN(pinf->extra.len); } for (k = 0; k < pinf->num_endpoints; k++) { pend = &pinf->endpoints[k]; endd = &ifd->endpoint[k]; endd->bLength = pend->desc.bLength; endd->bDescriptorType = pend->desc.bDescriptorType; endd->bEndpointAddress = pend->desc.bEndpointAddress; endd->bmAttributes = pend->desc.bmAttributes; endd->wMaxPacketSize = pend->desc.wMaxPacketSize; endd->bInterval = pend->desc.bInterval; endd->bRefresh = pend->desc.bRefresh; endd->bSynchAddress = pend->desc.bSynchAddress; if (pend->extra.len != 0) { endd->extra_length = pend->extra.len; endd->extra = pextra; memcpy(pextra, pend->extra.ptr, pend->extra.len); pextra += N_ALIGN(pend->extra.len); } } } } free(pconf); *config = pconfd; return (0); /* success */ } int libusb_get_config_descriptor_by_value(libusb_device *dev, uint8_t bConfigurationValue, struct libusb_config_descriptor **config) { struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; struct libusb20_device *pdev; int i; 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++) { err = libusb_get_config_descriptor(dev, i, config); if (err) return (err); if ((*config)->bConfigurationValue == bConfigurationValue) return (0); /* success */ libusb_free_config_descriptor(*config); } *config = NULL; return (LIBUSB_ERROR_NOT_FOUND); } void libusb_free_config_descriptor(struct libusb_config_descriptor *config) { free(config); } int libusb_get_string_descriptor_ascii(libusb_device_handle *pdev, uint8_t desc_index, unsigned char *data, int length) { if (pdev == NULL || data == NULL || length < 1) return (LIBUSB_ERROR_INVALID_PARAM); if (length > 65535) length = 65535; /* 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)); return (LIBUSB_ERROR_OTHER); } int libusb_get_descriptor(libusb_device_handle * devh, uint8_t desc_type, uint8_t desc_index, uint8_t *data, int length) { if (devh == NULL || data == NULL || length < 1) return (LIBUSB_ERROR_INVALID_PARAM); if (length > 65535) length = 65535; return (libusb_control_transfer(devh, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, length, 1000)); } int libusb_parse_ss_endpoint_comp(const void *buf, int len, struct libusb_ss_endpoint_companion_descriptor **ep_comp) { if (buf == NULL || ep_comp == NULL || len < 1) return (LIBUSB_ERROR_INVALID_PARAM); if (len > 65535) len = 65535; *ep_comp = NULL; while (len != 0) { uint8_t dlen; uint8_t dtype; dlen = ((const uint8_t *)buf)[0]; dtype = ((const uint8_t *)buf)[1]; if (dlen < 2 || dlen > len) break; if (dlen >= LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE && dtype == LIBUSB_DT_SS_ENDPOINT_COMPANION) { struct libusb_ss_endpoint_companion_descriptor *ptr; ptr = malloc(sizeof(*ptr)); if (ptr == NULL) return (LIBUSB_ERROR_NO_MEM); ptr->bLength = LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE; ptr->bDescriptorType = dtype; ptr->bMaxBurst = ((const uint8_t *)buf)[2]; ptr->bmAttributes = ((const uint8_t *)buf)[3]; ptr->wBytesPerInterval = ((const uint8_t *)buf)[4] | (((const uint8_t *)buf)[5] << 8); *ep_comp = ptr; return (0); /* success */ } buf = ((const uint8_t *)buf) + dlen; len -= dlen; } return (LIBUSB_ERROR_IO); } void libusb_free_ss_endpoint_comp(struct libusb_ss_endpoint_companion_descriptor *ep_comp) { if (ep_comp == NULL) return; free(ep_comp); } int libusb_parse_bos_descriptor(const void *buf, int len, struct libusb_bos_descriptor **bos) { struct libusb_bos_descriptor *ptr; - struct libusb_usb_2_0_device_capability_descriptor *dcap_20; - struct libusb_ss_usb_device_capability_descriptor *ss_cap; + struct libusb_usb_2_0_device_capability_descriptor *dcap_20 = NULL; + struct libusb_ss_usb_device_capability_descriptor *ss_cap = NULL; if (buf == NULL || bos == NULL || len < 1) return (LIBUSB_ERROR_INVALID_PARAM); if (len > 65535) len = 65535; *bos = ptr = NULL; while (len != 0) { uint8_t dlen; uint8_t dtype; dlen = ((const uint8_t *)buf)[0]; dtype = ((const uint8_t *)buf)[1]; if (dlen < 2 || dlen > len) break; if (dlen >= LIBUSB_DT_BOS_SIZE && dtype == LIBUSB_DT_BOS) { ptr = malloc(sizeof(*ptr) + sizeof(*dcap_20) + sizeof(*ss_cap)); if (ptr == NULL) return (LIBUSB_ERROR_NO_MEM); *bos = ptr; ptr->bLength = LIBUSB_DT_BOS_SIZE; ptr->bDescriptorType = dtype; ptr->wTotalLength = ((const uint8_t *)buf)[2] | (((const uint8_t *)buf)[3] << 8); ptr->bNumDeviceCapabilities = ((const uint8_t *)buf)[4]; ptr->usb_2_0_ext_cap = NULL; ptr->ss_usb_cap = NULL; dcap_20 = (void *)(ptr + 1); ss_cap = (void *)(dcap_20 + 1); } if (dlen >= 3 && ptr != NULL && dtype == LIBUSB_DT_DEVICE_CAPABILITY) { switch (((const uint8_t *)buf)[2]) { case LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY: - if (ptr->usb_2_0_ext_cap != NULL) + if (ptr->usb_2_0_ext_cap != NULL || dcap_20 == NULL) break; if (dlen < LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE) break; ptr->usb_2_0_ext_cap = dcap_20; dcap_20->bLength = LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE; dcap_20->bDescriptorType = dtype; dcap_20->bDevCapabilityType = ((const uint8_t *)buf)[2]; dcap_20->bmAttributes = ((const uint8_t *)buf)[3] | (((const uint8_t *)buf)[4] << 8) | (((const uint8_t *)buf)[5] << 16) | (((const uint8_t *)buf)[6] << 24); break; case LIBUSB_SS_USB_DEVICE_CAPABILITY: - if (ptr->ss_usb_cap != NULL) + if (ptr->ss_usb_cap != NULL || ss_cap == NULL) break; if (dlen < LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE) break; ptr->ss_usb_cap = ss_cap; ss_cap->bLength = LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE; ss_cap->bDescriptorType = dtype; ss_cap->bDevCapabilityType = ((const uint8_t *)buf)[2]; ss_cap->bmAttributes = ((const uint8_t *)buf)[3]; ss_cap->wSpeedSupported = ((const uint8_t *)buf)[4] | (((const uint8_t *)buf)[5] << 8); ss_cap->bFunctionalitySupport = ((const uint8_t *)buf)[6]; ss_cap->bU1DevExitLat = ((const uint8_t *)buf)[7]; ss_cap->wU2DevExitLat = ((const uint8_t *)buf)[8] | (((const uint8_t *)buf)[9] << 8); break; default: break; } } buf = ((const uint8_t *)buf) + dlen; len -= dlen; } if (ptr != NULL) return (0); /* success */ return (LIBUSB_ERROR_IO); } void libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) { if (bos == NULL) return; free(bos); } Index: head/lib/libusb/libusb10_io.c =================================================================== --- head/lib/libusb/libusb10_io.c (revision 234490) +++ head/lib/libusb/libusb10_io.c (revision 234491) @@ -1,738 +1,738 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #define libusb_device_handle libusb20_device #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" #include "libusb.h" #include "libusb10.h" UNEXPORTED void libusb10_add_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd, struct libusb20_device *pdev, int fd, short events) { if (ctx == NULL) return; /* invalid */ if (pollfd->entry.tqe_prev != NULL) return; /* already queued */ if (fd < 0) return; /* invalid */ pollfd->pdev = pdev; pollfd->pollfd.fd = fd; pollfd->pollfd.events = events; CTX_LOCK(ctx); TAILQ_INSERT_TAIL(&ctx->pollfds, pollfd, entry); CTX_UNLOCK(ctx); if (ctx->fd_added_cb) ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data); } UNEXPORTED void libusb10_remove_pollfd(libusb_context *ctx, struct libusb_super_pollfd *pollfd) { if (ctx == NULL) return; /* invalid */ if (pollfd->entry.tqe_prev == NULL) return; /* already dequeued */ CTX_LOCK(ctx); TAILQ_REMOVE(&ctx->pollfds, pollfd, entry); pollfd->entry.tqe_prev = NULL; CTX_UNLOCK(ctx); if (ctx->fd_removed_cb) ctx->fd_removed_cb(pollfd->pollfd.fd, ctx->fd_cb_user_data); } /* This function must be called locked */ static int libusb10_handle_events_sub(struct libusb_context *ctx, struct timeval *tv) { struct libusb_device *dev; struct libusb20_device **ppdev; struct libusb_super_pollfd *pfd; struct pollfd *fds; struct libusb_super_transfer *sxfer; struct libusb_transfer *uxfer; nfds_t nfds; int timeout; int i; int err; DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb10_handle_events_sub enter"); nfds = 0; i = 0; TAILQ_FOREACH(pfd, &ctx->pollfds, entry) nfds++; fds = alloca(sizeof(*fds) * nfds); if (fds == NULL) return (LIBUSB_ERROR_NO_MEM); ppdev = alloca(sizeof(*ppdev) * nfds); if (ppdev == NULL) return (LIBUSB_ERROR_NO_MEM); TAILQ_FOREACH(pfd, &ctx->pollfds, entry) { fds[i].fd = pfd->pollfd.fd; fds[i].events = pfd->pollfd.events; fds[i].revents = 0; ppdev[i] = pfd->pdev; if (pfd->pdev != NULL) libusb_get_device(pfd->pdev)->refcnt++; i++; } if (tv == NULL) timeout = -1; else timeout = (tv->tv_sec * 1000) + ((tv->tv_usec + 999) / 1000); CTX_UNLOCK(ctx); err = poll(fds, nfds, timeout); CTX_LOCK(ctx); if ((err == -1) && (errno == EINTR)) err = LIBUSB_ERROR_INTERRUPTED; else if (err < 0) err = LIBUSB_ERROR_IO; if (err < 1) { for (i = 0; i != (int)nfds; i++) { if (ppdev[i] != NULL) { CTX_UNLOCK(ctx); libusb_unref_device(libusb_get_device(ppdev[i])); CTX_LOCK(ctx); } } goto do_done; } for (i = 0; i != (int)nfds; i++) { if (ppdev[i] != NULL) { dev = libusb_get_device(ppdev[i]); if (fds[i].revents == 0) err = 0; /* nothing to do */ else err = libusb20_dev_process(ppdev[i]); if (err) { /* cancel all transfers - device is gone */ libusb10_cancel_all_transfer(dev); /* remove USB device from polling loop */ libusb10_remove_pollfd(dev->ctx, &dev->dev_poll); } CTX_UNLOCK(ctx); libusb_unref_device(dev); CTX_LOCK(ctx); } else { uint8_t dummy; while (1) { if (read(fds[i].fd, &dummy, 1) != 1) break; } } } err = 0; do_done: /* Do all done callbacks */ while ((sxfer = TAILQ_FIRST(&ctx->tr_done))) { uint8_t flags; TAILQ_REMOVE(&ctx->tr_done, sxfer, entry); sxfer->entry.tqe_prev = NULL; ctx->tr_done_ref++; CTX_UNLOCK(ctx); uxfer = (struct libusb_transfer *)( ((uint8_t *)sxfer) + sizeof(*sxfer)); /* Allow the callback to free the transfer itself. */ flags = uxfer->flags; if (uxfer->callback != NULL) (uxfer->callback) (uxfer); /* Check if the USB transfer should be automatically freed. */ if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) libusb_free_transfer(uxfer); CTX_LOCK(ctx); ctx->tr_done_ref--; ctx->tr_done_gen++; } /* Wakeup other waiters */ pthread_cond_broadcast(&ctx->ctx_cond); return (err); } /* Polling and timing */ int libusb_try_lock_events(libusb_context *ctx) { int err; ctx = GET_CONTEXT(ctx); if (ctx == NULL) return (1); err = CTX_TRYLOCK(ctx); if (err) return (1); err = (ctx->ctx_handler != NO_THREAD); if (err) CTX_UNLOCK(ctx); else ctx->ctx_handler = pthread_self(); return (err); } void libusb_lock_events(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); CTX_LOCK(ctx); if (ctx->ctx_handler == NO_THREAD) ctx->ctx_handler = pthread_self(); } void libusb_unlock_events(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); if (ctx->ctx_handler == pthread_self()) { ctx->ctx_handler = NO_THREAD; pthread_cond_broadcast(&ctx->ctx_cond); } CTX_UNLOCK(ctx); } int libusb_event_handling_ok(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); return (ctx->ctx_handler == pthread_self()); } int libusb_event_handler_active(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); return (ctx->ctx_handler != NO_THREAD); } void libusb_lock_event_waiters(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); CTX_LOCK(ctx); } void libusb_unlock_event_waiters(libusb_context *ctx) { ctx = GET_CONTEXT(ctx); CTX_UNLOCK(ctx); } int libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) { struct timespec ts; int err; ctx = GET_CONTEXT(ctx); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_wait_for_event enter"); if (tv == NULL) { pthread_cond_wait(&ctx->ctx_cond, &ctx->ctx_lock); return (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) { ts.tv_nsec -= 1000000000; ts.tv_sec++; } err = pthread_cond_timedwait(&ctx->ctx_cond, &ctx->ctx_lock, &ts); if (err == ETIMEDOUT) return (1); return (0); } int libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv) { int err; ctx = GET_CONTEXT(ctx); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout enter"); 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 (err); } int libusb_handle_events(libusb_context *ctx) { return (libusb_handle_events_timeout(ctx, NULL)); } int libusb_handle_events_locked(libusb_context *ctx, struct timeval *tv) { int err; ctx = GET_CONTEXT(ctx); if (libusb_event_handling_ok(ctx)) { err = libusb10_handle_events_sub(ctx, tv); } else { libusb_wait_for_event(ctx, tv); err = 0; } return (err); } int libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv) { /* all timeouts are currently being done by the kernel */ timerclear(tv); return (0); } void libusb_set_pollfd_notifiers(libusb_context *ctx, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data) { ctx = GET_CONTEXT(ctx); ctx->fd_added_cb = added_cb; ctx->fd_removed_cb = removed_cb; ctx->fd_cb_user_data = user_data; } struct libusb_pollfd ** libusb_get_pollfds(libusb_context *ctx) { struct libusb_super_pollfd *pollfd; libusb_pollfd **ret; int i; ctx = GET_CONTEXT(ctx); CTX_LOCK(ctx); i = 0; TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) i++; ret = calloc(i + 1, sizeof(struct libusb_pollfd *)); if (ret == NULL) goto done; i = 0; TAILQ_FOREACH(pollfd, &ctx->pollfds, entry) ret[i++] = &pollfd->pollfd; ret[i] = NULL; done: CTX_UNLOCK(ctx); return (ret); } /* Synchronous device I/O */ int libusb_control_transfer(libusb_device_handle *devh, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint8_t *data, uint16_t wLength, unsigned int timeout) { struct LIBUSB20_CONTROL_SETUP_DECODED req; int err; uint16_t actlen; if (devh == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if ((wLength != 0) && (data == NULL)) return (LIBUSB_ERROR_INVALID_PARAM); LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); req.bmRequestType = bmRequestType; req.bRequest = bRequest; req.wValue = wValue; req.wIndex = wIndex; req.wLength = wLength; err = libusb20_dev_request_sync(devh, &req, data, &actlen, timeout, 0); if (err == LIBUSB20_ERROR_PIPE) return (LIBUSB_ERROR_PIPE); else if (err == LIBUSB20_ERROR_TIMEOUT) return (LIBUSB_ERROR_TIMEOUT); else if (err) return (LIBUSB_ERROR_NO_DEVICE); return (actlen); } static void libusb10_do_transfer_cb(struct libusb_transfer *transfer) { libusb_context *ctx; int *pdone; ctx = GET_CONTEXT(NULL); DPRINTF(ctx, LIBUSB_DEBUG_TRANSFER, "sync I/O done"); pdone = transfer->user_data; *pdone = 1; } /* * TODO: Replace the following function. Allocating and freeing on a * per-transfer basis is slow. --HPS */ static int libusb10_do_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout, int type) { libusb_context *ctx; struct libusb_transfer *xfer; - volatile int complet; + int done; int ret; if (devh == NULL) return (LIBUSB_ERROR_INVALID_PARAM); if ((length != 0) && (data == NULL)) return (LIBUSB_ERROR_INVALID_PARAM); xfer = libusb_alloc_transfer(0); if (xfer == NULL) return (LIBUSB_ERROR_NO_MEM); ctx = libusb_get_device(devh)->ctx; xfer->dev_handle = devh; xfer->endpoint = endpoint; xfer->type = type; xfer->timeout = timeout; xfer->buffer = data; xfer->length = length; - xfer->user_data = (void *)&complet; + xfer->user_data = (void *)&done; xfer->callback = libusb10_do_transfer_cb; - complet = 0; + done = 0; if ((ret = libusb_submit_transfer(xfer)) < 0) { libusb_free_transfer(xfer); return (ret); } - while (complet == 0) { + while (done == 0) { if ((ret = libusb_handle_events(ctx)) < 0) { libusb_cancel_transfer(xfer); usleep(1000); /* nice it */ } } *transferred = xfer->actual_length; switch (xfer->status) { case LIBUSB_TRANSFER_COMPLETED: ret = 0; break; case LIBUSB_TRANSFER_TIMED_OUT: ret = LIBUSB_ERROR_TIMEOUT; break; case LIBUSB_TRANSFER_OVERFLOW: ret = LIBUSB_ERROR_OVERFLOW; break; case LIBUSB_TRANSFER_STALL: ret = LIBUSB_ERROR_PIPE; break; case LIBUSB_TRANSFER_NO_DEVICE: ret = LIBUSB_ERROR_NO_DEVICE; break; default: ret = LIBUSB_ERROR_OTHER; break; } libusb_free_transfer(xfer); return (ret); } int libusb_bulk_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout) { libusb_context *ctx; int ret; ctx = GET_CONTEXT(NULL); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_bulk_transfer enter"); 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); } int libusb_interrupt_transfer(libusb_device_handle *devh, uint8_t endpoint, uint8_t *data, int length, int *transferred, unsigned int timeout) { libusb_context *ctx; int ret; ctx = GET_CONTEXT(NULL); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer enter"); ret = libusb10_do_transfer(devh, endpoint, data, length, transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer leave"); return (ret); } uint8_t * -libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t index) +libusb_get_iso_packet_buffer(struct libusb_transfer *transfer, uint32_t off) { uint8_t *ptr; uint32_t n; if (transfer->num_iso_packets < 0) return (NULL); - if (index >= (uint32_t)transfer->num_iso_packets) + if (off >= (uint32_t)transfer->num_iso_packets) return (NULL); ptr = transfer->buffer; if (ptr == NULL) return (NULL); - for (n = 0; n != index; n++) { + for (n = 0; n != off; n++) { ptr += transfer->iso_packet_desc[n].length; } return (ptr); } uint8_t * -libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t index) +libusb_get_iso_packet_buffer_simple(struct libusb_transfer *transfer, uint32_t off) { uint8_t *ptr; if (transfer->num_iso_packets < 0) return (NULL); - if (index >= (uint32_t)transfer->num_iso_packets) + if (off >= (uint32_t)transfer->num_iso_packets) return (NULL); ptr = transfer->buffer; if (ptr == NULL) return (NULL); - ptr += transfer->iso_packet_desc[0].length * index; + ptr += transfer->iso_packet_desc[0].length * off; return (ptr); } void libusb_set_iso_packet_lengths(struct libusb_transfer *transfer, uint32_t length) { int n; if (transfer->num_iso_packets < 0) return; for (n = 0; n != transfer->num_iso_packets; n++) transfer->iso_packet_desc[n].length = length; } uint8_t * libusb_control_transfer_get_data(struct libusb_transfer *transfer) { if (transfer->buffer == NULL) return (NULL); return (transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE); } struct libusb_control_setup * libusb_control_transfer_get_setup(struct libusb_transfer *transfer) { return ((struct libusb_control_setup *)transfer->buffer); } void libusb_fill_control_setup(uint8_t *buf, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) { struct libusb_control_setup *req = (struct libusb_control_setup *)buf; /* The alignment is OK for all fields below. */ req->bmRequestType = bmRequestType; req->bRequest = bRequest; req->wValue = htole16(wValue); req->wIndex = htole16(wIndex); req->wLength = htole16(wLength); } void libusb_fill_control_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t *buf, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { struct libusb_control_setup *setup = (struct libusb_control_setup *)buf; transfer->dev_handle = devh; transfer->endpoint = 0; transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; transfer->timeout = timeout; transfer->buffer = buf; if (setup != NULL) transfer->length = LIBUSB_CONTROL_SETUP_SIZE + le16toh(setup->wLength); else transfer->length = 0; transfer->user_data = user_data; transfer->callback = callback; } void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { transfer->dev_handle = devh; transfer->endpoint = endpoint; transfer->type = LIBUSB_TRANSFER_TYPE_BULK; transfer->timeout = timeout; transfer->buffer = buf; transfer->length = length; transfer->user_data = user_data; transfer->callback = callback; } void libusb_fill_interrupt_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { transfer->dev_handle = devh; transfer->endpoint = endpoint; transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; transfer->timeout = timeout; transfer->buffer = buf; transfer->length = length; transfer->user_data = user_data; transfer->callback = callback; } void libusb_fill_iso_transfer(struct libusb_transfer *transfer, libusb_device_handle *devh, uint8_t endpoint, uint8_t *buf, int length, int npacket, libusb_transfer_cb_fn callback, void *user_data, uint32_t timeout) { transfer->dev_handle = devh; transfer->endpoint = endpoint; transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; transfer->timeout = timeout; transfer->buffer = buf; transfer->length = length; transfer->num_iso_packets = npacket; transfer->user_data = user_data; transfer->callback = callback; } Index: head/lib/libusb/libusb20.c =================================================================== --- head/lib/libusb/libusb20.c (revision 234490) +++ head/lib/libusb/libusb20.c (revision 234491) @@ -1,1320 +1,1319 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008-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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" static int dummy_int(void) { return (LIBUSB20_ERROR_NOT_SUPPORTED); } static void dummy_void(void) { return; } static void dummy_callback(struct libusb20_transfer *xfer) { ; /* style fix */ switch (libusb20_tr_get_status(xfer)) { case LIBUSB20_TRANSFER_START: libusb20_tr_submit(xfer); break; default: /* complete or error */ break; } return; } #define dummy_get_config_desc_full (void *)dummy_int #define dummy_get_config_index (void *)dummy_int #define dummy_set_config_index (void *)dummy_int #define dummy_set_alt_index (void *)dummy_int #define dummy_reset_device (void *)dummy_int #define dummy_check_connected (void *)dummy_int #define dummy_set_power_mode (void *)dummy_int #define dummy_get_power_mode (void *)dummy_int #define dummy_kernel_driver_active (void *)dummy_int #define dummy_detach_kernel_driver (void *)dummy_int #define dummy_do_request_sync (void *)dummy_int #define dummy_tr_open (void *)dummy_int #define dummy_tr_close (void *)dummy_int #define dummy_tr_clear_stall_sync (void *)dummy_int #define dummy_process (void *)dummy_int #define dummy_dev_info (void *)dummy_int #define dummy_dev_get_iface_driver (void *)dummy_int #define dummy_tr_submit (void *)dummy_void #define dummy_tr_cancel_async (void *)dummy_void static const struct libusb20_device_methods libusb20_dummy_methods = { LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy) }; void libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer) { ; /* style fix */ repeat: if (!xfer->is_pending) { xfer->status = LIBUSB20_TRANSFER_START; } else { xfer->is_pending = 0; } xfer->callback(xfer); if (xfer->is_restart) { xfer->is_restart = 0; goto repeat; } if (xfer->is_draining && (!xfer->is_pending)) { xfer->is_draining = 0; xfer->status = LIBUSB20_TRANSFER_DRAINED; xfer->callback(xfer); } return; } int libusb20_tr_close(struct libusb20_transfer *xfer) { int error; if (!xfer->is_opened) { return (LIBUSB20_ERROR_OTHER); } error = xfer->pdev->methods->tr_close(xfer); if (xfer->pLength) { free(xfer->pLength); } if (xfer->ppBuffer) { free(xfer->ppBuffer); } /* reset variable fields in case the transfer is opened again */ xfer->priv_sc0 = 0; xfer->priv_sc1 = 0; xfer->is_opened = 0; xfer->is_pending = 0; xfer->is_cancel = 0; xfer->is_draining = 0; xfer->is_restart = 0; xfer->status = 0; xfer->flags = 0; xfer->nFrames = 0; xfer->aFrames = 0; xfer->timeout = 0; xfer->maxFrames = 0; xfer->maxTotalLength = 0; xfer->maxPacketLen = 0; return (error); } int libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no) { uint32_t size; uint8_t pre_scale; int error; if (xfer->is_opened) return (LIBUSB20_ERROR_BUSY); if (MaxFrameCount & LIBUSB20_MAX_FRAME_PRE_SCALE) { MaxFrameCount &= ~LIBUSB20_MAX_FRAME_PRE_SCALE; pre_scale = 1; } else { pre_scale = 0; } if (MaxFrameCount == 0) return (LIBUSB20_ERROR_INVALID_PARAM); xfer->maxFrames = MaxFrameCount; size = MaxFrameCount * sizeof(xfer->pLength[0]); xfer->pLength = malloc(size); if (xfer->pLength == NULL) { return (LIBUSB20_ERROR_NO_MEM); } memset(xfer->pLength, 0, size); size = MaxFrameCount * sizeof(xfer->ppBuffer[0]); xfer->ppBuffer = malloc(size); if (xfer->ppBuffer == NULL) { free(xfer->pLength); return (LIBUSB20_ERROR_NO_MEM); } memset(xfer->ppBuffer, 0, size); error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, MaxFrameCount, ep_no, pre_scale); if (error) { free(xfer->ppBuffer); free(xfer->pLength); } else { xfer->is_opened = 1; } return (error); } struct libusb20_transfer * libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex) { if (trIndex >= pdev->nTransfer) { return (NULL); } return (pdev->pTransfer + trIndex); } uint32_t libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer) { return (xfer->aFrames); } uint16_t libusb20_tr_get_time_complete(struct libusb20_transfer *xfer) { return (xfer->timeComplete); } uint32_t libusb20_tr_get_actual_length(struct libusb20_transfer *xfer) { uint32_t x; uint32_t actlen = 0; for (x = 0; x != xfer->aFrames; x++) { actlen += xfer->pLength[x]; } return (actlen); } uint32_t libusb20_tr_get_max_frames(struct libusb20_transfer *xfer) { return (xfer->maxFrames); } uint32_t libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer) { /* * Special Case NOTE: If the packet multiplier is non-zero for * High Speed USB, the value returned is equal to * "wMaxPacketSize * multiplier" ! */ return (xfer->maxPacketLen); } uint32_t libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer) { return (xfer->maxTotalLength); } uint8_t libusb20_tr_get_status(struct libusb20_transfer *xfer) { return (xfer->status); } uint8_t libusb20_tr_pending(struct libusb20_transfer *xfer) { return (xfer->is_pending); } void * libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer) { return (xfer->priv_sc0); } void * libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer) { return (xfer->priv_sc1); } void libusb20_tr_stop(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } if (!xfer->is_pending) { /* transfer not pending */ return; } if (xfer->is_cancel) { /* already cancelling */ return; } xfer->is_cancel = 1; /* we are cancelling */ xfer->pdev->methods->tr_cancel_async(xfer); return; } void libusb20_tr_drain(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } /* make sure that we are cancelling */ libusb20_tr_stop(xfer); if (xfer->is_pending) { xfer->is_draining = 1; } return; } void libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer) { xfer->pdev->methods->tr_clear_stall_sync(xfer); return; } void libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex) { xfer->ppBuffer[frIndex] = libusb20_pass_ptr(buffer); return; } void libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb) { xfer->callback = cb; return; } void libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags) { xfer->flags = flags; return; } uint32_t libusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t frIndex) { return (xfer->pLength[frIndex]); } void libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex) { xfer->pLength[frIndex] = length; return; } void libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0) { xfer->priv_sc0 = sc0; return; } void libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1) { xfer->priv_sc1 = sc1; return; } void libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout) { xfer->timeout = timeout; return; } void libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames) { if (nFrames > xfer->maxFrames) { /* should not happen */ nFrames = xfer->maxFrames; } xfer->nFrames = nFrames; return; } void libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) { xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf); xfer->pLength[0] = length; xfer->timeout = timeout; xfer->nFrames = 1; return; } void libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout) { uint16_t len; xfer->ppBuffer[0] = libusb20_pass_ptr(psetup); xfer->pLength[0] = 8; /* fixed */ xfer->timeout = timeout; len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8); if (len != 0) { xfer->nFrames = 2; xfer->ppBuffer[1] = libusb20_pass_ptr(pBuf); xfer->pLength[1] = len; } else { xfer->nFrames = 1; } return; } void libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) { xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf); xfer->pLength[0] = length; xfer->timeout = timeout; xfer->nFrames = 1; return; } void libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex) { if (frIndex >= xfer->maxFrames) { /* should not happen */ return; } xfer->ppBuffer[frIndex] = libusb20_pass_ptr(pBuf); xfer->pLength[frIndex] = length; return; } uint8_t libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t *pactlen, uint32_t timeout) { struct libusb20_device *pdev = xfer->pdev; uint32_t transfer_max; uint32_t transfer_act; uint8_t retval; /* set some sensible default value */ if (pactlen != NULL) *pactlen = 0; /* check for error condition */ if (libusb20_tr_pending(xfer)) return (LIBUSB20_ERROR_OTHER); do { /* compute maximum transfer length */ transfer_max = libusb20_tr_get_max_total_length(xfer); if (transfer_max > length) transfer_max = length; /* setup bulk or interrupt transfer */ libusb20_tr_setup_bulk(xfer, pbuf, transfer_max, timeout); /* start the transfer */ libusb20_tr_start(xfer); /* wait for transfer completion */ while (libusb20_dev_process(pdev) == 0) { if (libusb20_tr_pending(xfer) == 0) break; libusb20_dev_wait_process(pdev, -1); } transfer_act = libusb20_tr_get_actual_length(xfer); /* update actual length, if any */ if (pactlen != NULL) pactlen[0] += transfer_act; /* check transfer status */ retval = libusb20_tr_get_status(xfer); if (retval) break; /* check for short transfer */ if (transfer_act != transfer_max) break; /* update buffer pointer and length */ pbuf = ((uint8_t *)pbuf) + transfer_max; length = length - transfer_max; } while (length != 0); return (retval); } void libusb20_tr_submit(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } if (xfer->is_pending) { /* should not happen */ return; } xfer->is_pending = 1; /* we are pending */ xfer->is_cancel = 0; /* not cancelling */ xfer->is_restart = 0; /* not restarting */ xfer->pdev->methods->tr_submit(xfer); return; } void libusb20_tr_start(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } if (xfer->is_pending) { if (xfer->is_cancel) { /* cancelling - restart */ xfer->is_restart = 1; } /* transfer not pending */ return; } /* get into the callback */ libusb20_tr_callback_wrapper(xfer); return; } /* USB device operations */ int libusb20_dev_close(struct libusb20_device *pdev) { struct libusb20_transfer *xfer; uint16_t x; int error = 0; if (!pdev->is_opened) { return (LIBUSB20_ERROR_OTHER); } for (x = 0; x != pdev->nTransfer; x++) { xfer = pdev->pTransfer + x; if (!xfer->is_opened) { /* transfer is not opened */ continue; } libusb20_tr_drain(xfer); libusb20_tr_close(xfer); } if (pdev->pTransfer != NULL) { free(pdev->pTransfer); pdev->pTransfer = NULL; } error = pdev->beMethods->close_device(pdev); pdev->methods = &libusb20_dummy_methods; pdev->is_opened = 0; /* * The following variable is only used by the libusb v0.1 * compat layer: */ pdev->claimed_interface = 0; return (error); } int libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex) { int error; error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex); return (error); } struct LIBUSB20_DEVICE_DESC_DECODED * libusb20_dev_get_device_desc(struct libusb20_device *pdev) { return (&(pdev->ddesc)); } int libusb20_dev_get_fd(struct libusb20_device *pdev) { return (pdev->file); } int libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex) { int error; error = pdev->methods->kernel_driver_active(pdev, ifaceIndex); return (error); } int libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax) { struct libusb20_transfer *xfer; uint32_t size; uint16_t x; int error; if (pdev->is_opened) { return (LIBUSB20_ERROR_BUSY); } if (nTransferMax >= 256) { return (LIBUSB20_ERROR_INVALID_PARAM); } else if (nTransferMax != 0) { size = sizeof(pdev->pTransfer[0]) * nTransferMax; pdev->pTransfer = malloc(size); if (pdev->pTransfer == NULL) { return (LIBUSB20_ERROR_NO_MEM); } memset(pdev->pTransfer, 0, size); } /* initialise all transfers */ for (x = 0; x != nTransferMax; x++) { xfer = pdev->pTransfer + x; xfer->pdev = pdev; xfer->trIndex = x; xfer->callback = &dummy_callback; } /* set "nTransfer" early */ pdev->nTransfer = nTransferMax; error = pdev->beMethods->open_device(pdev, nTransferMax); if (error) { if (pdev->pTransfer != NULL) { free(pdev->pTransfer); pdev->pTransfer = NULL; } pdev->file = -1; pdev->file_ctrl = -1; pdev->nTransfer = 0; } else { pdev->is_opened = 1; } return (error); } int libusb20_dev_reset(struct libusb20_device *pdev) { int error; error = pdev->methods->reset_device(pdev); return (error); } int libusb20_dev_check_connected(struct libusb20_device *pdev) { int error; error = pdev->methods->check_connected(pdev); return (error); } int libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) { int error; error = pdev->methods->set_power_mode(pdev, power_mode); return (error); } uint8_t libusb20_dev_get_power_mode(struct libusb20_device *pdev) { int error; uint8_t power_mode; error = pdev->methods->get_power_mode(pdev, &power_mode); if (error) power_mode = LIBUSB20_POWER_ON; /* fake power mode */ return (power_mode); } int libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex) { int error; error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex); return (error); } int libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex) { int error; error = pdev->methods->set_config_index(pdev, configIndex); return (error); } int libusb20_dev_request_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags) { int error; error = pdev->methods->do_request_sync(pdev, setup, data, pactlen, timeout, flags); return (error); } int libusb20_dev_req_string_sync(struct libusb20_device *pdev, uint8_t str_index, uint16_t langid, void *ptr, uint16_t len) { struct LIBUSB20_CONTROL_SETUP_DECODED req; int error; /* make sure memory is initialised */ memset(ptr, 0, len); if (len < 4) { /* invalid length */ return (LIBUSB20_ERROR_INVALID_PARAM); } LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); /* * We need to read the USB string in two steps else some USB * devices will complain. */ req.bmRequestType = LIBUSB20_REQUEST_TYPE_STANDARD | LIBUSB20_RECIPIENT_DEVICE | LIBUSB20_ENDPOINT_IN; req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR; req.wValue = (LIBUSB20_DT_STRING << 8) | str_index; req.wIndex = langid; req.wLength = 4; /* bytes */ error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); if (error) { return (error); } req.wLength = *(uint8_t *)ptr; /* bytes */ if (req.wLength > len) { /* partial string read */ req.wLength = len; } error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); if (error) { return (error); } if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING) { return (LIBUSB20_ERROR_OTHER); } return (0); /* success */ } int libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, uint8_t str_index, void *ptr, uint16_t len) { char *buf; int error; uint16_t langid; uint16_t n; uint16_t i; uint16_t c; uint8_t temp[255]; uint8_t swap; /* the following code derives from the FreeBSD USB kernel */ if ((len < 1) || (ptr == NULL)) { /* too short buffer */ return (LIBUSB20_ERROR_INVALID_PARAM); } error = libusb20_dev_req_string_sync(pdev, 0, 0, temp, sizeof(temp)); if (error < 0) { *(uint8_t *)ptr = 0; /* zero terminate */ return (error); } langid = temp[2] | (temp[3] << 8); error = libusb20_dev_req_string_sync(pdev, str_index, langid, temp, sizeof(temp)); if (error < 0) { *(uint8_t *)ptr = 0; /* zero terminate */ return (error); } if (temp[0] < 2) { /* string length is too short */ *(uint8_t *)ptr = 0; /* zero terminate */ return (LIBUSB20_ERROR_OTHER); } /* reserve one byte for terminating zero */ len--; /* find maximum length */ n = (temp[0] / 2) - 1; if (n > len) { n = len; } /* reset swap state */ swap = 3; /* setup output buffer pointer */ buf = ptr; /* convert and filter */ for (i = 0; (i != n); i++) { c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8); /* convert from Unicode, handle buggy strings */ if (((c & 0xff00) == 0) && (swap & 1)) { /* Little Endian, default */ *buf = c; swap = 1; } else if (((c & 0x00ff) == 0) && (swap & 2)) { /* Big Endian */ *buf = c >> 8; swap = 2; } else { /* skip invalid character */ continue; } /* * Filter by default - we don't allow greater and less than * signs because they might confuse the dmesg printouts! */ if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) { /* skip invalid character */ continue; } buf++; } *buf = 0; /* zero terminate string */ return (0); } struct libusb20_config * libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex) { struct libusb20_config *retval = NULL; uint8_t *ptr; uint16_t len; uint8_t do_close; int error; if (!pdev->is_opened) { error = libusb20_dev_open(pdev, 0); if (error) { return (NULL); } do_close = 1; } else { do_close = 0; } error = pdev->methods->get_config_desc_full(pdev, &ptr, &len, configIndex); if (error) { goto done; } /* parse new config descriptor */ retval = libusb20_parse_config_desc(ptr); /* free config descriptor */ free(ptr); done: if (do_close) { error = libusb20_dev_close(pdev); } return (retval); } struct libusb20_device * libusb20_dev_alloc(void) { struct libusb20_device *pdev; pdev = malloc(sizeof(*pdev)); if (pdev == NULL) { return (NULL); } memset(pdev, 0, sizeof(*pdev)); pdev->file = -1; pdev->file_ctrl = -1; pdev->methods = &libusb20_dummy_methods; return (pdev); } uint8_t libusb20_dev_get_config_index(struct libusb20_device *pdev) { int error; uint8_t cfg_index; uint8_t do_close; if (!pdev->is_opened) { error = libusb20_dev_open(pdev, 0); if (error == 0) { do_close = 1; } else { do_close = 0; } } else { do_close = 0; } error = pdev->methods->get_config_index(pdev, &cfg_index); - if (error) { - cfg_index = 0 - 1; /* current config index */ - } + if (error) + cfg_index = 0xFF; /* current config index */ if (do_close) { if (libusb20_dev_close(pdev)) { /* ignore */ } } return (cfg_index); } uint8_t libusb20_dev_get_mode(struct libusb20_device *pdev) { return (pdev->usb_mode); } uint8_t libusb20_dev_get_speed(struct libusb20_device *pdev) { return (pdev->usb_speed); } /* if this function returns an error, the device is gone */ int libusb20_dev_process(struct libusb20_device *pdev) { int error; error = pdev->methods->process(pdev); return (error); } void libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout) { struct pollfd pfd[1]; if (!pdev->is_opened) { return; } pfd[0].fd = pdev->file; pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); pfd[0].revents = 0; if (poll(pfd, 1, timeout)) { /* ignore any error */ } return; } void libusb20_dev_free(struct libusb20_device *pdev) { if (pdev == NULL) { /* be NULL safe */ return; } if (pdev->is_opened) { if (libusb20_dev_close(pdev)) { /* ignore any errors */ } } free(pdev); return; } int libusb20_dev_get_info(struct libusb20_device *pdev, struct usb_device_info *pinfo) { if (pinfo == NULL) return (LIBUSB20_ERROR_INVALID_PARAM); return (pdev->beMethods->dev_get_info(pdev, pinfo)); } const char * libusb20_dev_get_backend_name(struct libusb20_device *pdev) { return (pdev->beMethods->get_backend_name()); } const char * libusb20_dev_get_desc(struct libusb20_device *pdev) { return (pdev->usb_desc); } void libusb20_dev_set_debug(struct libusb20_device *pdev, int debug) { pdev->debug = debug; return; } int libusb20_dev_get_debug(struct libusb20_device *pdev) { return (pdev->debug); } uint8_t libusb20_dev_get_address(struct libusb20_device *pdev) { return (pdev->device_address); } uint8_t libusb20_dev_get_parent_address(struct libusb20_device *pdev) { return (pdev->parent_address); } uint8_t libusb20_dev_get_parent_port(struct libusb20_device *pdev) { return (pdev->parent_port); } uint8_t libusb20_dev_get_bus_number(struct libusb20_device *pdev) { return (pdev->bus_number); } int libusb20_dev_get_iface_desc(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len) { if ((buf == NULL) || (len == 0)) return (LIBUSB20_ERROR_INVALID_PARAM); buf[0] = 0; /* set default string value */ return (pdev->beMethods->dev_get_iface_desc( pdev, iface_index, buf, len)); } /* USB backend operations */ int libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq)); } int libusb20_be_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq)); } int libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { return (pbe->methods->root_add_dev_quirk(pbe, pq)); } int libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { return (pbe->methods->root_remove_dev_quirk(pbe, pq)); } int libusb20_be_set_template(struct libusb20_backend *pbe, int temp) { return (pbe->methods->root_set_template(pbe, temp)); } int libusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp) { int temp; if (ptemp == NULL) ptemp = &temp; return (pbe->methods->root_get_template(pbe, ptemp)); } struct libusb20_device * libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev) { if (pbe == NULL) { pdev = NULL; } else if (pdev == NULL) { pdev = TAILQ_FIRST(&(pbe->usb_devs)); } else { pdev = TAILQ_NEXT(pdev, dev_entry); } return (pdev); } struct libusb20_backend * libusb20_be_alloc(const struct libusb20_backend_methods *methods) { struct libusb20_backend *pbe; pbe = malloc(sizeof(*pbe)); if (pbe == NULL) { return (NULL); } memset(pbe, 0, sizeof(*pbe)); TAILQ_INIT(&(pbe->usb_devs)); pbe->methods = methods; /* set backend methods */ /* do the initial device scan */ if (pbe->methods->init_backend) { pbe->methods->init_backend(pbe); } return (pbe); } struct libusb20_backend * libusb20_be_alloc_linux(void) { struct libusb20_backend *pbe; #ifdef __linux__ pbe = libusb20_be_alloc(&libusb20_linux_backend); #else pbe = NULL; #endif return (pbe); } struct libusb20_backend * libusb20_be_alloc_ugen20(void) { struct libusb20_backend *pbe; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) pbe = libusb20_be_alloc(&libusb20_ugen20_backend); #else pbe = NULL; #endif return (pbe); } struct libusb20_backend * libusb20_be_alloc_default(void) { struct libusb20_backend *pbe; pbe = libusb20_be_alloc_linux(); if (pbe) { return (pbe); } pbe = libusb20_be_alloc_ugen20(); if (pbe) { return (pbe); } return (NULL); /* no backend found */ } void libusb20_be_free(struct libusb20_backend *pbe) { struct libusb20_device *pdev; if (pbe == NULL) { /* be NULL safe */ return; } while ((pdev = libusb20_be_device_foreach(pbe, NULL))) { libusb20_be_dequeue_device(pbe, pdev); libusb20_dev_free(pdev); } if (pbe->methods->exit_backend) { pbe->methods->exit_backend(pbe); } /* free backend */ free(pbe); } void libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) { pdev->beMethods = pbe->methods; /* copy backend methods */ TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry); } void libusb20_be_dequeue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) { TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry); } const char * libusb20_strerror(int code) { switch (code) { case LIBUSB20_SUCCESS: return ("Success"); case LIBUSB20_ERROR_IO: return ("I/O error"); case LIBUSB20_ERROR_INVALID_PARAM: return ("Invalid parameter"); case LIBUSB20_ERROR_ACCESS: return ("Permissions error"); case LIBUSB20_ERROR_NO_DEVICE: return ("No device"); case LIBUSB20_ERROR_NOT_FOUND: return ("Not found"); case LIBUSB20_ERROR_BUSY: return ("Device busy"); case LIBUSB20_ERROR_TIMEOUT: return ("Timeout"); case LIBUSB20_ERROR_OVERFLOW: return ("Overflow"); case LIBUSB20_ERROR_PIPE: return ("Pipe error"); case LIBUSB20_ERROR_INTERRUPTED: return ("Interrupted"); case LIBUSB20_ERROR_NO_MEM: return ("Out of memory"); case LIBUSB20_ERROR_NOT_SUPPORTED: return ("Not supported"); case LIBUSB20_ERROR_OTHER: return ("Other error"); default: return ("Unknown error"); } } const char * libusb20_error_name(int code) { switch (code) { case LIBUSB20_SUCCESS: return ("LIBUSB20_SUCCESS"); case LIBUSB20_ERROR_IO: return ("LIBUSB20_ERROR_IO"); case LIBUSB20_ERROR_INVALID_PARAM: return ("LIBUSB20_ERROR_INVALID_PARAM"); case LIBUSB20_ERROR_ACCESS: return ("LIBUSB20_ERROR_ACCESS"); case LIBUSB20_ERROR_NO_DEVICE: return ("LIBUSB20_ERROR_NO_DEVICE"); case LIBUSB20_ERROR_NOT_FOUND: return ("LIBUSB20_ERROR_NOT_FOUND"); case LIBUSB20_ERROR_BUSY: return ("LIBUSB20_ERROR_BUSY"); case LIBUSB20_ERROR_TIMEOUT: return ("LIBUSB20_ERROR_TIMEOUT"); case LIBUSB20_ERROR_OVERFLOW: return ("LIBUSB20_ERROR_OVERFLOW"); case LIBUSB20_ERROR_PIPE: return ("LIBUSB20_ERROR_PIPE"); case LIBUSB20_ERROR_INTERRUPTED: return ("LIBUSB20_ERROR_INTERRUPTED"); case LIBUSB20_ERROR_NO_MEM: return ("LIBUSB20_ERROR_NO_MEM"); case LIBUSB20_ERROR_NOT_SUPPORTED: return ("LIBUSB20_ERROR_NOT_SUPPORTED"); case LIBUSB20_ERROR_OTHER: return ("LIBUSB20_ERROR_OTHER"); default: return ("LIBUSB20_ERROR_UNKNOWN"); } } Index: head/lib/libusb/libusb20_desc.c =================================================================== --- head/lib/libusb/libusb20_desc.c (revision 234490) +++ head/lib/libusb/libusb20_desc.c (revision 234491) @@ -1,792 +1,792 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" static const uint32_t libusb20_me_encode_empty[2]; /* dummy */ LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC); LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR); /*------------------------------------------------------------------------* * libusb20_parse_config_desc * * Return values: * NULL: Out of memory. * Else: A valid config structure pointer which must be passed to "free()" *------------------------------------------------------------------------*/ struct libusb20_config * libusb20_parse_config_desc(const void *config_desc) { struct libusb20_config *lub_config; struct libusb20_interface *lub_interface; struct libusb20_interface *lub_alt_interface; struct libusb20_interface *last_if; struct libusb20_endpoint *lub_endpoint; struct libusb20_endpoint *last_ep; struct libusb20_me_struct pcdesc; const uint8_t *ptr; uint32_t size; uint16_t niface_no_alt; uint16_t niface; uint16_t nendpoint; - uint8_t iface_no; + uint16_t iface_no; ptr = config_desc; if (ptr[1] != LIBUSB20_DT_CONFIG) { return (NULL); /* not config descriptor */ } /* * The first "bInterfaceNumber" should never have the value 0xff. * Then it is corrupt. */ niface_no_alt = 0; nendpoint = 0; niface = 0; - iface_no = 0 - 1; + iface_no = 0xFFFF; ptr = NULL; /* get "wTotalLength" and setup "pcdesc" */ pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0); pcdesc.len = ((const uint8_t *)config_desc)[2] | (((const uint8_t *)config_desc)[3] << 8); pcdesc.type = LIBUSB20_ME_IS_RAW; /* descriptor pre-scan */ while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) { if (ptr[1] == LIBUSB20_DT_ENDPOINT) { nendpoint++; } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) { niface++; /* check "bInterfaceNumber" */ if (ptr[2] != iface_no) { iface_no = ptr[2]; niface_no_alt++; } } } /* sanity checking */ if (niface >= 256) { return (NULL); /* corrupt */ } if (nendpoint >= 256) { return (NULL); /* corrupt */ } size = sizeof(*lub_config) + (niface * sizeof(*lub_interface)) + (nendpoint * sizeof(*lub_endpoint)) + pcdesc.len; lub_config = malloc(size); if (lub_config == NULL) { return (NULL); /* out of memory */ } /* make sure memory is initialised */ memset(lub_config, 0, size); lub_interface = (void *)(lub_config + 1); lub_alt_interface = (void *)(lub_interface + niface_no_alt); lub_endpoint = (void *)(lub_interface + niface); /* * Make a copy of the config descriptor, so that the caller can free * the inital config descriptor pointer! */ ptr = (void *)(lub_endpoint + nendpoint); memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len); pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0); config_desc = LIBUSB20_ADD_BYTES(ptr, 0); /* init config structure */ ptr = config_desc; LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc); if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) { /* ignore */ } lub_config->num_interface = 0; lub_config->interface = lub_interface; lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); lub_config->extra.len = -ptr[0]; lub_config->extra.type = LIBUSB20_ME_IS_RAW; /* reset states */ niface = 0; - iface_no = 0 - 1; + iface_no = 0xFFFF; ptr = NULL; lub_interface--; lub_endpoint--; last_if = NULL; last_ep = NULL; /* descriptor pre-scan */ while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) { if (ptr[1] == LIBUSB20_DT_ENDPOINT) { if (last_if) { lub_endpoint++; last_ep = lub_endpoint; last_if->num_endpoints++; LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc); if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) { /* ignore */ } last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); last_ep->extra.len = 0; last_ep->extra.type = LIBUSB20_ME_IS_RAW; } else { lub_config->extra.len += ptr[0]; } } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) { if (ptr[2] != iface_no) { /* new interface */ iface_no = ptr[2]; lub_interface++; lub_config->num_interface++; last_if = lub_interface; niface++; } else { /* one more alternate setting */ lub_interface->num_altsetting++; last_if = lub_alt_interface; lub_alt_interface++; } LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc); if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) { /* ignore */ } /* * Sometimes USB devices have corrupt interface * descriptors and we need to overwrite the provided * interface number! */ last_if->desc.bInterfaceNumber = niface - 1; last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); last_if->extra.len = 0; last_if->extra.type = LIBUSB20_ME_IS_RAW; last_if->endpoints = lub_endpoint + 1; last_if->altsetting = lub_alt_interface; last_if->num_altsetting = 0; last_if->num_endpoints = 0; last_ep = NULL; } else { /* unknown descriptor */ if (last_if) { if (last_ep) { last_ep->extra.len += ptr[0]; } else { last_if->extra.len += ptr[0]; } } else { lub_config->extra.len += ptr[0]; } } } return (lub_config); } /*------------------------------------------------------------------------* * libusb20_desc_foreach * * Safe traversal of USB descriptors. * * Return values: * NULL: End of descriptors * Else: Pointer to next descriptor *------------------------------------------------------------------------*/ const uint8_t * libusb20_desc_foreach(const struct libusb20_me_struct *pdesc, const uint8_t *psubdesc) { const uint8_t *start; const uint8_t *end; const uint8_t *desc_next; /* be NULL safe */ if (pdesc == NULL) return (NULL); start = (const uint8_t *)pdesc->ptr; end = LIBUSB20_ADD_BYTES(start, pdesc->len); /* get start of next descriptor */ if (psubdesc == NULL) psubdesc = start; else psubdesc = psubdesc + psubdesc[0]; /* check that the next USB descriptor is within the range */ if ((psubdesc < start) || (psubdesc >= end)) return (NULL); /* out of range, or EOD */ /* check start of the second next USB descriptor, if any */ desc_next = psubdesc + psubdesc[0]; if ((desc_next < start) || (desc_next > end)) return (NULL); /* out of range */ /* check minimum descriptor length */ if (psubdesc[0] < 3) return (NULL); /* too short descriptor */ return (psubdesc); /* return start of next descriptor */ } /*------------------------------------------------------------------------* * libusb20_me_get_1 - safety wrapper to read out one byte *------------------------------------------------------------------------*/ uint8_t libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset) { if (offset < ie->len) { return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset))); } return (0); } /*------------------------------------------------------------------------* * libusb20_me_get_2 - safety wrapper to read out one word *------------------------------------------------------------------------*/ uint16_t libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset) { return (libusb20_me_get_1(ie, offset) | (libusb20_me_get_1(ie, offset + 1) << 8)); } /*------------------------------------------------------------------------* * libusb20_me_encode - encode a message structure * * Description of parameters: * "len" - maximum length of output buffer * "ptr" - pointer to output buffer. If NULL, no data will be written * "pd" - source structure * * Return values: * 0..65535 - Number of bytes used, limited by the "len" input parameter. *------------------------------------------------------------------------*/ uint16_t libusb20_me_encode(void *ptr, uint16_t len, const void *pd) { const uint8_t *pf; /* pointer to format data */ uint8_t *buf; /* pointer to output buffer */ uint32_t pd_offset; /* decoded structure offset */ uint16_t len_old; /* old length */ uint16_t pd_count; /* decoded element count */ uint8_t me; /* message element */ /* initialise */ len_old = len; buf = ptr; pd_offset = sizeof(void *); pf = (*((struct libusb20_me_format *const *)pd))->format; /* scan */ while (1) { /* get information element */ me = (pf[0]) & LIBUSB20_ME_MASK; pd_count = pf[1] | (pf[2] << 8); pf += 3; /* encode the message element */ switch (me) { case LIBUSB20_ME_INT8: while (pd_count--) { uint8_t temp; if (len < 1) /* overflow */ goto done; if (buf) { temp = *((const uint8_t *) LIBUSB20_ADD_BYTES(pd, pd_offset)); buf[0] = temp; buf += 1; } pd_offset += 1; len -= 1; } break; case LIBUSB20_ME_INT16: pd_offset = -((-pd_offset) & ~1); /* align */ while (pd_count--) { uint16_t temp; if (len < 2) /* overflow */ goto done; if (buf) { temp = *((const uint16_t *) LIBUSB20_ADD_BYTES(pd, pd_offset)); buf[1] = (temp >> 8) & 0xFF; buf[0] = temp & 0xFF; buf += 2; } pd_offset += 2; len -= 2; } break; case LIBUSB20_ME_INT32: pd_offset = -((-pd_offset) & ~3); /* align */ while (pd_count--) { uint32_t temp; if (len < 4) /* overflow */ goto done; if (buf) { temp = *((const uint32_t *) LIBUSB20_ADD_BYTES(pd, pd_offset)); buf[3] = (temp >> 24) & 0xFF; buf[2] = (temp >> 16) & 0xFF; buf[1] = (temp >> 8) & 0xFF; buf[0] = temp & 0xFF; buf += 4; } pd_offset += 4; len -= 4; } break; case LIBUSB20_ME_INT64: pd_offset = -((-pd_offset) & ~7); /* align */ while (pd_count--) { uint64_t temp; if (len < 8) /* overflow */ goto done; if (buf) { temp = *((const uint64_t *) LIBUSB20_ADD_BYTES(pd, pd_offset)); buf[7] = (temp >> 56) & 0xFF; buf[6] = (temp >> 48) & 0xFF; buf[5] = (temp >> 40) & 0xFF; buf[4] = (temp >> 32) & 0xFF; buf[3] = (temp >> 24) & 0xFF; buf[2] = (temp >> 16) & 0xFF; buf[1] = (temp >> 8) & 0xFF; buf[0] = temp & 0xFF; buf += 8; } pd_offset += 8; len -= 8; } break; case LIBUSB20_ME_STRUCT: pd_offset = -((-pd_offset) & ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */ while (pd_count--) { void *src_ptr; uint16_t src_len; struct libusb20_me_struct *ps; ps = LIBUSB20_ADD_BYTES(pd, pd_offset); switch (ps->type) { case LIBUSB20_ME_IS_RAW: src_len = ps->len; src_ptr = ps->ptr; break; case LIBUSB20_ME_IS_ENCODED: if (ps->len == 0) { /* * Length is encoded * in the data itself * and should be * correct: */ - ps->len = 0 - 1; + ps->len = 0xFFFF; } src_len = libusb20_me_get_1(pd, 0); src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1); if (src_len == 0xFF) { /* length is escaped */ src_len = libusb20_me_get_2(pd, 1); src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 3); } break; case LIBUSB20_ME_IS_DECODED: /* reserve 3 length bytes */ src_len = libusb20_me_encode(NULL, - 0 - 1 - 3, ps->ptr); + 0xFFFF - 3, ps->ptr); src_ptr = NULL; break; default: /* empty structure */ src_len = 0; src_ptr = NULL; break; } if (src_len > 0xFE) { - if (src_len > (uint16_t)(0 - 1 - 3)) + if (src_len > (0xFFFF - 3)) /* overflow */ goto done; if (len < (src_len + 3)) /* overflow */ goto done; if (buf) { buf[0] = 0xFF; buf[1] = (src_len & 0xFF); buf[2] = (src_len >> 8) & 0xFF; buf += 3; } len -= (src_len + 3); } else { if (len < (src_len + 1)) /* overflow */ goto done; if (buf) { buf[0] = (src_len & 0xFF); buf += 1; } len -= (src_len + 1); } /* check for buffer and non-zero length */ if (buf && src_len) { if (ps->type == LIBUSB20_ME_IS_DECODED) { /* * Repeat encode * procedure - we have * room for the * complete structure: */ uint16_t dummy; dummy = libusb20_me_encode(buf, - 0 - 1 - 3, ps->ptr); + 0xFFFF - 3, ps->ptr); } else { bcopy(src_ptr, buf, src_len); } buf += src_len; } pd_offset += sizeof(struct libusb20_me_struct); } break; default: goto done; } } done: return (len_old - len); } /*------------------------------------------------------------------------* * libusb20_me_decode - decode a message into a decoded structure * * Description of parameters: * "ptr" - message pointer * "len" - message length * "pd" - pointer to decoded structure * * Returns: * "0..65535" - number of bytes decoded, limited by "len" *------------------------------------------------------------------------*/ uint16_t libusb20_me_decode(const void *ptr, uint16_t len, void *pd) { const uint8_t *pf; /* pointer to format data */ const uint8_t *buf; /* pointer to input buffer */ uint32_t pd_offset; /* decoded structure offset */ uint16_t len_old; /* old length */ uint16_t pd_count; /* decoded element count */ uint8_t me; /* message element */ /* initialise */ len_old = len; buf = ptr; pd_offset = sizeof(void *); pf = (*((struct libusb20_me_format **)pd))->format; /* scan */ while (1) { /* get information element */ me = (pf[0]) & LIBUSB20_ME_MASK; pd_count = pf[1] | (pf[2] << 8); pf += 3; /* decode the message element by type */ switch (me) { case LIBUSB20_ME_INT8: while (pd_count--) { uint8_t temp; if (len < 1) { len = 0; temp = 0; } else { len -= 1; temp = buf[0]; buf++; } *((uint8_t *)LIBUSB20_ADD_BYTES(pd, pd_offset)) = temp; pd_offset += 1; } break; case LIBUSB20_ME_INT16: pd_offset = -((-pd_offset) & ~1); /* align */ while (pd_count--) { uint16_t temp; if (len < 2) { len = 0; temp = 0; } else { len -= 2; temp = buf[1] << 8; temp |= buf[0]; buf += 2; } *((uint16_t *)LIBUSB20_ADD_BYTES(pd, pd_offset)) = temp; pd_offset += 2; } break; case LIBUSB20_ME_INT32: pd_offset = -((-pd_offset) & ~3); /* align */ while (pd_count--) { uint32_t temp; if (len < 4) { len = 0; temp = 0; } else { len -= 4; temp = buf[3] << 24; temp |= buf[2] << 16; temp |= buf[1] << 8; temp |= buf[0]; buf += 4; } *((uint32_t *)LIBUSB20_ADD_BYTES(pd, pd_offset)) = temp; pd_offset += 4; } break; case LIBUSB20_ME_INT64: pd_offset = -((-pd_offset) & ~7); /* align */ while (pd_count--) { uint64_t temp; if (len < 8) { len = 0; temp = 0; } else { len -= 8; temp = ((uint64_t)buf[7]) << 56; temp |= ((uint64_t)buf[6]) << 48; temp |= ((uint64_t)buf[5]) << 40; temp |= ((uint64_t)buf[4]) << 32; temp |= buf[3] << 24; temp |= buf[2] << 16; temp |= buf[1] << 8; temp |= buf[0]; buf += 8; } *((uint64_t *)LIBUSB20_ADD_BYTES(pd, pd_offset)) = temp; pd_offset += 8; } break; case LIBUSB20_ME_STRUCT: pd_offset = -((-pd_offset) & ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */ while (pd_count--) { uint16_t temp; uint16_t dummy; struct libusb20_me_struct *ps; ps = LIBUSB20_ADD_BYTES(pd, pd_offset); if (ps->type == LIBUSB20_ME_IS_ENCODED) { /* * Pre-store a de-constified * pointer to the raw * structure: */ ps->ptr = LIBUSB20_ADD_BYTES(buf, 0); /* * Get the correct number of * length bytes: */ if (len != 0) { if (buf[0] == 0xFF) { ps->len = 3; } else { ps->len = 1; } } else { ps->len = 0; } } /* get the structure length */ if (len != 0) { if (buf[0] == 0xFF) { if (len < 3) { len = 0; temp = 0; } else { len -= 3; temp = buf[1] | (buf[2] << 8); buf += 3; } } else { len -= 1; temp = buf[0]; buf += 1; } } else { len = 0; temp = 0; } /* check for invalid length */ if (temp > len) { len = 0; temp = 0; } /* check wanted structure type */ switch (ps->type) { case LIBUSB20_ME_IS_ENCODED: /* check for zero length */ if (temp == 0) { /* * The pointer must * be valid: */ ps->ptr = LIBUSB20_ADD_BYTES( libusb20_me_encode_empty, 0); ps->len = 1; } else { ps->len += temp; } break; case LIBUSB20_ME_IS_RAW: /* update length and pointer */ ps->len = temp; ps->ptr = LIBUSB20_ADD_BYTES(buf, 0); break; case LIBUSB20_ME_IS_EMPTY: case LIBUSB20_ME_IS_DECODED: /* check for non-zero length */ if (temp != 0) { /* update type */ ps->type = LIBUSB20_ME_IS_DECODED; ps->len = 0; /* * Recursivly decode * the next structure */ dummy = libusb20_me_decode(buf, temp, ps->ptr); } else { /* update type */ ps->type = LIBUSB20_ME_IS_EMPTY; ps->len = 0; } break; default: /* * nothing to do - should * not happen */ ps->ptr = NULL; ps->len = 0; break; } buf += temp; len -= temp; pd_offset += sizeof(struct libusb20_me_struct); } break; default: goto done; } } done: return (len_old - len); } Index: head/lib/libusb/libusb20_ugen20.c =================================================================== --- head/lib/libusb/libusb20_ugen20.c (revision 234490) +++ head/lib/libusb/libusb20_ugen20.c (revision 234491) @@ -1,1022 +1,1022 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" #include #include #include static libusb20_init_backend_t ugen20_init_backend; static libusb20_open_device_t ugen20_open_device; static libusb20_close_device_t ugen20_close_device; static libusb20_get_backend_name_t ugen20_get_backend_name; static libusb20_exit_backend_t ugen20_exit_backend; static libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc; static libusb20_dev_get_info_t ugen20_dev_get_info; static libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk; static libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name; static libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk; static libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk; static libusb20_root_set_template_t ugen20_root_set_template; static libusb20_root_get_template_t ugen20_root_get_template; const struct libusb20_backend_methods libusb20_ugen20_backend = { LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20) }; /* USB device specific */ static libusb20_get_config_desc_full_t ugen20_get_config_desc_full; static libusb20_get_config_index_t ugen20_get_config_index; static libusb20_set_config_index_t ugen20_set_config_index; static libusb20_set_alt_index_t ugen20_set_alt_index; static libusb20_reset_device_t ugen20_reset_device; static libusb20_check_connected_t ugen20_check_connected; static libusb20_set_power_mode_t ugen20_set_power_mode; static libusb20_get_power_mode_t ugen20_get_power_mode; static libusb20_kernel_driver_active_t ugen20_kernel_driver_active; static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver; static libusb20_do_request_sync_t ugen20_do_request_sync; static libusb20_process_t ugen20_process; /* USB transfer specific */ static libusb20_tr_open_t ugen20_tr_open; static libusb20_tr_close_t ugen20_tr_close; static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync; static libusb20_tr_submit_t ugen20_tr_submit; static libusb20_tr_cancel_async_t ugen20_tr_cancel_async; static const struct libusb20_device_methods libusb20_ugen20_device_methods = { LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20) }; static const char * ugen20_get_backend_name(void) { return ("FreeBSD UGEN 2.0"); } static uint32_t ugen20_path_convert_one(const char **pp) { const char *ptr; uint32_t temp = 0; ptr = *pp; while ((*ptr >= '0') && (*ptr <= '9')) { temp *= 10; temp += (*ptr - '0'); if (temp >= 1000000) { /* catch overflow early */ - return (0 - 1); + return (0xFFFFFFFF); } ptr++; } if (*ptr == '.') { /* skip dot */ ptr++; } *pp = ptr; return (temp); } static int ugen20_enumerate(struct libusb20_device *pdev, const char *id) { const char *tmp = id; struct usb_device_descriptor ddesc; struct usb_device_info devinfo; uint32_t plugtime; char buf[64]; int f; int error; pdev->bus_number = ugen20_path_convert_one(&tmp); pdev->device_address = ugen20_path_convert_one(&tmp); snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", pdev->bus_number, pdev->device_address); f = open(buf, O_RDWR); if (f < 0) { return (LIBUSB20_ERROR_OTHER); } if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) { error = LIBUSB20_ERROR_OTHER; goto done; } /* store when the device was plugged */ pdev->session_data.plugtime = plugtime; if (ioctl(f, USB_GET_DEVICE_DESC, &ddesc)) { error = LIBUSB20_ERROR_OTHER; goto done; } LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc)); libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc)); if (pdev->ddesc.bNumConfigurations == 0) { error = LIBUSB20_ERROR_OTHER; goto done; } else if (pdev->ddesc.bNumConfigurations >= 8) { error = LIBUSB20_ERROR_OTHER; goto done; } if (ioctl(f, USB_GET_DEVICEINFO, &devinfo)) { error = LIBUSB20_ERROR_OTHER; goto done; } switch (devinfo.udi_mode) { case USB_MODE_DEVICE: pdev->usb_mode = LIBUSB20_MODE_DEVICE; break; default: pdev->usb_mode = LIBUSB20_MODE_HOST; break; } switch (devinfo.udi_speed) { case USB_SPEED_LOW: pdev->usb_speed = LIBUSB20_SPEED_LOW; break; case USB_SPEED_FULL: pdev->usb_speed = LIBUSB20_SPEED_FULL; break; case USB_SPEED_HIGH: pdev->usb_speed = LIBUSB20_SPEED_HIGH; break; case USB_SPEED_VARIABLE: pdev->usb_speed = LIBUSB20_SPEED_VARIABLE; break; case USB_SPEED_SUPER: pdev->usb_speed = LIBUSB20_SPEED_SUPER; break; default: pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN; break; } /* get parent HUB index and port */ pdev->parent_address = devinfo.udi_hubindex; pdev->parent_port = devinfo.udi_hubport; /* generate a nice description for printout */ snprintf(pdev->usb_desc, sizeof(pdev->usb_desc), USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number, pdev->device_address, devinfo.udi_product, devinfo.udi_vendor, pdev->bus_number); error = 0; done: close(f); return (error); } struct ugen20_urd_state { struct usb_read_dir urd; uint32_t nparsed; int f; uint8_t *ptr; const char *src; const char *dst; uint8_t buf[256]; uint8_t dummy_zero[1]; }; static int ugen20_readdir(struct ugen20_urd_state *st) { ; /* style fix */ repeat: if (st->ptr == NULL) { st->urd.urd_startentry += st->nparsed; st->urd.urd_data = libusb20_pass_ptr(st->buf); st->urd.urd_maxlen = sizeof(st->buf); st->nparsed = 0; if (ioctl(st->f, USB_READ_DIR, &st->urd)) { return (EINVAL); } st->ptr = st->buf; } if (st->ptr[0] == 0) { if (st->nparsed) { st->ptr = NULL; goto repeat; } else { return (ENXIO); } } st->src = (void *)(st->ptr + 1); st->dst = st->src + strlen(st->src) + 1; st->ptr = st->ptr + st->ptr[0]; st->nparsed++; if ((st->ptr < st->buf) || (st->ptr > st->dummy_zero)) { /* invalid entry */ return (EINVAL); } return (0); } static int ugen20_init_backend(struct libusb20_backend *pbe) { struct ugen20_urd_state state; struct libusb20_device *pdev; memset(&state, 0, sizeof(state)); state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); if (state.f < 0) return (LIBUSB20_ERROR_OTHER); while (ugen20_readdir(&state) == 0) { if ((state.src[0] != 'u') || (state.src[1] != 'g') || (state.src[2] != 'e') || (state.src[3] != 'n')) { continue; } pdev = libusb20_dev_alloc(); if (pdev == NULL) { continue; } if (ugen20_enumerate(pdev, state.src + 4)) { libusb20_dev_free(pdev); continue; } /* put the device on the backend list */ libusb20_be_enqueue_device(pbe, pdev); } close(state.f); return (0); /* success */ } static void ugen20_tr_release(struct libusb20_device *pdev) { struct usb_fs_uninit fs_uninit; if (pdev->nTransfer == 0) { return; } /* release all pending USB transfers */ if (pdev->privBeData != NULL) { memset(&fs_uninit, 0, sizeof(fs_uninit)); if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) { /* ignore any errors of this kind */ } } return; } static int ugen20_tr_renew(struct libusb20_device *pdev) { struct usb_fs_init fs_init; struct usb_fs_endpoint *pfse; int error; uint32_t size; uint16_t nMaxTransfer; nMaxTransfer = pdev->nTransfer; error = 0; if (nMaxTransfer == 0) { goto done; } size = nMaxTransfer * sizeof(*pfse); if (pdev->privBeData == NULL) { pfse = malloc(size); if (pfse == NULL) { error = LIBUSB20_ERROR_NO_MEM; goto done; } pdev->privBeData = pfse; } /* reset endpoint data */ memset(pdev->privBeData, 0, size); memset(&fs_init, 0, sizeof(fs_init)); fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData); fs_init.ep_index_max = nMaxTransfer; if (ioctl(pdev->file, USB_FS_INIT, &fs_init)) { error = LIBUSB20_ERROR_OTHER; goto done; } done: return (error); } static int ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer) { uint32_t plugtime; char buf[64]; int f; int g; int error; snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", pdev->bus_number, pdev->device_address); /* * We need two file handles, one for the control endpoint and one * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised * kernel locking. */ g = open(buf, O_RDWR); if (g < 0) { return (LIBUSB20_ERROR_NO_DEVICE); } f = open(buf, O_RDWR); if (f < 0) { close(g); return (LIBUSB20_ERROR_NO_DEVICE); } if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) { error = LIBUSB20_ERROR_OTHER; goto done; } /* check that the correct device is still plugged */ if (pdev->session_data.plugtime != plugtime) { error = LIBUSB20_ERROR_NO_DEVICE; goto done; } /* need to set this before "tr_renew()" */ pdev->file = f; pdev->file_ctrl = g; /* renew all USB transfers */ error = ugen20_tr_renew(pdev); if (error) { goto done; } /* set methods */ pdev->methods = &libusb20_ugen20_device_methods; done: if (error) { if (pdev->privBeData) { /* cleanup after "tr_renew()" */ free(pdev->privBeData); pdev->privBeData = NULL; } pdev->file = -1; pdev->file_ctrl = -1; close(f); close(g); } return (error); } static int ugen20_close_device(struct libusb20_device *pdev) { struct usb_fs_uninit fs_uninit; if (pdev->privBeData) { memset(&fs_uninit, 0, sizeof(fs_uninit)); if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) { /* ignore this error */ } free(pdev->privBeData); } pdev->nTransfer = 0; pdev->privBeData = NULL; close(pdev->file); close(pdev->file_ctrl); pdev->file = -1; pdev->file_ctrl = -1; return (0); /* success */ } static void ugen20_exit_backend(struct libusb20_backend *pbe) { return; /* nothing to do */ } static int ugen20_get_config_desc_full(struct libusb20_device *pdev, uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index) { struct usb_gen_descriptor gen_desc; struct usb_config_descriptor cdesc; uint8_t *ptr; uint16_t len; int error; /* make sure memory is initialised */ memset(&cdesc, 0, sizeof(cdesc)); memset(&gen_desc, 0, sizeof(gen_desc)); gen_desc.ugd_data = libusb20_pass_ptr(&cdesc); gen_desc.ugd_maxlen = sizeof(cdesc); gen_desc.ugd_config_index = cfg_index; error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc); if (error) { return (LIBUSB20_ERROR_OTHER); } len = UGETW(cdesc.wTotalLength); if (len < sizeof(cdesc)) { /* corrupt descriptor */ return (LIBUSB20_ERROR_OTHER); } ptr = malloc(len); if (!ptr) { return (LIBUSB20_ERROR_NO_MEM); } /* make sure memory is initialised */ memset(ptr, 0, len); gen_desc.ugd_data = libusb20_pass_ptr(ptr); gen_desc.ugd_maxlen = len; error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc); if (error) { free(ptr); return (LIBUSB20_ERROR_OTHER); } /* make sure that the device doesn't fool us */ memcpy(ptr, &cdesc, sizeof(cdesc)); *ppbuf = ptr; *plen = len; return (0); /* success */ } static int ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex) { int temp; if (ioctl(pdev->file_ctrl, USB_GET_CONFIG, &temp)) { return (LIBUSB20_ERROR_OTHER); } *pindex = temp; return (0); } static int ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index) { int temp = cfg_index; /* release all active USB transfers */ ugen20_tr_release(pdev); if (ioctl(pdev->file_ctrl, USB_SET_CONFIG, &temp)) { return (LIBUSB20_ERROR_OTHER); } return (ugen20_tr_renew(pdev)); } static int ugen20_set_alt_index(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index) { struct usb_alt_interface alt_iface; memset(&alt_iface, 0, sizeof(alt_iface)); alt_iface.uai_interface_index = iface_index; alt_iface.uai_alt_index = alt_index; /* release all active USB transfers */ ugen20_tr_release(pdev); if (ioctl(pdev->file_ctrl, USB_SET_ALTINTERFACE, &alt_iface)) { return (LIBUSB20_ERROR_OTHER); } return (ugen20_tr_renew(pdev)); } static int ugen20_reset_device(struct libusb20_device *pdev) { int temp = 0; /* release all active USB transfers */ ugen20_tr_release(pdev); if (ioctl(pdev->file_ctrl, USB_DEVICEENUMERATE, &temp)) { return (LIBUSB20_ERROR_OTHER); } return (ugen20_tr_renew(pdev)); } static int ugen20_check_connected(struct libusb20_device *pdev) { uint32_t plugtime; int error = 0; if (ioctl(pdev->file_ctrl, USB_GET_PLUGTIME, &plugtime)) { error = LIBUSB20_ERROR_NO_DEVICE; goto done; } if (pdev->session_data.plugtime != plugtime) { error = LIBUSB20_ERROR_NO_DEVICE; goto done; } done: return (error); } static int ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) { int temp; switch (power_mode) { case LIBUSB20_POWER_OFF: temp = USB_POWER_MODE_OFF; break; case LIBUSB20_POWER_ON: temp = USB_POWER_MODE_ON; break; case LIBUSB20_POWER_SAVE: temp = USB_POWER_MODE_SAVE; break; case LIBUSB20_POWER_SUSPEND: temp = USB_POWER_MODE_SUSPEND; break; case LIBUSB20_POWER_RESUME: temp = USB_POWER_MODE_RESUME; break; default: return (LIBUSB20_ERROR_INVALID_PARAM); } if (ioctl(pdev->file_ctrl, USB_SET_POWER_MODE, &temp)) { return (LIBUSB20_ERROR_OTHER); } return (0); } static int ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode) { int temp; if (ioctl(pdev->file_ctrl, USB_GET_POWER_MODE, &temp)) { return (LIBUSB20_ERROR_OTHER); } switch (temp) { case USB_POWER_MODE_OFF: temp = LIBUSB20_POWER_OFF; break; case USB_POWER_MODE_ON: temp = LIBUSB20_POWER_ON; break; case USB_POWER_MODE_SAVE: temp = LIBUSB20_POWER_SAVE; break; case USB_POWER_MODE_SUSPEND: temp = LIBUSB20_POWER_SUSPEND; break; case USB_POWER_MODE_RESUME: temp = LIBUSB20_POWER_RESUME; break; default: temp = LIBUSB20_POWER_ON; break; } *power_mode = temp; return (0); /* success */ } static int ugen20_kernel_driver_active(struct libusb20_device *pdev, uint8_t iface_index) { int temp = iface_index; if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_ACTIVE, &temp)) { return (LIBUSB20_ERROR_OTHER); } return (0); /* kernel driver is active */ } static int ugen20_detach_kernel_driver(struct libusb20_device *pdev, uint8_t iface_index) { int temp = iface_index; if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_DETACH, &temp)) { return (LIBUSB20_ERROR_OTHER); } return (0); /* kernel driver is active */ } static int ugen20_do_request_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags) { struct usb_ctl_request req; memset(&req, 0, sizeof(req)); req.ucr_data = libusb20_pass_ptr(data); if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { req.ucr_flags |= USB_SHORT_XFER_OK; } if (libusb20_me_encode(&req.ucr_request, sizeof(req.ucr_request), setup)) { /* ignore */ } if (ioctl(pdev->file_ctrl, USB_DO_REQUEST, &req)) { return (LIBUSB20_ERROR_OTHER); } if (pactlen) { /* get actual length */ *pactlen = req.ucr_actlen; } return (0); /* kernel driver is active */ } static int ugen20_process(struct libusb20_device *pdev) { struct usb_fs_complete temp; struct usb_fs_endpoint *fsep; struct libusb20_transfer *xfer; while (1) { if (ioctl(pdev->file, USB_FS_COMPLETE, &temp)) { if (errno == EBUSY) { break; } else { /* device detached */ return (LIBUSB20_ERROR_OTHER); } } fsep = pdev->privBeData; xfer = pdev->pTransfer; fsep += temp.ep_index; xfer += temp.ep_index; /* update transfer status */ if (fsep->status == 0) { xfer->aFrames = fsep->aFrames; xfer->timeComplete = fsep->isoc_time_complete; xfer->status = LIBUSB20_TRANSFER_COMPLETED; } else if (fsep->status == USB_ERR_CANCELLED) { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_CANCELLED; } else if (fsep->status == USB_ERR_STALLED) { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_STALL; } else if (fsep->status == USB_ERR_TIMEOUT) { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_TIMED_OUT; } else { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_ERROR; } libusb20_tr_callback_wrapper(xfer); } return (0); /* done */ } static int ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no, uint8_t pre_scale) { struct usb_fs_open temp; struct usb_fs_endpoint *fsep; if (pre_scale) MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE; memset(&temp, 0, sizeof(temp)); fsep = xfer->pdev->privBeData; fsep += xfer->trIndex; temp.max_bufsize = MaxBufSize; temp.max_frames = MaxFrameCount; temp.ep_index = xfer->trIndex; temp.ep_no = ep_no; if (ioctl(xfer->pdev->file, USB_FS_OPEN, &temp)) { return (LIBUSB20_ERROR_INVALID_PARAM); } /* maximums might have changed - update */ xfer->maxFrames = temp.max_frames; /* "max_bufsize" should be multiple of "max_packet_length" */ xfer->maxTotalLength = temp.max_bufsize; xfer->maxPacketLen = temp.max_packet_length; /* setup buffer and length lists using zero copy */ fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer); fsep->pLength = libusb20_pass_ptr(xfer->pLength); return (0); /* success */ } static int ugen20_tr_close(struct libusb20_transfer *xfer) { struct usb_fs_close temp; memset(&temp, 0, sizeof(temp)); temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, USB_FS_CLOSE, &temp)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); /* success */ } static int ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer) { struct usb_fs_clear_stall_sync temp; memset(&temp, 0, sizeof(temp)); /* if the transfer is active, an error will be returned */ temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, USB_FS_CLEAR_STALL_SYNC, &temp)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); /* success */ } static void ugen20_tr_submit(struct libusb20_transfer *xfer) { struct usb_fs_start temp; struct usb_fs_endpoint *fsep; memset(&temp, 0, sizeof(temp)); fsep = xfer->pdev->privBeData; fsep += xfer->trIndex; fsep->nFrames = xfer->nFrames; fsep->flags = 0; if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK; } if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) { fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK; } if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) { fsep->flags |= USB_FS_FLAG_FORCE_SHORT; } if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) { fsep->flags |= USB_FS_FLAG_CLEAR_STALL; } /* NOTE: The "fsep->timeout" variable is 16-bit. */ if (xfer->timeout > 65535) fsep->timeout = 65535; else fsep->timeout = xfer->timeout; temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, USB_FS_START, &temp)) { /* ignore any errors - should never happen */ } return; /* success */ } static void ugen20_tr_cancel_async(struct libusb20_transfer *xfer) { struct usb_fs_stop temp; memset(&temp, 0, sizeof(temp)); temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, USB_FS_STOP, &temp)) { /* ignore any errors - should never happen */ } return; } static int ugen20_be_ioctl(uint32_t cmd, void *data) { int f; int error; f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); if (f < 0) return (LIBUSB20_ERROR_OTHER); error = ioctl(f, cmd, data); if (error == -1) { if (errno == EPERM) { error = LIBUSB20_ERROR_ACCESS; } else { error = LIBUSB20_ERROR_OTHER; } } close(f); return (error); } static int ugen20_dev_get_iface_desc(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len) { struct usb_gen_descriptor ugd; memset(&ugd, 0, sizeof(ugd)); ugd.ugd_data = libusb20_pass_ptr(buf); ugd.ugd_maxlen = len; ugd.ugd_iface_index = iface_index; if (ioctl(pdev->file, USB_GET_IFACE_DRIVER, &ugd)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); } static int ugen20_dev_get_info(struct libusb20_device *pdev, struct usb_device_info *pinfo) { if (ioctl(pdev->file, USB_GET_DEVICEINFO, pinfo)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); } static int ugen20_root_get_dev_quirk(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.index = quirk_index; error = ugen20_be_ioctl(USB_DEV_QUIRK_GET, &q); if (error) { if (errno == EINVAL) { return (LIBUSB20_ERROR_NOT_FOUND); } } else { pq->vid = q.vid; pq->pid = q.pid; pq->bcdDeviceLow = q.bcdDeviceLow; pq->bcdDeviceHigh = q.bcdDeviceHigh; strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); } return (error); } static int ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.index = quirk_index; error = ugen20_be_ioctl(USB_QUIRK_NAME_GET, &q); if (error) { if (errno == EINVAL) { return (LIBUSB20_ERROR_NOT_FOUND); } } else { strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); } return (error); } static int ugen20_root_add_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.vid = pq->vid; q.pid = pq->pid; q.bcdDeviceLow = pq->bcdDeviceLow; q.bcdDeviceHigh = pq->bcdDeviceHigh; strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); error = ugen20_be_ioctl(USB_DEV_QUIRK_ADD, &q); if (error) { if (errno == ENOMEM) { return (LIBUSB20_ERROR_NO_MEM); } } return (error); } static int ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.vid = pq->vid; q.pid = pq->pid; q.bcdDeviceLow = pq->bcdDeviceLow; q.bcdDeviceHigh = pq->bcdDeviceHigh; strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); error = ugen20_be_ioctl(USB_DEV_QUIRK_REMOVE, &q); if (error) { if (errno == EINVAL) { return (LIBUSB20_ERROR_NOT_FOUND); } } return (error); } static int ugen20_root_set_template(struct libusb20_backend *pbe, int temp) { return (ugen20_be_ioctl(USB_SET_TEMPLATE, &temp)); } static int ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp) { return (ugen20_be_ioctl(USB_GET_TEMPLATE, ptemp)); }