diff options
Diffstat (limited to 'lib/libusb/libusb10.c')
-rw-r--r-- | lib/libusb/libusb10.c | 1140 |
1 files changed, 1140 insertions, 0 deletions
diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c new file mode 100644 index 0000000..223f18a --- /dev/null +++ b/lib/libusb/libusb10.c @@ -0,0 +1,1140 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2009 Sylvestre Gallon. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/queue.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <poll.h> +#include <pthread.h> +#include <time.h> +#include <errno.h> + +#include "libusb20.h" +#include "libusb20_desc.h" +#include "libusb20_int.h" +#include "libusb.h" +#include "libusb10.h" + +static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER; +struct libusb_context *usbi_default_context = NULL; +pthread_mutex_t libusb20_lock = PTHREAD_MUTEX_INITIALIZER; + +/* Library initialisation / deinitialisation */ + +void +libusb_set_debug(libusb_context * ctx, int level) +{ + GET_CONTEXT(ctx); + if (ctx) + ctx->debug = level; +} + +int +libusb_init(libusb_context ** context) +{ + struct libusb_context *ctx; + char * debug; + int ret; + + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return (LIBUSB_ERROR_INVALID_PARAM); + + memset(ctx, 0, sizeof(*ctx)); + + debug = getenv("LIBUSB_DEBUG"); + if (debug != NULL) { + ctx->debug = atoi(debug); + if (ctx->debug != 0) + ctx->debug_fixed = 1; + } + + pthread_mutex_init(&ctx->usb_devs_lock, NULL); + pthread_mutex_init(&ctx->open_devs_lock, NULL); + USB_LIST_INIT(&ctx->usb_devs); + USB_LIST_INIT(&ctx->open_devs); + + pthread_mutex_init(&ctx->flying_transfers_lock, NULL); + pthread_mutex_init(&ctx->pollfds_lock, NULL); + pthread_mutex_init(&ctx->pollfd_modify_lock, NULL); + pthread_mutex_init(&ctx->events_lock, NULL); + pthread_mutex_init(&ctx->event_waiters_lock, NULL); + pthread_cond_init(&ctx->event_waiters_cond, NULL); + + USB_LIST_INIT(&ctx->flying_transfers); + USB_LIST_INIT(&ctx->pollfds); + + ret = pipe(ctx->ctrl_pipe); + if (ret < 0) { + usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[1]); + free(ctx); + return (LIBUSB_ERROR_OTHER); + } + + ret = usb_add_pollfd(ctx, ctx->ctrl_pipe[0], POLLIN); + if (ret < 0) { + usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[1]); + free(ctx); + return ret; + } + + pthread_mutex_lock(&default_context_lock); + if (usbi_default_context == NULL) { + usbi_default_context = ctx; + } + pthread_mutex_unlock(&default_context_lock); + + if (context) + *context = ctx; + + return (0); +} + +void +libusb_exit(libusb_context * ctx) +{ + GET_CONTEXT(ctx); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_exit enter"); + usb_remove_pollfd(ctx, ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[0]); + close(ctx->ctrl_pipe[1]); + + pthread_mutex_lock(&default_context_lock); + if (ctx == usbi_default_context) { + usbi_default_context = NULL; + } + pthread_mutex_unlock(&default_context_lock); + + free(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_exit leave"); +} + +/* Device handling and initialisation. */ + +ssize_t +libusb_get_device_list(libusb_context * ctx, libusb_device *** list) +{ + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *ddesc; + struct libusb_device *dev; + struct libusb20_backend *usb_backend; + int i; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_list enter"); + + usb_backend = libusb20_be_alloc_default(); + if (usb_backend == NULL) + return (-1); + + pdev = NULL; + i = 0; + while ((pdev = libusb20_be_device_foreach(usb_backend, pdev))) + i++; + + if (list == NULL) { + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_INVALID_PARAM); + } + *list = malloc((i + 1) * sizeof(void *)); + if (*list == NULL) { + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_NO_MEM); + } + i = 0; + while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) { + /* get device into libUSB v1.0 list */ + libusb20_be_dequeue_device(usb_backend, pdev); + + ddesc = libusb20_dev_get_device_desc(pdev); + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + free(*list); + libusb20_be_free(usb_backend); + return (LIBUSB_ERROR_NO_MEM); + } + memset(dev, 0, sizeof(*dev)); + + pthread_mutex_init(&dev->lock, NULL); + dev->ctx = ctx; + dev->bus_number = pdev->bus_number; + dev->device_address = pdev->device_address; + dev->num_configurations = ddesc->bNumConfigurations; + + /* link together the two structures */ + dev->os_priv = pdev; + + pthread_mutex_lock(&ctx->usb_devs_lock); + LIST_ADD(&dev->list, &ctx->usb_devs); + pthread_mutex_unlock(&ctx->usb_devs_lock); + + (*list)[i] = libusb_ref_device(dev); + i++; + } + (*list)[i] = NULL; + + libusb20_be_free(usb_backend); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_list leave"); + return (i); +} + +/* + * In this function we cant free all the device contained into list because + * open_with_pid_vid use some node of list after the free_device_list. + */ +void +libusb_free_device_list(libusb_device **list, int unref_devices) +{ + int i; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_device_list enter"); + + if (list == NULL) + return ; + + if (unref_devices) { + for (i = 0; list[i] != NULL; i++) + libusb_unref_device(list[i]); + } + free(list); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_device_list leave"); +} + +uint8_t +libusb_get_bus_number(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_bus_number enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_NO_DEVICE); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_bus_number leave"); + return (dev->bus_number); +} + +uint8_t +libusb_get_device_address(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_address enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_NO_DEVICE); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device_address leave"); + return (dev->device_address); +} + +int +libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) +{ + struct libusb_config_descriptor *pdconf; + struct libusb_interface *pinf; + struct libusb_interface_descriptor *pdinf; + struct libusb_endpoint_descriptor *pdend; + libusb_context *ctx; + int i, j, k, ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_max_packet_size enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_NO_DEVICE); + + if (libusb_get_active_config_descriptor(dev, &pdconf) < 0) + return (LIBUSB_ERROR_OTHER); + + ret = LIBUSB_ERROR_NOT_FOUND; + for (i = 0 ; i < pdconf->bNumInterfaces ; i++) { + pinf = &pdconf->interface[i]; + for (j = 0 ; j < pinf->num_altsetting ; j++) { + pdinf = &pinf->altsetting[j]; + for (k = 0 ; k < pdinf->bNumEndpoints ; k++) { + pdend = &pdinf->endpoint[k]; + if (pdend->bEndpointAddress == endpoint) { + ret = pdend->wMaxPacketSize; + goto out; + } + } + } + } + +out: + libusb_free_config_descriptor(pdconf); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_max_packet_size leave"); + return (ret); +} + +libusb_device * +libusb_ref_device(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_ref_device enter"); + + if (dev == NULL) + return (NULL); + + pthread_mutex_lock(&dev->lock); + dev->refcnt++; + pthread_mutex_unlock(&dev->lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_ref_device leave"); + return (dev); +} + +void +libusb_unref_device(libusb_device * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unref_device enter"); + + if (dev == NULL) + return; + + pthread_mutex_lock(&dev->lock); + dev->refcnt--; + pthread_mutex_unlock(&dev->lock); + + if (dev->refcnt == 0) { + pthread_mutex_lock(&dev->ctx->usb_devs_lock); + LIST_DEL(&dev->list); + pthread_mutex_unlock(&dev->ctx->usb_devs_lock); + + libusb20_dev_free(dev->os_priv); + free(dev); + } + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unref_device leave"); +} + +int +libusb_open(libusb_device * dev, libusb_device_handle **devh) +{ + libusb_context *ctx = dev->ctx; + struct libusb20_device *pdev = dev->os_priv; + libusb_device_handle *hdl; + unsigned char dummy; + int err; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open enter"); + + dummy = 1; + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + hdl = malloc(sizeof(*hdl)); + if (hdl == NULL) + return (LIBUSB_ERROR_NO_MEM); + + err = libusb20_dev_open(pdev, 16 * 4 /* number of endpoints */ ); + if (err) { + free(hdl); + return (LIBUSB_ERROR_NO_MEM); + } + memset(hdl, 0, sizeof(*hdl)); + pthread_mutex_init(&hdl->lock, NULL); + + hdl->dev = libusb_ref_device(dev); + hdl->claimed_interfaces = 0; + hdl->os_priv = dev->os_priv; + err = usb_add_pollfd(ctx, libusb20_dev_get_fd(pdev), POLLIN | + POLLOUT | POLLRDNORM | POLLWRNORM); + if (err < 0) { + libusb_unref_device(dev); + free(hdl); + return (err); + } + + pthread_mutex_lock(&ctx->open_devs_lock); + LIST_ADD(&hdl->list, &ctx->open_devs); + pthread_mutex_unlock(&ctx->open_devs_lock); + + *devh = hdl; + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify++; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + if (err <= 0) { + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + return 0; + } + + libusb_lock_events(ctx); + read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy)); + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + libusb_unlock_events(ctx); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open leave"); + return (0); +} + +libusb_device_handle * +libusb_open_device_with_vid_pid(libusb_context * ctx, uint16_t vendor_id, + uint16_t product_id) +{ + struct libusb_device **devs; + struct libusb_device_handle *devh; + struct libusb20_device *pdev; + struct LIBUSB20_DEVICE_DESC_DECODED *pdesc; + int i, j; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid enter"); + + devh = NULL; + + if ((i = libusb_get_device_list(ctx, &devs)) < 0) + return (NULL); + + for (j = 0; j < i; j++) { + pdev = (struct libusb20_device *)devs[j]->os_priv; + pdesc = libusb20_dev_get_device_desc(pdev); + if (pdesc->idVendor == vendor_id && + pdesc->idProduct == product_id) + if (libusb_open(devs[j], &devh) < 0) + devh = NULL; + } + + libusb_free_device_list(devs, 1); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid leave"); + return (devh); +} + +void +libusb_close(libusb_device_handle * devh) +{ + libusb_context *ctx; + struct libusb20_device *pdev; + unsigned char dummy = 1; + int err; + + if (devh == NULL) + return ; + + ctx = devh->dev->ctx; + pdev = devh->os_priv; + + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close enter"); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify++; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + + if (err <= 0) { + pthread_mutex_lock(&ctx->open_devs_lock); + LIST_DEL(&devh->list); + pthread_mutex_unlock(&ctx->open_devs_lock); + + usb_remove_pollfd(ctx, libusb20_dev_get_fd(pdev)); + libusb_unref_device(devh->dev); + libusb20_dev_close(pdev); + free(devh); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + return ; + } + libusb_lock_events(ctx); + + read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy)); + pthread_mutex_lock(&ctx->open_devs_lock); + LIST_DEL(&devh->list); + pthread_mutex_unlock(&ctx->open_devs_lock); + + usb_remove_pollfd(ctx, libusb20_dev_get_fd(pdev)); + libusb_unref_device(devh->dev); + libusb20_dev_close(pdev); + free(devh); + + pthread_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + pthread_mutex_unlock(&ctx->pollfd_modify_lock); + + libusb_unlock_events(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close leave"); +} + +libusb_device * +libusb_get_device(libusb_device_handle * devh) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device enter"); + + if (devh == NULL) + return (NULL); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_device leave"); + return (devh->dev); +} + +int +libusb_get_configuration(libusb_device_handle * devh, int *config) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_configuration enter"); + + if (devh == NULL || config == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + *config = libusb20_dev_get_config_index((struct libusb20_device *) + devh->dev->os_priv); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_configuration leave"); + return (0); +} + +/* + * XXX this code is wrong. need update. + */ + +int +libusb_set_configuration(libusb_device_handle * devh, int configuration) +{ + struct libusb20_device *pdev; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_configuration enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = (struct libusb20_device *)devh->dev->os_priv; + + libusb20_dev_set_alt_index(pdev, libusb20_dev_get_config_index(pdev), + configuration); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_configuration leave"); + return (0); +} + +int +libusb_claim_interface(libusb_device_handle * dev, int interface_number) +{ + libusb_context *ctx; + int ret = 0; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_claim_interface enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + return (LIBUSB_ERROR_INVALID_PARAM); + + pthread_mutex_lock(&(dev->lock)); + if (dev->claimed_interfaces & (1 << interface_number)) + ret = LIBUSB_ERROR_BUSY; + + if (!ret) + dev->claimed_interfaces |= (1 << interface_number); + pthread_mutex_unlock(&(dev->lock)); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_claim_interface leave"); + return (ret); +} + +int +libusb_release_interface(libusb_device_handle * dev, int interface_number) +{ + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_release_interface enter"); + + ret = 0; + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number >= sizeof(dev->claimed_interfaces) * 8) + return (LIBUSB_ERROR_INVALID_PARAM); + + pthread_mutex_lock(&(dev->lock)); + if (!(dev->claimed_interfaces & (1 << interface_number))) + ret = LIBUSB_ERROR_NOT_FOUND; + + if (!ret) + dev->claimed_interfaces &= ~(1 << interface_number); + pthread_mutex_unlock(&(dev->lock)); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_release_interface leave"); + return (ret); +} + +int +libusb_set_interface_alt_setting(libusb_device_handle * dev, + int interface_number, int alternate_setting) +{ + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_interface_alt_setting enter"); + + if (dev == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + if (interface_number >= sizeof(dev->claimed_interfaces) *8) + return (LIBUSB_ERROR_INVALID_PARAM); + + pthread_mutex_lock(&dev->lock); + if (!(dev->claimed_interfaces & (1 << interface_number))) { + pthread_mutex_unlock(&dev->lock); + return (LIBUSB_ERROR_NOT_FOUND); + } + pthread_mutex_unlock(&dev->lock); + + if (libusb20_dev_set_alt_index(dev->os_priv, interface_number, + alternate_setting) != 0) + return (LIBUSB_ERROR_OTHER); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_interface_alt_setting leave"); + return (0); +} + +int +libusb_clear_halt(libusb_device_handle * devh, unsigned char endpoint) +{ + struct libusb20_transfer *xfer; + libusb_context *ctx; + int ret; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_clear_halt enter"); + + GET_XFER(xfer, endpoint, devh->os_priv); + + pthread_mutex_lock(&libusb20_lock); + ret = libusb20_tr_open(xfer, 0, 0, endpoint); + if (ret != 0 && ret != LIBUSB20_ERROR_BUSY) { + pthread_mutex_unlock(&libusb20_lock); + return (LIBUSB_ERROR_OTHER); + } + + libusb20_tr_clear_stall_sync(xfer); + if (ret == 0) /* check if we have open the device */ + libusb20_tr_close(xfer); + pthread_mutex_unlock(&libusb20_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_clear_halt leave"); + return (0); +} + +int +libusb_reset_device(libusb_device_handle * dev) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_reset_device enter"); + + if (dev == NULL) + return (LIBUSB20_ERROR_INVALID_PARAM); + + libusb20_dev_reset(dev->os_priv); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_reset_device leave"); + return (0); +} + +int +libusb_kernel_driver_active(libusb_device_handle * devh, int interface) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_kernel_driver_active enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_kernel_driver_active leave"); + return (libusb20_dev_kernel_driver_active(devh->os_priv, interface)); +} + +int +libusb_detach_kernel_driver(libusb_device_handle * devh, int interface) +{ + struct libusb20_device *pdev; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_detach_kernel_driver enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + pdev = (struct libusb20_device *)devh->dev->os_priv; + if (libusb20_dev_detach_kernel_driver(pdev, interface) == LIBUSB20_ERROR_OTHER) + return (LIBUSB_ERROR_OTHER); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_detach_kernel_driver leave"); + return (0); +} + +/* + * stub function. + * libusb20 doesn't support this feature. + */ +int +libusb_attach_kernel_driver(libusb_device_handle * devh, int interface) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_attach_kernel_driver enter"); + + if (devh == NULL) + return (LIBUSB_ERROR_INVALID_PARAM); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_attach_kernel_driver leave"); + return (0); +} + +/* Asynchronous device I/O */ + +struct libusb_transfer * +libusb_alloc_transfer(int iso_packets) +{ + struct libusb_transfer *xfer; + struct usb_transfer *bxfer; + libusb_context *ctx; + int len; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_alloc_transfer enter"); + + len = sizeof(struct libusb_transfer) + + sizeof(struct usb_transfer) + + (iso_packets * sizeof(libusb_iso_packet_descriptor)); + + bxfer = malloc(len); + if (bxfer == NULL) + return (NULL); + + memset(bxfer, 0, len); + bxfer->num_iso_packets = iso_packets; + + xfer = (struct libusb_transfer *) ((uint8_t *)bxfer + + sizeof(struct usb_transfer)); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_alloc_transfer leave"); + return (xfer); +} + +void +libusb_free_transfer(struct libusb_transfer *xfer) +{ + struct usb_transfer *bxfer; + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_transfer enter"); + + if (xfer == NULL) + return ; + + bxfer = (struct usb_transfer *) ((uint8_t *)xfer - + sizeof(struct usb_transfer)); + + free(bxfer); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_free_transfer leave"); + return; +} + +static void +libusb10_proxy(struct libusb20_transfer *xfer) +{ + struct usb_transfer *usb_backend; + struct libusb20_device *pdev; + libusb_transfer *usb_xfer; + libusb_context *ctx; + uint8_t status; + uint32_t iso_packets; + int i; + + status = libusb20_tr_get_status(xfer); + usb_xfer = libusb20_tr_get_priv_sc0(xfer); + usb_backend = (struct usb_transfer *) ((uint8_t *)usb_xfer - + sizeof(struct usb_transfer)); + pdev = usb_xfer->dev_handle->dev->os_priv; + ctx = usb_xfer->dev_handle->dev->ctx; + GET_CONTEXT(ctx); + + switch (status) { + case LIBUSB20_TRANSFER_COMPLETED: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 SUBMIT"); + usb_xfer->actual_length += libusb20_tr_get_actual_length(xfer); + usb_xfer->callback(usb_xfer); + + pthread_mutex_lock(&ctx->flying_transfers_lock); + LIST_DEL(&usb_backend->list); + pthread_mutex_unlock(&ctx->flying_transfers_lock); + break ; + case LIBUSB20_TRANSFER_START: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 START"); + usb_xfer->actual_length = 0; + switch (usb_xfer->type) { + case LIBUSB_TRANSFER_TYPE_CONTROL: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE CTR"); + libusb20_tr_setup_control(xfer, usb_xfer->buffer, + (void *)(((uint8_t *) usb_xfer->buffer) + + sizeof(libusb_control_setup)), + usb_xfer->timeout); + break ; + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE ISO"); + iso_packets = libusb20_tr_get_max_frames(xfer); + if (usb_xfer->num_iso_packets > iso_packets) + usb_xfer->num_iso_packets = iso_packets; + for (i = 0 ; i < usb_xfer->num_iso_packets ; i++) { + libusb20_tr_setup_isoc(xfer, + usb_xfer->buffer, usb_xfer->length, i); + } + libusb20_tr_set_total_frames(xfer, i); + break ; + case LIBUSB_TRANSFER_TYPE_BULK: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE BULK"); + libusb20_tr_setup_bulk(xfer, usb_xfer->buffer, + usb_xfer->length, usb_xfer->timeout); + break ; + case LIBUSB_TRANSFER_TYPE_INTERRUPT: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "TYPE INTR"); + libusb20_tr_setup_intr(xfer, usb_xfer->buffer, + usb_xfer->length, usb_xfer->timeout); + break ; + } + libusb20_tr_submit(xfer); + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20 SUBMITED"); + break ; + default: + if (ctx->debug == LIBUSB_DEBUG_TRANSFER) + printf("LIBUSB TRANSFER DEFAULT 0x%x\n", status); + usb_xfer->actual_length = 0; + usb_xfer->status = LIBUSB_TRANSFER_CANCELLED; + + pthread_mutex_lock(&ctx->flying_transfers_lock); + LIST_DEL(&usb_backend->list); + pthread_mutex_unlock(&ctx->flying_transfers_lock); + usb_xfer->callback(usb_xfer); + + break ; + } + + switch (status) { + case LIBUSB20_TRANSFER_COMPLETED: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS COMPLETED"); + usb_xfer->status = LIBUSB_TRANSFER_COMPLETED; + break ; + case LIBUSB20_TRANSFER_OVERFLOW: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR OVERFLOW"); + usb_xfer->status = LIBUSB_TRANSFER_OVERFLOW; + break ; + case LIBUSB20_TRANSFER_NO_DEVICE: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR NO DEVICE"); + usb_xfer->status = LIBUSB_TRANSFER_NO_DEVICE; + break ; + case LIBUSB20_TRANSFER_STALL: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR STALL"); + usb_xfer->status = LIBUSB_TRANSFER_STALL; + break ; + case LIBUSB20_TRANSFER_CANCELLED: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR CANCELLED"); + usb_xfer->status = LIBUSB_TRANSFER_CANCELLED; + break ; + case LIBUSB20_TRANSFER_TIMED_OUT: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "STATUS TR TIMEOUT"); + usb_xfer->status = LIBUSB_TRANSFER_TIMED_OUT; + break ; + case LIBUSB20_TRANSFER_ERROR: + dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "ERROR"); + usb_xfer->status = LIBUSB_TRANSFER_ERROR; + break ; + } +} + +static int +libusb_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer) +{ + int ret; + int usb_speed; + + usb_speed = libusb20_dev_get_speed(pdev); + + switch (xfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + switch (usb_speed) { + case LIBUSB20_SPEED_LOW: + case LIBUSB20_SPEED_FULL: + ret = 60 * 1; + break ; + default : + ret = 60 * 8; + break ; + } + break ; + case LIBUSB_TRANSFER_TYPE_CONTROL: + ret = 2; + break ; + default: + ret = 1; + break ; + } + + return ret; +} + +static int +libusb_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer) +{ + int ret; + int usb_speed; + + usb_speed = libusb20_dev_get_speed(pdev); + + switch (xfer->type) { + case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: + ret = 0; + break ; + case LIBUSB_TRANSFER_TYPE_CONTROL: + switch (usb_speed) { + case LIBUSB20_SPEED_LOW: + ret = 8; + break ; + case LIBUSB20_SPEED_FULL: + ret = 64; + break ; + case LIBUSB20_SPEED_HIGH: + ret = 64; + break ; + } + /*add */ + ret += 8; + break ; + default : + switch (usb_speed) { + case LIBUSB20_SPEED_LOW: + ret = 256; + break ; + case LIBUSB20_SPEED_FULL: + ret = 4096; + break ; + default: + ret = 16384; + break ; + } + break ; + } + + return ret; +} + +int +libusb_submit_transfer(struct libusb_transfer *xfer) +{ + struct libusb20_transfer **usb20_xfer; + struct usb_transfer *usb_backend; + struct usb_transfer *usb_node; + struct libusb20_device *pdev; + struct libusb_context *ctx; + struct timespec cur_ts; + struct timeval *cur_tv; + int maxframe; + int buffsize; + int num_frame; + int ep_idx; + int ret; + int i; + + if (xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + usb20_xfer = malloc(2 * sizeof(struct libusb20_transfer *)); + if (usb20_xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + ctx = xfer->dev_handle->dev->ctx; + pdev = xfer->dev_handle->os_priv; + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer enter"); + + usb_backend = (struct usb_transfer *) ((uint8_t *)xfer - + sizeof(struct usb_transfer)); + usb_backend->transferred = 0; + usb_backend->flags = 0; + + if (xfer->timeout != 0) { + clock_gettime(CLOCK_MONOTONIC, &cur_ts); + cur_ts.tv_sec += xfer->timeout / 1000; + cur_ts.tv_nsec += (xfer->timeout % 1000) * 1000000; + + if (cur_ts.tv_nsec > 1000000000) { + cur_ts.tv_nsec -= 1000000000; + cur_ts.tv_sec++; + } + + TIMESPEC_TO_TIMEVAL(&usb_backend->timeout, &cur_ts); + } + + /*Add to flying list*/ + pthread_mutex_lock(&ctx->flying_transfers_lock); + if (USB_LIST_EMPTY(&ctx->flying_transfers)) { + LIST_ADD(&usb_backend->list, &ctx->flying_transfers); + goto out; + } + if (timerisset(&usb_backend->timeout) == 0) { + LIST_ADD_TAIL(&usb_backend->list, &ctx->flying_transfers); + goto out; + } + LIST_FOREACH_ENTRY(usb_node, &ctx->flying_transfers, list) { + cur_tv = &usb_node->timeout; + if (timerisset(cur_tv) == 0 || + (cur_tv->tv_sec > usb_backend->timeout.tv_sec) || + (cur_tv->tv_sec == usb_backend->timeout.tv_sec && + cur_tv->tv_usec > usb_backend->timeout.tv_usec)) { + LIST_ADD_TAIL(&usb_backend->list, &usb_node->list); + goto out; + } + } + LIST_ADD_TAIL(&usb_backend->list, &ctx->flying_transfers); + +out: + pthread_mutex_unlock(&ctx->flying_transfers_lock); + + usb20_xfer[0] = libusb20_tr_get_pointer(pdev, + ((xfer->endpoint / 0x40) | (xfer->endpoint * 4)) % (16 * 4)); + usb20_xfer[1] = libusb20_tr_get_pointer(pdev, + (((xfer->endpoint / 0x40) | (xfer->endpoint * 4)) % (16 * 4)) + 1); + + if (usb20_xfer[0] == NULL) + return (LIBUSB_ERROR_OTHER); + + xfer->os_priv = usb20_xfer; + + pthread_mutex_lock(&libusb20_lock); + + buffsize = libusb_get_buffsize(pdev, xfer); + maxframe = libusb_get_maxframe(pdev, xfer); + + ret = libusb20_tr_open(usb20_xfer[0], buffsize, + maxframe, xfer->endpoint); + if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) + ret |= libusb20_tr_open(usb20_xfer[1], buffsize, + maxframe, xfer->endpoint); + + if (ret != 0) { + pthread_mutex_unlock(&libusb20_lock); + pthread_mutex_lock(&ctx->flying_transfers_lock); + LIST_DEL(&usb_backend->list); + pthread_mutex_unlock(&ctx->flying_transfers_lock); + return (LIBUSB_ERROR_OTHER); + } + + libusb20_tr_set_priv_sc0(usb20_xfer[0], xfer); + libusb20_tr_set_callback(usb20_xfer[0], libusb10_proxy); + if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { + libusb20_tr_set_priv_sc0(usb20_xfer[1], xfer); + libusb20_tr_set_callback(usb20_xfer[1], libusb10_proxy); + } + + libusb20_tr_start(usb20_xfer[0]); + if (xfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) + libusb20_tr_start(usb20_xfer[1]); + + pthread_mutex_unlock(&libusb20_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer leave"); + return (0); +} + +int +libusb_cancel_transfer(struct libusb_transfer *xfer) +{ + libusb_context *ctx; + + ctx = NULL; + GET_CONTEXT(ctx); + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter"); + + if (xfer == NULL) + return (LIBUSB_ERROR_NO_MEM); + + pthread_mutex_lock(&libusb20_lock); + libusb20_tr_stop(xfer->os_priv); + pthread_mutex_unlock(&libusb20_lock); + + dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave"); + return (0); +} + |