freebsd-skq/lib/libusb/libusb10.c
Andrew Thompson 8c8fff3177 Add files missed in r194674.
Add libusb 1.0 support which is compatible with the latest revision on
Sourceforge. Libusb 1.0 is a portable usb api released December 2008 and
supersedes the original libusb released 10 years ago, it supports isochronous
endpoints and asynchronous I/O.  Many applications have already started using
the interfaces.

This has been developed as part of Google Summer of Code this year by Sylvestre
Gallon and has been cribbed early due to it being desirable in FreeBSD 8.0

Submitted by: Sylvestre Gallon
Sponsored by: Google Summer of Code 2009
Reviewed by:  Hans Petter Selasky
2009-06-23 01:04:58 +00:00

1141 lines
28 KiB
C

/* $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);
}