xref: /freebsd/lib/libusb/libusb10.c (revision 9dc96d8bc3f282fefb186670edbf07c7cc32f9c5)
18c8fff31SAndrew Thompson /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
48c8fff31SAndrew Thompson  * Copyright (c) 2009 Sylvestre Gallon. All rights reserved.
54c6bcffdSHans Petter Selasky  * Copyright (c) 2009-2023 Hans Petter Selasky
68c8fff31SAndrew Thompson  *
78c8fff31SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
88c8fff31SAndrew Thompson  * modification, are permitted provided that the following conditions
98c8fff31SAndrew Thompson  * are met:
108c8fff31SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
118c8fff31SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
128c8fff31SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
138c8fff31SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
148c8fff31SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
158c8fff31SAndrew Thompson  *
168c8fff31SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
178c8fff31SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188c8fff31SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198c8fff31SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
208c8fff31SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
218c8fff31SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
228c8fff31SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238c8fff31SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
248c8fff31SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258c8fff31SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268c8fff31SAndrew Thompson  * SUCH DAMAGE.
278c8fff31SAndrew Thompson  */
288c8fff31SAndrew Thompson 
2966194130SHans Petter Selasky #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
3066194130SHans Petter Selasky #include LIBUSB_GLOBAL_INCLUDE_FILE
3166194130SHans Petter Selasky #else
32ac840bfcSWojciech A. Koszek #include <assert.h>
33f3cba95cSWojciech A. Koszek #include <errno.h>
348c8fff31SAndrew Thompson #include <poll.h>
358c8fff31SAndrew Thompson #include <pthread.h>
36d94d94e2SHans Petter Selasky #include <signal.h>
37f3cba95cSWojciech A. Koszek #include <stdio.h>
38f3cba95cSWojciech A. Koszek #include <stdlib.h>
3966194130SHans Petter Selasky #include <string.h>
40f3cba95cSWojciech A. Koszek #include <unistd.h>
4166194130SHans Petter Selasky #include <time.h>
4266194130SHans Petter Selasky #include <sys/fcntl.h>
4366194130SHans Petter Selasky #include <sys/ioctl.h>
4466194130SHans Petter Selasky #include <sys/queue.h>
4566194130SHans Petter Selasky #include <sys/endian.h>
4666194130SHans Petter Selasky #endif
478c8fff31SAndrew Thompson 
489c087c5aSAndrew Thompson #define	libusb_device_handle libusb20_device
499c087c5aSAndrew Thompson 
508c8fff31SAndrew Thompson #include "libusb20.h"
518c8fff31SAndrew Thompson #include "libusb20_desc.h"
528c8fff31SAndrew Thompson #include "libusb20_int.h"
538c8fff31SAndrew Thompson #include "libusb.h"
548c8fff31SAndrew Thompson #include "libusb10.h"
558c8fff31SAndrew Thompson 
5690988efdSHans Petter Selasky #define	LIBUSB_NUM_SW_ENDPOINTS	(16 * 4)
5790988efdSHans Petter Selasky 
588c8fff31SAndrew Thompson static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER;
598c8fff31SAndrew Thompson struct libusb_context *usbi_default_context = NULL;
60390065b1SAlfred Perlstein 
61390065b1SAlfred Perlstein /* Prototypes */
62390065b1SAlfred Perlstein 
63390065b1SAlfred Perlstein static struct libusb20_transfer *libusb10_get_transfer(struct libusb20_device *, uint8_t, uint8_t);
64390065b1SAlfred Perlstein static int libusb10_get_buffsize(struct libusb20_device *, libusb_transfer *);
65390065b1SAlfred Perlstein static int libusb10_convert_error(uint8_t status);
66390065b1SAlfred Perlstein static void libusb10_complete_transfer(struct libusb20_transfer *, struct libusb_super_transfer *, int);
67390065b1SAlfred Perlstein static void libusb10_isoc_proxy(struct libusb20_transfer *);
68390065b1SAlfred Perlstein static void libusb10_bulk_intr_proxy(struct libusb20_transfer *);
69390065b1SAlfred Perlstein static void libusb10_ctrl_proxy(struct libusb20_transfer *);
70390065b1SAlfred Perlstein static void libusb10_submit_transfer_sub(struct libusb20_device *, uint8_t);
718c8fff31SAndrew Thompson 
728c8fff31SAndrew Thompson /*  Library initialisation / deinitialisation */
738c8fff31SAndrew Thompson 
7414b896ceSHans Petter Selasky static const struct libusb_version libusb_version = {
7514b896ceSHans Petter Selasky 	.major = 1,
7614b896ceSHans Petter Selasky 	.minor = 0,
7714b896ceSHans Petter Selasky 	.micro = 0,
7814b896ceSHans Petter Selasky 	.nano = 2016,
7914b896ceSHans Petter Selasky 	.rc = "",
80a2aef24aSEitan Adler 	.describe = "https://www.freebsd.org"
8114b896ceSHans Petter Selasky };
8214b896ceSHans Petter Selasky 
8314b896ceSHans Petter Selasky const struct libusb_version *
8414b896ceSHans Petter Selasky libusb_get_version(void)
8514b896ceSHans Petter Selasky {
8614b896ceSHans Petter Selasky 
8714b896ceSHans Petter Selasky 	return (&libusb_version);
8814b896ceSHans Petter Selasky }
8914b896ceSHans Petter Selasky 
908c8fff31SAndrew Thompson void
918c8fff31SAndrew Thompson libusb_set_debug(libusb_context *ctx, int level)
928c8fff31SAndrew Thompson {
93390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
948495fa08SKyle Evans 	/* debug_fixed is set when the environment overrides libusb_set_debug */
958495fa08SKyle Evans 	if (ctx && ctx->debug_fixed == 0)
968c8fff31SAndrew Thompson 		ctx->debug = level;
978c8fff31SAndrew Thompson }
988c8fff31SAndrew Thompson 
99698e791aSHans Petter Selasky static void
100698e791aSHans Petter Selasky libusb_set_nonblocking(int f)
101698e791aSHans Petter Selasky {
102698e791aSHans Petter Selasky 	int flags;
103698e791aSHans Petter Selasky 
104698e791aSHans Petter Selasky 	/*
105698e791aSHans Petter Selasky 	 * We ignore any failures in this function, hence the
106698e791aSHans Petter Selasky 	 * non-blocking flag is not critical to the operation of
107698e791aSHans Petter Selasky 	 * libUSB. We use F_GETFL and F_SETFL to be compatible with
108698e791aSHans Petter Selasky 	 * Linux.
109698e791aSHans Petter Selasky 	 */
110698e791aSHans Petter Selasky 
111698e791aSHans Petter Selasky 	flags = fcntl(f, F_GETFL, NULL);
112698e791aSHans Petter Selasky 	if (flags == -1)
113698e791aSHans Petter Selasky 		return;
114698e791aSHans Petter Selasky 	flags |= O_NONBLOCK;
115698e791aSHans Petter Selasky 	fcntl(f, F_SETFL, flags);
116698e791aSHans Petter Selasky }
117698e791aSHans Petter Selasky 
118aa87aa52SHans Petter Selasky void
119aa87aa52SHans Petter Selasky libusb_interrupt_event_handler(libusb_context *ctx)
120540c7229SHans Petter Selasky {
121aa87aa52SHans Petter Selasky 	uint8_t dummy;
122540c7229SHans Petter Selasky 	int err;
123540c7229SHans Petter Selasky 
124aa87aa52SHans Petter Selasky 	if (ctx == NULL)
125aa87aa52SHans Petter Selasky 		return;
126aa87aa52SHans Petter Selasky 
127aa87aa52SHans Petter Selasky 	dummy = 0;
128540c7229SHans Petter Selasky 	err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
129540c7229SHans Petter Selasky 	if (err < (int)sizeof(dummy)) {
130540c7229SHans Petter Selasky 		/* ignore error, if any */
131540c7229SHans Petter Selasky 		DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "Waking up event loop failed!");
132540c7229SHans Petter Selasky 	}
133540c7229SHans Petter Selasky }
134540c7229SHans Petter Selasky 
1358c8fff31SAndrew Thompson int
1368c8fff31SAndrew Thompson libusb_init(libusb_context **context)
1378c8fff31SAndrew Thompson {
1384c6bcffdSHans Petter Selasky 	return (libusb_init_context(context, NULL, 0));
1394c6bcffdSHans Petter Selasky }
1404c6bcffdSHans Petter Selasky 
1414c6bcffdSHans Petter Selasky int
1424c6bcffdSHans Petter Selasky libusb_init_context(libusb_context **context,
1434c6bcffdSHans Petter Selasky     const struct libusb_init_option option[], int num_options)
1444c6bcffdSHans Petter Selasky {
1458c8fff31SAndrew Thompson 	struct libusb_context *ctx;
146f7287225SHans Petter Selasky 	pthread_condattr_t attr;
1478495fa08SKyle Evans 	char *debug, *ep;
1488c8fff31SAndrew Thompson 	int ret;
1498c8fff31SAndrew Thompson 
1504c6bcffdSHans Petter Selasky 	if (num_options < 0)
1514c6bcffdSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
1524c6bcffdSHans Petter Selasky 
1538c8fff31SAndrew Thompson 	ctx = malloc(sizeof(*ctx));
1548c8fff31SAndrew Thompson 	if (!ctx)
1558c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
1568c8fff31SAndrew Thompson 
1578c8fff31SAndrew Thompson 	memset(ctx, 0, sizeof(*ctx));
158*9dc96d8bSBaptiste Daroussin 	ctx->devd_pipe = -1;
1598c8fff31SAndrew Thompson 
1608c8fff31SAndrew Thompson 	debug = getenv("LIBUSB_DEBUG");
1618c8fff31SAndrew Thompson 	if (debug != NULL) {
1628495fa08SKyle Evans 		/*
1634c6bcffdSHans Petter Selasky 		 * If LIBUSB_DEBUG is set, we'll honor that first and
1644c6bcffdSHans Petter Selasky 		 * use it to override any future libusb_set_debug()
1654c6bcffdSHans Petter Selasky 		 * calls or init options.
1668495fa08SKyle Evans 		 */
1678495fa08SKyle Evans 		errno = 0;
1688495fa08SKyle Evans 		ctx->debug = strtol(debug, &ep, 10);
1698495fa08SKyle Evans 		if (errno == 0 && *ep == '\0') {
1708c8fff31SAndrew Thompson 			ctx->debug_fixed = 1;
1718495fa08SKyle Evans 		} else {
1728495fa08SKyle Evans 			/*
1738495fa08SKyle Evans 			 * LIBUSB_DEBUG conversion failed for some reason, but
1748495fa08SKyle Evans 			 * we don't care about the specifics all that much.  We
1758495fa08SKyle Evans 			 * can't use it either way.  Force it to the default,
1768495fa08SKyle Evans 			 * 0, in case we had a partial number.
1778495fa08SKyle Evans 			 */
1788495fa08SKyle Evans 			ctx->debug = 0;
1798495fa08SKyle Evans 		}
1804c6bcffdSHans Petter Selasky 	} else {
1814c6bcffdSHans Petter Selasky 		/*
1824c6bcffdSHans Petter Selasky 		 * If the LIBUSB_OPTION_LOG_LEVEL is set, honor that.
1834c6bcffdSHans Petter Selasky 		 */
1844c6bcffdSHans Petter Selasky 		for (int i = 0; i != num_options; i++) {
1854c6bcffdSHans Petter Selasky 			if (option[i].option != LIBUSB_OPTION_LOG_LEVEL)
1864c6bcffdSHans Petter Selasky 				continue;
1874c6bcffdSHans Petter Selasky 
1884c6bcffdSHans Petter Selasky 			ctx->debug = (int)option[i].value.ival;
1894c6bcffdSHans Petter Selasky 			if ((int64_t)ctx->debug == option[i].value.ival) {
1904c6bcffdSHans Petter Selasky 				ctx->debug_fixed = 1;
1914c6bcffdSHans Petter Selasky 			} else {
1924c6bcffdSHans Petter Selasky 				free(ctx);
1934c6bcffdSHans Petter Selasky 				return (LIBUSB_ERROR_INVALID_PARAM);
1948c8fff31SAndrew Thompson 			}
1954c6bcffdSHans Petter Selasky 		}
1964c6bcffdSHans Petter Selasky 	}
1974c6bcffdSHans Petter Selasky 
198c500e4ddSAndrew Thompson 	TAILQ_INIT(&ctx->pollfds);
199390065b1SAlfred Perlstein 	TAILQ_INIT(&ctx->tr_done);
2007bdc064bSHans Petter Selasky 	TAILQ_INIT(&ctx->hotplug_cbh);
2017bdc064bSHans Petter Selasky 	TAILQ_INIT(&ctx->hotplug_devs);
202390065b1SAlfred Perlstein 
203f7287225SHans Petter Selasky 	if (pthread_mutex_init(&ctx->ctx_lock, NULL) != 0) {
204f7287225SHans Petter Selasky 		free(ctx);
205f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
206f7287225SHans Petter Selasky 	}
2077bdc064bSHans Petter Selasky 	if (pthread_mutex_init(&ctx->hotplug_lock, NULL) != 0) {
2087bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
2097bdc064bSHans Petter Selasky 		free(ctx);
2107bdc064bSHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
2117bdc064bSHans Petter Selasky 	}
212f7287225SHans Petter Selasky 	if (pthread_condattr_init(&attr) != 0) {
213f7287225SHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
2147bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
215f7287225SHans Petter Selasky 		free(ctx);
216f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
217f7287225SHans Petter Selasky 	}
218f7287225SHans Petter Selasky 	if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) != 0) {
219f7287225SHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
2207bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
221f7287225SHans Petter Selasky 		pthread_condattr_destroy(&attr);
222f7287225SHans Petter Selasky 		free(ctx);
223f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_OTHER);
224f7287225SHans Petter Selasky 	}
225f7287225SHans Petter Selasky 	if (pthread_cond_init(&ctx->ctx_cond, &attr) != 0) {
226f7287225SHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
2277bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
228f7287225SHans Petter Selasky 		pthread_condattr_destroy(&attr);
229f7287225SHans Petter Selasky 		free(ctx);
230f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
231f7287225SHans Petter Selasky 	}
232f7287225SHans Petter Selasky 	pthread_condattr_destroy(&attr);
233390065b1SAlfred Perlstein 
234390065b1SAlfred Perlstein 	ctx->ctx_handler = NO_THREAD;
2357bdc064bSHans Petter Selasky 	ctx->hotplug_handler = NO_THREAD;
2368c8fff31SAndrew Thompson 
2378c8fff31SAndrew Thompson 	ret = pipe(ctx->ctrl_pipe);
2388c8fff31SAndrew Thompson 	if (ret < 0) {
239390065b1SAlfred Perlstein 		pthread_mutex_destroy(&ctx->ctx_lock);
2407bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
241390065b1SAlfred Perlstein 		pthread_cond_destroy(&ctx->ctx_cond);
2428c8fff31SAndrew Thompson 		free(ctx);
2438c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_OTHER);
2448c8fff31SAndrew Thompson 	}
245390065b1SAlfred Perlstein 	/* set non-blocking mode on the control pipe to avoid deadlock */
246698e791aSHans Petter Selasky 	libusb_set_nonblocking(ctx->ctrl_pipe[0]);
247698e791aSHans Petter Selasky 	libusb_set_nonblocking(ctx->ctrl_pipe[1]);
2488c8fff31SAndrew Thompson 
249390065b1SAlfred Perlstein 	libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->ctrl_pipe[0], POLLIN);
2508c8fff31SAndrew Thompson 
2518c8fff31SAndrew Thompson 	pthread_mutex_lock(&default_context_lock);
2528c8fff31SAndrew Thompson 	if (usbi_default_context == NULL) {
2538c8fff31SAndrew Thompson 		usbi_default_context = ctx;
2548c8fff31SAndrew Thompson 	}
2558c8fff31SAndrew Thompson 	pthread_mutex_unlock(&default_context_lock);
2568c8fff31SAndrew Thompson 
2578c8fff31SAndrew Thompson 	if (context)
2588c8fff31SAndrew Thompson 		*context = ctx;
2598c8fff31SAndrew Thompson 
260390065b1SAlfred Perlstein 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_init complete");
261390065b1SAlfred Perlstein 
262d94d94e2SHans Petter Selasky 	signal(SIGPIPE, SIG_IGN);
263d94d94e2SHans Petter Selasky 
2648c8fff31SAndrew Thompson 	return (0);
2658c8fff31SAndrew Thompson }
2668c8fff31SAndrew Thompson 
2678c8fff31SAndrew Thompson void
2688c8fff31SAndrew Thompson libusb_exit(libusb_context *ctx)
2698c8fff31SAndrew Thompson {
270390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
2718c8fff31SAndrew Thompson 
272390065b1SAlfred Perlstein 	if (ctx == NULL)
273390065b1SAlfred Perlstein 		return;
274390065b1SAlfred Perlstein 
2757bdc064bSHans Petter Selasky 	/* stop hotplug thread, if any */
2767bdc064bSHans Petter Selasky 
2777bdc064bSHans Petter Selasky 	if (ctx->hotplug_handler != NO_THREAD) {
2787bdc064bSHans Petter Selasky 		pthread_t td;
2797bdc064bSHans Petter Selasky 		void *ptr;
2807bdc064bSHans Petter Selasky 
2817bdc064bSHans Petter Selasky 		HOTPLUG_LOCK(ctx);
2827bdc064bSHans Petter Selasky 		td = ctx->hotplug_handler;
2837bdc064bSHans Petter Selasky 		ctx->hotplug_handler = NO_THREAD;
284*9dc96d8bSBaptiste Daroussin 		if (ctx->usb_event_mode == usb_event_devd) {
285*9dc96d8bSBaptiste Daroussin 			close(ctx->devd_pipe);
286*9dc96d8bSBaptiste Daroussin 			ctx->devd_pipe = -1;
287*9dc96d8bSBaptiste Daroussin 		} else if (ctx->usb_event_mode == usb_event_netlink) {
288*9dc96d8bSBaptiste Daroussin 			close(ctx->ss.fd);
289*9dc96d8bSBaptiste Daroussin 			ctx->ss.fd = -1;
290*9dc96d8bSBaptiste Daroussin 		}
2917bdc064bSHans Petter Selasky 		HOTPLUG_UNLOCK(ctx);
2927bdc064bSHans Petter Selasky 
2937bdc064bSHans Petter Selasky 		pthread_join(td, &ptr);
2947bdc064bSHans Petter Selasky 	}
2957bdc064bSHans Petter Selasky 
296390065b1SAlfred Perlstein 	/* XXX cleanup devices */
297390065b1SAlfred Perlstein 
298390065b1SAlfred Perlstein 	libusb10_remove_pollfd(ctx, &ctx->ctx_poll);
2998c8fff31SAndrew Thompson 	close(ctx->ctrl_pipe[0]);
3008c8fff31SAndrew Thompson 	close(ctx->ctrl_pipe[1]);
301390065b1SAlfred Perlstein 	pthread_mutex_destroy(&ctx->ctx_lock);
3027bdc064bSHans Petter Selasky 	pthread_mutex_destroy(&ctx->hotplug_lock);
303390065b1SAlfred Perlstein 	pthread_cond_destroy(&ctx->ctx_cond);
3048c8fff31SAndrew Thompson 
3058c8fff31SAndrew Thompson 	pthread_mutex_lock(&default_context_lock);
3068c8fff31SAndrew Thompson 	if (ctx == usbi_default_context) {
3078c8fff31SAndrew Thompson 		usbi_default_context = NULL;
3088c8fff31SAndrew Thompson 	}
3098c8fff31SAndrew Thompson 	pthread_mutex_unlock(&default_context_lock);
3108c8fff31SAndrew Thompson 
3118c8fff31SAndrew Thompson 	free(ctx);
3128c8fff31SAndrew Thompson }
3138c8fff31SAndrew Thompson 
3148c8fff31SAndrew Thompson /* Device handling and initialisation. */
3158c8fff31SAndrew Thompson 
3168c8fff31SAndrew Thompson ssize_t
3178c8fff31SAndrew Thompson libusb_get_device_list(libusb_context *ctx, libusb_device ***list)
3188c8fff31SAndrew Thompson {
3198c8fff31SAndrew Thompson 	struct libusb20_backend *usb_backend;
320390065b1SAlfred Perlstein 	struct libusb20_device *pdev;
321390065b1SAlfred Perlstein 	struct libusb_device *dev;
3228c8fff31SAndrew Thompson 	int i;
3238c8fff31SAndrew Thompson 
324390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
325390065b1SAlfred Perlstein 
326390065b1SAlfred Perlstein 	if (ctx == NULL)
327390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
328390065b1SAlfred Perlstein 
329390065b1SAlfred Perlstein 	if (list == NULL)
330390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
3318c8fff31SAndrew Thompson 
3328c8fff31SAndrew Thompson 	usb_backend = libusb20_be_alloc_default();
3338c8fff31SAndrew Thompson 	if (usb_backend == NULL)
334390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_NO_MEM);
3358c8fff31SAndrew Thompson 
336390065b1SAlfred Perlstein 	/* figure out how many USB devices are present */
3378c8fff31SAndrew Thompson 	pdev = NULL;
3388c8fff31SAndrew Thompson 	i = 0;
3398c8fff31SAndrew Thompson 	while ((pdev = libusb20_be_device_foreach(usb_backend, pdev)))
3408c8fff31SAndrew Thompson 		i++;
3418c8fff31SAndrew Thompson 
342390065b1SAlfred Perlstein 	/* allocate device pointer list */
3438c8fff31SAndrew Thompson 	*list = malloc((i + 1) * sizeof(void *));
3448c8fff31SAndrew Thompson 	if (*list == NULL) {
3458c8fff31SAndrew Thompson 		libusb20_be_free(usb_backend);
3468c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
3478c8fff31SAndrew Thompson 	}
348390065b1SAlfred Perlstein 	/* create libusb v1.0 compliant devices */
3498c8fff31SAndrew Thompson 	i = 0;
3508c8fff31SAndrew Thompson 	while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) {
3518c8fff31SAndrew Thompson 
3528c8fff31SAndrew Thompson 		dev = malloc(sizeof(*dev));
3538c8fff31SAndrew Thompson 		if (dev == NULL) {
354c500e4ddSAndrew Thompson 			while (i != 0) {
355c500e4ddSAndrew Thompson 				libusb_unref_device((*list)[i - 1]);
356c500e4ddSAndrew Thompson 				i--;
357c500e4ddSAndrew Thompson 			}
3588c8fff31SAndrew Thompson 			free(*list);
359390065b1SAlfred Perlstein 			*list = NULL;
3608c8fff31SAndrew Thompson 			libusb20_be_free(usb_backend);
3618c8fff31SAndrew Thompson 			return (LIBUSB_ERROR_NO_MEM);
3628c8fff31SAndrew Thompson 		}
363ccef4ddfSAndrew Thompson 		/* get device into libUSB v1.0 list */
364ccef4ddfSAndrew Thompson 		libusb20_be_dequeue_device(usb_backend, pdev);
365ccef4ddfSAndrew Thompson 
3668c8fff31SAndrew Thompson 		memset(dev, 0, sizeof(*dev));
3678c8fff31SAndrew Thompson 
368390065b1SAlfred Perlstein 		/* init transfer queues */
369390065b1SAlfred Perlstein 		TAILQ_INIT(&dev->tr_head);
370390065b1SAlfred Perlstein 
371390065b1SAlfred Perlstein 		/* set context we belong to */
3728c8fff31SAndrew Thompson 		dev->ctx = ctx;
3738c8fff31SAndrew Thompson 
3748c8fff31SAndrew Thompson 		/* link together the two structures */
3758c8fff31SAndrew Thompson 		dev->os_priv = pdev;
376390065b1SAlfred Perlstein 		pdev->privLuData = dev;
3778c8fff31SAndrew Thompson 
3788c8fff31SAndrew Thompson 		(*list)[i] = libusb_ref_device(dev);
3798c8fff31SAndrew Thompson 		i++;
3808c8fff31SAndrew Thompson 	}
3818c8fff31SAndrew Thompson 	(*list)[i] = NULL;
3828c8fff31SAndrew Thompson 
3838c8fff31SAndrew Thompson 	libusb20_be_free(usb_backend);
3848c8fff31SAndrew Thompson 	return (i);
3858c8fff31SAndrew Thompson }
3868c8fff31SAndrew Thompson 
3878c8fff31SAndrew Thompson void
3888c8fff31SAndrew Thompson libusb_free_device_list(libusb_device **list, int unref_devices)
3898c8fff31SAndrew Thompson {
3908c8fff31SAndrew Thompson 	int i;
3918c8fff31SAndrew Thompson 
3928c8fff31SAndrew Thompson 	if (list == NULL)
393390065b1SAlfred Perlstein 		return;			/* be NULL safe */
3948c8fff31SAndrew Thompson 
3958c8fff31SAndrew Thompson 	if (unref_devices) {
3968c8fff31SAndrew Thompson 		for (i = 0; list[i] != NULL; i++)
3978c8fff31SAndrew Thompson 			libusb_unref_device(list[i]);
3988c8fff31SAndrew Thompson 	}
3998c8fff31SAndrew Thompson 	free(list);
4008c8fff31SAndrew Thompson }
4018c8fff31SAndrew Thompson 
4028c8fff31SAndrew Thompson uint8_t
4038c8fff31SAndrew Thompson libusb_get_bus_number(libusb_device *dev)
4048c8fff31SAndrew Thompson {
4058c8fff31SAndrew Thompson 	if (dev == NULL)
406390065b1SAlfred Perlstein 		return (0);		/* should not happen */
407390065b1SAlfred Perlstein 	return (libusb20_dev_get_bus_number(dev->os_priv));
4088c8fff31SAndrew Thompson }
4098c8fff31SAndrew Thompson 
4100f2c7066SHans Petter Selasky uint8_t
4110f2c7066SHans Petter Selasky libusb_get_port_number(libusb_device *dev)
4120f2c7066SHans Petter Selasky {
4130f2c7066SHans Petter Selasky 	if (dev == NULL)
4140f2c7066SHans Petter Selasky 		return (0);		/* should not happen */
4150f2c7066SHans Petter Selasky 	return (libusb20_dev_get_parent_port(dev->os_priv));
4160f2c7066SHans Petter Selasky }
4170f2c7066SHans Petter Selasky 
4185906bf49SEd Maste int
419a9205626SEd Maste libusb_get_port_numbers(libusb_device *dev, uint8_t *buf, uint8_t bufsize)
420a9205626SEd Maste {
421a9205626SEd Maste 	return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize));
422a9205626SEd Maste }
423a9205626SEd Maste 
424a9205626SEd Maste int
4255906bf49SEd Maste libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *buf,
4265906bf49SEd Maste     uint8_t bufsize)
4275906bf49SEd Maste {
4285906bf49SEd Maste 	return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize));
4295906bf49SEd Maste }
4305906bf49SEd Maste 
4318c8fff31SAndrew Thompson uint8_t
4328c8fff31SAndrew Thompson libusb_get_device_address(libusb_device *dev)
4338c8fff31SAndrew Thompson {
4348c8fff31SAndrew Thompson 	if (dev == NULL)
435390065b1SAlfred Perlstein 		return (0);		/* should not happen */
436390065b1SAlfred Perlstein 	return (libusb20_dev_get_address(dev->os_priv));
4378c8fff31SAndrew Thompson }
4388c8fff31SAndrew Thompson 
4399a46d467SHans Petter Selasky enum libusb_speed
4409a46d467SHans Petter Selasky libusb_get_device_speed(libusb_device *dev)
4419a46d467SHans Petter Selasky {
4429a46d467SHans Petter Selasky 	if (dev == NULL)
44333ec9f0cSHans Petter Selasky 		return (LIBUSB_SPEED_UNKNOWN);	/* should not happen */
4449a46d467SHans Petter Selasky 
4459a46d467SHans Petter Selasky 	switch (libusb20_dev_get_speed(dev->os_priv)) {
4469a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_LOW:
4479a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_LOW);
4489a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_FULL:
4499a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_FULL);
4509a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_HIGH:
4519a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_HIGH);
4529a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_SUPER:
4539a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_SUPER);
4549a46d467SHans Petter Selasky 	default:
4559a46d467SHans Petter Selasky 		break;
4569a46d467SHans Petter Selasky 	}
4579a46d467SHans Petter Selasky 	return (LIBUSB_SPEED_UNKNOWN);
4589a46d467SHans Petter Selasky }
4599a46d467SHans Petter Selasky 
4608c8fff31SAndrew Thompson int
461390065b1SAlfred Perlstein libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint)
4628c8fff31SAndrew Thompson {
4638c8fff31SAndrew Thompson 	struct libusb_config_descriptor *pdconf;
4648c8fff31SAndrew Thompson 	struct libusb_interface *pinf;
4658c8fff31SAndrew Thompson 	struct libusb_interface_descriptor *pdinf;
4668c8fff31SAndrew Thompson 	struct libusb_endpoint_descriptor *pdend;
467390065b1SAlfred Perlstein 	int i;
468390065b1SAlfred Perlstein 	int j;
469390065b1SAlfred Perlstein 	int k;
470390065b1SAlfred Perlstein 	int ret;
4718c8fff31SAndrew Thompson 
4728c8fff31SAndrew Thompson 	if (dev == NULL)
4738c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_DEVICE);
4748c8fff31SAndrew Thompson 
475390065b1SAlfred Perlstein 	ret = libusb_get_active_config_descriptor(dev, &pdconf);
476390065b1SAlfred Perlstein 	if (ret < 0)
477390065b1SAlfred Perlstein 		return (ret);
4788c8fff31SAndrew Thompson 
4798c8fff31SAndrew Thompson 	ret = LIBUSB_ERROR_NOT_FOUND;
4808c8fff31SAndrew Thompson 	for (i = 0; i < pdconf->bNumInterfaces; i++) {
4818c8fff31SAndrew Thompson 		pinf = &pdconf->interface[i];
4828c8fff31SAndrew Thompson 		for (j = 0; j < pinf->num_altsetting; j++) {
4838c8fff31SAndrew Thompson 			pdinf = &pinf->altsetting[j];
4848c8fff31SAndrew Thompson 			for (k = 0; k < pdinf->bNumEndpoints; k++) {
4858c8fff31SAndrew Thompson 				pdend = &pdinf->endpoint[k];
4868c8fff31SAndrew Thompson 				if (pdend->bEndpointAddress == endpoint) {
4878c8fff31SAndrew Thompson 					ret = pdend->wMaxPacketSize;
4888c8fff31SAndrew Thompson 					goto out;
4898c8fff31SAndrew Thompson 				}
4908c8fff31SAndrew Thompson 			}
4918c8fff31SAndrew Thompson 		}
4928c8fff31SAndrew Thompson 	}
4938c8fff31SAndrew Thompson 
4948c8fff31SAndrew Thompson out:
4958c8fff31SAndrew Thompson 	libusb_free_config_descriptor(pdconf);
4968c8fff31SAndrew Thompson 	return (ret);
4978c8fff31SAndrew Thompson }
4988c8fff31SAndrew Thompson 
499748205a3SHans Petter Selasky int
500748205a3SHans Petter Selasky libusb_get_max_iso_packet_size(libusb_device *dev, uint8_t endpoint)
501748205a3SHans Petter Selasky {
502748205a3SHans Petter Selasky 	int multiplier;
503748205a3SHans Petter Selasky 	int ret;
504748205a3SHans Petter Selasky 
505748205a3SHans Petter Selasky 	ret = libusb_get_max_packet_size(dev, endpoint);
506748205a3SHans Petter Selasky 
507748205a3SHans Petter Selasky 	switch (libusb20_dev_get_speed(dev->os_priv)) {
508748205a3SHans Petter Selasky 	case LIBUSB20_SPEED_LOW:
509748205a3SHans Petter Selasky 	case LIBUSB20_SPEED_FULL:
510748205a3SHans Petter Selasky 		break;
511748205a3SHans Petter Selasky 	default:
512748205a3SHans Petter Selasky 		if (ret > -1) {
513748205a3SHans Petter Selasky 			multiplier = (1 + ((ret >> 11) & 3));
514748205a3SHans Petter Selasky 			if (multiplier > 3)
515748205a3SHans Petter Selasky 				multiplier = 3;
516748205a3SHans Petter Selasky 			ret = (ret & 0x7FF) * multiplier;
517748205a3SHans Petter Selasky 		}
518748205a3SHans Petter Selasky 		break;
519748205a3SHans Petter Selasky 	}
520748205a3SHans Petter Selasky 	return (ret);
521748205a3SHans Petter Selasky }
522748205a3SHans Petter Selasky 
5238c8fff31SAndrew Thompson libusb_device *
5248c8fff31SAndrew Thompson libusb_ref_device(libusb_device *dev)
5258c8fff31SAndrew Thompson {
5268c8fff31SAndrew Thompson 	if (dev == NULL)
527390065b1SAlfred Perlstein 		return (NULL);		/* be NULL safe */
5288c8fff31SAndrew Thompson 
529390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
5308c8fff31SAndrew Thompson 	dev->refcnt++;
531390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
5328c8fff31SAndrew Thompson 
5338c8fff31SAndrew Thompson 	return (dev);
5348c8fff31SAndrew Thompson }
5358c8fff31SAndrew Thompson 
5368c8fff31SAndrew Thompson void
5378c8fff31SAndrew Thompson libusb_unref_device(libusb_device *dev)
5388c8fff31SAndrew Thompson {
5398c8fff31SAndrew Thompson 	if (dev == NULL)
540390065b1SAlfred Perlstein 		return;			/* be NULL safe */
5418c8fff31SAndrew Thompson 
542390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
5438c8fff31SAndrew Thompson 	dev->refcnt--;
544390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
5458c8fff31SAndrew Thompson 
5468c8fff31SAndrew Thompson 	if (dev->refcnt == 0) {
5478c8fff31SAndrew Thompson 		libusb20_dev_free(dev->os_priv);
5488c8fff31SAndrew Thompson 		free(dev);
5498c8fff31SAndrew Thompson 	}
5508c8fff31SAndrew Thompson }
5518c8fff31SAndrew Thompson 
5528c8fff31SAndrew Thompson int
5538c8fff31SAndrew Thompson libusb_open(libusb_device *dev, libusb_device_handle **devh)
5548c8fff31SAndrew Thompson {
5558c8fff31SAndrew Thompson 	libusb_context *ctx = dev->ctx;
5568c8fff31SAndrew Thompson 	struct libusb20_device *pdev = dev->os_priv;
5578c8fff31SAndrew Thompson 	int err;
5588c8fff31SAndrew Thompson 
5598c8fff31SAndrew Thompson 	if (devh == NULL)
5608c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
5618c8fff31SAndrew Thompson 
562390065b1SAlfred Perlstein 	/* set default device handle value */
563390065b1SAlfred Perlstein 	*devh = NULL;
564390065b1SAlfred Perlstein 
565390065b1SAlfred Perlstein 	dev = libusb_ref_device(dev);
566390065b1SAlfred Perlstein 	if (dev == NULL)
567390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
5688c8fff31SAndrew Thompson 
56990988efdSHans Petter Selasky 	err = libusb20_dev_open(pdev, LIBUSB_NUM_SW_ENDPOINTS);
5708c8fff31SAndrew Thompson 	if (err) {
571390065b1SAlfred Perlstein 		libusb_unref_device(dev);
5728c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
5738c8fff31SAndrew Thompson 	}
5746847ea50SHans Petter Selasky 
5756847ea50SHans Petter Selasky 	/*
5766847ea50SHans Petter Selasky 	 * Clear the device gone flag, in case the device was opened
5776847ea50SHans Petter Selasky 	 * after a re-attach, to allow new transaction:
5786847ea50SHans Petter Selasky 	 */
5796847ea50SHans Petter Selasky 	CTX_LOCK(ctx);
5806847ea50SHans Petter Selasky 	dev->device_is_gone = 0;
5816847ea50SHans Petter Selasky 	CTX_UNLOCK(ctx);
5826847ea50SHans Petter Selasky 
583390065b1SAlfred Perlstein 	libusb10_add_pollfd(ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN |
5848c8fff31SAndrew Thompson 	    POLLOUT | POLLRDNORM | POLLWRNORM);
5858c8fff31SAndrew Thompson 
586390065b1SAlfred Perlstein 	/* make sure our event loop detects the new device */
587aa87aa52SHans Petter Selasky 	libusb_interrupt_event_handler(ctx);
588540c7229SHans Petter Selasky 
589390065b1SAlfred Perlstein 	*devh = pdev;
5908c8fff31SAndrew Thompson 
5918c8fff31SAndrew Thompson 	return (0);
5928c8fff31SAndrew Thompson }
5938c8fff31SAndrew Thompson 
5948c8fff31SAndrew Thompson libusb_device_handle *
5958c8fff31SAndrew Thompson libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vendor_id,
5968c8fff31SAndrew Thompson     uint16_t product_id)
5978c8fff31SAndrew Thompson {
5988c8fff31SAndrew Thompson 	struct libusb_device **devs;
5998c8fff31SAndrew Thompson 	struct libusb20_device *pdev;
6008c8fff31SAndrew Thompson 	struct LIBUSB20_DEVICE_DESC_DECODED *pdesc;
601390065b1SAlfred Perlstein 	int i;
602390065b1SAlfred Perlstein 	int j;
6038c8fff31SAndrew Thompson 
604390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
605390065b1SAlfred Perlstein 	if (ctx == NULL)
606390065b1SAlfred Perlstein 		return (NULL);		/* be NULL safe */
607390065b1SAlfred Perlstein 
6081efeb40dSHans Petter Selasky 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_with_vid_pid enter");
6098c8fff31SAndrew Thompson 
6108c8fff31SAndrew Thompson 	if ((i = libusb_get_device_list(ctx, &devs)) < 0)
6118c8fff31SAndrew Thompson 		return (NULL);
6128c8fff31SAndrew Thompson 
6133f709d07SHans Petter Selasky 	pdev = NULL;
6148c8fff31SAndrew Thompson 	for (j = 0; j < i; j++) {
6153f709d07SHans Petter Selasky 		struct libusb20_device *tdev;
6163f709d07SHans Petter Selasky 
6173f709d07SHans Petter Selasky 		tdev = devs[j]->os_priv;
6183f709d07SHans Petter Selasky 		pdesc = libusb20_dev_get_device_desc(tdev);
619390065b1SAlfred Perlstein 		/*
620390065b1SAlfred Perlstein 		 * NOTE: The USB library will automatically swap the
621390065b1SAlfred Perlstein 		 * fields in the device descriptor to be of host
622390065b1SAlfred Perlstein 		 * endian type!
623390065b1SAlfred Perlstein 		 */
6248c8fff31SAndrew Thompson 		if (pdesc->idVendor == vendor_id &&
625c500e4ddSAndrew Thompson 		    pdesc->idProduct == product_id) {
626dc934803SHans Petter Selasky 			libusb_open(devs[j], &pdev);
627c500e4ddSAndrew Thompson 			break;
628c500e4ddSAndrew Thompson 		}
6298c8fff31SAndrew Thompson 	}
6308c8fff31SAndrew Thompson 
6318c8fff31SAndrew Thompson 	libusb_free_device_list(devs, 1);
6321efeb40dSHans Petter Selasky 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_with_vid_pid leave");
633390065b1SAlfred Perlstein 	return (pdev);
6348c8fff31SAndrew Thompson }
6358c8fff31SAndrew Thompson 
6368c8fff31SAndrew Thompson void
637390065b1SAlfred Perlstein libusb_close(struct libusb20_device *pdev)
6388c8fff31SAndrew Thompson {
6398c8fff31SAndrew Thompson 	libusb_context *ctx;
640390065b1SAlfred Perlstein 	struct libusb_device *dev;
6418c8fff31SAndrew Thompson 
642390065b1SAlfred Perlstein 	if (pdev == NULL)
643390065b1SAlfred Perlstein 		return;			/* be NULL safe */
6448c8fff31SAndrew Thompson 
645390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
646390065b1SAlfred Perlstein 	ctx = dev->ctx;
6478c8fff31SAndrew Thompson 
648390065b1SAlfred Perlstein 	libusb10_remove_pollfd(ctx, &dev->dev_poll);
6498c8fff31SAndrew Thompson 
650390065b1SAlfred Perlstein 	libusb20_dev_close(pdev);
651ccef4ddfSAndrew Thompson 
652ccef4ddfSAndrew Thompson 	/* unref will free the "pdev" when the refcount reaches zero */
653390065b1SAlfred Perlstein 	libusb_unref_device(dev);
6548c8fff31SAndrew Thompson 
655390065b1SAlfred Perlstein 	/* make sure our event loop detects the closed device */
656aa87aa52SHans Petter Selasky 	libusb_interrupt_event_handler(ctx);
6578c8fff31SAndrew Thompson }
6588c8fff31SAndrew Thompson 
6598c8fff31SAndrew Thompson libusb_device *
660390065b1SAlfred Perlstein libusb_get_device(struct libusb20_device *pdev)
6618c8fff31SAndrew Thompson {
662390065b1SAlfred Perlstein 	if (pdev == NULL)
6638c8fff31SAndrew Thompson 		return (NULL);
664390065b1SAlfred Perlstein 	return ((libusb_device *)pdev->privLuData);
6658c8fff31SAndrew Thompson }
6668c8fff31SAndrew Thompson 
6678c8fff31SAndrew Thompson int
668390065b1SAlfred Perlstein libusb_get_configuration(struct libusb20_device *pdev, int *config)
6698c8fff31SAndrew Thompson {
670390065b1SAlfred Perlstein 	struct libusb20_config *pconf;
6718c8fff31SAndrew Thompson 
672390065b1SAlfred Perlstein 	if (pdev == NULL || config == NULL)
6738c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
6748c8fff31SAndrew Thompson 
675390065b1SAlfred Perlstein 	pconf = libusb20_dev_alloc_config(pdev, libusb20_dev_get_config_index(pdev));
676390065b1SAlfred Perlstein 	if (pconf == NULL)
677c500e4ddSAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
6788c8fff31SAndrew Thompson 
679390065b1SAlfred Perlstein 	*config = pconf->desc.bConfigurationValue;
6808c8fff31SAndrew Thompson 
681390065b1SAlfred Perlstein 	free(pconf);
6828c8fff31SAndrew Thompson 
6838c8fff31SAndrew Thompson 	return (0);
6848c8fff31SAndrew Thompson }
6858c8fff31SAndrew Thompson 
6868c8fff31SAndrew Thompson int
687390065b1SAlfred Perlstein libusb_set_configuration(struct libusb20_device *pdev, int configuration)
6888c8fff31SAndrew Thompson {
689390065b1SAlfred Perlstein 	struct libusb20_config *pconf;
690390065b1SAlfred Perlstein 	struct libusb_device *dev;
691390065b1SAlfred Perlstein 	int err;
692390065b1SAlfred Perlstein 	uint8_t i;
6938c8fff31SAndrew Thompson 
694390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
6958c8fff31SAndrew Thompson 	if (dev == NULL)
696390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
697390065b1SAlfred Perlstein 
698390065b1SAlfred Perlstein 	if (configuration < 1) {
699390065b1SAlfred Perlstein 		/* unconfigure */
700390065b1SAlfred Perlstein 		i = 255;
701390065b1SAlfred Perlstein 	} else {
702390065b1SAlfred Perlstein 		for (i = 0; i != 255; i++) {
703390065b1SAlfred Perlstein 			uint8_t found;
704390065b1SAlfred Perlstein 
705390065b1SAlfred Perlstein 			pconf = libusb20_dev_alloc_config(pdev, i);
706390065b1SAlfred Perlstein 			if (pconf == NULL)
707390065b1SAlfred Perlstein 				return (LIBUSB_ERROR_INVALID_PARAM);
708390065b1SAlfred Perlstein 			found = (pconf->desc.bConfigurationValue
709390065b1SAlfred Perlstein 			    == configuration);
710390065b1SAlfred Perlstein 			free(pconf);
711390065b1SAlfred Perlstein 
712390065b1SAlfred Perlstein 			if (found)
713390065b1SAlfred Perlstein 				goto set_config;
714390065b1SAlfred Perlstein 		}
715390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
716390065b1SAlfred Perlstein 	}
717390065b1SAlfred Perlstein 
718390065b1SAlfred Perlstein set_config:
719390065b1SAlfred Perlstein 
720390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
721390065b1SAlfred Perlstein 
722390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
723390065b1SAlfred Perlstein 
724390065b1SAlfred Perlstein 	err = libusb20_dev_set_config_index(pdev, i);
725390065b1SAlfred Perlstein 
726390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN |
727390065b1SAlfred Perlstein 	    POLLOUT | POLLRDNORM | POLLWRNORM);
728390065b1SAlfred Perlstein 
729390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_INVALID_PARAM : 0);
730390065b1SAlfred Perlstein }
731390065b1SAlfred Perlstein 
732390065b1SAlfred Perlstein int
733390065b1SAlfred Perlstein libusb_claim_interface(struct libusb20_device *pdev, int interface_number)
734390065b1SAlfred Perlstein {
735390065b1SAlfred Perlstein 	libusb_device *dev;
7365b40d960SHans Petter Selasky 	int err = 0;
737390065b1SAlfred Perlstein 
738390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
739390065b1SAlfred Perlstein 	if (dev == NULL)
740390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
741390065b1SAlfred Perlstein 
742390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
743390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
744390065b1SAlfred Perlstein 
7455b40d960SHans Petter Selasky 	if (pdev->auto_detach != 0) {
7465b40d960SHans Petter Selasky 		err = libusb_detach_kernel_driver(pdev, interface_number);
7475b40d960SHans Petter Selasky 		if (err != 0)
7485b40d960SHans Petter Selasky 			goto done;
7495b40d960SHans Petter Selasky 	}
7505b40d960SHans Petter Selasky 
751390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
752390065b1SAlfred Perlstein 	dev->claimed_interfaces |= (1 << interface_number);
753390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
7545b40d960SHans Petter Selasky done:
7555b40d960SHans Petter Selasky 	return (err);
756390065b1SAlfred Perlstein }
757390065b1SAlfred Perlstein 
758390065b1SAlfred Perlstein int
759390065b1SAlfred Perlstein libusb_release_interface(struct libusb20_device *pdev, int interface_number)
760390065b1SAlfred Perlstein {
761390065b1SAlfred Perlstein 	libusb_device *dev;
762390065b1SAlfred Perlstein 	int err = 0;
763390065b1SAlfred Perlstein 
764390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
765390065b1SAlfred Perlstein 	if (dev == NULL)
766390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
767390065b1SAlfred Perlstein 
768390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
769390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
770390065b1SAlfred Perlstein 
7715b40d960SHans Petter Selasky 	if (pdev->auto_detach != 0) {
7725b40d960SHans Petter Selasky 		err = libusb_attach_kernel_driver(pdev, interface_number);
7735b40d960SHans Petter Selasky 		if (err != 0)
7745b40d960SHans Petter Selasky 			goto done;
7755b40d960SHans Petter Selasky 	}
7765b40d960SHans Petter Selasky 
777390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
778390065b1SAlfred Perlstein 	if (!(dev->claimed_interfaces & (1 << interface_number)))
779390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_NOT_FOUND;
7805b40d960SHans Petter Selasky 	else
781390065b1SAlfred Perlstein 		dev->claimed_interfaces &= ~(1 << interface_number);
782390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
7835b40d960SHans Petter Selasky done:
784390065b1SAlfred Perlstein 	return (err);
785390065b1SAlfred Perlstein }
786390065b1SAlfred Perlstein 
787390065b1SAlfred Perlstein int
788390065b1SAlfred Perlstein libusb_set_interface_alt_setting(struct libusb20_device *pdev,
789390065b1SAlfred Perlstein     int interface_number, int alternate_setting)
790390065b1SAlfred Perlstein {
791390065b1SAlfred Perlstein 	libusb_device *dev;
792390065b1SAlfred Perlstein 	int err = 0;
793390065b1SAlfred Perlstein 
794390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
795390065b1SAlfred Perlstein 	if (dev == NULL)
796390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
797390065b1SAlfred Perlstein 
798390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
799390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
800390065b1SAlfred Perlstein 
801390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
802390065b1SAlfred Perlstein 	if (!(dev->claimed_interfaces & (1 << interface_number)))
803390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_NOT_FOUND;
804390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
805390065b1SAlfred Perlstein 
806390065b1SAlfred Perlstein 	if (err)
807390065b1SAlfred Perlstein 		return (err);
808390065b1SAlfred Perlstein 
809390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
810390065b1SAlfred Perlstein 
811390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
812390065b1SAlfred Perlstein 
813390065b1SAlfred Perlstein 	err = libusb20_dev_set_alt_index(pdev,
814390065b1SAlfred Perlstein 	    interface_number, alternate_setting);
815390065b1SAlfred Perlstein 
816390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll,
817390065b1SAlfred Perlstein 	    pdev, libusb20_dev_get_fd(pdev),
818390065b1SAlfred Perlstein 	    POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
819390065b1SAlfred Perlstein 
820390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_OTHER : 0);
821390065b1SAlfred Perlstein }
822390065b1SAlfred Perlstein 
823390065b1SAlfred Perlstein static struct libusb20_transfer *
824390065b1SAlfred Perlstein libusb10_get_transfer(struct libusb20_device *pdev,
825d81535d1SHans Petter Selasky     uint8_t endpoint, uint8_t xfer_index)
826390065b1SAlfred Perlstein {
827d81535d1SHans Petter Selasky 	xfer_index &= 1;	/* double buffering */
828390065b1SAlfred Perlstein 
829d81535d1SHans Petter Selasky 	xfer_index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4;
830390065b1SAlfred Perlstein 
831390065b1SAlfred Perlstein 	if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) {
832390065b1SAlfred Perlstein 		/* this is an IN endpoint */
833d81535d1SHans Petter Selasky 		xfer_index |= 2;
834390065b1SAlfred Perlstein 	}
835d81535d1SHans Petter Selasky 	return (libusb20_tr_get_pointer(pdev, xfer_index));
836390065b1SAlfred Perlstein }
837390065b1SAlfred Perlstein 
838390065b1SAlfred Perlstein int
839390065b1SAlfred Perlstein libusb_clear_halt(struct libusb20_device *pdev, uint8_t endpoint)
840390065b1SAlfred Perlstein {
841390065b1SAlfred Perlstein 	struct libusb20_transfer *xfer;
842390065b1SAlfred Perlstein 	struct libusb_device *dev;
843390065b1SAlfred Perlstein 	int err;
844390065b1SAlfred Perlstein 
845390065b1SAlfred Perlstein 	xfer = libusb10_get_transfer(pdev, endpoint, 0);
846390065b1SAlfred Perlstein 	if (xfer == NULL)
847390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
848390065b1SAlfred Perlstein 
849390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
850698e791aSHans Petter Selasky 	if (dev == NULL)
851698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
852390065b1SAlfred Perlstein 
853390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
854a7e048a2SHans Petter Selasky 	err = libusb20_tr_open(xfer, 0, 1, endpoint);
855390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
856390065b1SAlfred Perlstein 
857390065b1SAlfred Perlstein 	if (err != 0 && err != LIBUSB20_ERROR_BUSY)
858390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_OTHER);
859390065b1SAlfred Perlstein 
860390065b1SAlfred Perlstein 	libusb20_tr_clear_stall_sync(xfer);
861390065b1SAlfred Perlstein 
862390065b1SAlfred Perlstein 	/* check if we opened the transfer */
863390065b1SAlfred Perlstein 	if (err == 0) {
864390065b1SAlfred Perlstein 		CTX_LOCK(dev->ctx);
865390065b1SAlfred Perlstein 		libusb20_tr_close(xfer);
866390065b1SAlfred Perlstein 		CTX_UNLOCK(dev->ctx);
867390065b1SAlfred Perlstein 	}
868390065b1SAlfred Perlstein 	return (0);			/* success */
869390065b1SAlfred Perlstein }
870390065b1SAlfred Perlstein 
871390065b1SAlfred Perlstein int
872390065b1SAlfred Perlstein libusb_reset_device(struct libusb20_device *pdev)
873390065b1SAlfred Perlstein {
874390065b1SAlfred Perlstein 	libusb_device *dev;
875390065b1SAlfred Perlstein 	int err;
876390065b1SAlfred Perlstein 
877390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
878390065b1SAlfred Perlstein 	if (dev == NULL)
879698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
8808c8fff31SAndrew Thompson 
881390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
882390065b1SAlfred Perlstein 
883390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
884390065b1SAlfred Perlstein 
885390065b1SAlfred Perlstein 	err = libusb20_dev_reset(pdev);
886390065b1SAlfred Perlstein 
887390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll,
888390065b1SAlfred Perlstein 	    pdev, libusb20_dev_get_fd(pdev),
889390065b1SAlfred Perlstein 	    POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
890390065b1SAlfred Perlstein 
891390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_OTHER : 0);
8928c8fff31SAndrew Thompson }
8938c8fff31SAndrew Thompson 
8948c8fff31SAndrew Thompson int
895f1b5fa6eSHans Petter Selasky libusb_check_connected(struct libusb20_device *pdev)
896f1b5fa6eSHans Petter Selasky {
897f1b5fa6eSHans Petter Selasky 	libusb_device *dev;
898f1b5fa6eSHans Petter Selasky 	int err;
899f1b5fa6eSHans Petter Selasky 
900f1b5fa6eSHans Petter Selasky 	dev = libusb_get_device(pdev);
901f1b5fa6eSHans Petter Selasky 	if (dev == NULL)
902f1b5fa6eSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
903f1b5fa6eSHans Petter Selasky 
904f1b5fa6eSHans Petter Selasky 	err = libusb20_dev_check_connected(pdev);
905f1b5fa6eSHans Petter Selasky 
906f1b5fa6eSHans Petter Selasky 	return (err ? LIBUSB_ERROR_NO_DEVICE : 0);
907f1b5fa6eSHans Petter Selasky }
908f1b5fa6eSHans Petter Selasky 
909f1b5fa6eSHans Petter Selasky int
910390065b1SAlfred Perlstein libusb_kernel_driver_active(struct libusb20_device *pdev, int interface)
9118c8fff31SAndrew Thompson {
912390065b1SAlfred Perlstein 	if (pdev == NULL)
9138c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
9148c8fff31SAndrew Thompson 
9154d2472aaSHans Petter Selasky 	if (libusb20_dev_kernel_driver_active(pdev, interface))
9164d2472aaSHans Petter Selasky 		return (0);		/* no kernel driver is active */
9174d2472aaSHans Petter Selasky 	else
9184d2472aaSHans Petter Selasky 		return (1);		/* kernel driver is active */
9198c8fff31SAndrew Thompson }
9208c8fff31SAndrew Thompson 
9218c8fff31SAndrew Thompson int
922698e791aSHans Petter Selasky libusb_get_driver_np(struct libusb20_device *pdev, int interface,
923698e791aSHans Petter Selasky     char *name, int namelen)
924698e791aSHans Petter Selasky {
925698e791aSHans Petter Selasky 	return (libusb_get_driver(pdev, interface, name, namelen));
926698e791aSHans Petter Selasky }
927698e791aSHans Petter Selasky 
928698e791aSHans Petter Selasky int
929698e791aSHans Petter Selasky libusb_get_driver(struct libusb20_device *pdev, int interface,
930698e791aSHans Petter Selasky     char *name, int namelen)
931698e791aSHans Petter Selasky {
932698e791aSHans Petter Selasky 	char *ptr;
933698e791aSHans Petter Selasky 	int err;
934698e791aSHans Petter Selasky 
935698e791aSHans Petter Selasky 	if (pdev == NULL)
936698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
937698e791aSHans Petter Selasky 	if (namelen < 1)
938698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
9394eb5923dSHans Petter Selasky 	if (namelen > 255)
9404eb5923dSHans Petter Selasky 		namelen = 255;
941698e791aSHans Petter Selasky 
942698e791aSHans Petter Selasky 	err = libusb20_dev_get_iface_desc(
943698e791aSHans Petter Selasky 	    pdev, interface, name, namelen);
944698e791aSHans Petter Selasky 
945698e791aSHans Petter Selasky 	if (err != 0)
946698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_OTHER);
947698e791aSHans Petter Selasky 
948698e791aSHans Petter Selasky 	/* we only want the driver name */
949698e791aSHans Petter Selasky 	ptr = strstr(name, ":");
950698e791aSHans Petter Selasky 	if (ptr != NULL)
951698e791aSHans Petter Selasky 		*ptr = 0;
952698e791aSHans Petter Selasky 
953698e791aSHans Petter Selasky 	return (0);
954698e791aSHans Petter Selasky }
955698e791aSHans Petter Selasky 
956698e791aSHans Petter Selasky int
957698e791aSHans Petter Selasky libusb_detach_kernel_driver_np(struct libusb20_device *pdev, int interface)
958698e791aSHans Petter Selasky {
959698e791aSHans Petter Selasky 	return (libusb_detach_kernel_driver(pdev, interface));
960698e791aSHans Petter Selasky }
961698e791aSHans Petter Selasky 
962698e791aSHans Petter Selasky int
963390065b1SAlfred Perlstein libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface)
9648c8fff31SAndrew Thompson {
965390065b1SAlfred Perlstein 	int err;
9668c8fff31SAndrew Thompson 
967390065b1SAlfred Perlstein 	if (pdev == NULL)
9688c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
9698c8fff31SAndrew Thompson 
970390065b1SAlfred Perlstein 	err = libusb20_dev_detach_kernel_driver(
971390065b1SAlfred Perlstein 	    pdev, interface);
9728c8fff31SAndrew Thompson 
973698e791aSHans Petter Selasky 	return (err ? LIBUSB_ERROR_OTHER : 0);
9748c8fff31SAndrew Thompson }
9758c8fff31SAndrew Thompson 
9768c8fff31SAndrew Thompson int
977390065b1SAlfred Perlstein libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface)
9788c8fff31SAndrew Thompson {
979390065b1SAlfred Perlstein 	if (pdev == NULL)
9808c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
981390065b1SAlfred Perlstein 	/* stub - currently not supported by libusb20 */
9828c8fff31SAndrew Thompson 	return (0);
9838c8fff31SAndrew Thompson }
9848c8fff31SAndrew Thompson 
9855b40d960SHans Petter Selasky int
9865b40d960SHans Petter Selasky libusb_set_auto_detach_kernel_driver(libusb_device_handle *dev, int enable)
9875b40d960SHans Petter Selasky {
9885b40d960SHans Petter Selasky 	dev->auto_detach = (enable ? 1 : 0);
989a3d81a8aSHans Petter Selasky 	return (0);
9905b40d960SHans Petter Selasky }
9915b40d960SHans Petter Selasky 
9928c8fff31SAndrew Thompson /* Asynchronous device I/O */
9938c8fff31SAndrew Thompson 
9948c8fff31SAndrew Thompson struct libusb_transfer *
9958c8fff31SAndrew Thompson libusb_alloc_transfer(int iso_packets)
9968c8fff31SAndrew Thompson {
997390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
998390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
9998c8fff31SAndrew Thompson 	int len;
10008c8fff31SAndrew Thompson 
10018c8fff31SAndrew Thompson 	len = sizeof(struct libusb_transfer) +
1002390065b1SAlfred Perlstein 	    sizeof(struct libusb_super_transfer) +
10038c8fff31SAndrew Thompson 	    (iso_packets * sizeof(libusb_iso_packet_descriptor));
10048c8fff31SAndrew Thompson 
1005390065b1SAlfred Perlstein 	sxfer = malloc(len);
1006390065b1SAlfred Perlstein 	if (sxfer == NULL)
10078c8fff31SAndrew Thompson 		return (NULL);
10088c8fff31SAndrew Thompson 
1009390065b1SAlfred Perlstein 	memset(sxfer, 0, len);
10108c8fff31SAndrew Thompson 
1011390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1012390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
10138c8fff31SAndrew Thompson 
1014390065b1SAlfred Perlstein 	/* set default value */
1015390065b1SAlfred Perlstein 	uxfer->num_iso_packets = iso_packets;
1016390065b1SAlfred Perlstein 
1017390065b1SAlfred Perlstein 	return (uxfer);
10188c8fff31SAndrew Thompson }
10198c8fff31SAndrew Thompson 
10208c8fff31SAndrew Thompson void
1021390065b1SAlfred Perlstein libusb_free_transfer(struct libusb_transfer *uxfer)
10228c8fff31SAndrew Thompson {
1023390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
10248c8fff31SAndrew Thompson 
1025390065b1SAlfred Perlstein 	if (uxfer == NULL)
1026390065b1SAlfred Perlstein 		return;			/* be NULL safe */
10278c8fff31SAndrew Thompson 
102831f7072cSHans Petter Selasky 	/* check if we should free the transfer buffer */
102931f7072cSHans Petter Selasky 	if (uxfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
103031f7072cSHans Petter Selasky 		free(uxfer->buffer);
103131f7072cSHans Petter Selasky 
1032390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
1033390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
10348c8fff31SAndrew Thompson 
1035390065b1SAlfred Perlstein 	free(sxfer);
10368c8fff31SAndrew Thompson }
10378c8fff31SAndrew Thompson 
10381c497368SHans Petter Selasky static uint32_t
1039390065b1SAlfred Perlstein libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer)
10408c8fff31SAndrew Thompson {
10411c497368SHans Petter Selasky 	uint32_t ret;
10428c8fff31SAndrew Thompson 
10438c8fff31SAndrew Thompson 	switch (xfer->type) {
10448c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
10451c497368SHans Petter Selasky 		ret = 60 | LIBUSB20_MAX_FRAME_PRE_SCALE;	/* 60ms */
10468c8fff31SAndrew Thompson 		break;
10478c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_CONTROL:
10488c8fff31SAndrew Thompson 		ret = 2;
10498c8fff31SAndrew Thompson 		break;
10508c8fff31SAndrew Thompson 	default:
10518c8fff31SAndrew Thompson 		ret = 1;
10528c8fff31SAndrew Thompson 		break;
10538c8fff31SAndrew Thompson 	}
1054390065b1SAlfred Perlstein 	return (ret);
10558c8fff31SAndrew Thompson }
10568c8fff31SAndrew Thompson 
10578c8fff31SAndrew Thompson static int
1058390065b1SAlfred Perlstein libusb10_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer)
10598c8fff31SAndrew Thompson {
10608c8fff31SAndrew Thompson 	int ret;
10618c8fff31SAndrew Thompson 	int usb_speed;
10628c8fff31SAndrew Thompson 
10638c8fff31SAndrew Thompson 	usb_speed = libusb20_dev_get_speed(pdev);
10648c8fff31SAndrew Thompson 
10658c8fff31SAndrew Thompson 	switch (xfer->type) {
10668c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
1067390065b1SAlfred Perlstein 		ret = 0;		/* kernel will auto-select */
10688c8fff31SAndrew Thompson 		break;
10698c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_CONTROL:
1070390065b1SAlfred Perlstein 		ret = 1024;
10718c8fff31SAndrew Thompson 		break;
10728c8fff31SAndrew Thompson 	default:
10738c8fff31SAndrew Thompson 		switch (usb_speed) {
10748c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_LOW:
10758c8fff31SAndrew Thompson 			ret = 256;
10768c8fff31SAndrew Thompson 			break;
10778c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_FULL:
10788c8fff31SAndrew Thompson 			ret = 4096;
10798c8fff31SAndrew Thompson 			break;
1080f355a4ddSHans Petter Selasky 		case LIBUSB20_SPEED_SUPER:
1081f355a4ddSHans Petter Selasky 			ret = 65536;
1082f355a4ddSHans Petter Selasky 			break;
10838c8fff31SAndrew Thompson 		default:
10848c8fff31SAndrew Thompson 			ret = 16384;
10858c8fff31SAndrew Thompson 			break;
10868c8fff31SAndrew Thompson 		}
10878c8fff31SAndrew Thompson 		break;
10888c8fff31SAndrew Thompson 	}
1089390065b1SAlfred Perlstein 	return (ret);
10908c8fff31SAndrew Thompson }
10918c8fff31SAndrew Thompson 
1092390065b1SAlfred Perlstein static int
1093390065b1SAlfred Perlstein libusb10_convert_error(uint8_t status)
1094390065b1SAlfred Perlstein {
1095390065b1SAlfred Perlstein 	;				/* indent fix */
1096390065b1SAlfred Perlstein 
1097390065b1SAlfred Perlstein 	switch (status) {
1098390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
1099390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
1100390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_COMPLETED);
1101390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_OVERFLOW:
1102390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_OVERFLOW);
1103390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_NO_DEVICE:
1104390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_NO_DEVICE);
1105390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_STALL:
1106390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_STALL);
1107390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_CANCELLED:
1108390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_CANCELLED);
1109390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_TIMED_OUT:
1110390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_TIMED_OUT);
1111390065b1SAlfred Perlstein 	default:
1112390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_ERROR);
1113390065b1SAlfred Perlstein 	}
1114390065b1SAlfred Perlstein }
1115390065b1SAlfred Perlstein 
1116390065b1SAlfred Perlstein /* This function must be called locked */
1117390065b1SAlfred Perlstein 
1118c500e4ddSAndrew Thompson static void
1119390065b1SAlfred Perlstein libusb10_complete_transfer(struct libusb20_transfer *pxfer,
1120390065b1SAlfred Perlstein     struct libusb_super_transfer *sxfer, int status)
1121c500e4ddSAndrew Thompson {
1122390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1123390065b1SAlfred Perlstein 	struct libusb_device *dev;
1124c500e4ddSAndrew Thompson 
1125390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1126390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1127390065b1SAlfred Perlstein 
1128390065b1SAlfred Perlstein 	if (pxfer != NULL)
1129390065b1SAlfred Perlstein 		libusb20_tr_set_priv_sc1(pxfer, NULL);
1130390065b1SAlfred Perlstein 
11314594d907SAndrew Thompson 	/* set transfer status */
1132390065b1SAlfred Perlstein 	uxfer->status = status;
1133390065b1SAlfred Perlstein 
11344594d907SAndrew Thompson 	/* update super transfer state */
11354594d907SAndrew Thompson 	sxfer->state = LIBUSB_SUPER_XFER_ST_NONE;
11364594d907SAndrew Thompson 
1137390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
1138390065b1SAlfred Perlstein 
1139390065b1SAlfred Perlstein 	TAILQ_INSERT_TAIL(&dev->ctx->tr_done, sxfer, entry);
1140390065b1SAlfred Perlstein }
1141390065b1SAlfred Perlstein 
1142390065b1SAlfred Perlstein /* This function must be called locked */
1143390065b1SAlfred Perlstein 
1144390065b1SAlfred Perlstein static void
1145390065b1SAlfred Perlstein libusb10_isoc_proxy(struct libusb20_transfer *pxfer)
1146390065b1SAlfred Perlstein {
1147390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1148390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1149390065b1SAlfred Perlstein 	uint32_t actlen;
1150390065b1SAlfred Perlstein 	uint16_t iso_packets;
1151390065b1SAlfred Perlstein 	uint16_t i;
1152390065b1SAlfred Perlstein 	uint8_t status;
1153390065b1SAlfred Perlstein 
1154390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
1155390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
1156390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
1157390065b1SAlfred Perlstein 	iso_packets = libusb20_tr_get_max_frames(pxfer);
1158390065b1SAlfred Perlstein 
1159390065b1SAlfred Perlstein 	if (sxfer == NULL)
1160390065b1SAlfred Perlstein 		return; /* cancelled - nothing to do */
1161390065b1SAlfred Perlstein 
1162390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1163390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1164390065b1SAlfred Perlstein 
1165390065b1SAlfred Perlstein 	if (iso_packets > uxfer->num_iso_packets)
1166390065b1SAlfred Perlstein 		iso_packets = uxfer->num_iso_packets;
1167390065b1SAlfred Perlstein 
1168390065b1SAlfred Perlstein 	if (iso_packets == 0)
1169390065b1SAlfred Perlstein 		return; /* nothing to do */
1170390065b1SAlfred Perlstein 
1171390065b1SAlfred Perlstein 	/* make sure that the number of ISOCHRONOUS packets is valid */
1172390065b1SAlfred Perlstein 	uxfer->num_iso_packets = iso_packets;
1173390065b1SAlfred Perlstein 
1174c500e4ddSAndrew Thompson 	switch (status) {
1175c500e4ddSAndrew Thompson 	case LIBUSB20_TRANSFER_COMPLETED:
1176390065b1SAlfred Perlstein 		/* update actual length */
1177390065b1SAlfred Perlstein 		uxfer->actual_length = actlen;
1178390065b1SAlfred Perlstein 		for (i = 0; i != iso_packets; i++) {
1179390065b1SAlfred Perlstein 			uxfer->iso_packet_desc[i].actual_length =
1180390065b1SAlfred Perlstein 			    libusb20_tr_get_length(pxfer, i);
1181390065b1SAlfred Perlstein 		}
1182390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1183c500e4ddSAndrew Thompson 		break;
1184c500e4ddSAndrew Thompson 	case LIBUSB20_TRANSFER_START:
1185390065b1SAlfred Perlstein 		/* setup length(s) */
1186390065b1SAlfred Perlstein 		actlen = 0;
1187390065b1SAlfred Perlstein 		for (i = 0; i != iso_packets; i++) {
1188390065b1SAlfred Perlstein 			libusb20_tr_setup_isoc(pxfer,
1189390065b1SAlfred Perlstein 			    &uxfer->buffer[actlen],
1190390065b1SAlfred Perlstein 			    uxfer->iso_packet_desc[i].length, i);
1191390065b1SAlfred Perlstein 			actlen += uxfer->iso_packet_desc[i].length;
1192c500e4ddSAndrew Thompson 		}
1193390065b1SAlfred Perlstein 
1194390065b1SAlfred Perlstein 		/* no remainder */
1195390065b1SAlfred Perlstein 		sxfer->rem_len = 0;
1196390065b1SAlfred Perlstein 
1197390065b1SAlfred Perlstein 		libusb20_tr_set_total_frames(pxfer, iso_packets);
1198390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1199390065b1SAlfred Perlstein 
1200390065b1SAlfred Perlstein 		/* fork another USB transfer, if any */
1201390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1202c500e4ddSAndrew Thompson 		break;
1203390065b1SAlfred Perlstein 	default:
1204390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1205c500e4ddSAndrew Thompson 		break;
1206c500e4ddSAndrew Thompson 	}
1207390065b1SAlfred Perlstein }
1208390065b1SAlfred Perlstein 
1209390065b1SAlfred Perlstein /* This function must be called locked */
1210390065b1SAlfred Perlstein 
1211390065b1SAlfred Perlstein static void
1212390065b1SAlfred Perlstein libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer)
1213390065b1SAlfred Perlstein {
1214390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1215390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1216390065b1SAlfred Perlstein 	uint32_t max_bulk;
1217390065b1SAlfred Perlstein 	uint32_t actlen;
1218390065b1SAlfred Perlstein 	uint8_t status;
1219390065b1SAlfred Perlstein 	uint8_t flags;
1220390065b1SAlfred Perlstein 
1221390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
1222390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
1223390065b1SAlfred Perlstein 	max_bulk = libusb20_tr_get_max_total_length(pxfer);
1224390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
1225390065b1SAlfred Perlstein 
1226390065b1SAlfred Perlstein 	if (sxfer == NULL)
1227390065b1SAlfred Perlstein 		return;			/* cancelled - nothing to do */
1228390065b1SAlfred Perlstein 
1229390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1230390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1231390065b1SAlfred Perlstein 
1232390065b1SAlfred Perlstein 	flags = uxfer->flags;
1233390065b1SAlfred Perlstein 
1234390065b1SAlfred Perlstein 	switch (status) {
1235390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
1236390065b1SAlfred Perlstein 
1237390065b1SAlfred Perlstein 		uxfer->actual_length += actlen;
1238390065b1SAlfred Perlstein 
1239390065b1SAlfred Perlstein 		/* check for short packet */
1240390065b1SAlfred Perlstein 		if (sxfer->last_len != actlen) {
1241390065b1SAlfred Perlstein 			if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
1242390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR);
1243390065b1SAlfred Perlstein 			} else {
1244390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1245390065b1SAlfred Perlstein 			}
1246390065b1SAlfred Perlstein 			break;
1247390065b1SAlfred Perlstein 		}
1248390065b1SAlfred Perlstein 		/* check for end of data */
1249390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0) {
1250390065b1SAlfred Perlstein 			libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1251390065b1SAlfred Perlstein 			break;
1252390065b1SAlfred Perlstein 		}
1253390065b1SAlfred Perlstein 		/* FALLTHROUGH */
1254390065b1SAlfred Perlstein 
1255390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
1256390065b1SAlfred Perlstein 		if (max_bulk > sxfer->rem_len) {
1257390065b1SAlfred Perlstein 			max_bulk = sxfer->rem_len;
1258390065b1SAlfred Perlstein 		}
1259390065b1SAlfred Perlstein 		/* setup new BULK or INTERRUPT transaction */
1260390065b1SAlfred Perlstein 		libusb20_tr_setup_bulk(pxfer,
1261390065b1SAlfred Perlstein 		    sxfer->curr_data, max_bulk, uxfer->timeout);
1262390065b1SAlfred Perlstein 
1263390065b1SAlfred Perlstein 		/* update counters */
1264390065b1SAlfred Perlstein 		sxfer->last_len = max_bulk;
1265390065b1SAlfred Perlstein 		sxfer->curr_data += max_bulk;
1266390065b1SAlfred Perlstein 		sxfer->rem_len -= max_bulk;
1267390065b1SAlfred Perlstein 
1268390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1269390065b1SAlfred Perlstein 
1270390065b1SAlfred Perlstein 		/* check if we can fork another USB transfer */
1271390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0)
1272390065b1SAlfred Perlstein 			libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1273390065b1SAlfred Perlstein 		break;
1274390065b1SAlfred Perlstein 
1275390065b1SAlfred Perlstein 	default:
1276390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1277390065b1SAlfred Perlstein 		break;
1278390065b1SAlfred Perlstein 	}
1279390065b1SAlfred Perlstein }
1280390065b1SAlfred Perlstein 
1281390065b1SAlfred Perlstein /* This function must be called locked */
1282390065b1SAlfred Perlstein 
1283390065b1SAlfred Perlstein static void
1284390065b1SAlfred Perlstein libusb10_ctrl_proxy(struct libusb20_transfer *pxfer)
1285390065b1SAlfred Perlstein {
1286390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1287390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1288390065b1SAlfred Perlstein 	uint32_t max_bulk;
1289390065b1SAlfred Perlstein 	uint32_t actlen;
1290390065b1SAlfred Perlstein 	uint8_t status;
1291390065b1SAlfred Perlstein 	uint8_t flags;
1292390065b1SAlfred Perlstein 
1293390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
1294390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
1295390065b1SAlfred Perlstein 	max_bulk = libusb20_tr_get_max_total_length(pxfer);
1296390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
1297390065b1SAlfred Perlstein 
1298390065b1SAlfred Perlstein 	if (sxfer == NULL)
1299390065b1SAlfred Perlstein 		return;			/* cancelled - nothing to do */
1300390065b1SAlfred Perlstein 
1301390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1302390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1303390065b1SAlfred Perlstein 
1304390065b1SAlfred Perlstein 	flags = uxfer->flags;
1305390065b1SAlfred Perlstein 
1306390065b1SAlfred Perlstein 	switch (status) {
1307390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
1308390065b1SAlfred Perlstein 
1309390065b1SAlfred Perlstein 		uxfer->actual_length += actlen;
1310390065b1SAlfred Perlstein 
1311390065b1SAlfred Perlstein 		/* subtract length of SETUP packet, if any */
1312390065b1SAlfred Perlstein 		actlen -= libusb20_tr_get_length(pxfer, 0);
1313390065b1SAlfred Perlstein 
1314390065b1SAlfred Perlstein 		/* check for short packet */
1315390065b1SAlfred Perlstein 		if (sxfer->last_len != actlen) {
1316390065b1SAlfred Perlstein 			if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
1317390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR);
1318390065b1SAlfred Perlstein 			} else {
1319390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1320390065b1SAlfred Perlstein 			}
1321390065b1SAlfred Perlstein 			break;
1322390065b1SAlfred Perlstein 		}
1323390065b1SAlfred Perlstein 		/* check for end of data */
1324390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0) {
1325390065b1SAlfred Perlstein 			libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1326390065b1SAlfred Perlstein 			break;
1327390065b1SAlfred Perlstein 		}
1328390065b1SAlfred Perlstein 		/* FALLTHROUGH */
1329390065b1SAlfred Perlstein 
1330390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
1331390065b1SAlfred Perlstein 		if (max_bulk > sxfer->rem_len) {
1332390065b1SAlfred Perlstein 			max_bulk = sxfer->rem_len;
1333390065b1SAlfred Perlstein 		}
1334390065b1SAlfred Perlstein 		/* setup new CONTROL transaction */
1335390065b1SAlfred Perlstein 		if (status == LIBUSB20_TRANSFER_COMPLETED) {
1336390065b1SAlfred Perlstein 			/* next fragment - don't send SETUP packet */
1337390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, 0, 0);
1338390065b1SAlfred Perlstein 		} else {
1339390065b1SAlfred Perlstein 			/* first fragment - send SETUP packet */
1340390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, 8, 0);
1341390065b1SAlfred Perlstein 			libusb20_tr_set_buffer(pxfer, uxfer->buffer, 0);
1342390065b1SAlfred Perlstein 		}
1343390065b1SAlfred Perlstein 
1344390065b1SAlfred Perlstein 		if (max_bulk != 0) {
1345390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, max_bulk, 1);
1346390065b1SAlfred Perlstein 			libusb20_tr_set_buffer(pxfer, sxfer->curr_data, 1);
1347390065b1SAlfred Perlstein 			libusb20_tr_set_total_frames(pxfer, 2);
1348390065b1SAlfred Perlstein 		} else {
1349390065b1SAlfred Perlstein 			libusb20_tr_set_total_frames(pxfer, 1);
1350390065b1SAlfred Perlstein 		}
1351390065b1SAlfred Perlstein 
1352390065b1SAlfred Perlstein 		/* update counters */
1353390065b1SAlfred Perlstein 		sxfer->last_len = max_bulk;
1354390065b1SAlfred Perlstein 		sxfer->curr_data += max_bulk;
1355390065b1SAlfred Perlstein 		sxfer->rem_len -= max_bulk;
1356390065b1SAlfred Perlstein 
1357390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1358390065b1SAlfred Perlstein 
1359390065b1SAlfred Perlstein 		/* check if we can fork another USB transfer */
1360390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0)
1361390065b1SAlfred Perlstein 			libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1362390065b1SAlfred Perlstein 		break;
1363390065b1SAlfred Perlstein 
1364390065b1SAlfred Perlstein 	default:
1365390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1366390065b1SAlfred Perlstein 		break;
1367390065b1SAlfred Perlstein 	}
1368390065b1SAlfred Perlstein }
1369390065b1SAlfred Perlstein 
1370390065b1SAlfred Perlstein /* The following function must be called locked */
1371390065b1SAlfred Perlstein 
1372390065b1SAlfred Perlstein static void
1373390065b1SAlfred Perlstein libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint)
1374390065b1SAlfred Perlstein {
1375390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1376390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1377390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1378390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1379390065b1SAlfred Perlstein 	struct libusb_device *dev;
1380390065b1SAlfred Perlstein 	int err;
1381390065b1SAlfred Perlstein 	int buffsize;
1382390065b1SAlfred Perlstein 	int maxframe;
1383390065b1SAlfred Perlstein 	int temp;
1384390065b1SAlfred Perlstein 
1385390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
1386390065b1SAlfred Perlstein 
1387390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(pdev, endpoint, 0);
1388390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(pdev, endpoint, 1);
1389390065b1SAlfred Perlstein 
1390390065b1SAlfred Perlstein 	if (pxfer0 == NULL || pxfer1 == NULL)
1391390065b1SAlfred Perlstein 		return;			/* shouldn't happen */
1392390065b1SAlfred Perlstein 
1393390065b1SAlfred Perlstein 	temp = 0;
1394390065b1SAlfred Perlstein 	if (libusb20_tr_pending(pxfer0))
1395390065b1SAlfred Perlstein 		temp |= 1;
1396390065b1SAlfred Perlstein 	if (libusb20_tr_pending(pxfer1))
1397390065b1SAlfred Perlstein 		temp |= 2;
1398390065b1SAlfred Perlstein 
1399390065b1SAlfred Perlstein 	switch (temp) {
1400390065b1SAlfred Perlstein 	case 3:
1401390065b1SAlfred Perlstein 		/* wait till one of the transfers complete */
1402390065b1SAlfred Perlstein 		return;
1403390065b1SAlfred Perlstein 	case 2:
1404390065b1SAlfred Perlstein 		sxfer = libusb20_tr_get_priv_sc1(pxfer1);
14054594d907SAndrew Thompson 		if (sxfer == NULL)
14064594d907SAndrew Thompson 			return;		/* cancelling */
1407390065b1SAlfred Perlstein 		if (sxfer->rem_len)
1408390065b1SAlfred Perlstein 			return;		/* cannot queue another one */
1409390065b1SAlfred Perlstein 		/* swap transfers */
1410390065b1SAlfred Perlstein 		pxfer1 = pxfer0;
1411390065b1SAlfred Perlstein 		break;
1412390065b1SAlfred Perlstein 	case 1:
1413390065b1SAlfred Perlstein 		sxfer = libusb20_tr_get_priv_sc1(pxfer0);
14144594d907SAndrew Thompson 		if (sxfer == NULL)
14154594d907SAndrew Thompson 			return;		/* cancelling */
1416390065b1SAlfred Perlstein 		if (sxfer->rem_len)
1417390065b1SAlfred Perlstein 			return;		/* cannot queue another one */
1418390065b1SAlfred Perlstein 		/* swap transfers */
1419390065b1SAlfred Perlstein 		pxfer0 = pxfer1;
1420c500e4ddSAndrew Thompson 		break;
1421c500e4ddSAndrew Thompson 	default:
1422c500e4ddSAndrew Thompson 		break;
1423c500e4ddSAndrew Thompson 	}
1424c500e4ddSAndrew Thompson 
1425390065b1SAlfred Perlstein 	/* find next transfer on same endpoint */
1426390065b1SAlfred Perlstein 	TAILQ_FOREACH(sxfer, &dev->tr_head, entry) {
1427390065b1SAlfred Perlstein 
1428390065b1SAlfred Perlstein 		uxfer = (struct libusb_transfer *)(
1429390065b1SAlfred Perlstein 		    ((uint8_t *)sxfer) + sizeof(*sxfer));
1430390065b1SAlfred Perlstein 
1431390065b1SAlfred Perlstein 		if (uxfer->endpoint == endpoint) {
1432390065b1SAlfred Perlstein 			TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
1433390065b1SAlfred Perlstein 			sxfer->entry.tqe_prev = NULL;
1434390065b1SAlfred Perlstein 			goto found;
1435c500e4ddSAndrew Thompson 		}
1436c500e4ddSAndrew Thompson 	}
1437390065b1SAlfred Perlstein 	return;				/* success */
1438390065b1SAlfred Perlstein 
1439390065b1SAlfred Perlstein found:
1440390065b1SAlfred Perlstein 
1441390065b1SAlfred Perlstein 	libusb20_tr_set_priv_sc0(pxfer0, pdev);
1442390065b1SAlfred Perlstein 	libusb20_tr_set_priv_sc1(pxfer0, sxfer);
1443390065b1SAlfred Perlstein 
1444390065b1SAlfred Perlstein 	/* reset super transfer state */
1445390065b1SAlfred Perlstein 	sxfer->rem_len = uxfer->length;
1446390065b1SAlfred Perlstein 	sxfer->curr_data = uxfer->buffer;
1447390065b1SAlfred Perlstein 	uxfer->actual_length = 0;
1448390065b1SAlfred Perlstein 
1449390065b1SAlfred Perlstein 	switch (uxfer->type) {
1450390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
1451390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_isoc_proxy);
1452390065b1SAlfred Perlstein 		break;
1453390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_BULK:
1454390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
1455390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_bulk_intr_proxy);
1456390065b1SAlfred Perlstein 		break;
1457390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_CONTROL:
1458390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_ctrl_proxy);
1459390065b1SAlfred Perlstein 		if (sxfer->rem_len < 8)
1460390065b1SAlfred Perlstein 			goto failure;
1461390065b1SAlfred Perlstein 
1462390065b1SAlfred Perlstein 		/* remove SETUP packet from data */
1463390065b1SAlfred Perlstein 		sxfer->rem_len -= 8;
1464390065b1SAlfred Perlstein 		sxfer->curr_data += 8;
1465390065b1SAlfred Perlstein 		break;
1466390065b1SAlfred Perlstein 	default:
1467390065b1SAlfred Perlstein 		goto failure;
1468390065b1SAlfred Perlstein 	}
1469390065b1SAlfred Perlstein 
1470390065b1SAlfred Perlstein 	buffsize = libusb10_get_buffsize(pdev, uxfer);
1471390065b1SAlfred Perlstein 	maxframe = libusb10_get_maxframe(pdev, uxfer);
1472390065b1SAlfred Perlstein 
1473390065b1SAlfred Perlstein 	/* make sure the transfer is opened */
1474a0c93fa3SHans Petter Selasky 	err = libusb20_tr_open_stream(pxfer0, buffsize, maxframe,
1475a0c93fa3SHans Petter Selasky 	    endpoint, sxfer->stream_id);
1476390065b1SAlfred Perlstein 	if (err && (err != LIBUSB20_ERROR_BUSY)) {
1477390065b1SAlfred Perlstein 		goto failure;
1478390065b1SAlfred Perlstein 	}
1479390065b1SAlfred Perlstein 	libusb20_tr_start(pxfer0);
1480390065b1SAlfred Perlstein 	return;
1481390065b1SAlfred Perlstein 
1482390065b1SAlfred Perlstein failure:
1483390065b1SAlfred Perlstein 	libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_ERROR);
1484390065b1SAlfred Perlstein 	/* make sure our event loop spins the done handler */
1485aa87aa52SHans Petter Selasky 	libusb_interrupt_event_handler(dev->ctx);
1486390065b1SAlfred Perlstein }
1487390065b1SAlfred Perlstein 
1488390065b1SAlfred Perlstein /* The following function must be called unlocked */
1489c500e4ddSAndrew Thompson 
14908c8fff31SAndrew Thompson int
1491390065b1SAlfred Perlstein libusb_submit_transfer(struct libusb_transfer *uxfer)
14928c8fff31SAndrew Thompson {
1493390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1494390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1495390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1496390065b1SAlfred Perlstein 	struct libusb_device *dev;
149785ff9a03SHans Petter Selasky 	uint8_t endpoint;
1498390065b1SAlfred Perlstein 	int err;
14998c8fff31SAndrew Thompson 
1500390065b1SAlfred Perlstein 	if (uxfer == NULL)
1501390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
15028c8fff31SAndrew Thompson 
1503390065b1SAlfred Perlstein 	if (uxfer->dev_handle == NULL)
1504390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
15058c8fff31SAndrew Thompson 
1506390065b1SAlfred Perlstein 	endpoint = uxfer->endpoint;
15078c8fff31SAndrew Thompson 
1508390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
15098c8fff31SAndrew Thompson 
1510390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer enter");
15118c8fff31SAndrew Thompson 
1512390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
1513390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
1514390065b1SAlfred Perlstein 
1515390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
1516390065b1SAlfred Perlstein 
1517390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0);
1518390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1);
1519390065b1SAlfred Perlstein 
1520390065b1SAlfred Perlstein 	if (pxfer0 == NULL || pxfer1 == NULL) {
1521390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_OTHER;
1522390065b1SAlfred Perlstein 	} else if ((sxfer->entry.tqe_prev != NULL) ||
1523390065b1SAlfred Perlstein 	    (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) ||
1524390065b1SAlfred Perlstein 	    (libusb20_tr_get_priv_sc1(pxfer1) == sxfer)) {
1525390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_BUSY;
1526540c7229SHans Petter Selasky 	} else if (dev->device_is_gone != 0) {
1527540c7229SHans Petter Selasky 		err = LIBUSB_ERROR_NO_DEVICE;
1528390065b1SAlfred Perlstein 	} else {
15294594d907SAndrew Thompson 
15304594d907SAndrew Thompson 		/* set pending state */
15314594d907SAndrew Thompson 		sxfer->state = LIBUSB_SUPER_XFER_ST_PEND;
15324594d907SAndrew Thompson 
15334594d907SAndrew Thompson 		/* insert transfer into transfer head list */
1534390065b1SAlfred Perlstein 		TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry);
1535390065b1SAlfred Perlstein 
15364594d907SAndrew Thompson 		/* start work transfers */
1537390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(
1538390065b1SAlfred Perlstein 		    uxfer->dev_handle, endpoint);
1539390065b1SAlfred Perlstein 
1540390065b1SAlfred Perlstein 		err = 0;		/* success */
15418c8fff31SAndrew Thompson 	}
15428c8fff31SAndrew Thompson 
1543390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
1544390065b1SAlfred Perlstein 
1545390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer leave %d", err);
1546390065b1SAlfred Perlstein 
1547390065b1SAlfred Perlstein 	return (err);
15488c8fff31SAndrew Thompson }
15498c8fff31SAndrew Thompson 
1550390065b1SAlfred Perlstein /* Asynchronous transfer cancel */
15518c8fff31SAndrew Thompson 
1552390065b1SAlfred Perlstein int
1553390065b1SAlfred Perlstein libusb_cancel_transfer(struct libusb_transfer *uxfer)
1554390065b1SAlfred Perlstein {
1555390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1556390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1557390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1558390065b1SAlfred Perlstein 	struct libusb_device *dev;
1559540c7229SHans Petter Selasky 	struct libusb_device_handle *devh;
156085ff9a03SHans Petter Selasky 	uint8_t endpoint;
15614594d907SAndrew Thompson 	int retval;
15628c8fff31SAndrew Thompson 
1563390065b1SAlfred Perlstein 	if (uxfer == NULL)
1564390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
15658c8fff31SAndrew Thompson 
15664594d907SAndrew Thompson 	/* check if not initialised */
1567540c7229SHans Petter Selasky 	if ((devh = uxfer->dev_handle) == NULL)
15684594d907SAndrew Thompson 		return (LIBUSB_ERROR_NOT_FOUND);
15698c8fff31SAndrew Thompson 
1570390065b1SAlfred Perlstein 	endpoint = uxfer->endpoint;
15718c8fff31SAndrew Thompson 
1572540c7229SHans Petter Selasky 	dev = libusb_get_device(devh);
15738c8fff31SAndrew Thompson 
1574390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter");
15758c8fff31SAndrew Thompson 
1576390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
1577390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
1578390065b1SAlfred Perlstein 
15794594d907SAndrew Thompson 	retval = 0;
15804594d907SAndrew Thompson 
1581390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
1582390065b1SAlfred Perlstein 
1583540c7229SHans Petter Selasky 	pxfer0 = libusb10_get_transfer(devh, endpoint, 0);
1584540c7229SHans Petter Selasky 	pxfer1 = libusb10_get_transfer(devh, endpoint, 1);
1585390065b1SAlfred Perlstein 
15864594d907SAndrew Thompson 	if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) {
15874594d907SAndrew Thompson 		/* only update the transfer status */
15884594d907SAndrew Thompson 		uxfer->status = LIBUSB_TRANSFER_CANCELLED;
15894594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
15904594d907SAndrew Thompson 	} else if (sxfer->entry.tqe_prev != NULL) {
1591390065b1SAlfred Perlstein 		/* we are lucky - transfer is on a queue */
1592390065b1SAlfred Perlstein 		TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
1593390065b1SAlfred Perlstein 		sxfer->entry.tqe_prev = NULL;
15944594d907SAndrew Thompson 		libusb10_complete_transfer(NULL,
15954594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1596540c7229SHans Petter Selasky 		/* make sure our event loop spins the done handler */
1597aa87aa52SHans Petter Selasky 		libusb_interrupt_event_handler(dev->ctx);
1598390065b1SAlfred Perlstein 	} else if (pxfer0 == NULL || pxfer1 == NULL) {
1599390065b1SAlfred Perlstein 		/* not started */
16004594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
1601390065b1SAlfred Perlstein 	} else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) {
16024594d907SAndrew Thompson 		libusb10_complete_transfer(pxfer0,
16034594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1604540c7229SHans Petter Selasky 		if (dev->device_is_gone != 0) {
1605540c7229SHans Petter Selasky 			/* clear transfer pointer */
1606540c7229SHans Petter Selasky 			libusb20_tr_set_priv_sc1(pxfer0, NULL);
1607540c7229SHans Petter Selasky 			/* make sure our event loop spins the done handler */
1608aa87aa52SHans Petter Selasky 			libusb_interrupt_event_handler(dev->ctx);
1609540c7229SHans Petter Selasky 		} else {
1610390065b1SAlfred Perlstein 			libusb20_tr_stop(pxfer0);
1611390065b1SAlfred Perlstein 			/* make sure the queue doesn't stall */
1612540c7229SHans Petter Selasky 			libusb10_submit_transfer_sub(devh, endpoint);
1613540c7229SHans Petter Selasky 		}
1614390065b1SAlfred Perlstein 	} else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) {
16154594d907SAndrew Thompson 		libusb10_complete_transfer(pxfer1,
16164594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1617540c7229SHans Petter Selasky 		/* check if handle is still active */
1618540c7229SHans Petter Selasky 		if (dev->device_is_gone != 0) {
1619540c7229SHans Petter Selasky 			/* clear transfer pointer */
1620540c7229SHans Petter Selasky 			libusb20_tr_set_priv_sc1(pxfer1, NULL);
1621540c7229SHans Petter Selasky 			/* make sure our event loop spins the done handler */
1622aa87aa52SHans Petter Selasky 			libusb_interrupt_event_handler(dev->ctx);
1623540c7229SHans Petter Selasky 		} else {
1624390065b1SAlfred Perlstein 			libusb20_tr_stop(pxfer1);
1625390065b1SAlfred Perlstein 			/* make sure the queue doesn't stall */
1626540c7229SHans Petter Selasky 			libusb10_submit_transfer_sub(devh, endpoint);
1627540c7229SHans Petter Selasky 		}
1628390065b1SAlfred Perlstein 	} else {
1629390065b1SAlfred Perlstein 		/* not started */
16304594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
1631c500e4ddSAndrew Thompson 	}
16328c8fff31SAndrew Thompson 
1633390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
16348c8fff31SAndrew Thompson 
1635390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave");
16368c8fff31SAndrew Thompson 
16374594d907SAndrew Thompson 	return (retval);
16388c8fff31SAndrew Thompson }
16398c8fff31SAndrew Thompson 
1640390065b1SAlfred Perlstein UNEXPORTED void
1641390065b1SAlfred Perlstein libusb10_cancel_all_transfer(libusb_device *dev)
16428c8fff31SAndrew Thompson {
164390988efdSHans Petter Selasky 	struct libusb20_device *pdev = dev->os_priv;
164490988efdSHans Petter Selasky 	unsigned x;
164590988efdSHans Petter Selasky 
164690988efdSHans Petter Selasky 	for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) {
164790988efdSHans Petter Selasky 		struct libusb20_transfer *xfer;
164890988efdSHans Petter Selasky 
164990988efdSHans Petter Selasky 		xfer = libusb20_tr_get_pointer(pdev, x);
165090988efdSHans Petter Selasky 		if (xfer == NULL)
165190988efdSHans Petter Selasky 			continue;
165290988efdSHans Petter Selasky 		libusb20_tr_close(xfer);
165390988efdSHans Petter Selasky 	}
16548c8fff31SAndrew Thompson }
1655ccef4ddfSAndrew Thompson 
1656540c7229SHans Petter Selasky UNEXPORTED void
1657540c7229SHans Petter Selasky libusb10_cancel_all_transfer_locked(struct libusb20_device *pdev, struct libusb_device *dev)
1658540c7229SHans Petter Selasky {
1659540c7229SHans Petter Selasky 	struct libusb_super_transfer *sxfer;
1660540c7229SHans Petter Selasky 	unsigned x;
1661540c7229SHans Petter Selasky 
1662540c7229SHans Petter Selasky 	for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) {
1663540c7229SHans Petter Selasky 		struct libusb20_transfer *xfer;
1664540c7229SHans Petter Selasky 
1665540c7229SHans Petter Selasky 		xfer = libusb20_tr_get_pointer(pdev, x);
1666540c7229SHans Petter Selasky 		if (xfer == NULL)
1667540c7229SHans Petter Selasky 			continue;
1668540c7229SHans Petter Selasky 		if (libusb20_tr_pending(xfer) == 0)
1669540c7229SHans Petter Selasky 			continue;
1670540c7229SHans Petter Selasky 		sxfer = libusb20_tr_get_priv_sc1(xfer);
1671540c7229SHans Petter Selasky 		if (sxfer == NULL)
1672540c7229SHans Petter Selasky 			continue;
1673540c7229SHans Petter Selasky 		/* complete pending transfer */
1674540c7229SHans Petter Selasky 		libusb10_complete_transfer(xfer, sxfer, LIBUSB_TRANSFER_ERROR);
1675540c7229SHans Petter Selasky 	}
1676540c7229SHans Petter Selasky 
1677540c7229SHans Petter Selasky 	while ((sxfer = TAILQ_FIRST(&dev->tr_head))) {
1678540c7229SHans Petter Selasky 		TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
1679540c7229SHans Petter Selasky 
1680540c7229SHans Petter Selasky 		/* complete pending transfer */
1681540c7229SHans Petter Selasky 		libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_ERROR);
1682540c7229SHans Petter Selasky 	}
1683540c7229SHans Petter Selasky }
1684540c7229SHans Petter Selasky 
1685ccef4ddfSAndrew Thompson uint16_t
1686ccef4ddfSAndrew Thompson libusb_cpu_to_le16(uint16_t x)
1687ccef4ddfSAndrew Thompson {
1688ccef4ddfSAndrew Thompson 	return (htole16(x));
1689ccef4ddfSAndrew Thompson }
1690ccef4ddfSAndrew Thompson 
1691ccef4ddfSAndrew Thompson uint16_t
1692ccef4ddfSAndrew Thompson libusb_le16_to_cpu(uint16_t x)
1693ccef4ddfSAndrew Thompson {
1694ccef4ddfSAndrew Thompson 	return (le16toh(x));
1695ccef4ddfSAndrew Thompson }
1696ccef4ddfSAndrew Thompson 
1697698e791aSHans Petter Selasky const char *
1698698e791aSHans Petter Selasky libusb_strerror(int code)
1699698e791aSHans Petter Selasky {
1700c61f2561SHans Petter Selasky 	switch (code) {
1701c61f2561SHans Petter Selasky 	case LIBUSB_SUCCESS:
1702c61f2561SHans Petter Selasky 		return ("Success");
1703c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_IO:
1704c61f2561SHans Petter Selasky 		return ("I/O error");
1705c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INVALID_PARAM:
1706c61f2561SHans Petter Selasky 		return ("Invalid parameter");
1707c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_ACCESS:
1708c61f2561SHans Petter Selasky 		return ("Permissions error");
1709c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_DEVICE:
1710c61f2561SHans Petter Selasky 		return ("No device");
1711c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_FOUND:
1712c61f2561SHans Petter Selasky 		return ("Not found");
1713c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_BUSY:
1714c61f2561SHans Petter Selasky 		return ("Device busy");
1715c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_TIMEOUT:
1716c61f2561SHans Petter Selasky 		return ("Timeout");
1717c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OVERFLOW:
1718c61f2561SHans Petter Selasky 		return ("Overflow");
1719c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_PIPE:
1720c61f2561SHans Petter Selasky 		return ("Pipe error");
1721c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INTERRUPTED:
1722c61f2561SHans Petter Selasky 		return ("Interrupted");
1723c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_MEM:
1724c61f2561SHans Petter Selasky 		return ("Out of memory");
1725c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_SUPPORTED:
1726c61f2561SHans Petter Selasky 		return ("Not supported");
1727c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OTHER:
1728c61f2561SHans Petter Selasky 		return ("Other error");
1729c61f2561SHans Petter Selasky 	default:
1730698e791aSHans Petter Selasky 		return ("Unknown error");
1731698e791aSHans Petter Selasky 	}
1732c61f2561SHans Petter Selasky }
1733c61f2561SHans Petter Selasky 
1734c61f2561SHans Petter Selasky const char *
1735c61f2561SHans Petter Selasky libusb_error_name(int code)
1736c61f2561SHans Petter Selasky {
1737c61f2561SHans Petter Selasky 	switch (code) {
1738c61f2561SHans Petter Selasky 	case LIBUSB_SUCCESS:
1739c61f2561SHans Petter Selasky 		return ("LIBUSB_SUCCESS");
1740c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_IO:
1741c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_IO");
1742c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INVALID_PARAM:
1743c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_INVALID_PARAM");
1744c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_ACCESS:
1745c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_ACCESS");
1746c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_DEVICE:
1747c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NO_DEVICE");
1748c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_FOUND:
1749c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NOT_FOUND");
1750c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_BUSY:
1751c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_BUSY");
1752c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_TIMEOUT:
1753c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_TIMEOUT");
1754c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OVERFLOW:
1755c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_OVERFLOW");
1756c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_PIPE:
1757c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_PIPE");
1758c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INTERRUPTED:
1759c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_INTERRUPTED");
1760c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_MEM:
1761c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NO_MEM");
1762c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_SUPPORTED:
1763c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NOT_SUPPORTED");
1764c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OTHER:
1765c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_OTHER");
1766c61f2561SHans Petter Selasky 	default:
1767c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_UNKNOWN");
1768c61f2561SHans Petter Selasky 	}
1769c61f2561SHans Petter Selasky }
177004391da3SKyle Evans 
177104391da3SKyle Evans int
177204391da3SKyle Evans libusb_has_capability(uint32_t capability)
177304391da3SKyle Evans {
177404391da3SKyle Evans 
177504391da3SKyle Evans 	switch (capability) {
177604391da3SKyle Evans 	case LIBUSB_CAP_HAS_CAPABILITY:
177704391da3SKyle Evans 	case LIBUSB_CAP_HAS_HOTPLUG:
177804391da3SKyle Evans 	case LIBUSB_CAP_HAS_HID_ACCESS:
177904391da3SKyle Evans 	case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER:
178004391da3SKyle Evans 		return (1);
178104391da3SKyle Evans 	default:
178204391da3SKyle Evans 		return (0);
178304391da3SKyle Evans 	}
178404391da3SKyle Evans }
1785