Index: stable/8/lib/libusb/libusb10.c =================================================================== --- stable/8/lib/libusb/libusb10.c (revision 220433) +++ stable/8/lib/libusb/libusb10.c (revision 220434) @@ -1,1443 +1,1431 @@ /* $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_maxframe(struct libusb20_device *, libusb_transfer *); static int libusb10_get_buffsize(struct libusb20_device *, libusb_transfer *); static int libusb10_convert_error(uint8_t status); static void libusb10_complete_transfer(struct libusb20_transfer *, struct libusb_super_transfer *, int); static void libusb10_isoc_proxy(struct libusb20_transfer *); static void libusb10_bulk_intr_proxy(struct libusb20_transfer *); static void libusb10_ctrl_proxy(struct libusb20_transfer *); static void libusb10_submit_transfer_sub(struct libusb20_device *, uint8_t); /* Library initialisation / deinitialisation */ void libusb_set_debug(libusb_context *ctx, int level) { 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)); } 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); } 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); for (j = 0; j < i; j++) { pdev = devs[j]->os_priv; pdesc = libusb20_dev_get_device_desc(pdev); /* * NOTE: The USB library will automatically swap the * fields in the device descriptor to be of host * endian type! */ if (pdesc->idVendor == vendor_id && pdesc->idProduct == product_id) { if (libusb_open(devs[j], &pdev) < 0) pdev = NULL; break; } } if (j == i) pdev = NULL; 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) { index &= 1; /* double buffering */ index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4; if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) { /* this is an IN endpoint */ index |= 2; } return (libusb20_tr_get_pointer(pdev, index)); } int libusb_clear_halt(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, 0, 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); return (libusb20_dev_kernel_driver_active( pdev, interface)); } 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); 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 int +static uint32_t libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) { - int ret; - int usb_speed; + uint32_t ret; - usb_speed = libusb20_dev_get_speed(pdev); - switch (xfer->type) { case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: - switch (usb_speed) { - case LIBUSB20_SPEED_LOW: - case LIBUSB20_SPEED_FULL: - ret = 60 * 1; - break; - default: - ret = 60 * 8; - break; - } + 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) { /* TODO */ return ("Unknown error"); } Index: stable/8/lib/libusb/libusb20.3 =================================================================== --- stable/8/lib/libusb/libusb20.3 (revision 220433) +++ stable/8/lib/libusb/libusb20.3 (revision 220434) @@ -1,981 +1,991 @@ .\" .\" 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. .\" .\" $FreeBSD$ .\" .Dd October 14, 2010 .Dt LIBUSB20 3 .Os .Sh NAME .Nm libusb20 . .Nd "USB access library" . . .Sh LIBRARY . . USB access library (libusb -lusb) . . . .Sh SYNOPSIS .In libusb20.h .Ft int .Fn libusb20_tr_close "struct libusb20_transfer *xfer" .Ft int .Fn libusb20_tr_open "struct libusb20_transfer *xfer" "uint32_t max_buf_size" "uint32_t max_frame_count" "uint8_t ep_no" .Ft struct libusb20_transfer* .Fn libusb20_tr_get_pointer "struct libusb20_device *pdev" "uint16_t tr_index" .Ft uint16_t .Fn libusb20_tr_get_time_complete "struct libusb20_transfer *xfer" .Ft uint32_t .Fn libusb20_tr_get_actual_frames "struct libusb20_transfer *xfer" .Ft uint32_t .Fn libusb20_tr_get_actual_length "struct libusb20_transfer *xfer" .Ft uint32_t .Fn libusb20_tr_get_max_frames "struct libusb20_transfer *xfer" .Ft uint32_t .Fn libusb20_tr_get_max_packet_length "struct libusb20_transfer *xfer" .Ft uint32_t .Fn libusb20_tr_get_max_total_length "struct libusb20_transfer *xfer" .Ft uint8_t .Fn libusb20_tr_get_status "struct libusb20_transfer *xfer" .Ft uint8_t .Fn libusb20_tr_pending "struct libusb20_transfer *xfer" .Ft void .Fn libusb20_tr_callback_wrapper "struct libusb20_transfer *xfer" .Ft void .Fn libusb20_tr_clear_stall_sync "struct libusb20_transfer *xfer" .Ft void .Fn libusb20_tr_drain "struct libusb20_transfer *xfer" .Ft void .Fn libusb20_tr_set_buffer "struct libusb20_transfer *xfer" "void *buffer" "uint16_t fr_index" .Ft void .Fn libusb20_tr_set_callback "struct libusb20_transfer *xfer" "libusb20_tr_callback_t *cb" .Ft void .Fn libusb20_tr_set_flags "struct libusb20_transfer *xfer" "uint8_t flags" .Ft uint32_t .Fn libusb20_tr_get_length "struct libusb20_transfer *xfer" "uint16_t fr_index" .Ft void .Fn libusb20_tr_set_length "struct libusb20_transfer *xfer" "uint32_t length" "uint16_t fr_index" .Ft void .Fn libusb20_tr_set_priv_sc0 "struct libusb20_transfer *xfer" "void *sc0" .Ft void .Fn libusb20_tr_set_priv_sc1 "struct libusb20_transfer *xfer" "void *sc1" .Ft void .Fn libusb20_tr_set_timeout "struct libusb20_transfer *xfer" "uint32_t timeout" .Ft void .Fn libusb20_tr_set_total_frames "struct libusb20_transfer *xfer" "uint32_t nframes" .Ft void .Fn libusb20_tr_setup_bulk "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t timeout" .Ft void .Fn libusb20_tr_setup_control "struct libusb20_transfer *xfer" "void *psetup" "void *pbuf" "uint32_t timeout" .Ft void .Fn libusb20_tr_setup_intr "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t timeout" .Ft void .Fn libusb20_tr_setup_isoc "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint61_t fr_index" .Ft uint8_t .Fn libusb20_tr_bulk_intr_sync "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t *pactlen" "uint32_t timeout" .Ft void .Fn libusb20_tr_start "struct libusb20_transfer *xfer" .Ft void .Fn libusb20_tr_stop "struct libusb20_transfer *xfer" .Ft void .Fn libusb20_tr_submit "struct libusb20_transfer *xfer" .Ft void * .Fn libusb20_tr_get_priv_sc0 "struct libusb20_transfer *xfer" .Ft void * .Fn libusb20_tr_get_priv_sc1 "struct libusb20_transfer *xfer" .Ft const char * .Fn libusb20_dev_get_backend_name "struct libusb20_device *" .Ft int .Fn libusb20_dev_get_info "struct libusb20_device *pdev" "struct usb_device_info *pinfo" .Ft int .Fn libusb20_dev_get_iface_desc "struct libusb20_device *pdev" "uint8_t iface_index" "char *buf" "uint8_t len" .Ft const char * .Fn libusb20_dev_get_desc "struct libusb20_device *pdev" .Ft int .Fn libusb20_dev_close "struct libusb20_device *pdev" .Ft int .Fn libusb20_dev_detach_kernel_driver "struct libusb20_device *pdev" "uint8_t iface_index" .Ft int .Fn libusb20_dev_set_config_index "struct libusb20_device *pdev" "uint8_t configIndex" .Ft int .Fn libusb20_dev_get_debug "struct libusb20_device *pdev" .Ft int .Fn libusb20_dev_get_fd "struct libusb20_device *pdev" .Ft int .Fn libusb20_dev_kernel_driver_active "struct libusb20_device *pdev" "uint8_t iface_index" .Ft int .Fn libusb20_dev_open "struct libusb20_device *pdev" "uint16_t transfer_max" .Ft int .Fn libusb20_dev_process "struct libusb20_device *pdev" .Ft int .Fn 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" .Ft int .Fn libusb20_dev_req_string_sync "struct libusb20_device *pdev" "uint8_t index" "uint16_t langid" "void *ptr" "uint16_t len" .Ft int .Fn libusb20_dev_req_string_simple_sync "struct libusb20_device *pdev" "uint8_t index" "void *ptr" "uint16_t len" .Ft int .Fn libusb20_dev_reset "struct libusb20_device *pdev" .Ft int .Fn libusb20_dev_check_connected "struct libusb20_device *pdev" .Ft int .Fn libusb20_dev_set_power_mode "struct libusb20_device *pdev" "uint8_t power_mode" .Ft uint8_t .Fn libusb20_dev_get_power_mode "struct libusb20_device *pdev" .Ft int .Fn libusb20_dev_set_alt_index "struct libusb20_device *pdev" "uint8_t iface_index" "uint8_t alt_index" .Ft struct LIBUSB20_DEVICE_DESC_DECODED * .Fn libusb20_dev_get_device_desc "struct libusb20_device *pdev" .Ft struct libusb20_config * .Fn libusb20_dev_alloc_config "struct libusb20_device *pdev" "uint8_t config_index" .Ft struct libusb20_device * .Fn libusb20_dev_alloc "void" .Ft uint8_t .Fn libusb20_dev_get_address "struct libusb20_device *pdev" .Ft uint8_t .Fn libusb20_dev_get_bus_number "struct libusb20_device *pdev" .Ft uint8_t .Fn libusb20_dev_get_mode "struct libusb20_device *pdev" .Ft uint8_t .Fn libusb20_dev_get_speed "struct libusb20_device *pdev" .Ft uint8_t .Fn libusb20_dev_get_config_index "struct libusb20_device *pdev" .Ft void .Fn libusb20_dev_free "struct libusb20_device *pdev" .Ft void .Fn libusb20_dev_set_debug "struct libusb20_device *pdev" "int debug" .Ft void .Fn libusb20_dev_wait_process "struct libusb20_device *pdev" "int timeout" .Ft int .Fn libusb20_be_get_template "struct libusb20_backend *pbe" "int *ptemp" .Ft int .Fn libusb20_be_set_template "struct libusb20_backend *pbe" "int temp" .Ft int .Fn libusb20_be_get_dev_quirk "struct libusb20_backend *pber" "uint16_t index" "struct libusb20_quirk *pq" .Ft int .Fn libusb20_be_get_quirk_name "struct libusb20_backend *pbe" "uint16_t index" "struct libusb20_quirk *pq" .Ft int .Fn libusb20_be_add_dev_quirk "struct libusb20_backend *pbe" "struct libusb20_quirk *pq" .Ft int .Fn libusb20_be_remove_dev_quirk "struct libusb20_backend *pbe" "struct libusb20_quirk *pq" .Ft struct libusb20_backend * .Fn libusb20_be_alloc_default "void" .Ft struct libusb20_backend * .Fn libusb20_be_alloc_freebsd "void" .Ft struct libusb20_backend * .Fn libusb20_be_alloc_linux "void" .Ft struct libusb20_device * .Fn libusb20_be_device_foreach "struct libusb20_backend *pbe" "struct libusb20_device *pdev" .Ft void .Fn libusb20_be_dequeue_device "struct libusb20_backend *pbe" "struct libusb20_device *pdev" .Ft void .Fn libusb20_be_enqueue_device "struct libusb20_backend *pbe" "struct libusb20_device *pdev" .Ft void .Fn libusb20_be_free "struct libusb20_backend *pbe" .Ft uint8_t .Fn libusb20_me_get_1 "const struct libusb20_me_struct *me" "uint16_t off" .Ft uint16_t .Fn libusb20_me_get_2 "const struct libusb20_me_struct *me" "uint16_t off" .Ft uint16_t .Fn libusb20_me_encode "void *pdata" "uint16_t len" "const void *pdecoded" .Ft uint16_t .Fn libusb20_me_decode "const void *pdata" "uint16_t len" "void *pdecoded" .Ft "const uint8_t *" .Fn libusb20_desc_foreach "const struct libusb20_me_struct *me" "const uint8_t *pdesc" . . .Sh DESCRIPTION . The .Nm library implements functions to be able to easily access and control USB through the USB file system interface. The .Nm interfaces are specific to the .Fx usb stack and are not available on other operating systems, portable applications should consider using .Xr libusb 3 . . . .Sh USB TRANSFER OPERATIONS . .Pp . .Fn libusb20_tr_close will release all kernel resources associated with an USB .Fa xfer . . This function returns zero upon success. . Non-zero return values indicate a LIBUSB20_ERROR value. . .Pp . .Fn libusb20_tr_open will allocate kernel buffer resources according to .Fa max_buf_size and .Fa max_frame_count associated with an USB .Fa pxfer and bind the transfer to the specified .Fa ep_no . .Fa max_buf_size is the minimum buffer size which the data transport layer has to support. If .Fa max_buf_size is zero, the .Nm library will use wMaxPacketSize to compute the buffer size. This can be useful for isochronous transfers. The actual buffer size can be greater than .Fa max_buf_size and is returned by .Fn libusb20_tr_get_max_total_length . . +If +.Fa max_frame_count +is OR'ed with LIBUSB20_MAX_FRAME_PRE_SCALE the remaining part of the +argument is converted from milliseconds into the actual number of +frames rounded up, when this function returns. +This flag is only valid for ISOCHRONOUS transfers and has no effect +for other transfer types. +The actual number of frames setup is found by calling +.Fn libusb20_tr_get_max_frames . +. This function returns zero upon success. . Non-zero return values indicate a LIBUSB20_ERROR value. . .Pp . .Fn libusb20_tr_get_pointer will return a pointer to the allocated USB transfer according to the .Fa pdev and .Fa tr_index arguments. . This function returns NULL in case of failure. . .Pp . .Fn libusb20_tr_get_time_complete will return the completion time of an USB transfer in millisecond units. This function is most useful for isochronous USB transfers when doing echo cancelling. . .Pp . .Fn libusb20_tr_get_actual_frames will return the actual number of USB frames after an USB transfer completed. A value of zero means that no data was transferred. . .Pp . .Fn libusb20_tr_get_actual_length will return the sum of the actual length for all transferred USB frames for the given USB transfer. . .Pp . .Fn libusb20_tr_get_max_frames will return the maximum number of USB frames that were allocated when an USB transfer was setup for the given USB transfer. . .Pp . .Fn libusb20_tr_get_max_packet_length will return the maximum packet length in bytes associated with the given USB transfer. . The packet length can be used round up buffer sizes so that short USB packets are avoided for proxy buffers. . . .Pp . .Fn libusb20_tr_get_max_total_length function will return the maximum value for the data length sum of all USB frames associated with an USB transfer. In case of control transfers the value returned does not include the length of the SETUP packet, 8 bytes, which is part of frame zero. The returned value of this function is always aligned to the maximum packet size, wMaxPacketSize, of the endpoint which the USB transfer is bound to. . .Pp . .Fn libusb20_tr_get_status will return the status of an USB transfer. . Status values are defined by a set of LIBUSB20_TRANSFER_XXX enums. . .Pp . .Fn libusb20_tr_pending will return non-zero if the given USB transfer is pending for completion. . Else this function returns zero. . .Pp . .Fn libusb20_tr_callback_wrapper This is an internal function used to wrap asynchronous USB callbacks. . .Pp . .Fn libusb20_tr_clear_stall_sync This is an internal function used to synchronously clear the stall on the given USB transfer. . Please see the USB specification for more information on stall clearing. . If the given USB transfer is pending when this function is called, the USB transfer will complete with an error after that this function has been called. . .Pp . .Fn libusb20_tr_drain will stop the given USB transfer and will not return until the USB transfer has been stopped in hardware. . .Pp . .Fn libusb20_tr_set_buffer is used to set the .Fa buffer pointer for the given USB transfer and .Fa fr_index . . Typically the frame index is zero. . . .Pp . .Fn libusb20_tr_set_callback is used to set the USB callback for asynchronous USB transfers. . The callback type is defined by libusb20_tr_callback_t. . .Pp . .Fn libusb20_tr_set_flags is used to set various USB flags for the given USB transfer. .Bl -tag .It LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK Report a short frame as error. .It LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK Multiple short frames are not allowed. .It LIBUSB20_TRANSFER_FORCE_SHORT All transmitted frames are short terminated. .It LIBUSB20_TRANSFER_DO_CLEAR_STALL Will do a clear-stall before starting the transfer. .El . .Pp . .Fn libusb20_tr_get_length returns the length of the given USB frame by index. After an USB transfer is complete the USB frame length will get updated to the actual transferred length. . .Pp . .Fn libusb20_tr_set_length sets the length of the given USB frame by index. . .Pp . .Fn libusb20_tr_set_priv_sc0 sets private driver pointer number zero. . .Pp . .Fn libusb20_tr_set_priv_sc1 sets private driver pointer number one. . .Pp . .Fn libusb20_tr_set_timeout sets the timeout for the given USB transfer. . A timeout value of zero means no timeout. . The timeout is given in milliseconds. . .Pp . .Fn libusb20_tr_set_total_frames sets the total number of frames that should be executed when the USB transfer is submitted. . The total number of USB frames must be less than the maximum number of USB frames associated with the given USB transfer. . .Pp . .Fn libusb20_tr_setup_bulk is a helper function for setting up a single frame USB BULK transfer. . .Pp . .Fn libusb20_tr_setup_control is a helper function for setting up a single or dual frame USB CONTROL transfer depending on the control transfer length. . .Pp . .Fn libusb20_tr_setup_intr is a helper function for setting up a single frame USB INTERRUPT transfer. . .Pp . .Fn libusb20_tr_setup_isoc is a helper function for setting up a multi frame USB ISOCHRONOUS transfer. . .Pp . .Fn libusb20_tr_bulk_intr_sync will perform a synchronous BULK or INTERRUPT transfer having length given by the .Fa length argument and buffer pointer given by the .Fa pbuf argument on the USB transfer given by the .Fa xfer argument. . If the .Fa pactlen argument is non-NULL the actual transfer length will be stored at the given pointer destination. . If the .Fa timeout argument is non-zero the transfer will timeout after the given value in milliseconds. . This function does not change the transfer flags, like short packet not ok. . This function returns zero on success else a LIBUSB20_TRANSFER_XXX value is returned. . .Pp . .Fn libusb20_tr_start will get the USB transfer started, if not already started. . This function will not get the transfer queued in hardware. . This function is non-blocking. . .Pp . .Fn libusb20_tr_stop will get the USB transfer stopped, if not already stopped. . This function is non-blocking, which means that the actual stop can happen after the return of this function. . .Pp . .Fn libusb20_tr_submit will get the USB transfer queued in hardware. . . .Pp . .Fn libusb20_tr_get_priv_sc0 returns private driver pointer number zero associated with an USB transfer. . . .Pp . .Fn libusb20_tr_get_priv_sc1 returns private driver pointer number one associated with an USB transfer. . . .Sh USB DEVICE OPERATIONS . .Pp . .Fn libusb20_dev_get_backend_name returns a zero terminated string describing the backend used. . .Pp . .Fn libusb20_dev_get_info retrives the BSD specific usb_device_info structure into the memory location given by .Fa pinfo . The USB device given by .Fa pdev must be opened before this function will succeed. This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_get_iface_desc retrieves the kernel interface description for the given USB .Fa iface_index . The format of the USB interface description is: "drivername: " The description string is always zero terminated. A zero length string is written in case no driver is attached to the given interface. The USB device given by .Fa pdev must be opened before this function will succeed. This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_get_desc returns a zero terminated string describing the given USB device. The format of the string is: "drivername: " . .Pp . .Fn libusb20_dev_close will close the given USB device. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_detach_kernel_driver will try to detach the kernel driver for the USB interface given by .Fa iface_index . . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_set_config_index will try to set the configuration index on an USB device. . The first configuration index is zero. . The un-configure index is 255. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_get_debug returns the debug level of an USB device. . .Pp . .Fn libusb20_dev_get_fd returns the file descriptor of the given USB device. . A negative value is returned when no file descriptor is present. . The file descriptor can be used for polling purposes. . .Pp . .Fn libusb20_dev_kernel_driver_active returns a non-zero value if a kernel driver is active on the given USB interface. . Else zero is returned. . .Pp . .Fn libusb20_dev_open opens an USB device so that setting up USB transfers becomes possible. . The number of USB transfers can be zero which means only control transfers are allowed. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . A return value of LIBUSB20_ERROR_BUSY means that the device is already opened. . .Pp . .Fn libusb20_dev_process is called to sync kernel USB transfers with userland USB transfers. . This function returns zero on success else a LIBUSB20_ERROR value is returned typically indicating that the given USB device has been detached. . .Pp . .Fn libusb20_dev_request_sync will perform a synchronous control request on the given USB device. . Before this call will succeed the USB device must be opened. . .Fa setup is a pointer to a decoded and host endian SETUP packet. .Fa data is a pointer to a data transfer buffer associated with the control transaction. This argument can be NULL. .Fa pactlen is a pointer to a variable that will hold the actual transfer length after the control transaction is complete. .Fa timeout is the transaction timeout given in milliseconds. A timeout of zero means no timeout. .Fa flags is used to specify transaction flags, for example LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_req_string_sync will synchronously request an USB string by language ID and string index into the given buffer limited by a maximum length. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_req_string_simple_sync will synchronously request an USB string using the default language ID and convert the string into ASCII before storing the string into the given buffer limited by a maximum length which includes the terminating zero. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . . .Pp . .Fn libusb20_dev_reset will try to BUS reset the given USB device and restore the last set USB configuration. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . . .Pp . .Fn libusb20_dev_check_connected will check if an opened USB device is still connected. . This function returns zero if the device is still connected else a LIBUSB20_ERROR value is returned. . . .Pp . .Fn libusb20_dev_set_power_mode sets the power mode of the USB device. . Valid power modes: .Bl -tag .It LIBUSB20_POWER_OFF .It LIBUSB20_POWER_ON .It LIBUSB20_POWER_SAVE .It LIBUSB20_POWER_SUSPEND .It LIBUSB20_POWER_RESUME .El . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_get_power_mode returns the currently selected power mode for the given USB device. . .Pp . .Fn libusb20_dev_set_alt_index will try to set the given alternate index for the given USB interface index. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_dev_get_device_desc returns a pointer to the decoded and host endian version of the device descriptor. . The USB device need not be opened when calling this function. . .Pp . .Fn libusb20_dev_alloc_config will read out and decode the USB config descriptor for the given USB device and config index. This function returns a pointer to the decoded configuration which must eventually be passed to free(). NULL is returned in case of failure. . .Pp . .Fn libusb20_dev_alloc is an internal function to allocate a new USB device. . .Pp . .Fn libusb20_dev_get_address returns the internal and not necessarily the real hardware address of the given USB device. . .Pp . .Fn libusb20_dev_get_bus_number returns the internal bus number which the given USB device belongs to. . .Pp . .Fn libusb20_dev_get_mode returns the current operation mode of the USB entity. . Valid return values are: .Bl -tag .It LIBUSB20_MODE_HOST .It LIBUSB20_MODE_DEVICE .El . .Pp . .Fn libusb20_dev_get_speed returns the current speed of the given USB device. . .Bl -tag .It LIBUSB20_SPEED_UNKNOWN .It LIBUSB20_SPEED_LOW .It LIBUSB20_SPEED_FULL .It LIBUSB20_SPEED_HIGH .It LIBUSB20_SPEED_VARIABLE .It LIBUSB20_SPEED_SUPER .El . .Pp . .Fn libusb20_dev_get_config_index This function returns the currently select config index for the given USB device. . .Pp . .Fn libusb20_dev_free will free the given USB device and all associated USB transfers. . .Pp . .Fn libusb20_dev_set_debug will set the debug level for the given USB device. . .Pp . .Fn libusb20_dev_wait_process function will wait until a pending USB transfer has completed on the given USB device. . A timeout value can be specified which is passed on to the .Xr poll 2 function. . .Sh USB BACKEND OPERATIONS . .Fn libusb20_be_get_template will return the currently selected global USB device side mode template into the integer pointer .Fa ptemp . This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_be_set_template will set the global USB device side mode template to .Fa temp . The new template is not activated until after the next USB enumeration. The template number decides how the USB device will present itself to the USB Host, like Mass Storage Device, USB Ethernet Device. Also see the .Xr usb2_template 4 module. This function returns zero on success else a LIBUSB20_ERROR value is returned. . .Pp . .Fn libusb20_be_get_dev_quirk This function will return the device quirk according to .Fa index into the libusb20_quirk structure pointed to by .Fa pq . This function returns zero on success else a LIBUSB20_ERROR value is returned. . If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is returned. . .Pp . .Fn libusb20_be_get_quirk_name will return the quirk name according to .Fa index into the libusb20_quirk structure pointed to by .Fa pq . This function returns zero on success else a LIBUSB20_ERROR value is returned. . If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is returned. . .Pp . .Fn libusb20_be_add_dev_quirk will add the libusb20_quirk structure pointed to by the .Fa pq argument into the device quirk list. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . If the given quirk cannot be added LIBUSB20_ERROR_NO_MEM is returned. . .Pp . .Fn libusb20_be_remove_dev_quirk will remove the quirk matching the libusb20_quirk structure pointed to by the .Fa pq argument from the device quirk list. . This function returns zero on success else a LIBUSB20_ERROR value is returned. . If the given quirk does not exist LIBUSB20_ERROR_NOT_FOUND is returned. . .Pp . .Fn libusb20_be_alloc_default .Fn libusb20_be_alloc_freebsd .Fn libusb20_be_alloc_linux These functions are used to allocate a specific USB backend or the operating system default USB backend. Allocating a backend is a way to scan for currently present USB devices. . .Pp . .Fn libusb20_be_device_foreach is used to iterate USB devices present in a USB backend. . The starting value of .Fa pdev is NULL. . This function returns the next USB device in the list. . If NULL is returned the end of the USB device list has been reached. . .Pp . .Fn libusb20_be_dequeue_device will dequeue the given USB device pointer from the backend USB device list. . Dequeued USB devices will not be freed when the backend is freed. . .Pp . .Fn libusb20_be_enqueue_device This function will enqueue the given USB device pointer in the backend USB device list. . Enqueued USB devices will get freed when the backend is freed. . .Pp . .Fn libusb20_be_free will free the given backend and all USB devices in its device list. . . .Sh USB DESCRIPTOR PARSING . .Fn libusb20_me_get_1 pie offset This function will return a byte at the given byte offset of a message entity. . This function is safe against invalid offsets. . .Pp . .Fn libusb20_me_get_2 pie offset This function will return a little endian 16-bit value at the given byte offset of a message entity. . This function is safe against invalid offsets. . .Pp . .Fn libusb20_me_encode pbuf len pdecoded This function will encode a so-called *DECODED structure into binary format. . The total encoded length that will fit in the given buffer is returned. . If the buffer pointer is NULL no data will be written to the buffer location. . .Pp . .Fn libusb20_me_decode pbuf len pdecoded This function will decode a binary structure into a so-called *DECODED structure. . The total decoded length is returned. . The buffer pointer cannot be NULL. . . .Sh FILES . . /dev/usb .Sh SEE ALSO .Xr usb 4 , .Xr libusb 3 , .Xr usbconfig 8 . . .Sh HISTORY . . Some parts of the .Nm API derives from the libusb project at sourceforge. Index: stable/8/lib/libusb/libusb20.c =================================================================== --- stable/8/lib/libusb/libusb20.c (revision 220433) +++ stable/8/lib/libusb/libusb20.c (revision 220434) @@ -1,1226 +1,1232 @@ /* $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) { + 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) { + 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); + 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] = 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] = 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] = 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] = 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] = 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] = 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 (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_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); 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; #ifdef __FreeBSD__ 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); } Index: stable/8/lib/libusb/libusb20.h =================================================================== --- stable/8/lib/libusb/libusb20.h (revision 220433) +++ stable/8/lib/libusb/libusb20.h (revision 220434) @@ -1,301 +1,302 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved. * Copyright (c) 2007-2008 Daniel Drake. All rights reserved. * Copyright (c) 2001 Johannes Erdfelt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _LIBUSB20_H_ #define _LIBUSB20_H_ #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #if 0 }; /* style */ #endif /** \ingroup misc * Error codes. Most libusb20 functions return 0 on success or one of * these codes on failure. */ enum libusb20_error { /** Success (no error) */ LIBUSB20_SUCCESS = 0, /** Input/output error */ LIBUSB20_ERROR_IO = -1, /** Invalid parameter */ LIBUSB20_ERROR_INVALID_PARAM = -2, /** Access denied (insufficient permissions) */ LIBUSB20_ERROR_ACCESS = -3, /** No such device (it may have been disconnected) */ LIBUSB20_ERROR_NO_DEVICE = -4, /** Entity not found */ LIBUSB20_ERROR_NOT_FOUND = -5, /** Resource busy */ LIBUSB20_ERROR_BUSY = -6, /** Operation timed out */ LIBUSB20_ERROR_TIMEOUT = -7, /** Overflow */ LIBUSB20_ERROR_OVERFLOW = -8, /** Pipe error */ LIBUSB20_ERROR_PIPE = -9, /** System call interrupted (perhaps due to signal) */ LIBUSB20_ERROR_INTERRUPTED = -10, /** Insufficient memory */ LIBUSB20_ERROR_NO_MEM = -11, /** Operation not supported or unimplemented on this platform */ LIBUSB20_ERROR_NOT_SUPPORTED = -12, /** Other error */ LIBUSB20_ERROR_OTHER = -99, }; /** \ingroup asyncio * libusb20_tr_get_status() values */ enum libusb20_transfer_status { /** Transfer completed without error. Note that this does not * indicate that the entire amount of requested data was * transferred. */ LIBUSB20_TRANSFER_COMPLETED, /** Callback code to start transfer */ LIBUSB20_TRANSFER_START, /** Drain complete callback code */ LIBUSB20_TRANSFER_DRAINED, /** Transfer failed */ LIBUSB20_TRANSFER_ERROR, /** Transfer timed out */ LIBUSB20_TRANSFER_TIMED_OUT, /** Transfer was cancelled */ LIBUSB20_TRANSFER_CANCELLED, /** For bulk/interrupt endpoints: halt condition detected * (endpoint stalled). For control endpoints: control request * not supported. */ LIBUSB20_TRANSFER_STALL, /** Device was disconnected */ LIBUSB20_TRANSFER_NO_DEVICE, /** Device sent more data than requested */ LIBUSB20_TRANSFER_OVERFLOW, }; /** \ingroup asyncio * libusb20_tr_set_flags() values */ enum libusb20_transfer_flags { /** Report a short frame as error */ LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK = 0x0001, /** Multiple short frames are not allowed */ LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK = 0x0002, /** All transmitted frames are short terminated */ LIBUSB20_TRANSFER_FORCE_SHORT = 0x0004, /** Will do a clear-stall before xfer */ LIBUSB20_TRANSFER_DO_CLEAR_STALL = 0x0008, }; /** \ingroup misc * libusb20_dev_get_mode() values */ enum libusb20_device_mode { LIBUSB20_MODE_HOST, /* default */ LIBUSB20_MODE_DEVICE, }; /** \ingroup misc * libusb20_dev_get_speed() values */ enum { LIBUSB20_SPEED_UNKNOWN, /* default */ LIBUSB20_SPEED_LOW, LIBUSB20_SPEED_FULL, LIBUSB20_SPEED_HIGH, LIBUSB20_SPEED_VARIABLE, LIBUSB20_SPEED_SUPER, }; /** \ingroup misc * libusb20_dev_set_power() values */ enum { LIBUSB20_POWER_OFF, LIBUSB20_POWER_ON, LIBUSB20_POWER_SAVE, LIBUSB20_POWER_SUSPEND, LIBUSB20_POWER_RESUME, }; struct usb_device_info; struct libusb20_transfer; struct libusb20_backend; struct libusb20_backend_methods; struct libusb20_device; struct libusb20_device_methods; struct libusb20_config; struct LIBUSB20_CONTROL_SETUP_DECODED; struct LIBUSB20_DEVICE_DESC_DECODED; typedef void (libusb20_tr_callback_t)(struct libusb20_transfer *xfer); struct libusb20_quirk { uint16_t vid; /* vendor ID */ uint16_t pid; /* product ID */ uint16_t bcdDeviceLow; /* low revision value, inclusive */ uint16_t bcdDeviceHigh; /* high revision value, inclusive */ uint16_t reserved[2]; /* for the future */ /* quirk name, UQ_XXX, including terminating zero */ char quirkname[64 - 12]; }; -/* USB transfer operations */ +#define LIBUSB20_MAX_FRAME_PRE_SCALE (1U << 31) +/* USB transfer operations */ int libusb20_tr_close(struct libusb20_transfer *xfer); int libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t max_buf_size, uint32_t max_frame_count, uint8_t ep_no); struct libusb20_transfer *libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t tr_index); uint16_t libusb20_tr_get_time_complete(struct libusb20_transfer *xfer); uint32_t libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer); uint32_t libusb20_tr_get_actual_length(struct libusb20_transfer *xfer); uint32_t libusb20_tr_get_max_frames(struct libusb20_transfer *xfer); uint32_t libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer); uint32_t libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer); uint8_t libusb20_tr_get_status(struct libusb20_transfer *xfer); uint8_t libusb20_tr_pending(struct libusb20_transfer *xfer); void libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer); void libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer); void libusb20_tr_drain(struct libusb20_transfer *xfer); void libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t fr_index); void libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb); void libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags); uint32_t libusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t fr_index); void libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t fr_index); void libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0); void libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1); void libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout); void libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames); void libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t timeout); void libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pbuf, uint32_t timeout); void libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t timeout); void libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint16_t fr_index); uint8_t libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t *pactlen, uint32_t timeout); void libusb20_tr_start(struct libusb20_transfer *xfer); void libusb20_tr_stop(struct libusb20_transfer *xfer); void libusb20_tr_submit(struct libusb20_transfer *xfer); void *libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer); void *libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer); /* USB device operations */ const char *libusb20_dev_get_backend_name(struct libusb20_device *pdev); const char *libusb20_dev_get_desc(struct libusb20_device *pdev); int libusb20_dev_close(struct libusb20_device *pdev); int libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t iface_index); int libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex); int libusb20_dev_get_debug(struct libusb20_device *pdev); int libusb20_dev_get_fd(struct libusb20_device *pdev); int libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t iface_index); int libusb20_dev_open(struct libusb20_device *pdev, uint16_t transfer_max); int libusb20_dev_process(struct libusb20_device *pdev); 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 libusb20_dev_req_string_sync(struct libusb20_device *pdev, uint8_t index, uint16_t langid, void *ptr, uint16_t len); int libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, uint8_t index, void *ptr, uint16_t len); int libusb20_dev_reset(struct libusb20_device *pdev); int libusb20_dev_check_connected(struct libusb20_device *pdev); int libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode); uint8_t libusb20_dev_get_power_mode(struct libusb20_device *pdev); int libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index); int libusb20_dev_get_info(struct libusb20_device *pdev, struct usb_device_info *pinfo); int libusb20_dev_get_iface_desc(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len); struct LIBUSB20_DEVICE_DESC_DECODED *libusb20_dev_get_device_desc(struct libusb20_device *pdev); struct libusb20_config *libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t config_index); struct libusb20_device *libusb20_dev_alloc(void); uint8_t libusb20_dev_get_address(struct libusb20_device *pdev); uint8_t libusb20_dev_get_bus_number(struct libusb20_device *pdev); uint8_t libusb20_dev_get_mode(struct libusb20_device *pdev); uint8_t libusb20_dev_get_speed(struct libusb20_device *pdev); uint8_t libusb20_dev_get_config_index(struct libusb20_device *pdev); void libusb20_dev_free(struct libusb20_device *pdev); void libusb20_dev_set_debug(struct libusb20_device *pdev, int debug); void libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout); /* USB global operations */ int libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); int libusb20_be_get_quirk_name(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); int libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq); int libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq); int libusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp); int libusb20_be_set_template(struct libusb20_backend *pbe, int temp); /* USB backend operations */ struct libusb20_backend *libusb20_be_alloc(const struct libusb20_backend_methods *methods); struct libusb20_backend *libusb20_be_alloc_default(void); struct libusb20_backend *libusb20_be_alloc_freebsd(void); struct libusb20_backend *libusb20_be_alloc_linux(void); struct libusb20_backend *libusb20_be_alloc_ugen20(void); struct libusb20_device *libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev); void libusb20_be_dequeue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); void libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev); void libusb20_be_free(struct libusb20_backend *pbe); #if 0 { /* style */ #endif #ifdef __cplusplus } #endif #endif /* _LIBUSB20_H_ */ Index: stable/8/lib/libusb/libusb20_int.h =================================================================== --- stable/8/lib/libusb/libusb20_int.h (revision 220433) +++ stable/8/lib/libusb/libusb20_int.h (revision 220434) @@ -1,226 +1,226 @@ /* $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. */ /* * This file describes internal structures. */ #ifndef _LIBUSB20_INT_H_ #define _LIBUSB20_INT_H_ struct libusb20_device; struct libusb20_backend; struct libusb20_transfer; struct libusb20_quirk; union libusb20_session_data { unsigned long session_data; struct timespec tv; uint32_t plugtime; }; /* USB backend specific */ typedef const char *(libusb20_get_backend_name_t)(void); typedef int (libusb20_root_get_dev_quirk_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); typedef int (libusb20_root_get_quirk_name_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); typedef int (libusb20_root_add_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); typedef int (libusb20_root_remove_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); typedef int (libusb20_close_device_t)(struct libusb20_device *pdev); typedef int (libusb20_dev_get_info_t)(struct libusb20_device *pdev, struct usb_device_info *pinfo); typedef int (libusb20_dev_get_iface_desc_t)(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len); typedef int (libusb20_init_backend_t)(struct libusb20_backend *pbe); typedef int (libusb20_open_device_t)(struct libusb20_device *pdev, uint16_t transfer_count_max); typedef void (libusb20_exit_backend_t)(struct libusb20_backend *pbe); typedef int (libusb20_root_set_template_t)(struct libusb20_backend *pbe, int temp); typedef int (libusb20_root_get_template_t)(struct libusb20_backend *pbe, int *ptemp); #define LIBUSB20_DEFINE(n,field) \ libusb20_##field##_t *field; #define LIBUSB20_DECLARE(n,field) \ /* .field = */ n##_##field, #define LIBUSB20_BACKEND(m,n) \ /* description of this backend */ \ m(n, get_backend_name) \ /* optional backend methods */ \ m(n, init_backend) \ m(n, exit_backend) \ m(n, dev_get_info) \ m(n, dev_get_iface_desc) \ m(n, root_get_dev_quirk) \ m(n, root_get_quirk_name) \ m(n, root_add_dev_quirk) \ m(n, root_remove_dev_quirk) \ m(n, root_set_template) \ m(n, root_get_template) \ /* mandatory device methods */ \ m(n, open_device) \ m(n, close_device) \ struct libusb20_backend_methods { LIBUSB20_BACKEND(LIBUSB20_DEFINE,) }; /* USB dummy methods */ typedef int (libusb20_dummy_int_t)(void); typedef void (libusb20_dummy_void_t)(void); /* USB device specific */ typedef int (libusb20_detach_kernel_driver_t)(struct libusb20_device *pdev, uint8_t iface_index); typedef int (libusb20_do_request_sync_t)(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags); typedef int (libusb20_get_config_desc_full_t)(struct libusb20_device *pdev, uint8_t **ppbuf, uint16_t *plen, uint8_t index); typedef int (libusb20_get_config_index_t)(struct libusb20_device *pdev, uint8_t *pindex); typedef int (libusb20_kernel_driver_active_t)(struct libusb20_device *pdev, uint8_t iface_index); typedef int (libusb20_process_t)(struct libusb20_device *pdev); typedef int (libusb20_reset_device_t)(struct libusb20_device *pdev); typedef int (libusb20_set_power_mode_t)(struct libusb20_device *pdev, uint8_t power_mode); typedef int (libusb20_get_power_mode_t)(struct libusb20_device *pdev, uint8_t *power_mode); typedef int (libusb20_set_alt_index_t)(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index); typedef int (libusb20_set_config_index_t)(struct libusb20_device *pdev, uint8_t index); typedef int (libusb20_check_connected_t)(struct libusb20_device *pdev); /* USB transfer specific */ -typedef int (libusb20_tr_open_t)(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no); +typedef int (libusb20_tr_open_t)(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no, uint8_t pre_scale); typedef int (libusb20_tr_close_t)(struct libusb20_transfer *xfer); typedef int (libusb20_tr_clear_stall_sync_t)(struct libusb20_transfer *xfer); typedef void (libusb20_tr_submit_t)(struct libusb20_transfer *xfer); typedef void (libusb20_tr_cancel_async_t)(struct libusb20_transfer *xfer); #define LIBUSB20_DEVICE(m,n) \ m(n, detach_kernel_driver) \ m(n, do_request_sync) \ m(n, get_config_desc_full) \ m(n, get_config_index) \ m(n, kernel_driver_active) \ m(n, process) \ m(n, reset_device) \ m(n, check_connected) \ m(n, set_power_mode) \ m(n, get_power_mode) \ m(n, set_alt_index) \ m(n, set_config_index) \ m(n, tr_cancel_async) \ m(n, tr_clear_stall_sync) \ m(n, tr_close) \ m(n, tr_open) \ m(n, tr_submit) \ struct libusb20_device_methods { LIBUSB20_DEVICE(LIBUSB20_DEFINE,) }; struct libusb20_backend { TAILQ_HEAD(, libusb20_device) usb_devs; const struct libusb20_backend_methods *methods; }; struct libusb20_transfer { struct libusb20_device *pdev; /* the USB device we belong to */ libusb20_tr_callback_t *callback; void *priv_sc0; /* private client data */ void *priv_sc1; /* private client data */ /* * Pointer to a list of buffer pointers: */ void **ppBuffer; /* * Pointer to frame lengths, which are updated to actual length * after the USB transfer completes: */ uint32_t *pLength; uint32_t maxTotalLength; uint32_t maxFrames; /* total number of frames */ uint32_t nFrames; /* total number of frames */ uint32_t aFrames; /* actual number of frames */ uint32_t timeout; /* isochronous completion time in milliseconds */ uint16_t timeComplete; uint16_t trIndex; uint16_t maxPacketLen; uint8_t flags; /* see LIBUSB20_TRANSFER_XXX */ uint8_t status; /* see LIBUSB20_TRANSFER_XXX */ uint8_t is_opened; uint8_t is_pending; uint8_t is_cancel; uint8_t is_draining; uint8_t is_restart; }; struct libusb20_device { /* device descriptor */ struct LIBUSB20_DEVICE_DESC_DECODED ddesc; /* device timestamp */ union libusb20_session_data session_data; /* our device entry */ TAILQ_ENTRY(libusb20_device) dev_entry; /* device methods */ const struct libusb20_device_methods *methods; /* backend methods */ const struct libusb20_backend_methods *beMethods; /* list of USB transfers */ struct libusb20_transfer *pTransfer; /* private backend data */ void *privBeData; /* libUSB v0.1 and v1.0 compat data */ void *privLuData; /* claimed interface */ uint8_t claimed_interface; /* device file handle */ int file; /* device file handle (control transfers only) */ int file_ctrl; /* debugging level */ int debug; /* number of USB transfers */ uint16_t nTransfer; uint8_t bus_number; uint8_t device_address; uint8_t usb_mode; uint8_t usb_speed; uint8_t is_opened; char usb_desc[96]; }; extern const struct libusb20_backend_methods libusb20_ugen20_backend; extern const struct libusb20_backend_methods libusb20_linux_backend; #endif /* _LIBUSB20_INT_H_ */ Index: stable/8/lib/libusb/libusb20_ugen20.c =================================================================== --- stable/8/lib/libusb/libusb20_ugen20.c (revision 220433) +++ stable/8/lib/libusb/libusb20_ugen20.c (revision 220434) @@ -1,1014 +1,1017 @@ /* $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); } 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; } /* 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 = 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 = 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 = &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 = 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 = 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) + 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 */ fsep->ppBuffer = xfer->ppBuffer;/* zero copy */ fsep->pLength = xfer->pLength; /* zero copy */ 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 = 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)); } Index: stable/8/lib/libusb/usb.h =================================================================== --- stable/8/lib/libusb/usb.h (revision 220433) +++ stable/8/lib/libusb/usb.h (revision 220434) Property changes on: stable/8/lib/libusb/usb.h ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/lib/libusb/usb.h:r219100 Index: stable/8/lib/libusb =================================================================== --- stable/8/lib/libusb (revision 220433) +++ stable/8/lib/libusb (revision 220434) Property changes on: stable/8/lib/libusb ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/lib/libusb:r219100 Index: stable/8/share/man/man9/usbdi.9 =================================================================== --- stable/8/share/man/man9/usbdi.9 (revision 220433) +++ stable/8/share/man/man9/usbdi.9 (revision 220434) @@ -1,638 +1,642 @@ .\" .\" Copyright (c) 2005 Ian Dowse .\" 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. .\" .\" $FreeBSD$ .Dd June 24, 2009 .Dt USBDI 9 .Os .Sh NAME .Nm usb_fifo_alloc_buffer , .Nm usb_fifo_attach , .Nm usb_fifo_detach , .Nm usb_fifo_free_buffer , .Nm usb_fifo_get_data , .Nm usb_fifo_get_data_buffer , .Nm usb_fifo_get_data_error , .Nm usb_fifo_get_data_linear , .Nm usb_fifo_put_bytes_max , .Nm usb_fifo_put_data , .Nm usb_fifo_put_data_buffer , .Nm usb_fifo_put_data_error , .Nm usb_fifo_put_data_linear , .Nm usb_fifo_reset , .Nm usb_fifo_softc , .Nm usb_fifo_wakeup , .Nm usbd_do_request , .Nm usbd_do_request_flags , .Nm usbd_errstr , .Nm usbd_lookup_id_by_info , .Nm usbd_lookup_id_by_uaa , .Nm usbd_transfer_clear_stall , .Nm usbd_transfer_drain , .Nm usbd_transfer_pending , .Nm usbd_transfer_poll , .Nm usbd_transfer_setup , .Nm usbd_transfer_start , .Nm usbd_transfer_stop , .Nm usbd_transfer_submit , .Nm usbd_transfer_unsetup , .Nm usbd_xfer_clr_flag , .Nm usbd_xfer_frame_data , .Nm usbd_xfer_frame_len , .Nm usbd_xfer_get_frame , .Nm usbd_xfer_get_priv , .Nm usbd_xfer_is_stalled , .Nm usbd_xfer_max_framelen , .Nm usbd_xfer_max_frames , .Nm usbd_xfer_max_len , .Nm usbd_xfer_set_flag , .Nm usbd_xfer_set_frame_data , .Nm usbd_xfer_set_frame_len , .Nm usbd_xfer_set_frame_offset , .Nm usbd_xfer_set_frames , .Nm usbd_xfer_set_interval , .Nm usbd_xfer_set_priv , .Nm usbd_xfer_set_stall , .Nm usbd_xfer_set_timeout , .Nm usbd_xfer_softc , .Nm usbd_xfer_state , .Nm usbd_xfer_state , .Nm usbd_xfer_status .Nd Universal Serial Bus driver programming interface .Sh SYNOPSIS .In dev/usb/usb.h .In dev/usb/usbdi.h .In dev/usb/usbdi_util.h .Sh DESCRIPTION The Universal Serial Bus (USB) driver programming interface provides USB peripheral drivers with a host controller independent API for controlling and communicating with USB peripherals. The .Nm usb module supports both USB Host and USB Device side mode. . .Sh USB KERNEL PROGRAMMING Here is a list of commonly used functions: .Pp . .Ft "usb_error_t" .Fo "usbd_transfer_setup" .Fa "udev" .Fa "ifaces" .Fa "pxfer" .Fa "setup_start" .Fa "n_setup" .Fa "priv_sc" .Fa "priv_mtx" .Fc . .Pp . .Ft "void" .Fo "usbd_transfer_unsetup" .Fa "pxfer" .Fa "n_setup" .Fc . .Pp . .Ft "void" .Fo "usbd_transfer_start" .Fa "xfer" .Fc . .Pp . .Ft "void" .Fo "usbd_transfer_stop" .Fa "xfer" .Fc . .Pp . .Ft "void" .Fo "usbd_transfer_drain" .Fa "xfer" .Fc . . . .Sh USB TRANSFER MANAGEMENT FUNCTIONS The USB standard defines four types of USB transfers. . Control transfers, Bulk transfers, Interrupt transfers and Isochronous transfers. . All the transfer types are managed using the following five functions: . .Pp . .Fn usbd_transfer_setup This function will allocate memory for and initialise an array of USB transfers and all required DMA memory. . This function can sleep or block waiting for resources to become available. .Fa udev is a pointer to "struct usb_device". .Fa ifaces is an array of interface index numbers to use. See "if_index". .Fa pxfer is a pointer to an array of USB transfer pointers that are initialized to NULL, and then pointed to allocated USB transfers. .Fa setup_start is a pointer to an array of USB config structures. .Fa n_setup is a number telling the USB system how many USB transfers should be setup. .Fa priv_sc is the private softc pointer, which will be used to initialize "xfer->priv_sc". .Fa priv_mtx is the private mutex protecting the transfer structure and the softc. This pointer is used to initialize "xfer->priv_mtx". This function returns zero upon success. A non-zero return value indicates failure. . .Pp . .Fn usbd_transfer_unsetup This function will release the given USB transfers and all allocated resources associated with these USB transfers. .Fa pxfer is a pointer to an array of USB transfer pointers, that may be NULL, that should be freed by the USB system. .Fa n_setup is a number telling the USB system how many USB transfers should be unsetup. . This function can sleep waiting for USB transfers to complete. . This function is NULL safe with regard to the USB transfer structure pointer. . It is not allowed to call this function from the USB transfer callback. . .Pp . .Fn usbd_transfer_start This function will start the USB transfer pointed to by .Fa xfer, if not already started. . This function is always non-blocking and must be called with the so-called private USB mutex locked. . This function is NULL safe with regard to the USB transfer structure pointer. . .Pp . .Fn usbd_transfer_stop This function will stop the USB transfer pointed to by .Fa xfer, if not already stopped. . This function is always non-blocking and must be called with the so-called private USB mutex locked. . This function can return before the USB callback has been called. . This function is NULL safe with regard to the USB transfer structure pointer. . If the transfer was in progress, the callback will called with "USB_ST_ERROR" and "error = USB_ERR_CANCELLED". . .Pp . .Fn usbd_transfer_drain This function will stop an USB transfer, if not already stopped and wait for any additional USB hardware operations to complete. . Buffers that are loaded into DMA using "usbd_xfer_set_frame_data()" can safely be freed after that this function has returned. . This function can block the caller and will not return before the USB callback has been called. . This function is NULL safe with regard to the USB transfer structure pointer. . .Sh USB TRANSFER CALLBACK . The USB callback has three states. . USB_ST_SETUP, USB_ST_TRANSFERRED and USB_ST_ERROR. USB_ST_SETUP is the initial state. . After the callback has been called with this state it will always be called back at a later stage in one of the other two states. . The USB callback should not restart the USB transfer in case the error cause is USB_ERR_CANCELLED. . The USB callback is protected from recursion. . That means one can start and stop whatever transfer from the callback of another transfer one desires. . Also the transfer that is currently called back. . Recursion is handled like this that when the callback that wants to recurse returns it is called one more time. . . .Pp . .Fn usbd_transfer_submit This function should only be called from within the USB callback and is used to start the USB hardware. . An USB transfer can have multiple frames consisting of one or more USB packets making up an I/O vector for all USB transfer types. . .Bd -literal -offset indent void usb_default_callback(struct usb_xfer *xfer, usb_error_t error) { int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: /* * Setup xfer frame lengths/count and data */ usbd_transfer_submit(xfer); break; case USB_ST_TRANSFERRED: /* * Read usb frame data, if any. * "actlen" has the total length for all frames * transfered. */ break; default: /* Error */ /* * Print error message and clear stall * for example. */ break; } /* * Here it is safe to do something without the private * USB mutex locked. */ return; } .Ed . .Sh USB CONTROL TRANSFERS An USB control transfer has three parts. . First the SETUP packet, then DATA packet(s) and then a STATUS packet. . The SETUP packet is always pointed to by frame 0 and the length is set by .Fn usbd_xfer_frame_len also if there should not be sent any SETUP packet! If an USB control transfer has no DATA stage, then the number of frames should be set to 1. . Else the default number of frames is 2. . .Bd -literal -offset indent Example1: SETUP + STATUS usbd_xfer_set_frames(xfer, 1); usbd_xfer_set_frame_len(xfer, 0, 8); usbd_transfer_submit(xfer); Example2: SETUP + DATA + STATUS usbd_xfer_set_frames(xfer, 2); usbd_xfer_set_frame_len(xfer, 0, 8); usbd_xfer_set_frame_len(xfer, 1, 1); usbd_transfer_submit(xfer); Example3: SETUP + DATA + STATUS - split 1st callback: usbd_xfer_set_frames(xfer, 1); usbd_xfer_set_frame_len(xfer, 0, 8); usbd_transfer_submit(xfer); 2nd callback: /* IMPORTANT: frbuffers[0] must still point at the setup packet! */ usbd_xfer_set_frames(xfer, 2); usbd_xfer_set_frame_len(xfer, 0, 0); usbd_xfer_set_frame_len(xfer, 1, 1); usbd_transfer_submit(xfer); Example4: SETUP + STATUS - split 1st callback: usbd_xfer_set_frames(xfer, 1); usbd_xfer_set_frame_len(xfer, 0, 8); usbd_xfer_set_flag(xfer, USB_MANUAL_STATUS); usbd_transfer_submit(xfer); 2nd callback: usbd_xfer_set_frames(xfer, 1); usbd_xfer_set_frame_len(xfer, 0, 0); usbd_xfer_clr_flag(xfer, USB_MANUAL_STATUS); usbd_transfer_submit(xfer); .Ed .Sh USB TRANSFER CONFIG To simply the search for endpoints the .Nm usb module defines a USB config structure where it is possible to specify the characteristics of the wanted endpoint. .Bd -literal -offset indent struct usb_config { bufsize, callback direction, endpoint, frames, index flags, interval, timeout, type, }; .Ed . .Pp .Fa type field selects the USB pipe type. . Valid values are: UE_INTERRUPT, UE_CONTROL, UE_BULK, UE_ISOCHRONOUS. . The special value UE_BULK_INTR will select BULK and INTERRUPT pipes. . This field is mandatory. . .Pp .Fa endpoint field selects the USB endpoint number. . A value of 0xFF, "-1" or "UE_ADDR_ANY" will select the first matching endpoint. . This field is mandatory. . .Pp .Fa direction field selects the USB endpoint direction. . A value of "UE_DIR_ANY" will select the first matching endpoint. . Else valid values are: "UE_DIR_IN" and "UE_DIR_OUT". . "UE_DIR_IN" and "UE_DIR_OUT" can be binary OR'ed by "UE_DIR_SID" which means that the direction will be swapped in case of USB_MODE_DEVICE. . Note that "UE_DIR_IN" refers to the data transfer direction of the "IN" tokens and "UE_DIR_OUT" refers to the data transfer direction of the "OUT" tokens. . This field is mandatory. . .Pp .Fa interval field selects the interrupt interval. . The value of this field is given in milliseconds and is independent of device speed. . Depending on the endpoint type, this field has different meaning: .Bl -tag .It UE_INTERRUPT "0" use the default interrupt interval based on endpoint descriptor. "Else" use the given value for polling rate. .It UE_ISOCHRONOUS "0" use default. "Else" the value is ignored. .It UE_BULK .It UE_CONTROL "0" no transfer pre-delay. "Else" a delay as given by this field in milliseconds is inserted before the hardware is started when "usbd_transfer_submit()" is called. .Pp NOTE: The transfer timeout, if any, is started after that the pre-delay has elapsed! .El . .Pp .Fa timeout field, if non-zero, will set the transfer timeout in milliseconds. If the "timeout" field is zero and the transfer type is ISOCHRONOUS a timeout of 250ms will be used. . .Pp .Fa frames field sets the maximum number of frames. If zero is specified it will yield the following results: .Bl -tag .It UE_BULK xfer->nframes = 1; .It UE_INTERRUPT xfer->nframes = 1; .It UE_CONTROL xfer->nframes = 2; .It UE_ISOCHRONOUS Not allowed. Will cause an error. .El . .Pp .Fa ep_index field allows you to give a number, in case more endpoints match the description, that selects which matching "ep_index" should be used. . .Pp .Fa if_index field allows you to select which of the interface numbers in the "ifaces" array parameter passed to "usbd_transfer_setup" that should be used when setting up the given USB transfer. . .Pp .Fa flags field has type "struct usb_xfer_flags" and allows one to set initial flags an USB transfer. Valid flags are: .Bl -tag .It force_short_xfer This flag forces the last transmitted USB packet to be short. A short packet has a length of less than "xfer->max_packet_size", which derives from "wMaxPacketSize". This flag can be changed during operation. .It short_xfer_ok This flag allows the received transfer length, "xfer->actlen" to be less than "xfer->sumlen" upon completion of a transfer. This flag can be changed during operation. .It short_frames_ok This flag allows the reception of multiple short USB frames. This flag only has effect for BULK and INTERRUPT endpoints and if the number of frames received is greater than 1. This flag can be changed during operation. .It pipe_bof This flag causes a failing USB transfer to remain first in the PIPE queue except in the case of "xfer->error" equal to "USB_ERR_CANCELLED". No other USB transfers in the affected PIPE queue will be started until either: .Bl -tag .It 1 The failing USB transfer is stopped using "usbd_transfer_stop()". .It 2 The failing USB transfer performs a successful transfer. .El The purpose of this flag is to avoid races when multiple transfers are queued for execution on an USB endpoint, and the first executing transfer fails leading to the need for clearing of stall for example. . In this case this flag is used to prevent the following USB transfers from being executed at the same time the clear-stall command is executed on the USB control endpoint. . This flag can be changed during operation. .Pp "BOF" is short for "Block On Failure" .Pp NOTE: This flag should be set on all BULK and INTERRUPT USB transfers which use an endpoint that can be shared between userland and kernel. . . .It proxy_buffer Setting this flag will cause that the total buffer size will be rounded up to the nearest atomic hardware transfer size. . The maximum data length of any USB transfer is always stored in the "xfer->max_data_length". . For control transfers the USB kernel will allocate additional space for the 8-bytes of SETUP header. . These 8-bytes are not counted by the "xfer->max_data_length" variable. . This flag can not be changed during operation. . . .It ext_buffer Setting this flag will cause that no data buffer will be allocated. . Instead the USB client must supply a data buffer. . This flag can not be changed during operation. . . .It manual_status Setting this flag prevents an USB STATUS stage to be appended to the end of the USB control transfer. . If no control data is transferred this flag must be cleared. . Else an error will be returned to the USB callback. . This flag is mostly useful for the USB device side. . This flag can be changed during operation. . . .It no_pipe_ok Setting this flag causes the USB_ERR_NO_PIPE error to be ignored. This flag can not be changed during operation. . . .It stall_pipe .Bl -tag .It Device Side Mode Setting this flag will cause STALL pids to be sent to the endpoint belonging to this transfer before the transfer is started. . The transfer is started at the moment the host issues a clear-stall command on the STALL'ed endpoint. . This flag can be changed during operation. .It Host Side Mode Setting this flag will cause a clear-stall control request to be executed on the endpoint before the USB transfer is started. .El .Pp If this flag is changed outside the USB callback function you have to use the "usbd_xfer_set_stall()" and "usbd_transfer_clear_stall()" functions! This flag is automatically cleared after that the stall or clear stall has been executed. . +.It pre_scale_frames +If this flag is set the number of frames specified is assumed to give the buffering time in milliseconds instead of frames. +During transfer setup the frames field is pre scaled with the corresponding value for the endpoint and rounded to the nearest number of frames greater than zero. +This option only has effect for ISOCHRONOUS transfers. .El .Pp .Fa bufsize field sets the total buffer size in bytes. . If this field is zero, "wMaxPacketSize" will be used, multiplied by the "frames" field if the transfer type is ISOCHRONOUS. . This is useful for setting up interrupt pipes. . This field is mandatory. .Pp NOTE: For control transfers "bufsize" includes the length of the request structure. . .Pp .Fa callback pointer sets the USB callback. This field is mandatory. . . .Sh USB LINUX COMPAT LAYER The .Nm usb module supports the Linux USB API. . . .Sh SEE ALSO .Xr libusb 3 , .Xr usb 4 , .Xr usbconfig 8 .Sh STANDARDS The .Nm usb module complies with the USB 2.0 standard. .Sh HISTORY The .Nm usb module has been inspired by the NetBSD USB stack initially written by Lennart Augustsson. The .Nm usb module was written by .An Hans Petter Selasky Aq hselasky@freebsd.org . Index: stable/8/share/man/man9 =================================================================== --- stable/8/share/man/man9 (revision 220433) +++ stable/8/share/man/man9 (revision 220434) Property changes on: stable/8/share/man/man9 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/share/man/man9:r219100 Index: stable/8/sys/dev/usb/usb_generic.c =================================================================== --- stable/8/sys/dev/usb/usb_generic.c (revision 220433) +++ stable/8/sys/dev/usb/usb_generic.c (revision 220434) @@ -1,2237 +1,2250 @@ /* $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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR ugen_debug #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if USB_HAVE_UGEN /* defines */ #define UGEN_BULK_FS_BUFFER_SIZE (64*32) /* bytes */ #define UGEN_BULK_HS_BUFFER_SIZE (1024*32) /* bytes */ #define UGEN_HW_FRAMES 50 /* number of milliseconds per transfer */ /* function prototypes */ static usb_callback_t ugen_read_clear_stall_callback; static usb_callback_t ugen_write_clear_stall_callback; static usb_callback_t ugen_ctrl_read_callback; static usb_callback_t ugen_ctrl_write_callback; static usb_callback_t ugen_isoc_read_callback; static usb_callback_t ugen_isoc_write_callback; static usb_callback_t ugen_ctrl_fs_callback; static usb_fifo_open_t ugen_open; static usb_fifo_close_t ugen_close; static usb_fifo_ioctl_t ugen_ioctl; static usb_fifo_ioctl_t ugen_ioctl_post; static usb_fifo_cmd_t ugen_start_read; static usb_fifo_cmd_t ugen_start_write; static usb_fifo_cmd_t ugen_stop_io; static int ugen_transfer_setup(struct usb_fifo *, const struct usb_config *, uint8_t); static int ugen_open_pipe_write(struct usb_fifo *); static int ugen_open_pipe_read(struct usb_fifo *); static int ugen_set_config(struct usb_fifo *, uint8_t); static int ugen_set_interface(struct usb_fifo *, uint8_t, uint8_t); static int ugen_get_cdesc(struct usb_fifo *, struct usb_gen_descriptor *); static int ugen_get_sdesc(struct usb_fifo *, struct usb_gen_descriptor *); static int ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd); static int usb_gen_fill_deviceinfo(struct usb_fifo *, struct usb_device_info *); static int ugen_re_enumerate(struct usb_fifo *); static int ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int); static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *); static int ugen_fs_uninit(struct usb_fifo *f); /* structures */ struct usb_fifo_methods usb_ugen_methods = { .f_open = &ugen_open, .f_close = &ugen_close, .f_ioctl = &ugen_ioctl, .f_ioctl_post = &ugen_ioctl_post, .f_start_read = &ugen_start_read, .f_stop_read = &ugen_stop_io, .f_start_write = &ugen_start_write, .f_stop_write = &ugen_stop_io, }; #ifdef USB_DEBUG static int ugen_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB generic"); SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW, &ugen_debug, 0, "Debug level"); TUNABLE_INT("hw.usb.ugen.debug", &ugen_debug); #endif /* prototypes */ static int ugen_transfer_setup(struct usb_fifo *f, const struct usb_config *setup, uint8_t n_setup) { struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_device *udev = f->udev; uint8_t iface_index = ep->iface_index; int error; mtx_unlock(f->priv_mtx); /* * "usbd_transfer_setup()" can sleep so one needs to make a wrapper, * exiting the mutex and checking things */ error = usbd_transfer_setup(udev, &iface_index, f->xfer, setup, n_setup, f, f->priv_mtx); if (error == 0) { if (f->xfer[0]->nframes == 1) { error = usb_fifo_alloc_buffer(f, f->xfer[0]->max_data_length, 2); } else { error = usb_fifo_alloc_buffer(f, f->xfer[0]->max_frame_size, 2 * f->xfer[0]->nframes); } if (error) { usbd_transfer_unsetup(f->xfer, n_setup); } } mtx_lock(f->priv_mtx); return (error); } static int ugen_open(struct usb_fifo *f, int fflags) { struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; uint8_t type; DPRINTFN(6, "flag=0x%x\n", fflags); mtx_lock(f->priv_mtx); switch (usbd_get_speed(f->udev)) { case USB_SPEED_LOW: case USB_SPEED_FULL: f->nframes = UGEN_HW_FRAMES; f->bufsize = UGEN_BULK_FS_BUFFER_SIZE; break; default: f->nframes = UGEN_HW_FRAMES * 8; f->bufsize = UGEN_BULK_HS_BUFFER_SIZE; break; } type = ed->bmAttributes & UE_XFERTYPE; if (type == UE_INTERRUPT) { f->bufsize = 0; /* use "wMaxPacketSize" */ } f->timeout = USB_NO_TIMEOUT; f->flag_short = 0; f->fifo_zlp = 0; mtx_unlock(f->priv_mtx); return (0); } static void ugen_close(struct usb_fifo *f, int fflags) { DPRINTFN(6, "flag=0x%x\n", fflags); /* cleanup */ mtx_lock(f->priv_mtx); usbd_transfer_stop(f->xfer[0]); usbd_transfer_stop(f->xfer[1]); mtx_unlock(f->priv_mtx); usbd_transfer_unsetup(f->xfer, 2); usb_fifo_free_buffer(f); if (ugen_fs_uninit(f)) { /* ignore any errors - we are closing */ DPRINTFN(6, "no FIFOs\n"); } } static int ugen_open_pipe_write(struct usb_fifo *f) { struct usb_config usb_config[2]; struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; mtx_assert(f->priv_mtx, MA_OWNED); if (f->xfer[0] || f->xfer[1]) { /* transfers are already opened */ return (0); } bzero(usb_config, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; usb_config[1].direction = UE_DIR_ANY; usb_config[1].timeout = 1000; /* 1 second */ usb_config[1].interval = 50;/* 50 milliseconds */ usb_config[1].bufsize = sizeof(struct usb_device_request); usb_config[1].callback = &ugen_write_clear_stall_callback; usb_config[1].usb_mode = USB_MODE_HOST; usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].direction = UE_DIR_TX; usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ switch (ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_BULK: if (f->flag_short) { usb_config[0].flags.force_short_xfer = 1; } usb_config[0].callback = &ugen_ctrl_write_callback; usb_config[0].timeout = f->timeout; usb_config[0].frames = 1; usb_config[0].bufsize = f->bufsize; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } /* first transfer does not clear stall */ f->flag_stall = 0; break; case UE_ISOCHRONOUS: usb_config[0].flags.short_xfer_ok = 1; usb_config[0].bufsize = 0; /* use default */ usb_config[0].frames = f->nframes; usb_config[0].callback = &ugen_isoc_write_callback; usb_config[0].timeout = 0; /* clone configuration */ usb_config[1] = usb_config[0]; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } break; default: return (EINVAL); } return (0); } static int ugen_open_pipe_read(struct usb_fifo *f) { struct usb_config usb_config[2]; struct usb_endpoint *ep = usb_fifo_softc(f); struct usb_endpoint_descriptor *ed = ep->edesc; mtx_assert(f->priv_mtx, MA_OWNED); if (f->xfer[0] || f->xfer[1]) { /* transfers are already opened */ return (0); } bzero(usb_config, sizeof(usb_config)); usb_config[1].type = UE_CONTROL; usb_config[1].endpoint = 0; usb_config[1].direction = UE_DIR_ANY; usb_config[1].timeout = 1000; /* 1 second */ usb_config[1].interval = 50;/* 50 milliseconds */ usb_config[1].bufsize = sizeof(struct usb_device_request); usb_config[1].callback = &ugen_read_clear_stall_callback; usb_config[1].usb_mode = USB_MODE_HOST; usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].direction = UE_DIR_RX; usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ switch (ed->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: case UE_BULK: if (f->flag_short) { usb_config[0].flags.short_xfer_ok = 1; } usb_config[0].timeout = f->timeout; usb_config[0].frames = 1; usb_config[0].callback = &ugen_ctrl_read_callback; usb_config[0].bufsize = f->bufsize; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } /* first transfer does not clear stall */ f->flag_stall = 0; break; case UE_ISOCHRONOUS: usb_config[0].flags.short_xfer_ok = 1; usb_config[0].bufsize = 0; /* use default */ usb_config[0].frames = f->nframes; usb_config[0].callback = &ugen_isoc_read_callback; usb_config[0].timeout = 0; /* clone configuration */ usb_config[1] = usb_config[0]; if (ugen_transfer_setup(f, usb_config, 2)) { return (EIO); } break; default: return (EINVAL); } return (0); } static void ugen_start_read(struct usb_fifo *f) { /* check that pipes are open */ if (ugen_open_pipe_read(f)) { /* signal error */ usb_fifo_put_data_error(f); } /* start transfers */ usbd_transfer_start(f->xfer[0]); usbd_transfer_start(f->xfer[1]); } static void ugen_start_write(struct usb_fifo *f) { /* check that pipes are open */ if (ugen_open_pipe_write(f)) { /* signal error */ usb_fifo_get_data_error(f); } /* start transfers */ usbd_transfer_start(f->xfer[0]); usbd_transfer_start(f->xfer[1]); } static void ugen_stop_io(struct usb_fifo *f) { /* stop transfers */ usbd_transfer_stop(f->xfer[0]); usbd_transfer_stop(f->xfer[1]); } static void ugen_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_mbuf *m; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (xfer->actlen == 0) { if (f->fifo_zlp != 4) { f->fifo_zlp++; } else { /* * Throttle a little bit we have multiple ZLPs * in a row! */ xfer->interval = 64; /* ms */ } } else { /* clear throttle */ xfer->interval = 0; f->fifo_zlp = 0; } usb_fifo_put_data(f, xfer->frbuffers, 0, xfer->actlen, 1); case USB_ST_SETUP: if (f->flag_stall) { usbd_transfer_start(f->xfer[1]); break; } USB_IF_POLL(&f->free_q, m); if (m) { usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { /* send a zero length packet to userland */ usb_fifo_put_data(f, xfer->frbuffers, 0, 0, 1); f->flag_stall = 1; f->fifo_zlp = 0; usbd_transfer_start(f->xfer[1]); } break; } } static void ugen_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t actlen; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: /* * If writing is in stall, just jump to clear stall * callback and solve the situation. */ if (f->flag_stall) { usbd_transfer_start(f->xfer[1]); break; } /* * Write data, setup and perform hardware transfer. */ if (usb_fifo_get_data(f, xfer->frbuffers, 0, xfer->max_data_length, &actlen, 0)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } break; default: /* Error */ if (xfer->error != USB_ERR_CANCELLED) { f->flag_stall = 1; usbd_transfer_start(f->xfer[1]); } break; } } static void ugen_read_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = f->xfer[0]; if (f->flag_stall == 0) { /* nothing to do */ return; } if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTFN(5, "f=%p: stall cleared\n", f); f->flag_stall = 0; usbd_transfer_start(xfer_other); } } static void ugen_write_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); struct usb_xfer *xfer_other = f->xfer[0]; if (f->flag_stall == 0) { /* nothing to do */ return; } if (usbd_clear_stall_callback(xfer, xfer_other)) { DPRINTFN(5, "f=%p: stall cleared\n", f); f->flag_stall = 0; usbd_transfer_start(xfer_other); } } static void ugen_isoc_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t offset; usb_frcount_t n; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTFN(6, "actlen=%d\n", xfer->actlen); offset = 0; for (n = 0; n != xfer->aframes; n++) { usb_fifo_put_data(f, xfer->frbuffers, offset, xfer->frlengths[n], 1); offset += xfer->max_frame_size; } case USB_ST_SETUP: tr_setup: for (n = 0; n != xfer->nframes; n++) { /* setup size for next transfer */ usbd_xfer_set_frame_len(xfer, n, xfer->max_frame_size); } usbd_transfer_submit(xfer); break; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { break; } goto tr_setup; } } static void ugen_isoc_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_fifo *f = usbd_xfer_softc(xfer); usb_frlength_t actlen; usb_frlength_t offset; usb_frcount_t n; DPRINTFN(4, "actlen=%u, aframes=%u\n", xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: offset = 0; for (n = 0; n != xfer->nframes; n++) { if (usb_fifo_get_data(f, xfer->frbuffers, offset, xfer->max_frame_size, &actlen, 1)) { usbd_xfer_set_frame_len(xfer, n, actlen); offset += actlen; } else { break; } } for (; n != xfer->nframes; n++) { /* fill in zero frames */ usbd_xfer_set_frame_len(xfer, n, 0); } usbd_transfer_submit(xfer); break; default: /* Error */ if (xfer->error == USB_ERR_CANCELLED) { break; } goto tr_setup; } } static int ugen_set_config(struct usb_fifo *f, uint8_t index) { DPRINTFN(2, "index %u\n", index); if (f->udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ return (ENOTTY); } if (f->udev->curr_config_index == index) { /* no change needed */ return (0); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } /* change setting - will free generic FIFOs, if any */ if (usbd_set_config_index(f->udev, index)) { return (EIO); } /* probe and attach */ if (usb_probe_and_attach(f->udev, USB_IFACE_INDEX_ANY)) { return (EIO); } return (0); } static int ugen_set_interface(struct usb_fifo *f, uint8_t iface_index, uint8_t alt_index) { DPRINTFN(2, "%u, %u\n", iface_index, alt_index); if (f->udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } /* change setting - will free generic FIFOs, if any */ if (usbd_set_alt_interface_index(f->udev, iface_index, alt_index)) { return (EIO); } /* probe and attach */ if (usb_probe_and_attach(f->udev, iface_index)) { return (EIO); } return (0); } /*------------------------------------------------------------------------* * ugen_get_cdesc * * This function will retrieve the complete configuration descriptor * at the given index. *------------------------------------------------------------------------*/ static int ugen_get_cdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { struct usb_config_descriptor *cdesc; struct usb_device *udev = f->udev; int error; uint16_t len; uint8_t free_data; DPRINTFN(6, "\n"); if (ugd->ugd_data == NULL) { /* userland pointer should not be zero */ return (EINVAL); } if ((ugd->ugd_config_index == USB_UNCONFIG_INDEX) || (ugd->ugd_config_index == udev->curr_config_index)) { cdesc = usbd_get_config_descriptor(udev); if (cdesc == NULL) { return (ENXIO); } free_data = 0; } else { if (usbd_req_get_config_desc_full(udev, NULL, &cdesc, M_USBDEV, ugd->ugd_config_index)) { return (ENXIO); } free_data = 1; } len = UGETW(cdesc->wTotalLength); if (len > ugd->ugd_maxlen) { len = ugd->ugd_maxlen; } DPRINTFN(6, "len=%u\n", len); ugd->ugd_actlen = len; ugd->ugd_offset = 0; error = copyout(cdesc, ugd->ugd_data, len); if (free_data) { free(cdesc, M_USBDEV); } return (error); } static int ugen_get_sdesc(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { void *ptr = f->udev->bus->scratch[0].data; uint16_t size = sizeof(f->udev->bus->scratch[0].data); int error; if (usbd_req_get_string_desc(f->udev, NULL, ptr, size, ugd->ugd_lang_id, ugd->ugd_string_index)) { error = EINVAL; } else { if (size > ((uint8_t *)ptr)[0]) { size = ((uint8_t *)ptr)[0]; } if (size > ugd->ugd_maxlen) { size = ugd->ugd_maxlen; } ugd->ugd_actlen = size; ugd->ugd_offset = 0; error = copyout(ptr, ugd->ugd_data, size); } return (error); } /*------------------------------------------------------------------------* * ugen_get_iface_driver * * This function generates an USB interface description for userland. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int ugen_get_iface_driver(struct usb_fifo *f, struct usb_gen_descriptor *ugd) { struct usb_device *udev = f->udev; struct usb_interface *iface; const char *ptr; const char *desc; unsigned int len; unsigned int maxlen; char buf[128]; int error; DPRINTFN(6, "\n"); if ((ugd->ugd_data == NULL) || (ugd->ugd_maxlen == 0)) { /* userland pointer should not be zero */ return (EINVAL); } iface = usbd_get_iface(udev, ugd->ugd_iface_index); if ((iface == NULL) || (iface->idesc == NULL)) { /* invalid interface index */ return (EINVAL); } /* read out device nameunit string, if any */ if ((iface->subdev != NULL) && device_is_attached(iface->subdev) && (ptr = device_get_nameunit(iface->subdev)) && (desc = device_get_desc(iface->subdev))) { /* print description */ snprintf(buf, sizeof(buf), "%s: <%s>", ptr, desc); /* range checks */ maxlen = ugd->ugd_maxlen - 1; len = strlen(buf); if (len > maxlen) len = maxlen; /* update actual length, including terminating zero */ ugd->ugd_actlen = len + 1; /* copy out interface description */ error = copyout(buf, ugd->ugd_data, ugd->ugd_actlen); } else { /* zero length string is default */ error = copyout("", ugd->ugd_data, 1); } return (error); } /*------------------------------------------------------------------------* * usb_gen_fill_deviceinfo * * This function dumps information about an USB device to the * structure pointed to by the "di" argument. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usb_gen_fill_deviceinfo(struct usb_fifo *f, struct usb_device_info *di) { struct usb_device *udev; struct usb_device *hub; udev = f->udev; bzero(di, sizeof(di[0])); di->udi_bus = device_get_unit(udev->bus->bdev); di->udi_addr = udev->address; di->udi_index = udev->device_index; strlcpy(di->udi_serial, usb_get_serial(udev), sizeof(di->udi_serial)); strlcpy(di->udi_vendor, usb_get_manufacturer(udev), sizeof(di->udi_vendor)); strlcpy(di->udi_product, usb_get_product(udev), sizeof(di->udi_product)); usb_printbcd(di->udi_release, sizeof(di->udi_release), UGETW(udev->ddesc.bcdDevice)); di->udi_vendorNo = UGETW(udev->ddesc.idVendor); di->udi_productNo = UGETW(udev->ddesc.idProduct); di->udi_releaseNo = UGETW(udev->ddesc.bcdDevice); di->udi_class = udev->ddesc.bDeviceClass; di->udi_subclass = udev->ddesc.bDeviceSubClass; di->udi_protocol = udev->ddesc.bDeviceProtocol; di->udi_config_no = udev->curr_config_no; di->udi_config_index = udev->curr_config_index; di->udi_power = udev->flags.self_powered ? 0 : udev->power; di->udi_speed = udev->speed; di->udi_mode = udev->flags.usb_mode; di->udi_power_mode = udev->power_mode; di->udi_suspended = udev->flags.peer_suspended; hub = udev->parent_hub; if (hub) { di->udi_hubaddr = hub->address; di->udi_hubindex = hub->device_index; di->udi_hubport = udev->port_no; } return (0); } /*------------------------------------------------------------------------* * ugen_check_request * * Return values: * 0: Access allowed * Else: No access *------------------------------------------------------------------------*/ static int ugen_check_request(struct usb_device *udev, struct usb_device_request *req) { struct usb_endpoint *ep; int error; /* * Avoid requests that would damage the bus integrity: */ if (((req->bmRequestType == UT_WRITE_DEVICE) && (req->bRequest == UR_SET_ADDRESS)) || ((req->bmRequestType == UT_WRITE_DEVICE) && (req->bRequest == UR_SET_CONFIG)) || ((req->bmRequestType == UT_WRITE_INTERFACE) && (req->bRequest == UR_SET_INTERFACE))) { /* * These requests can be useful for testing USB drivers. */ error = priv_check(curthread, PRIV_DRIVER); if (error) { return (error); } } /* * Special case - handle clearing of stall */ if (req->bmRequestType == UT_WRITE_ENDPOINT) { ep = usbd_get_ep_by_addr(udev, req->wIndex[0]); if (ep == NULL) { return (EINVAL); } if ((req->bRequest == UR_CLEAR_FEATURE) && (UGETW(req->wValue) == UF_ENDPOINT_HALT)) { usbd_clear_data_toggle(udev, ep); } } /* TODO: add more checks to verify the interface index */ return (0); } int ugen_do_request(struct usb_fifo *f, struct usb_ctl_request *ur) { int error; uint16_t len; uint16_t actlen; if (ugen_check_request(f->udev, &ur->ucr_request)) { return (EPERM); } len = UGETW(ur->ucr_request.wLength); /* check if "ucr_data" is valid */ if (len != 0) { if (ur->ucr_data == NULL) { return (EFAULT); } } /* do the USB request */ error = usbd_do_request_flags (f->udev, NULL, &ur->ucr_request, ur->ucr_data, (ur->ucr_flags & USB_SHORT_XFER_OK) | USB_USER_DATA_PTR, &actlen, USB_DEFAULT_TIMEOUT); ur->ucr_actlen = actlen; if (error) { error = EIO; } return (error); } /*------------------------------------------------------------------------ * ugen_re_enumerate *------------------------------------------------------------------------*/ static int ugen_re_enumerate(struct usb_fifo *f) { struct usb_device *udev = f->udev; int error; /* * This request can be useful for testing USB drivers: */ error = priv_check(curthread, PRIV_DRIVER); if (error) { return (error); } if (udev->flags.usb_mode != USB_MODE_HOST) { /* not possible in device side mode */ return (ENOTTY); } /* make sure all FIFO's are gone */ /* else there can be a deadlock */ if (ugen_fs_uninit(f)) { /* ignore any errors */ DPRINTFN(6, "no FIFOs\n"); } if (udev->re_enumerate_wait == 0) { udev->re_enumerate_wait = 1; usb_needs_explore(udev->bus, 0); } return (0); } int ugen_fs_uninit(struct usb_fifo *f) { if (f->fs_xfer == NULL) { return (EINVAL); } usbd_transfer_unsetup(f->fs_xfer, f->fs_ep_max); free(f->fs_xfer, M_USB); f->fs_xfer = NULL; f->fs_ep_max = 0; f->fs_ep_ptr = NULL; f->flag_iscomplete = 0; usb_fifo_free_buffer(f); return (0); } static uint8_t ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->used_q, m); if (m) { *pindex = *((uint8_t *)(m->cur_data_ptr)); USB_IF_ENQUEUE(&f->free_q, m); return (0); /* success */ } else { *pindex = 0; /* fix compiler warning */ f->flag_iscomplete = 0; } return (1); /* failure */ } static void ugen_fs_set_complete(struct usb_fifo *f, uint8_t index) { struct usb_mbuf *m; USB_IF_DEQUEUE(&f->free_q, m); if (m == NULL) { /* can happen during close */ DPRINTF("out of buffers\n"); return; } USB_MBUF_RESET(m); *((uint8_t *)(m->cur_data_ptr)) = index; USB_IF_ENQUEUE(&f->used_q, m); f->flag_iscomplete = 1; usb_fifo_wakeup(f); } static int ugen_fs_copy_in(struct usb_fifo *f, uint8_t ep_index) { struct usb_device_request *req; struct usb_xfer *xfer; struct usb_fs_endpoint fs_ep; void *uaddr; /* userland pointer */ void *kaddr; usb_frlength_t offset; usb_frlength_t rem; usb_frcount_t n; uint32_t length; int error; uint8_t isread; if (ep_index >= f->fs_ep_max) { return (EINVAL); } xfer = f->fs_xfer[ep_index]; if (xfer == NULL) { return (EINVAL); } mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ } mtx_unlock(f->priv_mtx); error = copyin(f->fs_ep_ptr + ep_index, &fs_ep, sizeof(fs_ep)); if (error) { return (error); } /* security checks */ if (fs_ep.nFrames > xfer->max_frame_count) { xfer->error = USB_ERR_INVAL; goto complete; } if (fs_ep.nFrames == 0) { xfer->error = USB_ERR_INVAL; goto complete; } error = copyin(fs_ep.ppBuffer, &uaddr, sizeof(uaddr)); if (error) { return (error); } /* reset first frame */ usbd_xfer_set_frame_offset(xfer, 0, 0); if (xfer->flags_int.control_xfr) { req = xfer->frbuffers[0].buffer; error = copyin(fs_ep.pLength, &length, sizeof(length)); if (error) { return (error); } if (length != sizeof(*req)) { xfer->error = USB_ERR_INVAL; goto complete; } if (length != 0) { error = copyin(uaddr, req, length); if (error) { return (error); } } if (ugen_check_request(f->udev, req)) { xfer->error = USB_ERR_INVAL; goto complete; } usbd_xfer_set_frame_len(xfer, 0, length); /* Host mode only ! */ if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { isread = 1; } else { isread = 0; } n = 1; offset = sizeof(*req); } else { /* Device and Host mode */ if (USB_GET_DATA_ISREAD(xfer)) { isread = 1; } else { isread = 0; } n = 0; offset = 0; } rem = usbd_xfer_max_len(xfer); xfer->nframes = fs_ep.nFrames; xfer->timeout = fs_ep.timeout; if (xfer->timeout > 65535) { xfer->timeout = 65535; } if (fs_ep.flags & USB_FS_FLAG_SINGLE_SHORT_OK) xfer->flags.short_xfer_ok = 1; else xfer->flags.short_xfer_ok = 0; if (fs_ep.flags & USB_FS_FLAG_MULTI_SHORT_OK) xfer->flags.short_frames_ok = 1; else xfer->flags.short_frames_ok = 0; if (fs_ep.flags & USB_FS_FLAG_FORCE_SHORT) xfer->flags.force_short_xfer = 1; else xfer->flags.force_short_xfer = 0; if (fs_ep.flags & USB_FS_FLAG_CLEAR_STALL) usbd_xfer_set_stall(xfer); else xfer->flags.stall_pipe = 0; for (; n != xfer->nframes; n++) { error = copyin(fs_ep.pLength + n, &length, sizeof(length)); if (error) { break; } usbd_xfer_set_frame_len(xfer, n, length); if (length > rem) { xfer->error = USB_ERR_INVAL; goto complete; } rem -= length; if (!isread) { /* we need to know the source buffer */ error = copyin(fs_ep.ppBuffer + n, &uaddr, sizeof(uaddr)); if (error) { break; } if (xfer->flags_int.isochronous_xfr) { /* get kernel buffer address */ kaddr = xfer->frbuffers[0].buffer; kaddr = USB_ADD_BYTES(kaddr, offset); } else { /* set current frame offset */ usbd_xfer_set_frame_offset(xfer, offset, n); /* get kernel buffer address */ kaddr = xfer->frbuffers[n].buffer; } /* move data */ error = copyin(uaddr, kaddr, length); if (error) { break; } } offset += length; } return (error); complete: mtx_lock(f->priv_mtx); ugen_fs_set_complete(f, ep_index); mtx_unlock(f->priv_mtx); return (0); } static int ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index) { struct usb_device_request *req; struct usb_xfer *xfer; struct usb_fs_endpoint fs_ep; struct usb_fs_endpoint *fs_ep_uptr; /* userland ptr */ void *uaddr; /* userland ptr */ void *kaddr; usb_frlength_t offset; usb_frlength_t rem; usb_frcount_t n; uint32_t length; uint32_t temp; int error; uint8_t isread; if (ep_index >= f->fs_ep_max) return (EINVAL); xfer = f->fs_xfer[ep_index]; if (xfer == NULL) return (EINVAL); mtx_lock(f->priv_mtx); if (usbd_transfer_pending(xfer)) { mtx_unlock(f->priv_mtx); return (EBUSY); /* should not happen */ } mtx_unlock(f->priv_mtx); fs_ep_uptr = f->fs_ep_ptr + ep_index; error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep)); if (error) { return (error); } fs_ep.status = xfer->error; fs_ep.aFrames = xfer->aframes; fs_ep.isoc_time_complete = xfer->isoc_time_complete; if (xfer->error) { goto complete; } if (xfer->flags_int.control_xfr) { req = xfer->frbuffers[0].buffer; /* Host mode only ! */ if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_READ) { isread = 1; } else { isread = 0; } if (xfer->nframes == 0) n = 0; /* should never happen */ else n = 1; } else { /* Device and Host mode */ if (USB_GET_DATA_ISREAD(xfer)) { isread = 1; } else { isread = 0; } n = 0; } /* Update lengths and copy out data */ rem = usbd_xfer_max_len(xfer); offset = 0; for (; n != xfer->nframes; n++) { /* get initial length into "temp" */ error = copyin(fs_ep.pLength + n, &temp, sizeof(temp)); if (error) { return (error); } if (temp > rem) { /* the userland length has been corrupted */ DPRINTF("corrupt userland length " "%u > %u\n", temp, rem); fs_ep.status = USB_ERR_INVAL; goto complete; } rem -= temp; /* get actual transfer length */ length = xfer->frlengths[n]; if (length > temp) { /* data overflow */ fs_ep.status = USB_ERR_INVAL; DPRINTF("data overflow %u > %u\n", length, temp); goto complete; } if (isread) { /* we need to know the destination buffer */ error = copyin(fs_ep.ppBuffer + n, &uaddr, sizeof(uaddr)); if (error) { return (error); } if (xfer->flags_int.isochronous_xfr) { /* only one frame buffer */ kaddr = USB_ADD_BYTES( xfer->frbuffers[0].buffer, offset); } else { /* multiple frame buffers */ kaddr = xfer->frbuffers[n].buffer; } /* move data */ error = copyout(kaddr, uaddr, length); if (error) { return (error); } } /* * Update offset according to initial length, which is * needed by isochronous transfers! */ offset += temp; /* update length */ error = copyout(&length, fs_ep.pLength + n, sizeof(length)); if (error) { return (error); } } complete: /* update "aFrames" */ error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames, sizeof(fs_ep.aFrames)); if (error) goto done; /* update "isoc_time_complete" */ error = copyout(&fs_ep.isoc_time_complete, &fs_ep_uptr->isoc_time_complete, sizeof(fs_ep.isoc_time_complete)); if (error) goto done; /* update "status" */ error = copyout(&fs_ep.status, &fs_ep_uptr->status, sizeof(fs_ep.status)); done: return (error); } static uint8_t ugen_fifo_in_use(struct usb_fifo *f, int fflags) { struct usb_fifo *f_rx; struct usb_fifo *f_tx; f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; if ((fflags & FREAD) && f_rx && (f_rx->xfer[0] || f_rx->xfer[1])) { return (1); /* RX FIFO in use */ } if ((fflags & FWRITE) && f_tx && (f_tx->xfer[0] || f_tx->xfer[1])) { return (1); /* TX FIFO in use */ } return (0); /* not in use */ } static int ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { struct usb_config usb_config[1]; struct usb_device_request req; union { struct usb_fs_complete *pcomp; struct usb_fs_start *pstart; struct usb_fs_stop *pstop; struct usb_fs_open *popen; struct usb_fs_close *pclose; struct usb_fs_clear_stall_sync *pstall; void *addr; } u; struct usb_endpoint *ep; struct usb_endpoint_descriptor *ed; int error = 0; uint8_t iface_index; uint8_t isread; uint8_t ep_index; + uint8_t pre_scale; u.addr = addr; DPRINTFN(6, "cmd=0x%08lx\n", cmd); switch (cmd) { case USB_FS_COMPLETE: mtx_lock(f->priv_mtx); error = ugen_fs_get_complete(f, &ep_index); mtx_unlock(f->priv_mtx); if (error) { error = EBUSY; break; } u.pcomp->ep_index = ep_index; error = ugen_fs_copy_out(f, u.pcomp->ep_index); break; case USB_FS_START: error = ugen_fs_copy_in(f, u.pstart->ep_index); if (error) { break; } mtx_lock(f->priv_mtx); usbd_transfer_start(f->fs_xfer[u.pstart->ep_index]); mtx_unlock(f->priv_mtx); break; case USB_FS_STOP: if (u.pstop->ep_index >= f->fs_ep_max) { error = EINVAL; break; } mtx_lock(f->priv_mtx); usbd_transfer_stop(f->fs_xfer[u.pstop->ep_index]); mtx_unlock(f->priv_mtx); break; case USB_FS_OPEN: if (u.popen->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.popen->ep_index] != NULL) { error = EBUSY; break; } if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) { u.popen->max_bufsize = USB_FS_MAX_BUFSIZE; } + if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) { + pre_scale = 1; + u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE; + } else { + pre_scale = 0; + } if (u.popen->max_frames > USB_FS_MAX_FRAMES) { u.popen->max_frames = USB_FS_MAX_FRAMES; break; } if (u.popen->max_frames == 0) { error = EINVAL; break; } ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no); if (ep == NULL) { error = EINVAL; break; } ed = ep->edesc; if (ed == NULL) { error = ENXIO; break; } iface_index = ep->iface_index; - bzero(usb_config, sizeof(usb_config)); + memset(usb_config, 0, sizeof(usb_config)); usb_config[0].type = ed->bmAttributes & UE_XFERTYPE; usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); usb_config[0].interval = USB_DEFAULT_INTERVAL; usb_config[0].flags.proxy_buffer = 1; + if (pre_scale != 0) + usb_config[0].flags.pre_scale_frames = 1; usb_config[0].callback = &ugen_ctrl_fs_callback; usb_config[0].timeout = 0; /* no timeout */ usb_config[0].frames = u.popen->max_frames; usb_config[0].bufsize = u.popen->max_bufsize; usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */ if (usb_config[0].type == UE_CONTROL) { if (f->udev->flags.usb_mode != USB_MODE_HOST) { error = EINVAL; break; } } else { isread = ((usb_config[0].endpoint & (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); if (f->udev->flags.usb_mode != USB_MODE_HOST) { isread = !isread; } /* check permissions */ if (isread) { if (!(fflags & FREAD)) { error = EPERM; break; } } else { if (!(fflags & FWRITE)) { error = EPERM; break; } } } error = usbd_transfer_setup(f->udev, &iface_index, f->fs_xfer + u.popen->ep_index, usb_config, 1, f, f->priv_mtx); if (error == 0) { /* update maximums */ u.popen->max_packet_length = f->fs_xfer[u.popen->ep_index]->max_frame_size; u.popen->max_bufsize = f->fs_xfer[u.popen->ep_index]->max_data_length; + /* update number of frames */ + u.popen->max_frames = + f->fs_xfer[u.popen->ep_index]->nframes; + /* store index of endpoint */ f->fs_xfer[u.popen->ep_index]->priv_fifo = ((uint8_t *)0) + u.popen->ep_index; } else { error = ENOMEM; } break; case USB_FS_CLOSE: if (u.pclose->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.pclose->ep_index] == NULL) { error = EINVAL; break; } usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1); break; case USB_FS_CLEAR_STALL_SYNC: if (u.pstall->ep_index >= f->fs_ep_max) { error = EINVAL; break; } if (f->fs_xfer[u.pstall->ep_index] == NULL) { error = EINVAL; break; } if (f->udev->flags.usb_mode != USB_MODE_HOST) { error = EINVAL; break; } mtx_lock(f->priv_mtx); error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]); mtx_unlock(f->priv_mtx); if (error) { return (EBUSY); } ep = f->fs_xfer[u.pstall->ep_index]->endpoint; /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = ep->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); error = usbd_do_request(f->udev, NULL, &req, NULL); if (error == 0) { usbd_clear_data_toggle(f->udev, ep); } else { error = ENXIO; } break; default: error = ENOIOCTL; break; } DPRINTFN(6, "error=%d\n", error); return (error); } static int ugen_set_short_xfer(struct usb_fifo *f, void *addr) { uint8_t t; if (*(int *)addr) t = 1; else t = 0; if (f->flag_short == t) { /* same value like before - accept */ return (0); } if (f->xfer[0] || f->xfer[1]) { /* cannot change this during transfer */ return (EBUSY); } f->flag_short = t; return (0); } static int ugen_set_timeout(struct usb_fifo *f, void *addr) { f->timeout = *(int *)addr; if (f->timeout > 65535) { /* limit user input */ f->timeout = 65535; } return (0); } static int ugen_get_frame_size(struct usb_fifo *f, void *addr) { if (f->xfer[0]) { *(int *)addr = f->xfer[0]->max_frame_size; } else { return (EINVAL); } return (0); } static int ugen_set_buffer_size(struct usb_fifo *f, void *addr) { usb_frlength_t t; if (*(int *)addr < 0) t = 0; /* use "wMaxPacketSize" */ else if (*(int *)addr < (256 * 1024)) t = *(int *)addr; else t = 256 * 1024; if (f->bufsize == t) { /* same value like before - accept */ return (0); } if (f->xfer[0] || f->xfer[1]) { /* cannot change this during transfer */ return (EBUSY); } f->bufsize = t; return (0); } static int ugen_get_buffer_size(struct usb_fifo *f, void *addr) { *(int *)addr = f->bufsize; return (0); } static int ugen_get_iface_desc(struct usb_fifo *f, struct usb_interface_descriptor *idesc) { struct usb_interface *iface; iface = usbd_get_iface(f->udev, f->iface_index); if (iface && iface->idesc) { *idesc = *(iface->idesc); } else { return (EIO); } return (0); } static int ugen_get_endpoint_desc(struct usb_fifo *f, struct usb_endpoint_descriptor *ed) { struct usb_endpoint *ep; ep = usb_fifo_softc(f); if (ep && ep->edesc) { *ed = *ep->edesc; } else { return (EINVAL); } return (0); } static int ugen_set_power_mode(struct usb_fifo *f, int mode) { struct usb_device *udev = f->udev; int err; uint8_t old_mode; if ((udev == NULL) || (udev->parent_hub == NULL)) { return (EINVAL); } err = priv_check(curthread, PRIV_DRIVER); if (err) return (err); /* get old power mode */ old_mode = udev->power_mode; /* if no change, then just return */ if (old_mode == mode) return (0); switch (mode) { case USB_POWER_MODE_OFF: /* get the device unconfigured */ err = ugen_set_config(f, USB_UNCONFIG_INDEX); if (err) { DPRINTFN(0, "Could not unconfigure " "device (ignored)\n"); } /* clear port enable */ err = usbd_req_clear_port_feature(udev->parent_hub, NULL, udev->port_no, UHF_PORT_ENABLE); break; case USB_POWER_MODE_ON: case USB_POWER_MODE_SAVE: break; case USB_POWER_MODE_RESUME: #if USB_HAVE_POWERD /* let USB-powerd handle resume */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs++; udev->pwr_save.last_xfer_time = ticks; USB_BUS_UNLOCK(udev->bus); /* set new power mode */ usbd_set_power_mode(udev, USB_POWER_MODE_SAVE); /* wait for resume to complete */ usb_pause_mtx(NULL, hz / 4); /* clear write reference */ USB_BUS_LOCK(udev->bus); udev->pwr_save.write_refs--; USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; case USB_POWER_MODE_SUSPEND: #if USB_HAVE_POWERD /* let USB-powerd handle suspend */ USB_BUS_LOCK(udev->bus); udev->pwr_save.last_xfer_time = ticks - (256 * hz); USB_BUS_UNLOCK(udev->bus); #endif mode = USB_POWER_MODE_SAVE; break; default: return (EINVAL); } if (err) return (ENXIO); /* I/O failure */ /* if we are powered off we need to re-enumerate first */ if (old_mode == USB_POWER_MODE_OFF) { if (udev->flags.usb_mode == USB_MODE_HOST) { if (udev->re_enumerate_wait == 0) udev->re_enumerate_wait = 1; } /* set power mode will wake up the explore thread */ } /* set new power mode */ usbd_set_power_mode(udev, mode); return (0); /* success */ } static int ugen_get_power_mode(struct usb_fifo *f) { struct usb_device *udev = f->udev; if (udev == NULL) return (USB_POWER_MODE_ON); return (udev->power_mode); } static int ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no, uint8_t set, uint16_t feature) { struct usb_device *udev = f->udev; struct usb_hub *hub; int err; err = priv_check(curthread, PRIV_DRIVER); if (err) { return (err); } if (port_no == 0) { return (EINVAL); } if ((udev == NULL) || (udev->hub == NULL)) { return (EINVAL); } hub = udev->hub; if (port_no > hub->nports) { return (EINVAL); } if (set) err = usbd_req_set_port_feature(udev, NULL, port_no, feature); else err = usbd_req_clear_port_feature(udev, NULL, port_no, feature); if (err) return (ENXIO); /* failure */ return (0); /* success */ } static int ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { struct usb_fifo *f_rx; struct usb_fifo *f_tx; int error = 0; f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; switch (cmd) { case USB_SET_RX_SHORT_XFER: if (fflags & FREAD) { error = ugen_set_short_xfer(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_FORCE_SHORT: if (fflags & FWRITE) { error = ugen_set_short_xfer(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_TIMEOUT: if (fflags & FREAD) { error = ugen_set_timeout(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_TIMEOUT: if (fflags & FWRITE) { error = ugen_set_timeout(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_FRAME_SIZE: if (fflags & FREAD) { error = ugen_get_frame_size(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_FRAME_SIZE: if (fflags & FWRITE) { error = ugen_get_frame_size(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_BUFFER_SIZE: if (fflags & FREAD) { error = ugen_set_buffer_size(f_rx, addr); } else { error = EINVAL; } break; case USB_SET_TX_BUFFER_SIZE: if (fflags & FWRITE) { error = ugen_set_buffer_size(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_BUFFER_SIZE: if (fflags & FREAD) { error = ugen_get_buffer_size(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_BUFFER_SIZE: if (fflags & FWRITE) { error = ugen_get_buffer_size(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_INTERFACE_DESC: if (fflags & FREAD) { error = ugen_get_iface_desc(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_INTERFACE_DESC: if (fflags & FWRITE) { error = ugen_get_iface_desc(f_tx, addr); } else { error = EINVAL; } break; case USB_GET_RX_ENDPOINT_DESC: if (fflags & FREAD) { error = ugen_get_endpoint_desc(f_rx, addr); } else { error = EINVAL; } break; case USB_GET_TX_ENDPOINT_DESC: if (fflags & FWRITE) { error = ugen_get_endpoint_desc(f_tx, addr); } else { error = EINVAL; } break; case USB_SET_RX_STALL_FLAG: if ((fflags & FREAD) && (*(int *)addr)) { f_rx->flag_stall = 1; } break; case USB_SET_TX_STALL_FLAG: if ((fflags & FWRITE) && (*(int *)addr)) { f_tx->flag_stall = 1; } break; default: error = ENOIOCTL; break; } return (error); } static int ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags) { union { struct usb_interface_descriptor *idesc; struct usb_alt_interface *ai; struct usb_device_descriptor *ddesc; struct usb_config_descriptor *cdesc; struct usb_device_stats *stat; struct usb_fs_init *pinit; struct usb_fs_uninit *puninit; uint32_t *ptime; void *addr; int *pint; } u; struct usb_device_descriptor *dtemp; struct usb_config_descriptor *ctemp; struct usb_interface *iface; int error = 0; uint8_t n; u.addr = addr; DPRINTFN(6, "cmd=0x%08lx\n", cmd); switch (cmd) { case USB_DISCOVER: usb_needs_explore_all(); break; case USB_SETDEBUG: if (!(fflags & FWRITE)) { error = EPERM; break; } usb_debug = *(int *)addr; break; case USB_GET_CONFIG: *(int *)addr = f->udev->curr_config_index; break; case USB_SET_CONFIG: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_set_config(f, *(int *)addr); break; case USB_GET_ALTINTERFACE: iface = usbd_get_iface(f->udev, u.ai->uai_interface_index); if (iface && iface->idesc) { u.ai->uai_alt_index = iface->alt_index; } else { error = EINVAL; } break; case USB_SET_ALTINTERFACE: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_set_interface(f, u.ai->uai_interface_index, u.ai->uai_alt_index); break; case USB_GET_DEVICE_DESC: dtemp = usbd_get_device_descriptor(f->udev); if (!dtemp) { error = EIO; break; } *u.ddesc = *dtemp; break; case USB_GET_CONFIG_DESC: ctemp = usbd_get_config_descriptor(f->udev); if (!ctemp) { error = EIO; break; } *u.cdesc = *ctemp; break; case USB_GET_FULL_DESC: error = ugen_get_cdesc(f, addr); break; case USB_GET_STRING_DESC: error = ugen_get_sdesc(f, addr); break; case USB_GET_IFACE_DRIVER: error = ugen_get_iface_driver(f, addr); break; case USB_REQUEST: case USB_DO_REQUEST: if (!(fflags & FWRITE)) { error = EPERM; break; } error = ugen_do_request(f, addr); break; case USB_DEVICEINFO: case USB_GET_DEVICEINFO: error = usb_gen_fill_deviceinfo(f, addr); break; case USB_DEVICESTATS: for (n = 0; n != 4; n++) { u.stat->uds_requests_fail[n] = f->udev->bus->stats_err.uds_requests[n]; u.stat->uds_requests_ok[n] = f->udev->bus->stats_ok.uds_requests[n]; } break; case USB_DEVICEENUMERATE: error = ugen_re_enumerate(f); break; case USB_GET_PLUGTIME: *u.ptime = f->udev->plugtime; break; case USB_CLAIM_INTERFACE: case USB_RELEASE_INTERFACE: /* TODO */ break; case USB_IFACE_DRIVER_ACTIVE: n = *u.pint & 0xFF; iface = usbd_get_iface(f->udev, n); if (iface && iface->subdev) error = 0; else error = ENXIO; break; case USB_IFACE_DRIVER_DETACH: error = priv_check(curthread, PRIV_DRIVER); if (error) break; n = *u.pint & 0xFF; if (n == USB_IFACE_INDEX_ANY) { error = EINVAL; break; } usb_detach_device(f->udev, n, 0); break; case USB_SET_POWER_MODE: error = ugen_set_power_mode(f, *u.pint); break; case USB_GET_POWER_MODE: *u.pint = ugen_get_power_mode(f); break; case USB_SET_PORT_ENABLE: error = ugen_do_port_feature(f, *u.pint, 1, UHF_PORT_ENABLE); break; case USB_SET_PORT_DISABLE: error = ugen_do_port_feature(f, *u.pint, 0, UHF_PORT_ENABLE); break; case USB_FS_INIT: /* verify input parameters */ if (u.pinit->pEndpoints == NULL) { error = EINVAL; break; } if (u.pinit->ep_index_max > 127) { error = EINVAL; break; } if (u.pinit->ep_index_max == 0) { error = EINVAL; break; } if (f->fs_xfer != NULL) { error = EBUSY; break; } if (f->dev_ep_index != 0) { error = EINVAL; break; } if (ugen_fifo_in_use(f, fflags)) { error = EBUSY; break; } error = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max); if (error) { break; } f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO); if (f->fs_xfer == NULL) { usb_fifo_free_buffer(f); error = ENOMEM; break; } f->fs_ep_max = u.pinit->ep_index_max; f->fs_ep_ptr = u.pinit->pEndpoints; break; case USB_FS_UNINIT: if (u.puninit->dummy != 0) { error = EINVAL; break; } error = ugen_fs_uninit(f); break; default: mtx_lock(f->priv_mtx); error = ugen_iface_ioctl(f, cmd, addr, fflags); mtx_unlock(f->priv_mtx); break; } DPRINTFN(6, "error=%d\n", error); return (error); } static void ugen_ctrl_fs_callback(struct usb_xfer *xfer, usb_error_t error) { ; /* workaround for a bug in "indent" */ DPRINTF("st=%u alen=%u aframes=%u\n", USB_GET_STATE(xfer), xfer->actlen, xfer->aframes); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: usbd_transfer_submit(xfer); break; default: ugen_fs_set_complete(xfer->priv_sc, USB_P2U(xfer->priv_fifo)); break; } } #endif /* USB_HAVE_UGEN */ Index: stable/8/sys/dev/usb/usb_ioctl.h =================================================================== --- stable/8/sys/dev/usb/usb_ioctl.h (revision 220433) +++ stable/8/sys/dev/usb/usb_ioctl.h (revision 220434) @@ -1,272 +1,273 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved. * Copyright (c) 1998 Lennart Augustsson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _USB_IOCTL_H_ #define _USB_IOCTL_H_ #include /* Building "kdump" depends on these includes */ #include #include #define USB_DEVICE_NAME "usbctl" #define USB_DEVICE_DIR "usb" #define USB_GENERIC_NAME "ugen" struct usb_read_dir { void *urd_data; uint32_t urd_startentry; uint32_t urd_maxlen; }; struct usb_ctl_request { void *ucr_data; uint16_t ucr_flags; uint16_t ucr_actlen; /* actual length transferred */ uint8_t ucr_addr; /* zero - currently not used */ struct usb_device_request ucr_request; }; struct usb_alt_interface { uint8_t uai_interface_index; uint8_t uai_alt_index; }; struct usb_gen_descriptor { void *ugd_data; uint16_t ugd_lang_id; uint16_t ugd_maxlen; uint16_t ugd_actlen; uint16_t ugd_offset; uint8_t ugd_config_index; uint8_t ugd_string_index; uint8_t ugd_iface_index; uint8_t ugd_altif_index; uint8_t ugd_endpt_index; uint8_t ugd_report_type; uint8_t reserved[8]; }; struct usb_device_info { uint16_t udi_productNo; uint16_t udi_vendorNo; uint16_t udi_releaseNo; uint16_t udi_power; /* power consumption in mA, 0 if * selfpowered */ uint8_t udi_bus; uint8_t udi_addr; /* device address */ uint8_t udi_index; /* device index */ uint8_t udi_class; uint8_t udi_subclass; uint8_t udi_protocol; uint8_t udi_config_no; /* current config number */ uint8_t udi_config_index; /* current config index */ uint8_t udi_speed; /* see "USB_SPEED_XXX" */ uint8_t udi_mode; /* see "USB_MODE_XXX" */ uint8_t udi_nports; uint8_t udi_hubaddr; /* parent HUB address */ uint8_t udi_hubindex; /* parent HUB device index */ uint8_t udi_hubport; /* parent HUB port */ uint8_t udi_power_mode; /* see "USB_POWER_MODE_XXX" */ uint8_t udi_suspended; /* set if device is suspended */ uint8_t udi_reserved[16]; /* leave space for the future */ char udi_product[128]; char udi_vendor[128]; char udi_serial[64]; char udi_release[8]; }; struct usb_device_stats { uint32_t uds_requests_ok[4]; /* Indexed by transfer type UE_XXX */ uint32_t uds_requests_fail[4]; /* Indexed by transfer type UE_XXX */ }; struct usb_fs_start { uint8_t ep_index; }; struct usb_fs_stop { uint8_t ep_index; }; struct usb_fs_complete { uint8_t ep_index; }; /* This structure is used for all endpoint types */ struct usb_fs_endpoint { /* * NOTE: isochronous USB transfer only use one buffer, but can have * multiple frame lengths ! */ void **ppBuffer; /* pointer to userland buffers */ uint32_t *pLength; /* pointer to frame lengths, updated * to actual length */ uint32_t nFrames; /* number of frames */ uint32_t aFrames; /* actual number of frames */ uint16_t flags; /* a single short frame will terminate */ #define USB_FS_FLAG_SINGLE_SHORT_OK 0x0001 /* multiple short frames are allowed */ #define USB_FS_FLAG_MULTI_SHORT_OK 0x0002 /* all frame(s) transmitted are short terminated */ #define USB_FS_FLAG_FORCE_SHORT 0x0004 /* will do a clear-stall before xfer */ #define USB_FS_FLAG_CLEAR_STALL 0x0008 uint16_t timeout; /* in milliseconds */ /* isocronous completion time in milliseconds - used for echo cancel */ uint16_t isoc_time_complete; /* timeout value for no timeout */ #define USB_FS_TIMEOUT_NONE 0 int status; /* see USB_ERR_XXX */ }; struct usb_fs_init { /* userland pointer to endpoints structure */ struct usb_fs_endpoint *pEndpoints; /* maximum number of endpoints */ uint8_t ep_index_max; }; struct usb_fs_uninit { uint8_t dummy; /* zero */ }; struct usb_fs_open { #define USB_FS_MAX_BUFSIZE (1 << 18) uint32_t max_bufsize; -#define USB_FS_MAX_FRAMES (1 << 12) - uint32_t max_frames; +#define USB_FS_MAX_FRAMES (1U << 12) +#define USB_FS_MAX_FRAMES_PRE_SCALE (1U << 31) /* for ISOCHRONOUS transfers */ + uint32_t max_frames; /* read and write */ uint16_t max_packet_length; /* read only */ uint8_t dev_index; /* currently unused */ uint8_t ep_index; uint8_t ep_no; /* bEndpointNumber */ }; struct usb_fs_close { uint8_t ep_index; }; struct usb_fs_clear_stall_sync { uint8_t ep_index; }; struct usb_gen_quirk { uint16_t index; /* Quirk Index */ uint16_t vid; /* Vendor ID */ uint16_t pid; /* Product ID */ uint16_t bcdDeviceLow; /* Low Device Revision */ uint16_t bcdDeviceHigh; /* High Device Revision */ uint16_t reserved[2]; /* * String version of quirk including terminating zero. See UQ_XXX in * "usb_quirk.h". */ char quirkname[64 - 14]; }; /* USB controller */ #define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request) #define USB_SETDEBUG _IOW ('U', 2, int) #define USB_DISCOVER _IO ('U', 3) #define USB_DEVICEINFO _IOWR('U', 4, struct usb_device_info) #define USB_DEVICESTATS _IOR ('U', 5, struct usb_device_stats) #define USB_DEVICEENUMERATE _IOW ('U', 6, int) /* Generic HID device */ #define USB_GET_REPORT_DESC _IOWR('U', 21, struct usb_gen_descriptor) #define USB_SET_IMMED _IOW ('U', 22, int) #define USB_GET_REPORT _IOWR('U', 23, struct usb_gen_descriptor) #define USB_SET_REPORT _IOW ('U', 24, struct usb_gen_descriptor) #define USB_GET_REPORT_ID _IOR ('U', 25, int) /* Generic USB device */ #define USB_GET_CONFIG _IOR ('U', 100, int) #define USB_SET_CONFIG _IOW ('U', 101, int) #define USB_GET_ALTINTERFACE _IOWR('U', 102, struct usb_alt_interface) #define USB_SET_ALTINTERFACE _IOWR('U', 103, struct usb_alt_interface) #define USB_GET_DEVICE_DESC _IOR ('U', 105, struct usb_device_descriptor) #define USB_GET_CONFIG_DESC _IOR ('U', 106, struct usb_config_descriptor) #define USB_GET_RX_INTERFACE_DESC _IOR ('U', 107, struct usb_interface_descriptor) #define USB_GET_RX_ENDPOINT_DESC _IOR ('U', 108, struct usb_endpoint_descriptor) #define USB_GET_FULL_DESC _IOWR('U', 109, struct usb_gen_descriptor) #define USB_GET_STRING_DESC _IOWR('U', 110, struct usb_gen_descriptor) #define USB_DO_REQUEST _IOWR('U', 111, struct usb_ctl_request) #define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb_device_info) #define USB_SET_RX_SHORT_XFER _IOW ('U', 113, int) #define USB_SET_RX_TIMEOUT _IOW ('U', 114, int) #define USB_GET_RX_FRAME_SIZE _IOR ('U', 115, int) #define USB_GET_RX_BUFFER_SIZE _IOR ('U', 117, int) #define USB_SET_RX_BUFFER_SIZE _IOW ('U', 118, int) #define USB_SET_RX_STALL_FLAG _IOW ('U', 119, int) #define USB_SET_TX_STALL_FLAG _IOW ('U', 120, int) #define USB_GET_IFACE_DRIVER _IOWR('U', 121, struct usb_gen_descriptor) #define USB_CLAIM_INTERFACE _IOW ('U', 122, int) #define USB_RELEASE_INTERFACE _IOW ('U', 123, int) #define USB_IFACE_DRIVER_ACTIVE _IOW ('U', 124, int) #define USB_IFACE_DRIVER_DETACH _IOW ('U', 125, int) #define USB_GET_PLUGTIME _IOR ('U', 126, uint32_t) #define USB_READ_DIR _IOW ('U', 127, struct usb_read_dir) /* 128 - 135 unused */ #define USB_SET_TX_FORCE_SHORT _IOW ('U', 136, int) #define USB_SET_TX_TIMEOUT _IOW ('U', 137, int) #define USB_GET_TX_FRAME_SIZE _IOR ('U', 138, int) #define USB_GET_TX_BUFFER_SIZE _IOR ('U', 139, int) #define USB_SET_TX_BUFFER_SIZE _IOW ('U', 140, int) #define USB_GET_TX_INTERFACE_DESC _IOR ('U', 141, struct usb_interface_descriptor) #define USB_GET_TX_ENDPOINT_DESC _IOR ('U', 142, struct usb_endpoint_descriptor) #define USB_SET_PORT_ENABLE _IOW ('U', 143, int) #define USB_SET_PORT_DISABLE _IOW ('U', 144, int) #define USB_SET_POWER_MODE _IOW ('U', 145, int) #define USB_GET_POWER_MODE _IOR ('U', 146, int) #define USB_SET_TEMPLATE _IOW ('U', 147, int) #define USB_GET_TEMPLATE _IOR ('U', 148, int) /* Modem device */ #define USB_GET_CM_OVER_DATA _IOR ('U', 180, int) #define USB_SET_CM_OVER_DATA _IOW ('U', 181, int) /* USB file system interface */ #define USB_FS_START _IOW ('U', 192, struct usb_fs_start) #define USB_FS_STOP _IOW ('U', 193, struct usb_fs_stop) #define USB_FS_COMPLETE _IOR ('U', 194, struct usb_fs_complete) #define USB_FS_INIT _IOW ('U', 195, struct usb_fs_init) #define USB_FS_UNINIT _IOW ('U', 196, struct usb_fs_uninit) #define USB_FS_OPEN _IOWR('U', 197, struct usb_fs_open) #define USB_FS_CLOSE _IOW ('U', 198, struct usb_fs_close) #define USB_FS_CLEAR_STALL_SYNC _IOW ('U', 199, struct usb_fs_clear_stall_sync) /* USB quirk system interface */ #define USB_DEV_QUIRK_GET _IOWR('Q', 0, struct usb_gen_quirk) #define USB_QUIRK_NAME_GET _IOWR('Q', 1, struct usb_gen_quirk) #define USB_DEV_QUIRK_ADD _IOW ('Q', 2, struct usb_gen_quirk) #define USB_DEV_QUIRK_REMOVE _IOW ('Q', 3, struct usb_gen_quirk) #endif /* _USB_IOCTL_H_ */ Index: stable/8/sys/dev/usb/usb_transfer.c =================================================================== --- stable/8/sys/dev/usb/usb_transfer.c (revision 220433) +++ stable/8/sys/dev/usb/usb_transfer.c (revision 220434) @@ -1,3277 +1,3279 @@ /* $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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include struct usb_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb_request_callback; static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usbd_update_max_frame_size(struct usb_xfer *); static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usbd_control_transfer_init(struct usb_xfer *); static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb_request_callback *------------------------------------------------------------------------*/ static void usb_request_callback(struct usb_xfer *xfer, usb_error_t error) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb_handle_request_callback(xfer, error); else usbd_do_request_callback(xfer, error); } /*------------------------------------------------------------------------* * usbd_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ /* this computation should not overflow 16-bit */ /* max = 15 * 1024 */ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* * usbd_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usbd_get_dma_delay(struct usb_device *udev) { struct usb_bus_methods *mtod; uint32_t temp; mtod = udev->bus->methods; temp = 0; if (mtod->get_dma_delay) { (mtod->get_dma_delay) (udev, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; usb_size_t n_dma_pc; usb_size_t n_obj; usb_size_t x; usb_size_t y; usb_size_t r; usb_size_t z; USB_ASSERT(align > 1, ("Invalid alignment, 0x%08x\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (size >= PAGE_SIZE) { n_dma_pc = count; n_obj = 1; } else { /* compute number of objects per page */ n_obj = (PAGE_SIZE / size); /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = ((count + n_obj - 1) / n_obj); } if (parm->buf == NULL) { /* for the future */ parm->dma_page_ptr += n_dma_pc; parm->dma_page_cache_ptr += n_dma_pc; parm->dma_page_ptr += count; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } if (ppc) { *ppc = parm->xfer_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and one page */ parm->dma_page_cache_ptr++; pg++; for (y = 0; (y != n_obj); y++, r--, pc++, pg++) { /* Load sub-chunk into DMA */ if (usb_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; mtx_lock(pc->tag_parent->mtx); if (usb_pc_load_mem(pc, size, 1 /* synchronous */ )) { mtx_unlock(pc->tag_parent->mtx); return (1); /* failure */ } mtx_unlock(pc->tag_parent->mtx); } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usbd_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usbd_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->endpoint->edesc; ecomp = xfer->endpoint->ecomp; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpointno = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; switch (parm->speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (xfer->max_packet_count > 3) xfer->max_packet_count = 3; break; default: break; } xfer->max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; if (ecomp != NULL) xfer->max_packet_count += ecomp->bMaxBurst; if ((xfer->max_packet_count == 0) || (xfer->max_packet_count > 16)) xfer->max_packet_count = 16; switch (type) { case UE_CONTROL: xfer->max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = (ecomp->bmAttributes & 3) + 1; if (mult > 3) mult = 3; xfer->max_packet_count *= mult; } break; default: break; } xfer->max_packet_size &= 0x7FF; break; default: break; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usbd_get_std_packet_size(&std_size, type, parm->speed); if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* compute "max_frame_size" */ usbd_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = 0; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = edesc->bInterval; if (xfer->fps_shift > 0) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; + if (xfer->flags.pre_scale_frames != 0) + xfer->nframes <<= (3 - xfer->fps_shift); break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * If a value is specified use that else check the * endpoint descriptor! */ if (type == UE_INTERRUPT) { uint32_t temp; if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1 << (16 - 4)); else xfer->interval = (1 << (xfer->interval - 4)); break; } } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } xfer->fps_shift = 0; temp = 1; while ((temp != 0) && (temp < xfer->interval)) { xfer->fps_shift++; temp *= 2; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: xfer->fps_shift += 3; break; } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence * this leads to alot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usbd_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* setup "frlengths" */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } /*------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usbd_transfer_unsetup" after calling * "usbd_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { struct usb_xfer dummy; struct usb_setup_params parm; const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; uint16_t n; uint16_t refcount; parm.err = 0; refcount = 0; info = NULL; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == 0) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { parm.err = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { parm.err = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (parm.err) { goto done; } bzero(&parm, sizeof(parm)); parm.udev = udev; parm.speed = usbd_get_speed(udev); parm.hc_max_packet_count = 1; if (parm.speed >= USB_SPEED_MAX) { parm.err = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm.size[0]; #if USB_HAVE_BUSDMA info->dma_page_cache_start = USB_ADD_BYTES(buf, parm.size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm.size[5]); #endif info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm.size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm.size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, parm.dma_tag_p, udev->bus->dma_parent_tag[0].tag, xfer_mtx, &usb_bdma_done_event, 32, parm.dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usbd_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb_control_ep_cfg) info->done_p = &udev->bus->control_xfer_proc; else if (xfer_mtx == &Giant) info->done_p = &udev->bus->giant_callback_proc; else info->done_p = &udev->bus->non_giant_callback_proc; } /* reset sizes */ parm.size[0] = 0; parm.buf = buf; parm.size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ ep = usbd_get_endpoint(udev, ifaces[setup->if_index], setup); if ((ep == NULL) || (ep->methods == NULL)) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm.err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm.curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm.size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &dummy; bzero(&dummy, sizeof(dummy)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; parm.size[0] += sizeof(xfer[0]); parm.methods = xfer->endpoint->methods; parm.curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (&parm); /* check for error */ if (parm.err) goto done; if (buf) { /* * Increment the endpoint refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "endpoint->refcount_alloc" if you * want more information. */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) parm.err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; if (xfer->endpoint->refcount_alloc == 0) panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); USB_BUS_UNLOCK(info->bus); /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } /* check for error */ if (parm.err) goto done; } if (buf || parm.err) { goto done; } if (refcount == 0) { /* no transfers - nothing to do ! */ goto done; } /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm.size[1] = parm.size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is two. */ parm.dma_tag_max += 2 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm.dma_tag_max += 8; parm.dma_tag_p += parm.dma_tag_max; parm.size[0] += ((uint8_t *)parm.dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm.size[3] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm.size[4] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm.size[5] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm.size[2] = parm.size[0]; /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); parm.size[6] = parm.size[0]; parm.size[0] += ((uint8_t *)parm.xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm.size[0] += ((-parm.size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm.size[0], M_USB, M_WAITOK | M_ZERO); if (buf == NULL) { parm.err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm.size[0]); goto done; } parm.dma_tag_p = USB_ADD_BYTES(buf, parm.size[1]); parm.dma_page_ptr = USB_ADD_BYTES(buf, parm.size[3]); parm.dma_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[4]); parm.xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm.size[5]); parm.xfer_length_ptr = USB_ADD_BYTES(buf, parm.size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usbd_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usbd_transfer_unsetup_sub(info, 0); } } if (parm.err) { usbd_transfer_unsetup(ppxfer, n_setup); } return (parm.err); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { #if USB_HAVE_BUSDMA struct usb_page_cache *pc; #endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usbd_get_dma_delay(info->udev); if (temp != 0) { usb_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } } /* make sure that our done messages are not queued anywhere */ usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb_dma_tag_unsetup(&info->dma_parent_tag); #endif cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usbd_transfer_start(sc->pxfer[0]); * usbd_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usbd_transfer_start and * usbd_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usbd_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default endpoint does not have an * interface, even if endpoint->iface_index == 0 */ USB_BUS_LOCK(info->bus); xfer->endpoint->refcount_alloc--; USB_BUS_UNLOCK(info->bus); usb_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usbd_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usbd_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usbd_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpointno |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usbd_setup_ctrl_transfer(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usbd_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usbd_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usbd_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length (%d) greater than " "remaining length (%d)\n", len, xfer->flags_int.control_rem); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_transfer_submit - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usbd_transfer_submit(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n", xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #ifdef USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb_dump_endpoint(xfer->endpoint); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usbd_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usbd_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usbd_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb_command_wrapper(&xfer->endpoint->endpoint_q, xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute total transfer length */ for (x = 0; x != xfer->nframes; x++) { xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usbd_setup_ctrl_transfer(xfer)) { USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usbd_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usbd_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usbd_pipe_enter(struct usb_xfer *xfer) { struct usb_endpoint *ep; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); ep = xfer->endpoint; DPRINTF("enter\n"); /* enter the transfer */ (ep->methods->enter) (xfer); xfer->flags_int.can_cancel_immed = 1; /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb_command_wrapper(&ep->endpoint_q, xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usbd_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usbd_transfer_drain()". *------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usb_xfer *xfer) { struct usb_endpoint *ep; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { if (xfer->flags_int.started) { /* nothing to do except clearing the "started" flag */ /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); /* override any previous error */ xfer->error = USB_ERR_CANCELLED; /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->endpoint->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->endpoint->methods->close) (xfer); /* * Any additional DMA delay is done by * "usbd_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * endpoint. */ ep = xfer->endpoint; /* * If the current USB transfer is completing we need * to start the next one: */ if (ep->endpoint_q.curr == xfer) { usb_command_wrapper(&ep->endpoint_q, NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usbd_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usbd_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usbd_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usbd_transfer_stop(xfer); while (usbd_transfer_pending(xfer) || xfer->flags_int.doing_callback) { /* * It is allowed that the callback can drop its * transfer mutex. In that case checking only * "usbd_transfer_pending()" is not enough to tell if * the USB transfer is fully drained. We also need to * check the internal "doing_callback" flag. */ xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (&xfer->frbuffers[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * * The following function is only useful for isochronous transfers. It * returns how many times the frame execution rate has been shifted * down. * * Return value: * Success: 0..3 * Failure: 0 *------------------------------------------------------------------------*/ uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer) { return (xfer->fps_shift); } usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; usbd_xfer_set_frame_len(xfer, frindex, len); } void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); if (ptr != NULL) *ptr = xfer->frbuffers[frindex].buffer; if (len != NULL) *len = xfer->frlengths[frindex]; } void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) { if (actlen != NULL) *actlen = xfer->actlen; if (sumlen != NULL) *sumlen = xfer->sumlen; if (aframes != NULL) *aframes = xfer->aframes; if (nframes != NULL) *nframes = xfer->nframes; } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external\n")); KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } void usbd_xfer_set_interval(struct usb_xfer *xfer, int i) { xfer->interval = i; } void usbd_xfer_set_timeout(struct usb_xfer *xfer, int t) { xfer->timeout = t; } void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n) { xfer->nframes = n; } usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer) { return (xfer->max_frame_count); } usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer) { return (xfer->max_data_length); } usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer) { return (xfer->max_frame_size); } void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); xfer->frlengths[frindex] = len; } /*------------------------------------------------------------------------* * usb_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ mtx_lock(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb_command_wrapper(&info->done_q, info->done_q.curr); mtx_unlock(info->xfer_mtx); } /*------------------------------------------------------------------------* * usbd_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usbd_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usbd_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usbd_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if (!mtx_owned(info->xfer_mtx)) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. */ DPRINTFN(3, "case 5\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ if (usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1])) { /* ignore */ } return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; /* set flag in case of drain */ xfer->flags_int.doing_callback = 1; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usbd_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usbd_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb_bdma_post_sync(xfer); } #endif } } /* call processing routine */ (xfer->callback) (xfer, xfer->error); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb_state == USB_ST_ERROR)) { /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* try to loop, but not recursivly */ usb_command_wrapper(&info->done_q, xfer); return; } done: /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usbd_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is ususally only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ void usb_dma_delay_done_cb(struct usb_xfer *xfer) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usbd_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usbd_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usbd_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usbd_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("err=%s\n", usbd_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); /* end of control transfer, if any */ xfer->flags_int.control_act = 0; return; } /* only set transfer error if not already set */ if (!xfer->error) { xfer->error = error; } /* stop any callouts */ usb_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usbd_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(xfer->xroot->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &xfer->xroot->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error) { xfer->xroot->bus->stats_err.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else { xfer->xroot->bus->stats_ok.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usbd_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usbd_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_endpoint *ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); /* start the transfer */ (ep->methods->start) (xfer); xfer->flags_int.can_cancel_immed = 1; /* check for error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_xfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_xfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } int usbd_xfer_is_stalled(struct usb_xfer *xfer) { return (xfer->endpoint->is_stalled); } /*------------------------------------------------------------------------* * usbd_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usbd_pipe_start(struct usb_xfer_queue *pq) { struct usb_endpoint *ep; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the endpoint is already stalled we do nothing ! */ if (ep->is_stalled) { return; } /* * Check if we are supposed to stall the endpoint: */ if (xfer->flags.stall_pipe) { struct usb_device *udev; struct usb_xfer_root *info; /* clear stall command */ xfer->flags.stall_pipe = 0; /* get pointer to USB device */ info = xfer->xroot; udev = info->udev; /* * Only stall BULK and INTERRUPT endpoints. */ type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { uint8_t did_stall; did_stall = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, NULL, ep, &did_stall); } else if (udev->ctrl_xfer[1]) { info = udev->ctrl_xfer[1]->xroot; usb_proc_msignal( &info->bus->non_giant_callback_proc, &udev->cs_msg[0], &udev->cs_msg[1]); } else { /* should not happen */ DPRINTFN(0, "No stall handler\n"); } /* * Check if we should stall. Some USB hardware * handles set- and clear-stall in hardware. */ if (did_stall) { /* * The transfer will be continued when * the clear-stall control endpoint * message is received. */ ep->is_stalled = 1; return; } } else if (type == UE_ISOCHRONOUS) { /* * Make sure any FIFO overflow or other FIFO * error conditions go away by resetting the * endpoint FIFO through the clear stall * method. */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->clear_stall) (udev, ep); } } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usbd_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usbd_transfer_timeout_ms(xfer, &usbd_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); /* start USB transfer */ (ep->methods->start) (xfer); xfer->flags_int.can_cancel_immed = 1; /* check for error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usbd_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms), cb, xfer); } /*------------------------------------------------------------------------* * usbd_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * ep transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; struct usb_bus *bus; usb_frcount_t x; bus = xfer->xroot->bus; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && (xfer->error == USB_ERR_CANCELLED || xfer->error == USB_ERR_TIMEOUT || bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usbd_get_dma_delay(xfer->xroot->udev); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(bus); /* * Some hardware solutions have dedicated * events when it is safe to free DMA'ed * memory. For the other hardware platforms we * use a static delay. */ if (bus->methods->start_dma_delay != NULL) { (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, (void *)&usb_dma_delay_done_cb, temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { usbd_xfer_set_frame_len(xfer, x, 0); } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on endpoint %p.\n", xfer, xfer->endpoint); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } } ep = xfer->endpoint; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(bus); if (ep->endpoint_q.curr == xfer) { usb_command_wrapper(&ep->endpoint_q, NULL); if (ep->endpoint_q.curr || TAILQ_FIRST(&ep->endpoint_q.head)) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->endpoint->is_synced = 0; } } USB_BUS_UNLOCK(bus); done: return (0); } /*------------------------------------------------------------------------* * usb_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { do { /* set both recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usbd_ctrl_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usbd_ctrl_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->ctrl_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->ctrl_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usbd_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->ctrl_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usbd_transfer_setup(udev, &iface_index, udev->ctrl_xfer, usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL, &udev->device_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) { USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check that we have a valid case */ if (udev->flags.usb_mode == USB_MODE_HOST && udev->parent_hub != NULL && udev->bus->methods->clear_stall != NULL && ep->methods != NULL) { (udev->bus->methods->clear_stall) (udev, ep); } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle on the USB device side. *------------------------------------------------------------------------*/ void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usbd_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usbd_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usbd_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usbd_transfer_submit(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } /*------------------------------------------------------------------------* * usbd_transfer_poll * * The following function gets called from the USB keyboard driver and * UMASS when the system has paniced. * * NOTE: It is currently not possible to resume normal operation on * the USB controller which has been polled, due to clearing of the * "up_dsleep" and "up_msleep" flags. *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { struct usb_xfer *xfer; struct usb_xfer_root *xroot; struct usb_device *udev; struct usb_proc_msg *pm; uint16_t n; uint16_t drop_bus; uint16_t drop_xfer; for (n = 0; n != max; n++) { /* Extra checks to avoid panic */ xfer = ppxfer[n]; if (xfer == NULL) continue; /* no USB transfer */ xroot = xfer->xroot; if (xroot == NULL) continue; /* no USB root */ udev = xroot->udev; if (udev == NULL) continue; /* no USB device */ if (udev->bus == NULL) continue; /* no BUS structure */ if (udev->bus->methods == NULL) continue; /* no BUS methods */ if (udev->bus->methods->xfer_poll == NULL) continue; /* no poll method */ /* make sure that the BUS mutex is not locked */ drop_bus = 0; while (mtx_owned(&xroot->udev->bus->bus_mtx)) { mtx_unlock(&xroot->udev->bus->bus_mtx); drop_bus++; } /* make sure that the transfer mutex is not locked */ drop_xfer = 0; while (mtx_owned(xroot->xfer_mtx)) { mtx_unlock(xroot->xfer_mtx); drop_xfer++; } /* Make sure cv_signal() and cv_broadcast() is not called */ udev->bus->control_xfer_proc.up_msleep = 0; udev->bus->explore_proc.up_msleep = 0; udev->bus->giant_callback_proc.up_msleep = 0; udev->bus->non_giant_callback_proc.up_msleep = 0; /* poll USB hardware */ (udev->bus->methods->xfer_poll) (udev->bus); USB_BUS_LOCK(xroot->bus); /* check for clear stall */ if (udev->ctrl_xfer[1] != NULL) { /* poll clear stall start */ pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); /* poll clear stall done thread */ pm = &udev->ctrl_xfer[1]-> xroot->done_m[0].hdr; (pm->pm_callback) (pm); } /* poll done thread */ pm = &xroot->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xroot->bus); /* restore transfer mutex */ while (drop_xfer--) mtx_lock(xroot->xfer_mtx); /* restore BUS mutex */ while (drop_bus--) mtx_lock(&xroot->udev->bus->bus_mtx); } } static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } void * usbd_xfer_softc(struct usb_xfer *xfer) { return (xfer->priv_sc); } void * usbd_xfer_get_priv(struct usb_xfer *xfer) { return (xfer->priv_fifo); } void usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr) { xfer->priv_fifo = ptr; } uint8_t usbd_xfer_state(struct usb_xfer *xfer) { return (xfer->usb_state); } void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 1; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 1; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 1; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 1; break; } } void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 0; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 0; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 0; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 0; break; } } /* * The following function returns in milliseconds when the isochronous * transfer was completed by the hardware. The returned value wraps * around 65536 milliseconds. */ uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer) { return (xfer->isoc_time_complete); } Index: stable/8/sys/dev/usb/usbdi.h =================================================================== --- stable/8/sys/dev/usb/usbdi.h (revision 220433) +++ stable/8/sys/dev/usb/usbdi.h (revision 220434) @@ -1,562 +1,572 @@ /*- * Copyright (c) 2009 Andrew Thompson * * 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 ``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 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. * * $FreeBSD$ */ #ifndef _USB_USBDI_H_ #define _USB_USBDI_H_ struct usb_fifo; struct usb_xfer; struct usb_device; struct usb_attach_arg; struct usb_interface; struct usb_endpoint; struct usb_page_cache; struct usb_page_search; struct usb_process; struct usb_proc_msg; struct usb_mbuf; struct mbuf; typedef enum { /* keep in sync with usb_errstr_table */ USB_ERR_NORMAL_COMPLETION = 0, USB_ERR_PENDING_REQUESTS, /* 1 */ USB_ERR_NOT_STARTED, /* 2 */ USB_ERR_INVAL, /* 3 */ USB_ERR_NOMEM, /* 4 */ USB_ERR_CANCELLED, /* 5 */ USB_ERR_BAD_ADDRESS, /* 6 */ USB_ERR_BAD_BUFSIZE, /* 7 */ USB_ERR_BAD_FLAG, /* 8 */ USB_ERR_NO_CALLBACK, /* 9 */ USB_ERR_IN_USE, /* 10 */ USB_ERR_NO_ADDR, /* 11 */ USB_ERR_NO_PIPE, /* 12 */ USB_ERR_ZERO_NFRAMES, /* 13 */ USB_ERR_ZERO_MAXP, /* 14 */ USB_ERR_SET_ADDR_FAILED, /* 15 */ USB_ERR_NO_POWER, /* 16 */ USB_ERR_TOO_DEEP, /* 17 */ USB_ERR_IOERROR, /* 18 */ USB_ERR_NOT_CONFIGURED, /* 19 */ USB_ERR_TIMEOUT, /* 20 */ USB_ERR_SHORT_XFER, /* 21 */ USB_ERR_STALLED, /* 22 */ USB_ERR_INTERRUPTED, /* 23 */ USB_ERR_DMA_LOAD_FAILED, /* 24 */ USB_ERR_BAD_CONTEXT, /* 25 */ USB_ERR_NO_ROOT_HUB, /* 26 */ USB_ERR_NO_INTR_THREAD, /* 27 */ USB_ERR_NOT_LOCKED, /* 28 */ USB_ERR_MAX } usb_error_t; /* * Flags for transfers */ #define USB_FORCE_SHORT_XFER 0x0001 /* force a short transmit last */ #define USB_SHORT_XFER_OK 0x0004 /* allow short reads */ #define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */ #define USB_USER_DATA_PTR 0x0020 /* internal flag */ #define USB_MULTI_SHORT_OK 0x0040 /* allow multiple short frames */ #define USB_MANUAL_STATUS 0x0080 /* manual ctrl status */ #define USB_NO_TIMEOUT 0 #define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */ #if defined(_KERNEL) /* typedefs */ typedef void (usb_callback_t)(struct usb_xfer *, usb_error_t); typedef void (usb_proc_callback_t)(struct usb_proc_msg *); typedef usb_error_t (usb_handle_req_t)(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); typedef int (usb_fifo_open_t)(struct usb_fifo *fifo, int fflags); typedef void (usb_fifo_close_t)(struct usb_fifo *fifo, int fflags); typedef int (usb_fifo_ioctl_t)(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags); typedef void (usb_fifo_cmd_t)(struct usb_fifo *fifo); typedef void (usb_fifo_filter_t)(struct usb_fifo *fifo, struct usb_mbuf *m); /* USB events */ #include typedef void (*usb_dev_configured_t)(void *, struct usb_device *, struct usb_attach_arg *); EVENTHANDLER_DECLARE(usb_dev_configured, usb_dev_configured_t); /* * The following macros are used used to convert milliseconds into * HZ. We use 1024 instead of 1000 milliseconds per second to save a * full division. */ #define USB_MS_HZ 1024 #define USB_MS_TO_TICKS(ms) \ (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ) /* * Common queue structure for USB transfers. */ struct usb_xfer_queue { TAILQ_HEAD(, usb_xfer) head; struct usb_xfer *curr; /* current USB transfer processed */ void (*command) (struct usb_xfer_queue *pq); uint8_t recurse_1:1; uint8_t recurse_2:1; }; /* * The following structure defines an USB endpoint * USB endpoint. */ struct usb_endpoint { struct usb_xfer_queue endpoint_q; /* queue of USB transfers */ struct usb_endpoint_descriptor *edesc; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_pipe_methods *methods; /* set by HC driver */ uint16_t isoc_next; uint8_t toggle_next:1; /* next data toggle value */ uint8_t is_stalled:1; /* set if endpoint is stalled */ uint8_t is_synced:1; /* set if we a synchronised */ uint8_t unused:5; uint8_t iface_index; /* not used by "default endpoint" */ uint8_t refcount_alloc; /* allocation refcount */ uint8_t refcount_bw; /* bandwidth refcount */ #define USB_EP_REF_MAX 0x3f /* High-Speed resource allocation (valid if "refcount_bw" > 0) */ uint8_t usb_smask; /* USB start mask */ uint8_t usb_cmask; /* USB complete mask */ uint8_t usb_uframe; /* USB microframe */ }; /* * The following structure defines an USB interface. */ struct usb_interface { struct usb_interface_descriptor *idesc; device_t subdev; uint8_t alt_index; uint8_t parent_iface_index; /* Linux compat */ struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; struct usb_device *linux_udev; void *bsd_priv_sc; /* device specific information */ uint8_t num_altsetting; /* number of alternate settings */ uint8_t bsd_iface_index; }; /* * The following structure defines a set of USB transfer flags. */ struct usb_xfer_flags { uint8_t force_short_xfer:1; /* force a short transmit transfer * last */ uint8_t short_xfer_ok:1; /* allow short receive transfers */ uint8_t short_frames_ok:1; /* allow short frames */ uint8_t pipe_bof:1; /* block pipe on failure */ uint8_t proxy_buffer:1; /* makes buffer size a factor of * "max_frame_size" */ uint8_t ext_buffer:1; /* uses external DMA buffer */ uint8_t manual_status:1; /* non automatic status stage on * control transfers */ uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can * be ignored */ uint8_t stall_pipe:1; /* set if the endpoint belonging to * this USB transfer should be stalled * before starting this transfer! */ + uint8_t pre_scale_frames:1; /* "usb_config->frames" is + * assumed to give the + * buffering time in + * milliseconds and is + * converted into the nearest + * number of frames when the + * USB transfer is setup. This + * option only has effect for + * ISOCHRONOUS transfers. + */ }; /* * The following structure define an USB configuration, that basically * is used when setting up an USB transfer. */ struct usb_config { usb_callback_t *callback; /* USB transfer callback */ usb_frlength_t bufsize; /* total pipe buffer size in bytes */ usb_frcount_t frames; /* maximum number of USB frames */ usb_timeout_t interval; /* interval in milliseconds */ #define USB_DEFAULT_INTERVAL 0 usb_timeout_t timeout; /* transfer timeout in milliseconds */ struct usb_xfer_flags flags; /* transfer flags */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t type; /* pipe type */ uint8_t endpoint; /* pipe number */ uint8_t direction; /* pipe direction */ uint8_t ep_index; /* pipe index match to use */ uint8_t if_index; /* "ifaces" index to use */ }; /* * The following structure is used when looking up an USB driver for * an USB device. It is inspired by the Linux structure called * "usb_device_id". */ struct usb_device_id { /* Hook for driver specific information */ unsigned long driver_info; /* Used for product specific matches; the BCD range is inclusive */ uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice_lo; uint16_t bcdDevice_hi; /* Used for device class matches */ uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; /* Used for interface class matches */ uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; /* Select which fields to match against */ uint8_t match_flag_vendor:1; uint8_t match_flag_product:1; uint8_t match_flag_dev_lo:1; uint8_t match_flag_dev_hi:1; uint8_t match_flag_dev_class:1; uint8_t match_flag_dev_subclass:1; uint8_t match_flag_dev_protocol:1; uint8_t match_flag_int_class:1; uint8_t match_flag_int_subclass:1; uint8_t match_flag_int_protocol:1; #if USB_HAVE_COMPAT_LINUX /* which fields to match against */ uint16_t match_flags; #define USB_DEVICE_ID_MATCH_VENDOR 0x0001 #define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 #define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 #define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 #define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 #endif }; #define USB_VENDOR(vend) \ .match_flag_vendor = 1, .idVendor = (vend) #define USB_PRODUCT(prod) \ .match_flag_product = 1, .idProduct = (prod) #define USB_VP(vend,prod) \ USB_VENDOR(vend), USB_PRODUCT(prod) #define USB_VPI(vend,prod,info) \ USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info) #define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \ .match_flag_dev_lo = 1, .bcdDevice_lo = (lo) #define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \ .match_flag_dev_hi = 1, .bcdDevice_hi = (hi) #define USB_DEV_CLASS(dc) \ .match_flag_dev_class = 1, .bDeviceClass = (dc) #define USB_DEV_SUBCLASS(dsc) \ .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc) #define USB_DEV_PROTOCOL(dp) \ .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp) #define USB_IFACE_CLASS(ic) \ .match_flag_int_class = 1, .bInterfaceClass = (ic) #define USB_IFACE_SUBCLASS(isc) \ .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc) #define USB_IFACE_PROTOCOL(ip) \ .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip) #define USB_IF_CSI(class,subclass,info) \ USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info) #define USB_DRIVER_INFO(n) \ .driver_info = (n) #define USB_GET_DRIVER_INFO(did) \ (did)->driver_info /* * The following structure keeps information that is used to match * against an array of "usb_device_id" elements. */ struct usbd_lookup_info { uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t bIfaceIndex; uint8_t bIfaceNum; uint8_t bConfigIndex; uint8_t bConfigNum; }; /* Structure used by probe and attach */ struct usb_attach_arg { struct usbd_lookup_info info; device_t temp_dev; /* for internal use */ unsigned long driver_info; /* for internal use */ void *driver_ivar; struct usb_device *device; /* current device */ struct usb_interface *iface; /* current interface */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t port; uint8_t use_generic; /* hint for generic drivers */ uint8_t dev_state; #define UAA_DEV_READY 0 #define UAA_DEV_DISABLED 1 #define UAA_DEV_EJECTING 2 }; /* * The following is a wrapper for the callout structure to ease * porting the code to other platforms. */ struct usb_callout { struct callout co; }; #define usb_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f) #define usb_callout_reset(c,t,f,d) callout_reset(&(c)->co,t,f,d) #define usb_callout_stop(c) callout_stop(&(c)->co) #define usb_callout_drain(c) callout_drain(&(c)->co) #define usb_callout_pending(c) callout_pending(&(c)->co) /* USB transfer states */ #define USB_ST_SETUP 0 #define USB_ST_TRANSFERRED 1 #define USB_ST_ERROR 2 /* USB handle request states */ #define USB_HR_NOT_COMPLETE 0 #define USB_HR_COMPLETE_OK 1 #define USB_HR_COMPLETE_ERR 2 /* * The following macro will return the current state of an USB * transfer like defined by the "USB_ST_XXX" enums. */ #define USB_GET_STATE(xfer) (usbd_xfer_state(xfer)) /* * The following structure defines the USB process message header. */ struct usb_proc_msg { TAILQ_ENTRY(usb_proc_msg) pm_qentry; usb_proc_callback_t *pm_callback; usb_size_t pm_num; }; #define USB_FIFO_TX 0 #define USB_FIFO_RX 1 /* * Locking note for the following functions. All the * "usb_fifo_cmd_t" and "usb_fifo_filter_t" functions are called * locked. The others are called unlocked. */ struct usb_fifo_methods { usb_fifo_open_t *f_open; usb_fifo_close_t *f_close; usb_fifo_ioctl_t *f_ioctl; /* * NOTE: The post-ioctl callback is called after the USB reference * gets locked in the IOCTL handler: */ usb_fifo_ioctl_t *f_ioctl_post; usb_fifo_cmd_t *f_start_read; usb_fifo_cmd_t *f_stop_read; usb_fifo_cmd_t *f_start_write; usb_fifo_cmd_t *f_stop_write; usb_fifo_filter_t *f_filter_read; usb_fifo_filter_t *f_filter_write; const char *basename[4]; const char *postfix[4]; }; struct usb_fifo_sc { struct usb_fifo *fp[2]; struct cdev* dev; }; const char *usbd_errstr(usb_error_t error); void *usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask); struct usb_config_descriptor *usbd_get_config_descriptor( struct usb_device *udev); struct usb_device_descriptor *usbd_get_device_descriptor( struct usb_device *udev); struct usb_interface *usbd_get_iface(struct usb_device *udev, uint8_t iface_index); struct usb_interface_descriptor *usbd_get_interface_descriptor( struct usb_interface *iface); struct usb_endpoint *usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup); struct usb_endpoint *usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val); usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count); enum usb_hc_mode usbd_get_mode(struct usb_device *udev); enum usb_dev_speed usbd_get_speed(struct usb_device *udev); void device_set_usb_desc(device_t dev); void usb_pause_mtx(struct mtx *mtx, int _ticks); const struct usb_device_id *usbd_lookup_id_by_info( const struct usb_device_id *id, usb_size_t sizeof_id, const struct usbd_lookup_info *info); int usbd_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id, struct usb_attach_arg *uaa); usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout); #define usbd_do_request(u,m,r,d) \ usbd_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT) uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2); uint8_t usbd_get_interface_altindex(struct usb_interface *iface); usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index); uint32_t usbd_get_isoc_fps(struct usb_device *udev); usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **pxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx); void usbd_transfer_submit(struct usb_xfer *xfer); void usbd_transfer_clear_stall(struct usb_xfer *xfer); void usbd_transfer_drain(struct usb_xfer *xfer); uint8_t usbd_transfer_pending(struct usb_xfer *xfer); void usbd_transfer_start(struct usb_xfer *xfer); void usbd_transfer_stop(struct usb_xfer *xfer); void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup); void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max); void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index); uint8_t usbd_get_bus_index(struct usb_device *udev); uint8_t usbd_get_device_index(struct usb_device *udev); void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_device_attached(struct usb_device *udev); void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes); struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex); void *usbd_xfer_softc(struct usb_xfer *xfer); void *usbd_xfer_get_priv(struct usb_xfer *xfer); void usbd_xfer_set_priv(struct usb_xfer *xfer, void *); void usbd_xfer_set_interval(struct usb_xfer *xfer, int); uint8_t usbd_xfer_state(struct usb_xfer *xfer); void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len); void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len); void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex); usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer); usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer); uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len); void usbd_xfer_set_timeout(struct usb_xfer *xfer, int timeout); void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n); void usbd_xfer_set_stall(struct usb_xfer *xfer); int usbd_xfer_is_stalled(struct usb_xfer *xfer); void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag); void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag); uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer); void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); int usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); void usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); int usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); void usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, struct usb_page_search *res); void usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len); void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, usb_frlength_t len); int usb_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, struct usb_fifo_sc *f_sc, uint16_t unit, uint16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode); void usb_fifo_detach(struct usb_fifo_sc *f_sc); int usb_fifo_alloc_buffer(struct usb_fifo *f, uint32_t bufsize, uint16_t nbuf); void usb_fifo_free_buffer(struct usb_fifo *f); uint32_t usb_fifo_put_bytes_max(struct usb_fifo *fifo); void usb_fifo_put_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, uint8_t what); void usb_fifo_put_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, uint8_t what); uint8_t usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len); void usb_fifo_put_data_error(struct usb_fifo *fifo); uint8_t usb_fifo_get_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, usb_size_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen); void usb_fifo_reset(struct usb_fifo *f); void usb_fifo_wakeup(struct usb_fifo *f); void usb_fifo_get_data_error(struct usb_fifo *fifo); void *usb_fifo_softc(struct usb_fifo *fifo); void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t); void usb_fifo_set_write_defrag(struct usb_fifo *, uint8_t); void usb_fifo_free(struct usb_fifo *f); #endif /* _KERNEL */ #endif /* _USB_USBDI_H_ */