aboutsummaryrefslogtreecommitdiff
path: root/lib/libusb/libusb10_io.c
diff options
context:
space:
mode:
authorAndrew Thompson <thompsa@FreeBSD.org>2009-06-23 01:04:58 +0000
committerAndrew Thompson <thompsa@FreeBSD.org>2009-06-23 01:04:58 +0000
commit8c8fff3177f8d9611f191878b3864dc8ca3e5965 (patch)
tree45a0f173b653577e06aeab761541c61ddfad7dea /lib/libusb/libusb10_io.c
parent8112ca35f730f4b46254f98112393bad1813b036 (diff)
downloadsrc-8c8fff3177f8d9611f191878b3864dc8ca3e5965.tar.gz
src-8c8fff3177f8d9611f191878b3864dc8ca3e5965.zip
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
Notes
Notes: svn path=/head/; revision=194676
Diffstat (limited to 'lib/libusb/libusb10_io.c')
-rw-r--r--lib/libusb/libusb10_io.c748
1 files changed, 748 insertions, 0 deletions
diff --git a/lib/libusb/libusb10_io.c b/lib/libusb/libusb10_io.c
new file mode 100644
index 000000000000..4eb30c442380
--- /dev/null
+++ b/lib/libusb/libusb10_io.c
@@ -0,0 +1,748 @@
+/* $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 int
+get_next_timeout(libusb_context *ctx, struct timeval *tv, struct timeval *out)
+{
+ struct timeval timeout;
+ int ret;
+
+ ret = libusb_get_next_timeout(ctx, &timeout);
+
+ if (ret) {
+ if (timerisset(&timeout) == 0)
+ return 1;
+ if (timercmp(&timeout, tv, <) != 0)
+ *out = timeout;
+ else
+ *out = *tv;
+ } else {
+ *out = *tv;
+ }
+
+ return (0);
+}
+
+static int
+handle_timeouts(struct libusb_context *ctx)
+{
+ struct timespec sys_ts;
+ struct timeval sys_tv;
+ struct timeval *cur_tv;
+ struct usb_transfer *xfer;
+ struct libusb_transfer *uxfer;
+ int ret;
+
+ GET_CONTEXT(ctx);
+ ret = 0;
+
+ pthread_mutex_lock(&ctx->flying_transfers_lock);
+ if (USB_LIST_EMPTY(&ctx->flying_transfers))
+ goto out;
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &sys_ts);
+ TIMESPEC_TO_TIMEVAL(&sys_tv, &sys_ts);
+
+ LIST_FOREACH_ENTRY(xfer, &ctx->flying_transfers, list) {
+ cur_tv = &xfer->timeout;
+
+ if (timerisset(cur_tv) == 0)
+ goto out;
+
+ if (xfer->flags & USB_TIMED_OUT)
+ continue;
+
+ if ((cur_tv->tv_sec > sys_tv.tv_sec) || (cur_tv->tv_sec == sys_tv.tv_sec &&
+ cur_tv->tv_usec > sys_tv.tv_usec))
+ goto out;
+
+ xfer->flags |= USB_TIMED_OUT;
+ uxfer = (libusb_transfer *) ((uint8_t *)xfer +
+ sizeof(struct usb_transfer));
+ ret = libusb_cancel_transfer(uxfer);
+ }
+out:
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
+ return (ret);
+}
+
+static int
+handle_events(struct libusb_context *ctx, struct timeval *tv)
+{
+ struct libusb_pollfd *tmppollfd;
+ struct libusb_device_handle *devh;
+ struct usb_pollfd *ipollfd;
+ struct usb_transfer *cur;
+ struct usb_transfer *cancel;
+ struct libusb_transfer *xfer;
+ struct pollfd *fds;
+ struct pollfd *tfds;
+ nfds_t nfds;
+ int tmpfd;
+ int tmp;
+ int ret;
+ int timeout;
+ int i;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "handle_events enter");
+
+ nfds = 0;
+ i = -1;
+
+ pthread_mutex_lock(&ctx->pollfds_lock);
+ LIST_FOREACH_ENTRY(ipollfd, &ctx->pollfds, list)
+ nfds++;
+
+ fds = malloc(sizeof(*fds) * nfds);
+ if (fds == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ LIST_FOREACH_ENTRY(ipollfd, &ctx->pollfds, list) {
+ tmppollfd = &ipollfd->pollfd;
+ tmpfd = tmppollfd->fd;
+ i++;
+ fds[i].fd = tmpfd;
+ fds[i].events = tmppollfd->events;
+ fds[i].revents = 0;
+ }
+
+ pthread_mutex_unlock(&ctx->pollfds_lock);
+
+ timeout = (tv->tv_sec * 1000) + (tv->tv_usec / 1000);
+ if (tv->tv_usec % 1000)
+ timeout++;
+
+ ret = poll(fds, nfds, timeout);
+ if (ret == 0) {
+ free(fds);
+ return (handle_timeouts(ctx));
+ } else if (ret == -1 && errno == EINTR) {
+ free(fds);
+ return (LIBUSB_ERROR_INTERRUPTED);
+ } else if (ret < 0) {
+ free(fds);
+ return (LIBUSB_ERROR_IO);
+ }
+
+ if (fds[0].revents) {
+ if (ret == 1){
+ ret = 0;
+ goto handled;
+ } else {
+ fds[0].revents = 0;
+ ret--;
+ }
+ }
+
+ pthread_mutex_lock(&ctx->open_devs_lock);
+ for (i = 0 ; i < nfds && ret > 0 ; i++) {
+
+ tfds = &fds[i];
+ if (!tfds->revents)
+ continue;
+
+ ret--;
+ LIST_FOREACH_ENTRY(devh, &ctx->open_devs, list) {
+ if (libusb20_dev_get_fd(devh->os_priv) == tfds->fd)
+ break ;
+ }
+
+ if (tfds->revents & POLLERR) {
+ usb_remove_pollfd(ctx, libusb20_dev_get_fd(devh->os_priv));
+ usb_handle_disconnect(devh);
+ continue ;
+ }
+
+
+ pthread_mutex_lock(&libusb20_lock);
+ dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "LIBUSB20_PROCESS");
+ ret = libusb20_dev_process(devh->os_priv);
+ pthread_mutex_unlock(&libusb20_lock);
+
+
+ if (ret == 0 || ret == LIBUSB20_ERROR_NO_DEVICE)
+ continue;
+ else if (ret < 0)
+ goto out;
+ }
+
+ ret = 0;
+out:
+ pthread_mutex_unlock(&ctx->open_devs_lock);
+
+handled:
+ free(fds);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "handle_events leave");
+ return ret;
+}
+
+/* Polling and timing */
+
+int
+libusb_try_lock_events(libusb_context * ctx)
+{
+ int ret;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_try_lock_events enter");
+
+ pthread_mutex_lock(&ctx->pollfd_modify_lock);
+ ret = ctx->pollfd_modify;
+ pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+
+ if (ret != 0)
+ return (1);
+
+ ret = pthread_mutex_trylock(&ctx->events_lock);
+
+ if (ret != 0)
+ return (1);
+
+ ctx->event_handler_active = 1;
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_try_lock_events leave");
+ return (0);
+}
+
+void
+libusb_lock_events(libusb_context * ctx)
+{
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_events enter");
+
+ pthread_mutex_lock(&ctx->events_lock);
+ ctx->event_handler_active = 1;
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_events leave");
+}
+
+void
+libusb_unlock_events(libusb_context * ctx)
+{
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_events enter");
+
+ ctx->event_handler_active = 0;
+ pthread_mutex_unlock(&ctx->events_lock);
+
+ pthread_mutex_lock(&ctx->event_waiters_lock);
+ pthread_cond_broadcast(&ctx->event_waiters_cond);
+ pthread_mutex_unlock(&ctx->event_waiters_lock);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_events leave");
+}
+
+int
+libusb_event_handling_ok(libusb_context * ctx)
+{
+ int ret;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handling_ok enter");
+
+ pthread_mutex_lock(&ctx->pollfd_modify_lock);
+ ret = ctx->pollfd_modify;
+ pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+
+ if (ret != 0)
+ return (0);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handling_ok leave");
+ return (1);
+}
+
+int
+libusb_event_handler_active(libusb_context * ctx)
+{
+ int ret;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handler_active enter");
+
+ pthread_mutex_lock(&ctx->pollfd_modify_lock);
+ ret = ctx->pollfd_modify;
+ pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+
+ if (ret != 0)
+ return (1);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_event_handler_active leave");
+ return (ctx->event_handler_active);
+}
+
+void
+libusb_lock_event_waiters(libusb_context * ctx)
+{
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_event_waiters enter");
+
+ pthread_mutex_lock(&ctx->event_waiters_lock);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_lock_event_waiters leave");
+}
+
+void
+libusb_unlock_event_waiters(libusb_context * ctx)
+{
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_event_waiters enter");
+
+ pthread_mutex_unlock(&ctx->event_waiters_lock);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_unlock_event_waiters leave");
+}
+
+int
+libusb_wait_for_event(libusb_context * ctx, struct timeval *tv)
+{
+ int ret;
+ struct timespec ts;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_wait_for_event enter");
+
+ if (tv == NULL) {
+ pthread_cond_wait(&ctx->event_waiters_cond,
+ &ctx->event_waiters_lock);
+ return (0);
+ }
+
+ ret = clock_gettime(CLOCK_REALTIME, &ts);
+ if (ret < 0)
+ return (LIBUSB_ERROR_OTHER);
+
+ ts.tv_sec = tv->tv_sec;
+ ts.tv_nsec = tv->tv_usec * 1000;
+ if (ts.tv_nsec > 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+
+ ret = pthread_cond_timedwait(&ctx->event_waiters_cond,
+ &ctx->event_waiters_lock, &ts);
+
+ if (ret == ETIMEDOUT)
+ return (1);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_wait_for_event leave");
+ return (0);
+}
+
+int
+libusb_handle_events_timeout(libusb_context * ctx, struct timeval *tv)
+{
+ struct timeval timeout;
+ struct timeval poll_timeout;
+ int ret;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout enter");
+
+ ret = get_next_timeout(ctx, tv, &poll_timeout);
+ if (ret != 0) {
+ return handle_timeouts(ctx);
+ }
+retry:
+ if (libusb_try_lock_events(ctx) == 0) {
+ ret = handle_events(ctx, &poll_timeout);
+ libusb_unlock_events(ctx);
+ return ret;
+ }
+
+ libusb_lock_event_waiters(ctx);
+ if (libusb_event_handler_active(ctx) == 0) {
+ libusb_unlock_event_waiters(ctx);
+ goto retry;
+ }
+
+ ret = libusb_wait_for_event(ctx, &poll_timeout);
+ libusb_unlock_event_waiters(ctx);
+
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ return (handle_timeouts(ctx));
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_timeout leave");
+ return (0);
+}
+
+int
+libusb_handle_events(libusb_context * ctx)
+{
+ struct timeval tv;
+ int ret;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events enter");
+
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ ret = libusb_handle_events_timeout(ctx, &tv);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events leave");
+ return (ret);
+}
+
+int
+libusb_handle_events_locked(libusb_context * ctx, struct timeval *tv)
+{
+ int ret;
+ struct timeval poll_tv;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_locked enter");
+
+ ret = get_next_timeout(ctx, tv, &poll_tv);
+ if (ret != 0) {
+ return handle_timeouts(ctx);
+ }
+
+ ret = handle_events(ctx, &poll_tv);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_handle_events_locked leave");
+ return (ret);
+}
+
+int
+libusb_get_next_timeout(libusb_context * ctx, struct timeval *tv)
+{
+ struct usb_transfer *xfer;
+ struct timeval *next_tv;
+ struct timeval cur_tv;
+ struct timespec cur_ts;
+ int found;
+ int ret;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_next_timeout enter");
+
+ found = 0;
+ pthread_mutex_lock(&ctx->flying_transfers_lock);
+ if (USB_LIST_EMPTY(&ctx->flying_transfers)) {
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
+ return (0);
+ }
+
+ LIST_FOREACH_ENTRY(xfer, &ctx->flying_transfers, list) {
+ if (!(xfer->flags & USB_TIMED_OUT)) {
+ found = 1;
+ break ;
+ }
+ }
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
+
+ if (found == 0) {
+ return 0;
+ }
+
+ next_tv = &xfer->timeout;
+ if (timerisset(next_tv) == 0)
+ return (0);
+
+ ret = clock_gettime(CLOCK_MONOTONIC, &cur_ts);
+ if (ret < 0)
+ return (LIBUSB_ERROR_OTHER);
+ TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);
+
+ if (timercmp(&cur_tv, next_tv, >=) != 0)
+ timerclear(tv);
+ else
+ timersub(next_tv, &cur_tv, tv);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_next_timeout leave");
+ return (1);
+}
+
+void
+libusb_set_pollfd_notifiers(libusb_context * ctx,
+ libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
+ void *user_data)
+{
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_pollfd_notifiers enter");
+
+ ctx->fd_added_cb = added_cb;
+ ctx->fd_removed_cb = removed_cb;
+ ctx->fd_cb_user_data = user_data;
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_set_pollfd_notifiers leave");
+}
+
+struct libusb_pollfd **
+libusb_get_pollfds(libusb_context * ctx)
+{
+ struct usb_pollfd *pollfd;
+ libusb_pollfd **ret;
+ int i;
+
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_pollfds enter");
+
+ i = 0;
+ pthread_mutex_lock(&ctx->pollfds_lock);
+ LIST_FOREACH_ENTRY(pollfd, &ctx->pollfds, list)
+ i++;
+
+ ret = calloc(i + 1 , sizeof(struct libusb_pollfd *));
+ if (ret == NULL) {
+ pthread_mutex_unlock(&ctx->pollfds_lock);
+ return (ret);
+ }
+
+ i = 0;
+ LIST_FOREACH_ENTRY(pollfd, &ctx->pollfds, list)
+ ret[i++] = (struct libusb_pollfd *) pollfd;
+ ret[i] = NULL;
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_get_pollfds leave");
+ return (ret);
+}
+
+
+/* Synchronous device I/O */
+
+static void ctrl_tr_cb(struct libusb_transfer *transfer)
+{
+ libusb_context *ctx;
+ int *complet;
+
+ ctx = NULL;
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_TRANSFER, "CALLBACK ENTER");
+
+ complet = transfer->user_data;
+ *complet = 1;
+}
+
+int
+libusb_control_transfer(libusb_device_handle * devh,
+ uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+ unsigned char *data, uint16_t wLength, unsigned int timeout)
+{
+ struct libusb_transfer *xfer;
+ struct libusb_control_setup *ctr;
+ libusb_context *ctx;
+ unsigned char *buff;
+ int complet;
+ int ret;
+
+ ctx = devh->dev->ctx;
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_control_transfer enter");
+
+ if (devh == NULL || data == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ xfer = libusb_alloc_transfer(0);
+ if (xfer == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ buff = malloc(sizeof(libusb_control_setup) + wLength);
+ if (buff == NULL) {
+ libusb_free_transfer(xfer);
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+
+ ctr = (libusb_control_setup *)buff;
+ ctr->bmRequestType = bmRequestType;
+ ctr->bRequest = bRequest;
+ ctr->wValue = wValue;
+ ctr->wIndex = wIndex;
+ ctr->wLength = wLength;
+ if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT)
+ memcpy(buff + sizeof(libusb_control_setup), data, wLength);
+
+ xfer->dev_handle = devh;
+ xfer->endpoint = 0;
+ xfer->type = LIBUSB_TRANSFER_TYPE_CONTROL;
+ xfer->timeout = timeout;
+ xfer->buffer = buff;
+ xfer->length = sizeof(libusb_control_setup) + wLength;
+ xfer->user_data = &complet;
+ xfer->callback = ctrl_tr_cb;
+ xfer->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
+ complet = 0;
+
+ if ((ret = libusb_submit_transfer(xfer)) < 0) {
+ libusb_free_transfer(xfer);
+ return (ret);
+ }
+
+ while (complet == 0)
+ if ((ret = libusb_handle_events(ctx)) < 0) {
+ libusb_cancel_transfer(xfer);
+ while (complet == 0)
+ if (libusb_handle_events(ctx) < 0) {
+ break;
+ }
+ libusb_free_transfer(xfer);
+ return (ret);
+ }
+
+
+ if ((bmRequestType & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
+ memcpy(data, buff + sizeof(libusb_control_setup), wLength);
+
+ switch (xfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ ret = xfer->actual_length;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ case LIBUSB_TRANSFER_STALL:
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ ret = xfer->status;
+ break;
+ default:
+ ret = LIBUSB_ERROR_OTHER;
+ }
+ libusb_free_transfer(xfer);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_control_transfer leave");
+ return (ret);
+}
+
+static int
+do_transfer(struct libusb_device_handle *devh,
+ unsigned char endpoint, unsigned char *data, int length,
+ int *transferred, unsigned int timeout, int type)
+{
+ struct libusb_transfer *xfer;
+ libusb_context *ctx;
+ int complet;
+ int ret;
+
+ if (devh == NULL || data == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ xfer = libusb_alloc_transfer(0);
+ if (xfer == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+
+ ctx = devh->dev->ctx;
+
+ xfer->dev_handle = devh;
+ xfer->endpoint = endpoint;
+ xfer->type = type;
+ xfer->timeout = timeout;
+ xfer->buffer = data;
+ xfer->length = length;
+ xfer->user_data = &complet;
+ xfer->callback = ctrl_tr_cb;
+ complet = 0;
+
+ if ((ret = libusb_submit_transfer(xfer)) < 0) {
+ libusb_free_transfer(xfer);
+ return (ret);
+ }
+
+ while (complet == 0) {
+ if ((ret = libusb_handle_events(ctx)) < 0) {
+ libusb_cancel_transfer(xfer);
+ libusb_free_transfer(xfer);
+ while (complet == 0) {
+ if (libusb_handle_events(ctx) < 0)
+ break ;
+ }
+ return (ret);
+ }
+ }
+
+ *transferred = xfer->actual_length;
+ switch (xfer->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ ret = xfer->actual_length;
+ break;
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ case LIBUSB_TRANSFER_OVERFLOW:
+ case LIBUSB_TRANSFER_STALL:
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ ret = xfer->status;
+ break;
+ default:
+ ret = LIBUSB_ERROR_OTHER;
+ }
+
+ libusb_free_transfer(xfer);
+ return (ret);
+}
+
+int
+libusb_bulk_transfer(struct libusb_device_handle *devh,
+ unsigned char endpoint, unsigned char *data, int length,
+ int *transferred, unsigned int timeout)
+{
+ libusb_context *ctx;
+ int ret;
+
+ ctx = NULL;
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_bulk_transfer enter");
+
+ ret = do_transfer(devh, endpoint, data, length, transferred,
+ timeout, LIBUSB_TRANSFER_TYPE_BULK);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_bulk_transfer leave");
+ return (ret);
+}
+
+/*
+ * Need to fix xfer->type
+ */
+int
+libusb_interrupt_transfer(struct libusb_device_handle *devh,
+ unsigned char endpoint, unsigned char *data, int length,
+ int *transferred, unsigned int timeout)
+{
+ libusb_context *ctx;
+ int ret;
+
+ ctx = NULL;
+ GET_CONTEXT(ctx);
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer enter");
+
+ ret = do_transfer(devh, endpoint, data, length, transferred,
+ timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT);
+
+ dprintf(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_interrupt_transfer leave");
+ return (ret);
+}