xref: /freebsd/lib/libusb/libusb10.c (revision 5e53a4f90f82c4345f277dd87cc9292f26e04a29)
18c8fff31SAndrew Thompson /* $FreeBSD$ */
28c8fff31SAndrew Thompson /*-
3*5e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4*5e53a4f9SPedro F. Giffuni  *
58c8fff31SAndrew Thompson  * Copyright (c) 2009 Sylvestre Gallon. All rights reserved.
6390065b1SAlfred Perlstein  * Copyright (c) 2009 Hans Petter Selasky. All rights reserved.
78c8fff31SAndrew Thompson  *
88c8fff31SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
98c8fff31SAndrew Thompson  * modification, are permitted provided that the following conditions
108c8fff31SAndrew Thompson  * are met:
118c8fff31SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
128c8fff31SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
138c8fff31SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
148c8fff31SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
158c8fff31SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
168c8fff31SAndrew Thompson  *
178c8fff31SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
188c8fff31SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
198c8fff31SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
208c8fff31SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
218c8fff31SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
228c8fff31SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
238c8fff31SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
248c8fff31SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
258c8fff31SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
268c8fff31SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
278c8fff31SAndrew Thompson  * SUCH DAMAGE.
288c8fff31SAndrew Thompson  */
298c8fff31SAndrew Thompson 
3066194130SHans Petter Selasky #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
3166194130SHans Petter Selasky #include LIBUSB_GLOBAL_INCLUDE_FILE
3266194130SHans Petter Selasky #else
33ac840bfcSWojciech A. Koszek #include <assert.h>
34f3cba95cSWojciech A. Koszek #include <errno.h>
358c8fff31SAndrew Thompson #include <poll.h>
368c8fff31SAndrew Thompson #include <pthread.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);
948c8fff31SAndrew Thompson 	if (ctx)
958c8fff31SAndrew Thompson 		ctx->debug = level;
968c8fff31SAndrew Thompson }
978c8fff31SAndrew Thompson 
98698e791aSHans Petter Selasky static void
99698e791aSHans Petter Selasky libusb_set_nonblocking(int f)
100698e791aSHans Petter Selasky {
101698e791aSHans Petter Selasky 	int flags;
102698e791aSHans Petter Selasky 
103698e791aSHans Petter Selasky 	/*
104698e791aSHans Petter Selasky 	 * We ignore any failures in this function, hence the
105698e791aSHans Petter Selasky 	 * non-blocking flag is not critical to the operation of
106698e791aSHans Petter Selasky 	 * libUSB. We use F_GETFL and F_SETFL to be compatible with
107698e791aSHans Petter Selasky 	 * Linux.
108698e791aSHans Petter Selasky 	 */
109698e791aSHans Petter Selasky 
110698e791aSHans Petter Selasky 	flags = fcntl(f, F_GETFL, NULL);
111698e791aSHans Petter Selasky 	if (flags == -1)
112698e791aSHans Petter Selasky 		return;
113698e791aSHans Petter Selasky 	flags |= O_NONBLOCK;
114698e791aSHans Petter Selasky 	fcntl(f, F_SETFL, flags);
115698e791aSHans Petter Selasky }
116698e791aSHans Petter Selasky 
1178c8fff31SAndrew Thompson int
1188c8fff31SAndrew Thompson libusb_init(libusb_context **context)
1198c8fff31SAndrew Thompson {
1208c8fff31SAndrew Thompson 	struct libusb_context *ctx;
121f7287225SHans Petter Selasky 	pthread_condattr_t attr;
1228c8fff31SAndrew Thompson 	char *debug;
1238c8fff31SAndrew Thompson 	int ret;
1248c8fff31SAndrew Thompson 
1258c8fff31SAndrew Thompson 	ctx = malloc(sizeof(*ctx));
1268c8fff31SAndrew Thompson 	if (!ctx)
1278c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
1288c8fff31SAndrew Thompson 
1298c8fff31SAndrew Thompson 	memset(ctx, 0, sizeof(*ctx));
1308c8fff31SAndrew Thompson 
1318c8fff31SAndrew Thompson 	debug = getenv("LIBUSB_DEBUG");
1328c8fff31SAndrew Thompson 	if (debug != NULL) {
1338c8fff31SAndrew Thompson 		ctx->debug = atoi(debug);
1348c8fff31SAndrew Thompson 		if (ctx->debug != 0)
1358c8fff31SAndrew Thompson 			ctx->debug_fixed = 1;
1368c8fff31SAndrew Thompson 	}
137c500e4ddSAndrew Thompson 	TAILQ_INIT(&ctx->pollfds);
138390065b1SAlfred Perlstein 	TAILQ_INIT(&ctx->tr_done);
1397bdc064bSHans Petter Selasky 	TAILQ_INIT(&ctx->hotplug_cbh);
1407bdc064bSHans Petter Selasky 	TAILQ_INIT(&ctx->hotplug_devs);
141390065b1SAlfred Perlstein 
142f7287225SHans Petter Selasky 	if (pthread_mutex_init(&ctx->ctx_lock, NULL) != 0) {
143f7287225SHans Petter Selasky 		free(ctx);
144f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
145f7287225SHans Petter Selasky 	}
1467bdc064bSHans Petter Selasky 	if (pthread_mutex_init(&ctx->hotplug_lock, NULL) != 0) {
1477bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
1487bdc064bSHans Petter Selasky 		free(ctx);
1497bdc064bSHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
1507bdc064bSHans Petter Selasky 	}
151f7287225SHans Petter Selasky 	if (pthread_condattr_init(&attr) != 0) {
152f7287225SHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
1537bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
154f7287225SHans Petter Selasky 		free(ctx);
155f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
156f7287225SHans Petter Selasky 	}
157f7287225SHans Petter Selasky 	if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) != 0) {
158f7287225SHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
1597bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
160f7287225SHans Petter Selasky 		pthread_condattr_destroy(&attr);
161f7287225SHans Petter Selasky 		free(ctx);
162f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_OTHER);
163f7287225SHans Petter Selasky 	}
164f7287225SHans Petter Selasky 	if (pthread_cond_init(&ctx->ctx_cond, &attr) != 0) {
165f7287225SHans Petter Selasky 		pthread_mutex_destroy(&ctx->ctx_lock);
1667bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
167f7287225SHans Petter Selasky 		pthread_condattr_destroy(&attr);
168f7287225SHans Petter Selasky 		free(ctx);
169f7287225SHans Petter Selasky 		return (LIBUSB_ERROR_NO_MEM);
170f7287225SHans Petter Selasky 	}
171f7287225SHans Petter Selasky 	pthread_condattr_destroy(&attr);
172390065b1SAlfred Perlstein 
173390065b1SAlfred Perlstein 	ctx->ctx_handler = NO_THREAD;
1747bdc064bSHans Petter Selasky 	ctx->hotplug_handler = NO_THREAD;
1758c8fff31SAndrew Thompson 
1768c8fff31SAndrew Thompson 	ret = pipe(ctx->ctrl_pipe);
1778c8fff31SAndrew Thompson 	if (ret < 0) {
178390065b1SAlfred Perlstein 		pthread_mutex_destroy(&ctx->ctx_lock);
1797bdc064bSHans Petter Selasky 		pthread_mutex_destroy(&ctx->hotplug_lock);
180390065b1SAlfred Perlstein 		pthread_cond_destroy(&ctx->ctx_cond);
1818c8fff31SAndrew Thompson 		free(ctx);
1828c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_OTHER);
1838c8fff31SAndrew Thompson 	}
184390065b1SAlfred Perlstein 	/* set non-blocking mode on the control pipe to avoid deadlock */
185698e791aSHans Petter Selasky 	libusb_set_nonblocking(ctx->ctrl_pipe[0]);
186698e791aSHans Petter Selasky 	libusb_set_nonblocking(ctx->ctrl_pipe[1]);
1878c8fff31SAndrew Thompson 
188390065b1SAlfred Perlstein 	libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->ctrl_pipe[0], POLLIN);
1898c8fff31SAndrew Thompson 
1908c8fff31SAndrew Thompson 	pthread_mutex_lock(&default_context_lock);
1918c8fff31SAndrew Thompson 	if (usbi_default_context == NULL) {
1928c8fff31SAndrew Thompson 		usbi_default_context = ctx;
1938c8fff31SAndrew Thompson 	}
1948c8fff31SAndrew Thompson 	pthread_mutex_unlock(&default_context_lock);
1958c8fff31SAndrew Thompson 
1968c8fff31SAndrew Thompson 	if (context)
1978c8fff31SAndrew Thompson 		*context = ctx;
1988c8fff31SAndrew Thompson 
199390065b1SAlfred Perlstein 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_init complete");
200390065b1SAlfred Perlstein 
2018c8fff31SAndrew Thompson 	return (0);
2028c8fff31SAndrew Thompson }
2038c8fff31SAndrew Thompson 
2048c8fff31SAndrew Thompson void
2058c8fff31SAndrew Thompson libusb_exit(libusb_context *ctx)
2068c8fff31SAndrew Thompson {
207390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
2088c8fff31SAndrew Thompson 
209390065b1SAlfred Perlstein 	if (ctx == NULL)
210390065b1SAlfred Perlstein 		return;
211390065b1SAlfred Perlstein 
2127bdc064bSHans Petter Selasky 	/* stop hotplug thread, if any */
2137bdc064bSHans Petter Selasky 
2147bdc064bSHans Petter Selasky 	if (ctx->hotplug_handler != NO_THREAD) {
2157bdc064bSHans Petter Selasky 		pthread_t td;
2167bdc064bSHans Petter Selasky 		void *ptr;
2177bdc064bSHans Petter Selasky 
2187bdc064bSHans Petter Selasky 		HOTPLUG_LOCK(ctx);
2197bdc064bSHans Petter Selasky 		td = ctx->hotplug_handler;
2207bdc064bSHans Petter Selasky 		ctx->hotplug_handler = NO_THREAD;
2217bdc064bSHans Petter Selasky 		HOTPLUG_UNLOCK(ctx);
2227bdc064bSHans Petter Selasky 
2237bdc064bSHans Petter Selasky 		pthread_join(td, &ptr);
2247bdc064bSHans Petter Selasky 	}
2257bdc064bSHans Petter Selasky 
226390065b1SAlfred Perlstein 	/* XXX cleanup devices */
227390065b1SAlfred Perlstein 
228390065b1SAlfred Perlstein 	libusb10_remove_pollfd(ctx, &ctx->ctx_poll);
2298c8fff31SAndrew Thompson 	close(ctx->ctrl_pipe[0]);
2308c8fff31SAndrew Thompson 	close(ctx->ctrl_pipe[1]);
231390065b1SAlfred Perlstein 	pthread_mutex_destroy(&ctx->ctx_lock);
2327bdc064bSHans Petter Selasky 	pthread_mutex_destroy(&ctx->hotplug_lock);
233390065b1SAlfred Perlstein 	pthread_cond_destroy(&ctx->ctx_cond);
2348c8fff31SAndrew Thompson 
2358c8fff31SAndrew Thompson 	pthread_mutex_lock(&default_context_lock);
2368c8fff31SAndrew Thompson 	if (ctx == usbi_default_context) {
2378c8fff31SAndrew Thompson 		usbi_default_context = NULL;
2388c8fff31SAndrew Thompson 	}
2398c8fff31SAndrew Thompson 	pthread_mutex_unlock(&default_context_lock);
2408c8fff31SAndrew Thompson 
2418c8fff31SAndrew Thompson 	free(ctx);
2428c8fff31SAndrew Thompson }
2438c8fff31SAndrew Thompson 
2448c8fff31SAndrew Thompson /* Device handling and initialisation. */
2458c8fff31SAndrew Thompson 
2468c8fff31SAndrew Thompson ssize_t
2478c8fff31SAndrew Thompson libusb_get_device_list(libusb_context *ctx, libusb_device ***list)
2488c8fff31SAndrew Thompson {
2498c8fff31SAndrew Thompson 	struct libusb20_backend *usb_backend;
250390065b1SAlfred Perlstein 	struct libusb20_device *pdev;
251390065b1SAlfred Perlstein 	struct libusb_device *dev;
2528c8fff31SAndrew Thompson 	int i;
2538c8fff31SAndrew Thompson 
254390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
255390065b1SAlfred Perlstein 
256390065b1SAlfred Perlstein 	if (ctx == NULL)
257390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
258390065b1SAlfred Perlstein 
259390065b1SAlfred Perlstein 	if (list == NULL)
260390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
2618c8fff31SAndrew Thompson 
2628c8fff31SAndrew Thompson 	usb_backend = libusb20_be_alloc_default();
2638c8fff31SAndrew Thompson 	if (usb_backend == NULL)
264390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_NO_MEM);
2658c8fff31SAndrew Thompson 
266390065b1SAlfred Perlstein 	/* figure out how many USB devices are present */
2678c8fff31SAndrew Thompson 	pdev = NULL;
2688c8fff31SAndrew Thompson 	i = 0;
2698c8fff31SAndrew Thompson 	while ((pdev = libusb20_be_device_foreach(usb_backend, pdev)))
2708c8fff31SAndrew Thompson 		i++;
2718c8fff31SAndrew Thompson 
272390065b1SAlfred Perlstein 	/* allocate device pointer list */
2738c8fff31SAndrew Thompson 	*list = malloc((i + 1) * sizeof(void *));
2748c8fff31SAndrew Thompson 	if (*list == NULL) {
2758c8fff31SAndrew Thompson 		libusb20_be_free(usb_backend);
2768c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
2778c8fff31SAndrew Thompson 	}
278390065b1SAlfred Perlstein 	/* create libusb v1.0 compliant devices */
2798c8fff31SAndrew Thompson 	i = 0;
2808c8fff31SAndrew Thompson 	while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) {
2818c8fff31SAndrew Thompson 
2828c8fff31SAndrew Thompson 		dev = malloc(sizeof(*dev));
2838c8fff31SAndrew Thompson 		if (dev == NULL) {
284c500e4ddSAndrew Thompson 			while (i != 0) {
285c500e4ddSAndrew Thompson 				libusb_unref_device((*list)[i - 1]);
286c500e4ddSAndrew Thompson 				i--;
287c500e4ddSAndrew Thompson 			}
2888c8fff31SAndrew Thompson 			free(*list);
289390065b1SAlfred Perlstein 			*list = NULL;
2908c8fff31SAndrew Thompson 			libusb20_be_free(usb_backend);
2918c8fff31SAndrew Thompson 			return (LIBUSB_ERROR_NO_MEM);
2928c8fff31SAndrew Thompson 		}
293ccef4ddfSAndrew Thompson 		/* get device into libUSB v1.0 list */
294ccef4ddfSAndrew Thompson 		libusb20_be_dequeue_device(usb_backend, pdev);
295ccef4ddfSAndrew Thompson 
2968c8fff31SAndrew Thompson 		memset(dev, 0, sizeof(*dev));
2978c8fff31SAndrew Thompson 
298390065b1SAlfred Perlstein 		/* init transfer queues */
299390065b1SAlfred Perlstein 		TAILQ_INIT(&dev->tr_head);
300390065b1SAlfred Perlstein 
301390065b1SAlfred Perlstein 		/* set context we belong to */
3028c8fff31SAndrew Thompson 		dev->ctx = ctx;
3038c8fff31SAndrew Thompson 
3048c8fff31SAndrew Thompson 		/* link together the two structures */
3058c8fff31SAndrew Thompson 		dev->os_priv = pdev;
306390065b1SAlfred Perlstein 		pdev->privLuData = dev;
3078c8fff31SAndrew Thompson 
3088c8fff31SAndrew Thompson 		(*list)[i] = libusb_ref_device(dev);
3098c8fff31SAndrew Thompson 		i++;
3108c8fff31SAndrew Thompson 	}
3118c8fff31SAndrew Thompson 	(*list)[i] = NULL;
3128c8fff31SAndrew Thompson 
3138c8fff31SAndrew Thompson 	libusb20_be_free(usb_backend);
3148c8fff31SAndrew Thompson 	return (i);
3158c8fff31SAndrew Thompson }
3168c8fff31SAndrew Thompson 
3178c8fff31SAndrew Thompson void
3188c8fff31SAndrew Thompson libusb_free_device_list(libusb_device **list, int unref_devices)
3198c8fff31SAndrew Thompson {
3208c8fff31SAndrew Thompson 	int i;
3218c8fff31SAndrew Thompson 
3228c8fff31SAndrew Thompson 	if (list == NULL)
323390065b1SAlfred Perlstein 		return;			/* be NULL safe */
3248c8fff31SAndrew Thompson 
3258c8fff31SAndrew Thompson 	if (unref_devices) {
3268c8fff31SAndrew Thompson 		for (i = 0; list[i] != NULL; i++)
3278c8fff31SAndrew Thompson 			libusb_unref_device(list[i]);
3288c8fff31SAndrew Thompson 	}
3298c8fff31SAndrew Thompson 	free(list);
3308c8fff31SAndrew Thompson }
3318c8fff31SAndrew Thompson 
3328c8fff31SAndrew Thompson uint8_t
3338c8fff31SAndrew Thompson libusb_get_bus_number(libusb_device *dev)
3348c8fff31SAndrew Thompson {
3358c8fff31SAndrew Thompson 	if (dev == NULL)
336390065b1SAlfred Perlstein 		return (0);		/* should not happen */
337390065b1SAlfred Perlstein 	return (libusb20_dev_get_bus_number(dev->os_priv));
3388c8fff31SAndrew Thompson }
3398c8fff31SAndrew Thompson 
3400f2c7066SHans Petter Selasky uint8_t
3410f2c7066SHans Petter Selasky libusb_get_port_number(libusb_device *dev)
3420f2c7066SHans Petter Selasky {
3430f2c7066SHans Petter Selasky 	if (dev == NULL)
3440f2c7066SHans Petter Selasky 		return (0);		/* should not happen */
3450f2c7066SHans Petter Selasky 	return (libusb20_dev_get_parent_port(dev->os_priv));
3460f2c7066SHans Petter Selasky }
3470f2c7066SHans Petter Selasky 
3485906bf49SEd Maste int
349a9205626SEd Maste libusb_get_port_numbers(libusb_device *dev, uint8_t *buf, uint8_t bufsize)
350a9205626SEd Maste {
351a9205626SEd Maste 	return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize));
352a9205626SEd Maste }
353a9205626SEd Maste 
354a9205626SEd Maste int
3555906bf49SEd Maste libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *buf,
3565906bf49SEd Maste     uint8_t bufsize)
3575906bf49SEd Maste {
3585906bf49SEd Maste 	return (libusb20_dev_get_port_path(dev->os_priv, buf, bufsize));
3595906bf49SEd Maste }
3605906bf49SEd Maste 
3618c8fff31SAndrew Thompson uint8_t
3628c8fff31SAndrew Thompson libusb_get_device_address(libusb_device *dev)
3638c8fff31SAndrew Thompson {
3648c8fff31SAndrew Thompson 	if (dev == NULL)
365390065b1SAlfred Perlstein 		return (0);		/* should not happen */
366390065b1SAlfred Perlstein 	return (libusb20_dev_get_address(dev->os_priv));
3678c8fff31SAndrew Thompson }
3688c8fff31SAndrew Thompson 
3699a46d467SHans Petter Selasky enum libusb_speed
3709a46d467SHans Petter Selasky libusb_get_device_speed(libusb_device *dev)
3719a46d467SHans Petter Selasky {
3729a46d467SHans Petter Selasky 	if (dev == NULL)
37333ec9f0cSHans Petter Selasky 		return (LIBUSB_SPEED_UNKNOWN);	/* should not happen */
3749a46d467SHans Petter Selasky 
3759a46d467SHans Petter Selasky 	switch (libusb20_dev_get_speed(dev->os_priv)) {
3769a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_LOW:
3779a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_LOW);
3789a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_FULL:
3799a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_FULL);
3809a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_HIGH:
3819a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_HIGH);
3829a46d467SHans Petter Selasky 	case LIBUSB20_SPEED_SUPER:
3839a46d467SHans Petter Selasky 		return (LIBUSB_SPEED_SUPER);
3849a46d467SHans Petter Selasky 	default:
3859a46d467SHans Petter Selasky 		break;
3869a46d467SHans Petter Selasky 	}
3879a46d467SHans Petter Selasky 	return (LIBUSB_SPEED_UNKNOWN);
3889a46d467SHans Petter Selasky }
3899a46d467SHans Petter Selasky 
3908c8fff31SAndrew Thompson int
391390065b1SAlfred Perlstein libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint)
3928c8fff31SAndrew Thompson {
3938c8fff31SAndrew Thompson 	struct libusb_config_descriptor *pdconf;
3948c8fff31SAndrew Thompson 	struct libusb_interface *pinf;
3958c8fff31SAndrew Thompson 	struct libusb_interface_descriptor *pdinf;
3968c8fff31SAndrew Thompson 	struct libusb_endpoint_descriptor *pdend;
397390065b1SAlfred Perlstein 	int i;
398390065b1SAlfred Perlstein 	int j;
399390065b1SAlfred Perlstein 	int k;
400390065b1SAlfred Perlstein 	int ret;
4018c8fff31SAndrew Thompson 
4028c8fff31SAndrew Thompson 	if (dev == NULL)
4038c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_DEVICE);
4048c8fff31SAndrew Thompson 
405390065b1SAlfred Perlstein 	ret = libusb_get_active_config_descriptor(dev, &pdconf);
406390065b1SAlfred Perlstein 	if (ret < 0)
407390065b1SAlfred Perlstein 		return (ret);
4088c8fff31SAndrew Thompson 
4098c8fff31SAndrew Thompson 	ret = LIBUSB_ERROR_NOT_FOUND;
4108c8fff31SAndrew Thompson 	for (i = 0; i < pdconf->bNumInterfaces; i++) {
4118c8fff31SAndrew Thompson 		pinf = &pdconf->interface[i];
4128c8fff31SAndrew Thompson 		for (j = 0; j < pinf->num_altsetting; j++) {
4138c8fff31SAndrew Thompson 			pdinf = &pinf->altsetting[j];
4148c8fff31SAndrew Thompson 			for (k = 0; k < pdinf->bNumEndpoints; k++) {
4158c8fff31SAndrew Thompson 				pdend = &pdinf->endpoint[k];
4168c8fff31SAndrew Thompson 				if (pdend->bEndpointAddress == endpoint) {
4178c8fff31SAndrew Thompson 					ret = pdend->wMaxPacketSize;
4188c8fff31SAndrew Thompson 					goto out;
4198c8fff31SAndrew Thompson 				}
4208c8fff31SAndrew Thompson 			}
4218c8fff31SAndrew Thompson 		}
4228c8fff31SAndrew Thompson 	}
4238c8fff31SAndrew Thompson 
4248c8fff31SAndrew Thompson out:
4258c8fff31SAndrew Thompson 	libusb_free_config_descriptor(pdconf);
4268c8fff31SAndrew Thompson 	return (ret);
4278c8fff31SAndrew Thompson }
4288c8fff31SAndrew Thompson 
429748205a3SHans Petter Selasky int
430748205a3SHans Petter Selasky libusb_get_max_iso_packet_size(libusb_device *dev, uint8_t endpoint)
431748205a3SHans Petter Selasky {
432748205a3SHans Petter Selasky 	int multiplier;
433748205a3SHans Petter Selasky 	int ret;
434748205a3SHans Petter Selasky 
435748205a3SHans Petter Selasky 	ret = libusb_get_max_packet_size(dev, endpoint);
436748205a3SHans Petter Selasky 
437748205a3SHans Petter Selasky 	switch (libusb20_dev_get_speed(dev->os_priv)) {
438748205a3SHans Petter Selasky 	case LIBUSB20_SPEED_LOW:
439748205a3SHans Petter Selasky 	case LIBUSB20_SPEED_FULL:
440748205a3SHans Petter Selasky 		break;
441748205a3SHans Petter Selasky 	default:
442748205a3SHans Petter Selasky 		if (ret > -1) {
443748205a3SHans Petter Selasky 			multiplier = (1 + ((ret >> 11) & 3));
444748205a3SHans Petter Selasky 			if (multiplier > 3)
445748205a3SHans Petter Selasky 				multiplier = 3;
446748205a3SHans Petter Selasky 			ret = (ret & 0x7FF) * multiplier;
447748205a3SHans Petter Selasky 		}
448748205a3SHans Petter Selasky 		break;
449748205a3SHans Petter Selasky 	}
450748205a3SHans Petter Selasky 	return (ret);
451748205a3SHans Petter Selasky }
452748205a3SHans Petter Selasky 
4538c8fff31SAndrew Thompson libusb_device *
4548c8fff31SAndrew Thompson libusb_ref_device(libusb_device *dev)
4558c8fff31SAndrew Thompson {
4568c8fff31SAndrew Thompson 	if (dev == NULL)
457390065b1SAlfred Perlstein 		return (NULL);		/* be NULL safe */
4588c8fff31SAndrew Thompson 
459390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
4608c8fff31SAndrew Thompson 	dev->refcnt++;
461390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
4628c8fff31SAndrew Thompson 
4638c8fff31SAndrew Thompson 	return (dev);
4648c8fff31SAndrew Thompson }
4658c8fff31SAndrew Thompson 
4668c8fff31SAndrew Thompson void
4678c8fff31SAndrew Thompson libusb_unref_device(libusb_device *dev)
4688c8fff31SAndrew Thompson {
4698c8fff31SAndrew Thompson 	if (dev == NULL)
470390065b1SAlfred Perlstein 		return;			/* be NULL safe */
4718c8fff31SAndrew Thompson 
472390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
4738c8fff31SAndrew Thompson 	dev->refcnt--;
474390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
4758c8fff31SAndrew Thompson 
4768c8fff31SAndrew Thompson 	if (dev->refcnt == 0) {
4778c8fff31SAndrew Thompson 		libusb20_dev_free(dev->os_priv);
4788c8fff31SAndrew Thompson 		free(dev);
4798c8fff31SAndrew Thompson 	}
4808c8fff31SAndrew Thompson }
4818c8fff31SAndrew Thompson 
4828c8fff31SAndrew Thompson int
4838c8fff31SAndrew Thompson libusb_open(libusb_device *dev, libusb_device_handle **devh)
4848c8fff31SAndrew Thompson {
4858c8fff31SAndrew Thompson 	libusb_context *ctx = dev->ctx;
4868c8fff31SAndrew Thompson 	struct libusb20_device *pdev = dev->os_priv;
487390065b1SAlfred Perlstein 	uint8_t dummy;
4888c8fff31SAndrew Thompson 	int err;
4898c8fff31SAndrew Thompson 
4908c8fff31SAndrew Thompson 	if (devh == NULL)
4918c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
4928c8fff31SAndrew Thompson 
493390065b1SAlfred Perlstein 	/* set default device handle value */
494390065b1SAlfred Perlstein 	*devh = NULL;
495390065b1SAlfred Perlstein 
496390065b1SAlfred Perlstein 	dev = libusb_ref_device(dev);
497390065b1SAlfred Perlstein 	if (dev == NULL)
498390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
4998c8fff31SAndrew Thompson 
50090988efdSHans Petter Selasky 	err = libusb20_dev_open(pdev, LIBUSB_NUM_SW_ENDPOINTS);
5018c8fff31SAndrew Thompson 	if (err) {
502390065b1SAlfred Perlstein 		libusb_unref_device(dev);
5038c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
5048c8fff31SAndrew Thompson 	}
505390065b1SAlfred Perlstein 	libusb10_add_pollfd(ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN |
5068c8fff31SAndrew Thompson 	    POLLOUT | POLLRDNORM | POLLWRNORM);
5078c8fff31SAndrew Thompson 
508390065b1SAlfred Perlstein 	/* make sure our event loop detects the new device */
509390065b1SAlfred Perlstein 	dummy = 0;
5108c8fff31SAndrew Thompson 	err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
511698e791aSHans Petter Selasky 	if (err < (int)sizeof(dummy)) {
512390065b1SAlfred Perlstein 		/* ignore error, if any */
513390065b1SAlfred Perlstein 		DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open write failed!");
5148c8fff31SAndrew Thompson 	}
515390065b1SAlfred Perlstein 	*devh = pdev;
5168c8fff31SAndrew Thompson 
5178c8fff31SAndrew Thompson 	return (0);
5188c8fff31SAndrew Thompson }
5198c8fff31SAndrew Thompson 
5208c8fff31SAndrew Thompson libusb_device_handle *
5218c8fff31SAndrew Thompson libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vendor_id,
5228c8fff31SAndrew Thompson     uint16_t product_id)
5238c8fff31SAndrew Thompson {
5248c8fff31SAndrew Thompson 	struct libusb_device **devs;
5258c8fff31SAndrew Thompson 	struct libusb20_device *pdev;
5268c8fff31SAndrew Thompson 	struct LIBUSB20_DEVICE_DESC_DECODED *pdesc;
527390065b1SAlfred Perlstein 	int i;
528390065b1SAlfred Perlstein 	int j;
5298c8fff31SAndrew Thompson 
530390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
531390065b1SAlfred Perlstein 	if (ctx == NULL)
532390065b1SAlfred Perlstein 		return (NULL);		/* be NULL safe */
533390065b1SAlfred Perlstein 
534c500e4ddSAndrew Thompson 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid enter");
5358c8fff31SAndrew Thompson 
5368c8fff31SAndrew Thompson 	if ((i = libusb_get_device_list(ctx, &devs)) < 0)
5378c8fff31SAndrew Thompson 		return (NULL);
5388c8fff31SAndrew Thompson 
5393f709d07SHans Petter Selasky 	pdev = NULL;
5408c8fff31SAndrew Thompson 	for (j = 0; j < i; j++) {
5413f709d07SHans Petter Selasky 		struct libusb20_device *tdev;
5423f709d07SHans Petter Selasky 
5433f709d07SHans Petter Selasky 		tdev = devs[j]->os_priv;
5443f709d07SHans Petter Selasky 		pdesc = libusb20_dev_get_device_desc(tdev);
545390065b1SAlfred Perlstein 		/*
546390065b1SAlfred Perlstein 		 * NOTE: The USB library will automatically swap the
547390065b1SAlfred Perlstein 		 * fields in the device descriptor to be of host
548390065b1SAlfred Perlstein 		 * endian type!
549390065b1SAlfred Perlstein 		 */
5508c8fff31SAndrew Thompson 		if (pdesc->idVendor == vendor_id &&
551c500e4ddSAndrew Thompson 		    pdesc->idProduct == product_id) {
552dc934803SHans Petter Selasky 			libusb_open(devs[j], &pdev);
553c500e4ddSAndrew Thompson 			break;
554c500e4ddSAndrew Thompson 		}
5558c8fff31SAndrew Thompson 	}
5568c8fff31SAndrew Thompson 
5578c8fff31SAndrew Thompson 	libusb_free_device_list(devs, 1);
558c500e4ddSAndrew Thompson 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid leave");
559390065b1SAlfred Perlstein 	return (pdev);
5608c8fff31SAndrew Thompson }
5618c8fff31SAndrew Thompson 
5628c8fff31SAndrew Thompson void
563390065b1SAlfred Perlstein libusb_close(struct libusb20_device *pdev)
5648c8fff31SAndrew Thompson {
5658c8fff31SAndrew Thompson 	libusb_context *ctx;
566390065b1SAlfred Perlstein 	struct libusb_device *dev;
567390065b1SAlfred Perlstein 	uint8_t dummy;
5688c8fff31SAndrew Thompson 	int err;
5698c8fff31SAndrew Thompson 
570390065b1SAlfred Perlstein 	if (pdev == NULL)
571390065b1SAlfred Perlstein 		return;			/* be NULL safe */
5728c8fff31SAndrew Thompson 
573390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
574390065b1SAlfred Perlstein 	ctx = dev->ctx;
5758c8fff31SAndrew Thompson 
576390065b1SAlfred Perlstein 	libusb10_remove_pollfd(ctx, &dev->dev_poll);
5778c8fff31SAndrew Thompson 
578390065b1SAlfred Perlstein 	libusb20_dev_close(pdev);
579ccef4ddfSAndrew Thompson 
580ccef4ddfSAndrew Thompson 	/* unref will free the "pdev" when the refcount reaches zero */
581390065b1SAlfred Perlstein 	libusb_unref_device(dev);
5828c8fff31SAndrew Thompson 
583390065b1SAlfred Perlstein 	/* make sure our event loop detects the closed device */
584390065b1SAlfred Perlstein 	dummy = 0;
5858c8fff31SAndrew Thompson 	err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
586698e791aSHans Petter Selasky 	if (err < (int)sizeof(dummy)) {
587390065b1SAlfred Perlstein 		/* ignore error, if any */
588390065b1SAlfred Perlstein 		DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close write failed!");
589c500e4ddSAndrew Thompson 	}
5908c8fff31SAndrew Thompson }
5918c8fff31SAndrew Thompson 
5928c8fff31SAndrew Thompson libusb_device *
593390065b1SAlfred Perlstein libusb_get_device(struct libusb20_device *pdev)
5948c8fff31SAndrew Thompson {
595390065b1SAlfred Perlstein 	if (pdev == NULL)
5968c8fff31SAndrew Thompson 		return (NULL);
597390065b1SAlfred Perlstein 	return ((libusb_device *)pdev->privLuData);
5988c8fff31SAndrew Thompson }
5998c8fff31SAndrew Thompson 
6008c8fff31SAndrew Thompson int
601390065b1SAlfred Perlstein libusb_get_configuration(struct libusb20_device *pdev, int *config)
6028c8fff31SAndrew Thompson {
603390065b1SAlfred Perlstein 	struct libusb20_config *pconf;
6048c8fff31SAndrew Thompson 
605390065b1SAlfred Perlstein 	if (pdev == NULL || config == NULL)
6068c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
6078c8fff31SAndrew Thompson 
608390065b1SAlfred Perlstein 	pconf = libusb20_dev_alloc_config(pdev, libusb20_dev_get_config_index(pdev));
609390065b1SAlfred Perlstein 	if (pconf == NULL)
610c500e4ddSAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
6118c8fff31SAndrew Thompson 
612390065b1SAlfred Perlstein 	*config = pconf->desc.bConfigurationValue;
6138c8fff31SAndrew Thompson 
614390065b1SAlfred Perlstein 	free(pconf);
6158c8fff31SAndrew Thompson 
6168c8fff31SAndrew Thompson 	return (0);
6178c8fff31SAndrew Thompson }
6188c8fff31SAndrew Thompson 
6198c8fff31SAndrew Thompson int
620390065b1SAlfred Perlstein libusb_set_configuration(struct libusb20_device *pdev, int configuration)
6218c8fff31SAndrew Thompson {
622390065b1SAlfred Perlstein 	struct libusb20_config *pconf;
623390065b1SAlfred Perlstein 	struct libusb_device *dev;
624390065b1SAlfred Perlstein 	int err;
625390065b1SAlfred Perlstein 	uint8_t i;
6268c8fff31SAndrew Thompson 
627390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
6288c8fff31SAndrew Thompson 	if (dev == NULL)
629390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
630390065b1SAlfred Perlstein 
631390065b1SAlfred Perlstein 	if (configuration < 1) {
632390065b1SAlfred Perlstein 		/* unconfigure */
633390065b1SAlfred Perlstein 		i = 255;
634390065b1SAlfred Perlstein 	} else {
635390065b1SAlfred Perlstein 		for (i = 0; i != 255; i++) {
636390065b1SAlfred Perlstein 			uint8_t found;
637390065b1SAlfred Perlstein 
638390065b1SAlfred Perlstein 			pconf = libusb20_dev_alloc_config(pdev, i);
639390065b1SAlfred Perlstein 			if (pconf == NULL)
640390065b1SAlfred Perlstein 				return (LIBUSB_ERROR_INVALID_PARAM);
641390065b1SAlfred Perlstein 			found = (pconf->desc.bConfigurationValue
642390065b1SAlfred Perlstein 			    == configuration);
643390065b1SAlfred Perlstein 			free(pconf);
644390065b1SAlfred Perlstein 
645390065b1SAlfred Perlstein 			if (found)
646390065b1SAlfred Perlstein 				goto set_config;
647390065b1SAlfred Perlstein 		}
648390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
649390065b1SAlfred Perlstein 	}
650390065b1SAlfred Perlstein 
651390065b1SAlfred Perlstein set_config:
652390065b1SAlfred Perlstein 
653390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
654390065b1SAlfred Perlstein 
655390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
656390065b1SAlfred Perlstein 
657390065b1SAlfred Perlstein 	err = libusb20_dev_set_config_index(pdev, i);
658390065b1SAlfred Perlstein 
659390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN |
660390065b1SAlfred Perlstein 	    POLLOUT | POLLRDNORM | POLLWRNORM);
661390065b1SAlfred Perlstein 
662390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_INVALID_PARAM : 0);
663390065b1SAlfred Perlstein }
664390065b1SAlfred Perlstein 
665390065b1SAlfred Perlstein int
666390065b1SAlfred Perlstein libusb_claim_interface(struct libusb20_device *pdev, int interface_number)
667390065b1SAlfred Perlstein {
668390065b1SAlfred Perlstein 	libusb_device *dev;
6695b40d960SHans Petter Selasky 	int err = 0;
670390065b1SAlfred Perlstein 
671390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
672390065b1SAlfred Perlstein 	if (dev == NULL)
673390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
674390065b1SAlfred Perlstein 
675390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
676390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
677390065b1SAlfred Perlstein 
6785b40d960SHans Petter Selasky 	if (pdev->auto_detach != 0) {
6795b40d960SHans Petter Selasky 		err = libusb_detach_kernel_driver(pdev, interface_number);
6805b40d960SHans Petter Selasky 		if (err != 0)
6815b40d960SHans Petter Selasky 			goto done;
6825b40d960SHans Petter Selasky 	}
6835b40d960SHans Petter Selasky 
684390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
685390065b1SAlfred Perlstein 	dev->claimed_interfaces |= (1 << interface_number);
686390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
6875b40d960SHans Petter Selasky done:
6885b40d960SHans Petter Selasky 	return (err);
689390065b1SAlfred Perlstein }
690390065b1SAlfred Perlstein 
691390065b1SAlfred Perlstein int
692390065b1SAlfred Perlstein libusb_release_interface(struct libusb20_device *pdev, int interface_number)
693390065b1SAlfred Perlstein {
694390065b1SAlfred Perlstein 	libusb_device *dev;
695390065b1SAlfred Perlstein 	int err = 0;
696390065b1SAlfred Perlstein 
697390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
698390065b1SAlfred Perlstein 	if (dev == NULL)
699390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
700390065b1SAlfred Perlstein 
701390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
702390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
703390065b1SAlfred Perlstein 
7045b40d960SHans Petter Selasky 	if (pdev->auto_detach != 0) {
7055b40d960SHans Petter Selasky 		err = libusb_attach_kernel_driver(pdev, interface_number);
7065b40d960SHans Petter Selasky 		if (err != 0)
7075b40d960SHans Petter Selasky 			goto done;
7085b40d960SHans Petter Selasky 	}
7095b40d960SHans Petter Selasky 
710390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
711390065b1SAlfred Perlstein 	if (!(dev->claimed_interfaces & (1 << interface_number)))
712390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_NOT_FOUND;
7135b40d960SHans Petter Selasky 	else
714390065b1SAlfred Perlstein 		dev->claimed_interfaces &= ~(1 << interface_number);
715390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
7165b40d960SHans Petter Selasky done:
717390065b1SAlfred Perlstein 	return (err);
718390065b1SAlfred Perlstein }
719390065b1SAlfred Perlstein 
720390065b1SAlfred Perlstein int
721390065b1SAlfred Perlstein libusb_set_interface_alt_setting(struct libusb20_device *pdev,
722390065b1SAlfred Perlstein     int interface_number, int alternate_setting)
723390065b1SAlfred Perlstein {
724390065b1SAlfred Perlstein 	libusb_device *dev;
725390065b1SAlfred Perlstein 	int err = 0;
726390065b1SAlfred Perlstein 
727390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
728390065b1SAlfred Perlstein 	if (dev == NULL)
729390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
730390065b1SAlfred Perlstein 
731390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
732390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
733390065b1SAlfred Perlstein 
734390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
735390065b1SAlfred Perlstein 	if (!(dev->claimed_interfaces & (1 << interface_number)))
736390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_NOT_FOUND;
737390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
738390065b1SAlfred Perlstein 
739390065b1SAlfred Perlstein 	if (err)
740390065b1SAlfred Perlstein 		return (err);
741390065b1SAlfred Perlstein 
742390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
743390065b1SAlfred Perlstein 
744390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
745390065b1SAlfred Perlstein 
746390065b1SAlfred Perlstein 	err = libusb20_dev_set_alt_index(pdev,
747390065b1SAlfred Perlstein 	    interface_number, alternate_setting);
748390065b1SAlfred Perlstein 
749390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll,
750390065b1SAlfred Perlstein 	    pdev, libusb20_dev_get_fd(pdev),
751390065b1SAlfred Perlstein 	    POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
752390065b1SAlfred Perlstein 
753390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_OTHER : 0);
754390065b1SAlfred Perlstein }
755390065b1SAlfred Perlstein 
756390065b1SAlfred Perlstein static struct libusb20_transfer *
757390065b1SAlfred Perlstein libusb10_get_transfer(struct libusb20_device *pdev,
758d81535d1SHans Petter Selasky     uint8_t endpoint, uint8_t xfer_index)
759390065b1SAlfred Perlstein {
760d81535d1SHans Petter Selasky 	xfer_index &= 1;	/* double buffering */
761390065b1SAlfred Perlstein 
762d81535d1SHans Petter Selasky 	xfer_index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4;
763390065b1SAlfred Perlstein 
764390065b1SAlfred Perlstein 	if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) {
765390065b1SAlfred Perlstein 		/* this is an IN endpoint */
766d81535d1SHans Petter Selasky 		xfer_index |= 2;
767390065b1SAlfred Perlstein 	}
768d81535d1SHans Petter Selasky 	return (libusb20_tr_get_pointer(pdev, xfer_index));
769390065b1SAlfred Perlstein }
770390065b1SAlfred Perlstein 
771390065b1SAlfred Perlstein int
772390065b1SAlfred Perlstein libusb_clear_halt(struct libusb20_device *pdev, uint8_t endpoint)
773390065b1SAlfred Perlstein {
774390065b1SAlfred Perlstein 	struct libusb20_transfer *xfer;
775390065b1SAlfred Perlstein 	struct libusb_device *dev;
776390065b1SAlfred Perlstein 	int err;
777390065b1SAlfred Perlstein 
778390065b1SAlfred Perlstein 	xfer = libusb10_get_transfer(pdev, endpoint, 0);
779390065b1SAlfred Perlstein 	if (xfer == NULL)
780390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
781390065b1SAlfred Perlstein 
782390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
783698e791aSHans Petter Selasky 	if (dev == NULL)
784698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
785390065b1SAlfred Perlstein 
786390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
787a7e048a2SHans Petter Selasky 	err = libusb20_tr_open(xfer, 0, 1, endpoint);
788390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
789390065b1SAlfred Perlstein 
790390065b1SAlfred Perlstein 	if (err != 0 && err != LIBUSB20_ERROR_BUSY)
791390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_OTHER);
792390065b1SAlfred Perlstein 
793390065b1SAlfred Perlstein 	libusb20_tr_clear_stall_sync(xfer);
794390065b1SAlfred Perlstein 
795390065b1SAlfred Perlstein 	/* check if we opened the transfer */
796390065b1SAlfred Perlstein 	if (err == 0) {
797390065b1SAlfred Perlstein 		CTX_LOCK(dev->ctx);
798390065b1SAlfred Perlstein 		libusb20_tr_close(xfer);
799390065b1SAlfred Perlstein 		CTX_UNLOCK(dev->ctx);
800390065b1SAlfred Perlstein 	}
801390065b1SAlfred Perlstein 	return (0);			/* success */
802390065b1SAlfred Perlstein }
803390065b1SAlfred Perlstein 
804390065b1SAlfred Perlstein int
805390065b1SAlfred Perlstein libusb_reset_device(struct libusb20_device *pdev)
806390065b1SAlfred Perlstein {
807390065b1SAlfred Perlstein 	libusb_device *dev;
808390065b1SAlfred Perlstein 	int err;
809390065b1SAlfred Perlstein 
810390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
811390065b1SAlfred Perlstein 	if (dev == NULL)
812698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
8138c8fff31SAndrew Thompson 
814390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
815390065b1SAlfred Perlstein 
816390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
817390065b1SAlfred Perlstein 
818390065b1SAlfred Perlstein 	err = libusb20_dev_reset(pdev);
819390065b1SAlfred Perlstein 
820390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll,
821390065b1SAlfred Perlstein 	    pdev, libusb20_dev_get_fd(pdev),
822390065b1SAlfred Perlstein 	    POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
823390065b1SAlfred Perlstein 
824390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_OTHER : 0);
8258c8fff31SAndrew Thompson }
8268c8fff31SAndrew Thompson 
8278c8fff31SAndrew Thompson int
828f1b5fa6eSHans Petter Selasky libusb_check_connected(struct libusb20_device *pdev)
829f1b5fa6eSHans Petter Selasky {
830f1b5fa6eSHans Petter Selasky 	libusb_device *dev;
831f1b5fa6eSHans Petter Selasky 	int err;
832f1b5fa6eSHans Petter Selasky 
833f1b5fa6eSHans Petter Selasky 	dev = libusb_get_device(pdev);
834f1b5fa6eSHans Petter Selasky 	if (dev == NULL)
835f1b5fa6eSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
836f1b5fa6eSHans Petter Selasky 
837f1b5fa6eSHans Petter Selasky 	err = libusb20_dev_check_connected(pdev);
838f1b5fa6eSHans Petter Selasky 
839f1b5fa6eSHans Petter Selasky 	return (err ? LIBUSB_ERROR_NO_DEVICE : 0);
840f1b5fa6eSHans Petter Selasky }
841f1b5fa6eSHans Petter Selasky 
842f1b5fa6eSHans Petter Selasky int
843390065b1SAlfred Perlstein libusb_kernel_driver_active(struct libusb20_device *pdev, int interface)
8448c8fff31SAndrew Thompson {
845390065b1SAlfred Perlstein 	if (pdev == NULL)
8468c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
8478c8fff31SAndrew Thompson 
8484d2472aaSHans Petter Selasky 	if (libusb20_dev_kernel_driver_active(pdev, interface))
8494d2472aaSHans Petter Selasky 		return (0);		/* no kernel driver is active */
8504d2472aaSHans Petter Selasky 	else
8514d2472aaSHans Petter Selasky 		return (1);		/* kernel driver is active */
8528c8fff31SAndrew Thompson }
8538c8fff31SAndrew Thompson 
8548c8fff31SAndrew Thompson int
855698e791aSHans Petter Selasky libusb_get_driver_np(struct libusb20_device *pdev, int interface,
856698e791aSHans Petter Selasky     char *name, int namelen)
857698e791aSHans Petter Selasky {
858698e791aSHans Petter Selasky 	return (libusb_get_driver(pdev, interface, name, namelen));
859698e791aSHans Petter Selasky }
860698e791aSHans Petter Selasky 
861698e791aSHans Petter Selasky int
862698e791aSHans Petter Selasky libusb_get_driver(struct libusb20_device *pdev, int interface,
863698e791aSHans Petter Selasky     char *name, int namelen)
864698e791aSHans Petter Selasky {
865698e791aSHans Petter Selasky 	char *ptr;
866698e791aSHans Petter Selasky 	int err;
867698e791aSHans Petter Selasky 
868698e791aSHans Petter Selasky 	if (pdev == NULL)
869698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
870698e791aSHans Petter Selasky 	if (namelen < 1)
871698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
8724eb5923dSHans Petter Selasky 	if (namelen > 255)
8734eb5923dSHans Petter Selasky 		namelen = 255;
874698e791aSHans Petter Selasky 
875698e791aSHans Petter Selasky 	err = libusb20_dev_get_iface_desc(
876698e791aSHans Petter Selasky 	    pdev, interface, name, namelen);
877698e791aSHans Petter Selasky 
878698e791aSHans Petter Selasky 	if (err != 0)
879698e791aSHans Petter Selasky 		return (LIBUSB_ERROR_OTHER);
880698e791aSHans Petter Selasky 
881698e791aSHans Petter Selasky 	/* we only want the driver name */
882698e791aSHans Petter Selasky 	ptr = strstr(name, ":");
883698e791aSHans Petter Selasky 	if (ptr != NULL)
884698e791aSHans Petter Selasky 		*ptr = 0;
885698e791aSHans Petter Selasky 
886698e791aSHans Petter Selasky 	return (0);
887698e791aSHans Petter Selasky }
888698e791aSHans Petter Selasky 
889698e791aSHans Petter Selasky int
890698e791aSHans Petter Selasky libusb_detach_kernel_driver_np(struct libusb20_device *pdev, int interface)
891698e791aSHans Petter Selasky {
892698e791aSHans Petter Selasky 	return (libusb_detach_kernel_driver(pdev, interface));
893698e791aSHans Petter Selasky }
894698e791aSHans Petter Selasky 
895698e791aSHans Petter Selasky int
896390065b1SAlfred Perlstein libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface)
8978c8fff31SAndrew Thompson {
898390065b1SAlfred Perlstein 	int err;
8998c8fff31SAndrew Thompson 
900390065b1SAlfred Perlstein 	if (pdev == NULL)
9018c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
9028c8fff31SAndrew Thompson 
903390065b1SAlfred Perlstein 	err = libusb20_dev_detach_kernel_driver(
904390065b1SAlfred Perlstein 	    pdev, interface);
9058c8fff31SAndrew Thompson 
906698e791aSHans Petter Selasky 	return (err ? LIBUSB_ERROR_OTHER : 0);
9078c8fff31SAndrew Thompson }
9088c8fff31SAndrew Thompson 
9098c8fff31SAndrew Thompson int
910390065b1SAlfred Perlstein libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface)
9118c8fff31SAndrew Thompson {
912390065b1SAlfred Perlstein 	if (pdev == NULL)
9138c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
914390065b1SAlfred Perlstein 	/* stub - currently not supported by libusb20 */
9158c8fff31SAndrew Thompson 	return (0);
9168c8fff31SAndrew Thompson }
9178c8fff31SAndrew Thompson 
9185b40d960SHans Petter Selasky int
9195b40d960SHans Petter Selasky libusb_set_auto_detach_kernel_driver(libusb_device_handle *dev, int enable)
9205b40d960SHans Petter Selasky {
9215b40d960SHans Petter Selasky 	dev->auto_detach = (enable ? 1 : 0);
922a3d81a8aSHans Petter Selasky 	return (0);
9235b40d960SHans Petter Selasky }
9245b40d960SHans Petter Selasky 
9258c8fff31SAndrew Thompson /* Asynchronous device I/O */
9268c8fff31SAndrew Thompson 
9278c8fff31SAndrew Thompson struct libusb_transfer *
9288c8fff31SAndrew Thompson libusb_alloc_transfer(int iso_packets)
9298c8fff31SAndrew Thompson {
930390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
931390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
9328c8fff31SAndrew Thompson 	int len;
9338c8fff31SAndrew Thompson 
9348c8fff31SAndrew Thompson 	len = sizeof(struct libusb_transfer) +
935390065b1SAlfred Perlstein 	    sizeof(struct libusb_super_transfer) +
9368c8fff31SAndrew Thompson 	    (iso_packets * sizeof(libusb_iso_packet_descriptor));
9378c8fff31SAndrew Thompson 
938390065b1SAlfred Perlstein 	sxfer = malloc(len);
939390065b1SAlfred Perlstein 	if (sxfer == NULL)
9408c8fff31SAndrew Thompson 		return (NULL);
9418c8fff31SAndrew Thompson 
942390065b1SAlfred Perlstein 	memset(sxfer, 0, len);
9438c8fff31SAndrew Thompson 
944390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
945390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
9468c8fff31SAndrew Thompson 
947390065b1SAlfred Perlstein 	/* set default value */
948390065b1SAlfred Perlstein 	uxfer->num_iso_packets = iso_packets;
949390065b1SAlfred Perlstein 
950390065b1SAlfred Perlstein 	return (uxfer);
9518c8fff31SAndrew Thompson }
9528c8fff31SAndrew Thompson 
9538c8fff31SAndrew Thompson void
954390065b1SAlfred Perlstein libusb_free_transfer(struct libusb_transfer *uxfer)
9558c8fff31SAndrew Thompson {
956390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
9578c8fff31SAndrew Thompson 
958390065b1SAlfred Perlstein 	if (uxfer == NULL)
959390065b1SAlfred Perlstein 		return;			/* be NULL safe */
9608c8fff31SAndrew Thompson 
96131f7072cSHans Petter Selasky 	/* check if we should free the transfer buffer */
96231f7072cSHans Petter Selasky 	if (uxfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
96331f7072cSHans Petter Selasky 		free(uxfer->buffer);
96431f7072cSHans Petter Selasky 
965390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
966390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
9678c8fff31SAndrew Thompson 
968390065b1SAlfred Perlstein 	free(sxfer);
9698c8fff31SAndrew Thompson }
9708c8fff31SAndrew Thompson 
9711c497368SHans Petter Selasky static uint32_t
972390065b1SAlfred Perlstein libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer)
9738c8fff31SAndrew Thompson {
9741c497368SHans Petter Selasky 	uint32_t ret;
9758c8fff31SAndrew Thompson 
9768c8fff31SAndrew Thompson 	switch (xfer->type) {
9778c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
9781c497368SHans Petter Selasky 		ret = 60 | LIBUSB20_MAX_FRAME_PRE_SCALE;	/* 60ms */
9798c8fff31SAndrew Thompson 		break;
9808c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_CONTROL:
9818c8fff31SAndrew Thompson 		ret = 2;
9828c8fff31SAndrew Thompson 		break;
9838c8fff31SAndrew Thompson 	default:
9848c8fff31SAndrew Thompson 		ret = 1;
9858c8fff31SAndrew Thompson 		break;
9868c8fff31SAndrew Thompson 	}
987390065b1SAlfred Perlstein 	return (ret);
9888c8fff31SAndrew Thompson }
9898c8fff31SAndrew Thompson 
9908c8fff31SAndrew Thompson static int
991390065b1SAlfred Perlstein libusb10_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer)
9928c8fff31SAndrew Thompson {
9938c8fff31SAndrew Thompson 	int ret;
9948c8fff31SAndrew Thompson 	int usb_speed;
9958c8fff31SAndrew Thompson 
9968c8fff31SAndrew Thompson 	usb_speed = libusb20_dev_get_speed(pdev);
9978c8fff31SAndrew Thompson 
9988c8fff31SAndrew Thompson 	switch (xfer->type) {
9998c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
1000390065b1SAlfred Perlstein 		ret = 0;		/* kernel will auto-select */
10018c8fff31SAndrew Thompson 		break;
10028c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_CONTROL:
1003390065b1SAlfred Perlstein 		ret = 1024;
10048c8fff31SAndrew Thompson 		break;
10058c8fff31SAndrew Thompson 	default:
10068c8fff31SAndrew Thompson 		switch (usb_speed) {
10078c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_LOW:
10088c8fff31SAndrew Thompson 			ret = 256;
10098c8fff31SAndrew Thompson 			break;
10108c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_FULL:
10118c8fff31SAndrew Thompson 			ret = 4096;
10128c8fff31SAndrew Thompson 			break;
1013f355a4ddSHans Petter Selasky 		case LIBUSB20_SPEED_SUPER:
1014f355a4ddSHans Petter Selasky 			ret = 65536;
1015f355a4ddSHans Petter Selasky 			break;
10168c8fff31SAndrew Thompson 		default:
10178c8fff31SAndrew Thompson 			ret = 16384;
10188c8fff31SAndrew Thompson 			break;
10198c8fff31SAndrew Thompson 		}
10208c8fff31SAndrew Thompson 		break;
10218c8fff31SAndrew Thompson 	}
1022390065b1SAlfred Perlstein 	return (ret);
10238c8fff31SAndrew Thompson }
10248c8fff31SAndrew Thompson 
1025390065b1SAlfred Perlstein static int
1026390065b1SAlfred Perlstein libusb10_convert_error(uint8_t status)
1027390065b1SAlfred Perlstein {
1028390065b1SAlfred Perlstein 	;				/* indent fix */
1029390065b1SAlfred Perlstein 
1030390065b1SAlfred Perlstein 	switch (status) {
1031390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
1032390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
1033390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_COMPLETED);
1034390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_OVERFLOW:
1035390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_OVERFLOW);
1036390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_NO_DEVICE:
1037390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_NO_DEVICE);
1038390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_STALL:
1039390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_STALL);
1040390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_CANCELLED:
1041390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_CANCELLED);
1042390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_TIMED_OUT:
1043390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_TIMED_OUT);
1044390065b1SAlfred Perlstein 	default:
1045390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_ERROR);
1046390065b1SAlfred Perlstein 	}
1047390065b1SAlfred Perlstein }
1048390065b1SAlfred Perlstein 
1049390065b1SAlfred Perlstein /* This function must be called locked */
1050390065b1SAlfred Perlstein 
1051c500e4ddSAndrew Thompson static void
1052390065b1SAlfred Perlstein libusb10_complete_transfer(struct libusb20_transfer *pxfer,
1053390065b1SAlfred Perlstein     struct libusb_super_transfer *sxfer, int status)
1054c500e4ddSAndrew Thompson {
1055390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1056390065b1SAlfred Perlstein 	struct libusb_device *dev;
1057c500e4ddSAndrew Thompson 
1058390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1059390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1060390065b1SAlfred Perlstein 
1061390065b1SAlfred Perlstein 	if (pxfer != NULL)
1062390065b1SAlfred Perlstein 		libusb20_tr_set_priv_sc1(pxfer, NULL);
1063390065b1SAlfred Perlstein 
10644594d907SAndrew Thompson 	/* set transfer status */
1065390065b1SAlfred Perlstein 	uxfer->status = status;
1066390065b1SAlfred Perlstein 
10674594d907SAndrew Thompson 	/* update super transfer state */
10684594d907SAndrew Thompson 	sxfer->state = LIBUSB_SUPER_XFER_ST_NONE;
10694594d907SAndrew Thompson 
1070390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
1071390065b1SAlfred Perlstein 
1072390065b1SAlfred Perlstein 	TAILQ_INSERT_TAIL(&dev->ctx->tr_done, sxfer, entry);
1073390065b1SAlfred Perlstein }
1074390065b1SAlfred Perlstein 
1075390065b1SAlfred Perlstein /* This function must be called locked */
1076390065b1SAlfred Perlstein 
1077390065b1SAlfred Perlstein static void
1078390065b1SAlfred Perlstein libusb10_isoc_proxy(struct libusb20_transfer *pxfer)
1079390065b1SAlfred Perlstein {
1080390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1081390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1082390065b1SAlfred Perlstein 	uint32_t actlen;
1083390065b1SAlfred Perlstein 	uint16_t iso_packets;
1084390065b1SAlfred Perlstein 	uint16_t i;
1085390065b1SAlfred Perlstein 	uint8_t status;
1086390065b1SAlfred Perlstein 
1087390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
1088390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
1089390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
1090390065b1SAlfred Perlstein 	iso_packets = libusb20_tr_get_max_frames(pxfer);
1091390065b1SAlfred Perlstein 
1092390065b1SAlfred Perlstein 	if (sxfer == NULL)
1093390065b1SAlfred Perlstein 		return; /* cancelled - nothing to do */
1094390065b1SAlfred Perlstein 
1095390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1096390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1097390065b1SAlfred Perlstein 
1098390065b1SAlfred Perlstein 	if (iso_packets > uxfer->num_iso_packets)
1099390065b1SAlfred Perlstein 		iso_packets = uxfer->num_iso_packets;
1100390065b1SAlfred Perlstein 
1101390065b1SAlfred Perlstein 	if (iso_packets == 0)
1102390065b1SAlfred Perlstein 		return; /* nothing to do */
1103390065b1SAlfred Perlstein 
1104390065b1SAlfred Perlstein 	/* make sure that the number of ISOCHRONOUS packets is valid */
1105390065b1SAlfred Perlstein 	uxfer->num_iso_packets = iso_packets;
1106390065b1SAlfred Perlstein 
1107c500e4ddSAndrew Thompson 	switch (status) {
1108c500e4ddSAndrew Thompson 	case LIBUSB20_TRANSFER_COMPLETED:
1109390065b1SAlfred Perlstein 		/* update actual length */
1110390065b1SAlfred Perlstein 		uxfer->actual_length = actlen;
1111390065b1SAlfred Perlstein 		for (i = 0; i != iso_packets; i++) {
1112390065b1SAlfred Perlstein 			uxfer->iso_packet_desc[i].actual_length =
1113390065b1SAlfred Perlstein 			    libusb20_tr_get_length(pxfer, i);
1114390065b1SAlfred Perlstein 		}
1115390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1116c500e4ddSAndrew Thompson 		break;
1117c500e4ddSAndrew Thompson 	case LIBUSB20_TRANSFER_START:
1118390065b1SAlfred Perlstein 		/* setup length(s) */
1119390065b1SAlfred Perlstein 		actlen = 0;
1120390065b1SAlfred Perlstein 		for (i = 0; i != iso_packets; i++) {
1121390065b1SAlfred Perlstein 			libusb20_tr_setup_isoc(pxfer,
1122390065b1SAlfred Perlstein 			    &uxfer->buffer[actlen],
1123390065b1SAlfred Perlstein 			    uxfer->iso_packet_desc[i].length, i);
1124390065b1SAlfred Perlstein 			actlen += uxfer->iso_packet_desc[i].length;
1125c500e4ddSAndrew Thompson 		}
1126390065b1SAlfred Perlstein 
1127390065b1SAlfred Perlstein 		/* no remainder */
1128390065b1SAlfred Perlstein 		sxfer->rem_len = 0;
1129390065b1SAlfred Perlstein 
1130390065b1SAlfred Perlstein 		libusb20_tr_set_total_frames(pxfer, iso_packets);
1131390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1132390065b1SAlfred Perlstein 
1133390065b1SAlfred Perlstein 		/* fork another USB transfer, if any */
1134390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1135c500e4ddSAndrew Thompson 		break;
1136390065b1SAlfred Perlstein 	default:
1137390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1138c500e4ddSAndrew Thompson 		break;
1139c500e4ddSAndrew Thompson 	}
1140390065b1SAlfred Perlstein }
1141390065b1SAlfred Perlstein 
1142390065b1SAlfred Perlstein /* This function must be called locked */
1143390065b1SAlfred Perlstein 
1144390065b1SAlfred Perlstein static void
1145390065b1SAlfred Perlstein libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer)
1146390065b1SAlfred Perlstein {
1147390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1148390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1149390065b1SAlfred Perlstein 	uint32_t max_bulk;
1150390065b1SAlfred Perlstein 	uint32_t actlen;
1151390065b1SAlfred Perlstein 	uint8_t status;
1152390065b1SAlfred Perlstein 	uint8_t flags;
1153390065b1SAlfred Perlstein 
1154390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
1155390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
1156390065b1SAlfred Perlstein 	max_bulk = libusb20_tr_get_max_total_length(pxfer);
1157390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(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 	flags = uxfer->flags;
1166390065b1SAlfred Perlstein 
1167390065b1SAlfred Perlstein 	switch (status) {
1168390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
1169390065b1SAlfred Perlstein 
1170390065b1SAlfred Perlstein 		uxfer->actual_length += actlen;
1171390065b1SAlfred Perlstein 
1172390065b1SAlfred Perlstein 		/* check for short packet */
1173390065b1SAlfred Perlstein 		if (sxfer->last_len != actlen) {
1174390065b1SAlfred Perlstein 			if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
1175390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR);
1176390065b1SAlfred Perlstein 			} else {
1177390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1178390065b1SAlfred Perlstein 			}
1179390065b1SAlfred Perlstein 			break;
1180390065b1SAlfred Perlstein 		}
1181390065b1SAlfred Perlstein 		/* check for end of data */
1182390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0) {
1183390065b1SAlfred Perlstein 			libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1184390065b1SAlfred Perlstein 			break;
1185390065b1SAlfred Perlstein 		}
1186390065b1SAlfred Perlstein 		/* FALLTHROUGH */
1187390065b1SAlfred Perlstein 
1188390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
1189390065b1SAlfred Perlstein 		if (max_bulk > sxfer->rem_len) {
1190390065b1SAlfred Perlstein 			max_bulk = sxfer->rem_len;
1191390065b1SAlfred Perlstein 		}
1192390065b1SAlfred Perlstein 		/* setup new BULK or INTERRUPT transaction */
1193390065b1SAlfred Perlstein 		libusb20_tr_setup_bulk(pxfer,
1194390065b1SAlfred Perlstein 		    sxfer->curr_data, max_bulk, uxfer->timeout);
1195390065b1SAlfred Perlstein 
1196390065b1SAlfred Perlstein 		/* update counters */
1197390065b1SAlfred Perlstein 		sxfer->last_len = max_bulk;
1198390065b1SAlfred Perlstein 		sxfer->curr_data += max_bulk;
1199390065b1SAlfred Perlstein 		sxfer->rem_len -= max_bulk;
1200390065b1SAlfred Perlstein 
1201390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1202390065b1SAlfred Perlstein 
1203390065b1SAlfred Perlstein 		/* check if we can fork another USB transfer */
1204390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0)
1205390065b1SAlfred Perlstein 			libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1206390065b1SAlfred Perlstein 		break;
1207390065b1SAlfred Perlstein 
1208390065b1SAlfred Perlstein 	default:
1209390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1210390065b1SAlfred Perlstein 		break;
1211390065b1SAlfred Perlstein 	}
1212390065b1SAlfred Perlstein }
1213390065b1SAlfred Perlstein 
1214390065b1SAlfred Perlstein /* This function must be called locked */
1215390065b1SAlfred Perlstein 
1216390065b1SAlfred Perlstein static void
1217390065b1SAlfred Perlstein libusb10_ctrl_proxy(struct libusb20_transfer *pxfer)
1218390065b1SAlfred Perlstein {
1219390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1220390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1221390065b1SAlfred Perlstein 	uint32_t max_bulk;
1222390065b1SAlfred Perlstein 	uint32_t actlen;
1223390065b1SAlfred Perlstein 	uint8_t status;
1224390065b1SAlfred Perlstein 	uint8_t flags;
1225390065b1SAlfred Perlstein 
1226390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
1227390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
1228390065b1SAlfred Perlstein 	max_bulk = libusb20_tr_get_max_total_length(pxfer);
1229390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
1230390065b1SAlfred Perlstein 
1231390065b1SAlfred Perlstein 	if (sxfer == NULL)
1232390065b1SAlfred Perlstein 		return;			/* cancelled - nothing to do */
1233390065b1SAlfred Perlstein 
1234390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1235390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1236390065b1SAlfred Perlstein 
1237390065b1SAlfred Perlstein 	flags = uxfer->flags;
1238390065b1SAlfred Perlstein 
1239390065b1SAlfred Perlstein 	switch (status) {
1240390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
1241390065b1SAlfred Perlstein 
1242390065b1SAlfred Perlstein 		uxfer->actual_length += actlen;
1243390065b1SAlfred Perlstein 
1244390065b1SAlfred Perlstein 		/* subtract length of SETUP packet, if any */
1245390065b1SAlfred Perlstein 		actlen -= libusb20_tr_get_length(pxfer, 0);
1246390065b1SAlfred Perlstein 
1247390065b1SAlfred Perlstein 		/* check for short packet */
1248390065b1SAlfred Perlstein 		if (sxfer->last_len != actlen) {
1249390065b1SAlfred Perlstein 			if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
1250390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR);
1251390065b1SAlfred Perlstein 			} else {
1252390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1253390065b1SAlfred Perlstein 			}
1254390065b1SAlfred Perlstein 			break;
1255390065b1SAlfred Perlstein 		}
1256390065b1SAlfred Perlstein 		/* check for end of data */
1257390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0) {
1258390065b1SAlfred Perlstein 			libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1259390065b1SAlfred Perlstein 			break;
1260390065b1SAlfred Perlstein 		}
1261390065b1SAlfred Perlstein 		/* FALLTHROUGH */
1262390065b1SAlfred Perlstein 
1263390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
1264390065b1SAlfred Perlstein 		if (max_bulk > sxfer->rem_len) {
1265390065b1SAlfred Perlstein 			max_bulk = sxfer->rem_len;
1266390065b1SAlfred Perlstein 		}
1267390065b1SAlfred Perlstein 		/* setup new CONTROL transaction */
1268390065b1SAlfred Perlstein 		if (status == LIBUSB20_TRANSFER_COMPLETED) {
1269390065b1SAlfred Perlstein 			/* next fragment - don't send SETUP packet */
1270390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, 0, 0);
1271390065b1SAlfred Perlstein 		} else {
1272390065b1SAlfred Perlstein 			/* first fragment - send SETUP packet */
1273390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, 8, 0);
1274390065b1SAlfred Perlstein 			libusb20_tr_set_buffer(pxfer, uxfer->buffer, 0);
1275390065b1SAlfred Perlstein 		}
1276390065b1SAlfred Perlstein 
1277390065b1SAlfred Perlstein 		if (max_bulk != 0) {
1278390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, max_bulk, 1);
1279390065b1SAlfred Perlstein 			libusb20_tr_set_buffer(pxfer, sxfer->curr_data, 1);
1280390065b1SAlfred Perlstein 			libusb20_tr_set_total_frames(pxfer, 2);
1281390065b1SAlfred Perlstein 		} else {
1282390065b1SAlfred Perlstein 			libusb20_tr_set_total_frames(pxfer, 1);
1283390065b1SAlfred Perlstein 		}
1284390065b1SAlfred Perlstein 
1285390065b1SAlfred Perlstein 		/* update counters */
1286390065b1SAlfred Perlstein 		sxfer->last_len = max_bulk;
1287390065b1SAlfred Perlstein 		sxfer->curr_data += max_bulk;
1288390065b1SAlfred Perlstein 		sxfer->rem_len -= max_bulk;
1289390065b1SAlfred Perlstein 
1290390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1291390065b1SAlfred Perlstein 
1292390065b1SAlfred Perlstein 		/* check if we can fork another USB transfer */
1293390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0)
1294390065b1SAlfred Perlstein 			libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1295390065b1SAlfred Perlstein 		break;
1296390065b1SAlfred Perlstein 
1297390065b1SAlfred Perlstein 	default:
1298390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1299390065b1SAlfred Perlstein 		break;
1300390065b1SAlfred Perlstein 	}
1301390065b1SAlfred Perlstein }
1302390065b1SAlfred Perlstein 
1303390065b1SAlfred Perlstein /* The following function must be called locked */
1304390065b1SAlfred Perlstein 
1305390065b1SAlfred Perlstein static void
1306390065b1SAlfred Perlstein libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint)
1307390065b1SAlfred Perlstein {
1308390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1309390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1310390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1311390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1312390065b1SAlfred Perlstein 	struct libusb_device *dev;
1313390065b1SAlfred Perlstein 	int err;
1314390065b1SAlfred Perlstein 	int buffsize;
1315390065b1SAlfred Perlstein 	int maxframe;
1316390065b1SAlfred Perlstein 	int temp;
1317390065b1SAlfred Perlstein 	uint8_t dummy;
1318390065b1SAlfred Perlstein 
1319390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
1320390065b1SAlfred Perlstein 
1321390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(pdev, endpoint, 0);
1322390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(pdev, endpoint, 1);
1323390065b1SAlfred Perlstein 
1324390065b1SAlfred Perlstein 	if (pxfer0 == NULL || pxfer1 == NULL)
1325390065b1SAlfred Perlstein 		return;			/* shouldn't happen */
1326390065b1SAlfred Perlstein 
1327390065b1SAlfred Perlstein 	temp = 0;
1328390065b1SAlfred Perlstein 	if (libusb20_tr_pending(pxfer0))
1329390065b1SAlfred Perlstein 		temp |= 1;
1330390065b1SAlfred Perlstein 	if (libusb20_tr_pending(pxfer1))
1331390065b1SAlfred Perlstein 		temp |= 2;
1332390065b1SAlfred Perlstein 
1333390065b1SAlfred Perlstein 	switch (temp) {
1334390065b1SAlfred Perlstein 	case 3:
1335390065b1SAlfred Perlstein 		/* wait till one of the transfers complete */
1336390065b1SAlfred Perlstein 		return;
1337390065b1SAlfred Perlstein 	case 2:
1338390065b1SAlfred Perlstein 		sxfer = libusb20_tr_get_priv_sc1(pxfer1);
13394594d907SAndrew Thompson 		if (sxfer == NULL)
13404594d907SAndrew Thompson 			return;		/* cancelling */
1341390065b1SAlfred Perlstein 		if (sxfer->rem_len)
1342390065b1SAlfred Perlstein 			return;		/* cannot queue another one */
1343390065b1SAlfred Perlstein 		/* swap transfers */
1344390065b1SAlfred Perlstein 		pxfer1 = pxfer0;
1345390065b1SAlfred Perlstein 		break;
1346390065b1SAlfred Perlstein 	case 1:
1347390065b1SAlfred Perlstein 		sxfer = libusb20_tr_get_priv_sc1(pxfer0);
13484594d907SAndrew Thompson 		if (sxfer == NULL)
13494594d907SAndrew Thompson 			return;		/* cancelling */
1350390065b1SAlfred Perlstein 		if (sxfer->rem_len)
1351390065b1SAlfred Perlstein 			return;		/* cannot queue another one */
1352390065b1SAlfred Perlstein 		/* swap transfers */
1353390065b1SAlfred Perlstein 		pxfer0 = pxfer1;
1354c500e4ddSAndrew Thompson 		break;
1355c500e4ddSAndrew Thompson 	default:
1356c500e4ddSAndrew Thompson 		break;
1357c500e4ddSAndrew Thompson 	}
1358c500e4ddSAndrew Thompson 
1359390065b1SAlfred Perlstein 	/* find next transfer on same endpoint */
1360390065b1SAlfred Perlstein 	TAILQ_FOREACH(sxfer, &dev->tr_head, entry) {
1361390065b1SAlfred Perlstein 
1362390065b1SAlfred Perlstein 		uxfer = (struct libusb_transfer *)(
1363390065b1SAlfred Perlstein 		    ((uint8_t *)sxfer) + sizeof(*sxfer));
1364390065b1SAlfred Perlstein 
1365390065b1SAlfred Perlstein 		if (uxfer->endpoint == endpoint) {
1366390065b1SAlfred Perlstein 			TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
1367390065b1SAlfred Perlstein 			sxfer->entry.tqe_prev = NULL;
1368390065b1SAlfred Perlstein 			goto found;
1369c500e4ddSAndrew Thompson 		}
1370c500e4ddSAndrew Thompson 	}
1371390065b1SAlfred Perlstein 	return;				/* success */
1372390065b1SAlfred Perlstein 
1373390065b1SAlfred Perlstein found:
1374390065b1SAlfred Perlstein 
1375390065b1SAlfred Perlstein 	libusb20_tr_set_priv_sc0(pxfer0, pdev);
1376390065b1SAlfred Perlstein 	libusb20_tr_set_priv_sc1(pxfer0, sxfer);
1377390065b1SAlfred Perlstein 
1378390065b1SAlfred Perlstein 	/* reset super transfer state */
1379390065b1SAlfred Perlstein 	sxfer->rem_len = uxfer->length;
1380390065b1SAlfred Perlstein 	sxfer->curr_data = uxfer->buffer;
1381390065b1SAlfred Perlstein 	uxfer->actual_length = 0;
1382390065b1SAlfred Perlstein 
1383390065b1SAlfred Perlstein 	switch (uxfer->type) {
1384390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
1385390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_isoc_proxy);
1386390065b1SAlfred Perlstein 		break;
1387390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_BULK:
1388390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
1389390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_bulk_intr_proxy);
1390390065b1SAlfred Perlstein 		break;
1391390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_CONTROL:
1392390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_ctrl_proxy);
1393390065b1SAlfred Perlstein 		if (sxfer->rem_len < 8)
1394390065b1SAlfred Perlstein 			goto failure;
1395390065b1SAlfred Perlstein 
1396390065b1SAlfred Perlstein 		/* remove SETUP packet from data */
1397390065b1SAlfred Perlstein 		sxfer->rem_len -= 8;
1398390065b1SAlfred Perlstein 		sxfer->curr_data += 8;
1399390065b1SAlfred Perlstein 		break;
1400390065b1SAlfred Perlstein 	default:
1401390065b1SAlfred Perlstein 		goto failure;
1402390065b1SAlfred Perlstein 	}
1403390065b1SAlfred Perlstein 
1404390065b1SAlfred Perlstein 	buffsize = libusb10_get_buffsize(pdev, uxfer);
1405390065b1SAlfred Perlstein 	maxframe = libusb10_get_maxframe(pdev, uxfer);
1406390065b1SAlfred Perlstein 
1407390065b1SAlfred Perlstein 	/* make sure the transfer is opened */
1408a0c93fa3SHans Petter Selasky 	err = libusb20_tr_open_stream(pxfer0, buffsize, maxframe,
1409a0c93fa3SHans Petter Selasky 	    endpoint, sxfer->stream_id);
1410390065b1SAlfred Perlstein 	if (err && (err != LIBUSB20_ERROR_BUSY)) {
1411390065b1SAlfred Perlstein 		goto failure;
1412390065b1SAlfred Perlstein 	}
1413390065b1SAlfred Perlstein 	libusb20_tr_start(pxfer0);
1414390065b1SAlfred Perlstein 	return;
1415390065b1SAlfred Perlstein 
1416390065b1SAlfred Perlstein failure:
1417390065b1SAlfred Perlstein 	libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_ERROR);
1418390065b1SAlfred Perlstein 
1419390065b1SAlfred Perlstein 	/* make sure our event loop spins the done handler */
1420390065b1SAlfred Perlstein 	dummy = 0;
142166194130SHans Petter Selasky 	err = write(dev->ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
1422390065b1SAlfred Perlstein }
1423390065b1SAlfred Perlstein 
1424390065b1SAlfred Perlstein /* The following function must be called unlocked */
1425c500e4ddSAndrew Thompson 
14268c8fff31SAndrew Thompson int
1427390065b1SAlfred Perlstein libusb_submit_transfer(struct libusb_transfer *uxfer)
14288c8fff31SAndrew Thompson {
1429390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1430390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1431390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1432390065b1SAlfred Perlstein 	struct libusb_device *dev;
143385ff9a03SHans Petter Selasky 	uint8_t endpoint;
1434390065b1SAlfred Perlstein 	int err;
14358c8fff31SAndrew Thompson 
1436390065b1SAlfred Perlstein 	if (uxfer == NULL)
1437390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
14388c8fff31SAndrew Thompson 
1439390065b1SAlfred Perlstein 	if (uxfer->dev_handle == NULL)
1440390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
14418c8fff31SAndrew Thompson 
1442390065b1SAlfred Perlstein 	endpoint = uxfer->endpoint;
14438c8fff31SAndrew Thompson 
1444390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
14458c8fff31SAndrew Thompson 
1446390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer enter");
14478c8fff31SAndrew Thompson 
1448390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
1449390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
1450390065b1SAlfred Perlstein 
1451390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
1452390065b1SAlfred Perlstein 
1453390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0);
1454390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1);
1455390065b1SAlfred Perlstein 
1456390065b1SAlfred Perlstein 	if (pxfer0 == NULL || pxfer1 == NULL) {
1457390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_OTHER;
1458390065b1SAlfred Perlstein 	} else if ((sxfer->entry.tqe_prev != NULL) ||
1459390065b1SAlfred Perlstein 	    (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) ||
1460390065b1SAlfred Perlstein 	    (libusb20_tr_get_priv_sc1(pxfer1) == sxfer)) {
1461390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_BUSY;
1462390065b1SAlfred Perlstein 	} else {
14634594d907SAndrew Thompson 
14644594d907SAndrew Thompson 		/* set pending state */
14654594d907SAndrew Thompson 		sxfer->state = LIBUSB_SUPER_XFER_ST_PEND;
14664594d907SAndrew Thompson 
14674594d907SAndrew Thompson 		/* insert transfer into transfer head list */
1468390065b1SAlfred Perlstein 		TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry);
1469390065b1SAlfred Perlstein 
14704594d907SAndrew Thompson 		/* start work transfers */
1471390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(
1472390065b1SAlfred Perlstein 		    uxfer->dev_handle, endpoint);
1473390065b1SAlfred Perlstein 
1474390065b1SAlfred Perlstein 		err = 0;		/* success */
14758c8fff31SAndrew Thompson 	}
14768c8fff31SAndrew Thompson 
1477390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
1478390065b1SAlfred Perlstein 
1479390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer leave %d", err);
1480390065b1SAlfred Perlstein 
1481390065b1SAlfred Perlstein 	return (err);
14828c8fff31SAndrew Thompson }
14838c8fff31SAndrew Thompson 
1484390065b1SAlfred Perlstein /* Asynchronous transfer cancel */
14858c8fff31SAndrew Thompson 
1486390065b1SAlfred Perlstein int
1487390065b1SAlfred Perlstein libusb_cancel_transfer(struct libusb_transfer *uxfer)
1488390065b1SAlfred Perlstein {
1489390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1490390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1491390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1492390065b1SAlfred Perlstein 	struct libusb_device *dev;
149385ff9a03SHans Petter Selasky 	uint8_t endpoint;
14944594d907SAndrew Thompson 	int retval;
14958c8fff31SAndrew Thompson 
1496390065b1SAlfred Perlstein 	if (uxfer == NULL)
1497390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
14988c8fff31SAndrew Thompson 
14994594d907SAndrew Thompson 	/* check if not initialised */
1500390065b1SAlfred Perlstein 	if (uxfer->dev_handle == NULL)
15014594d907SAndrew Thompson 		return (LIBUSB_ERROR_NOT_FOUND);
15028c8fff31SAndrew Thompson 
1503390065b1SAlfred Perlstein 	endpoint = uxfer->endpoint;
15048c8fff31SAndrew Thompson 
1505390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
15068c8fff31SAndrew Thompson 
1507390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter");
15088c8fff31SAndrew Thompson 
1509390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
1510390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
1511390065b1SAlfred Perlstein 
15124594d907SAndrew Thompson 	retval = 0;
15134594d907SAndrew Thompson 
1514390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
1515390065b1SAlfred Perlstein 
1516390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0);
1517390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1);
1518390065b1SAlfred Perlstein 
15194594d907SAndrew Thompson 	if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) {
15204594d907SAndrew Thompson 		/* only update the transfer status */
15214594d907SAndrew Thompson 		uxfer->status = LIBUSB_TRANSFER_CANCELLED;
15224594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
15234594d907SAndrew Thompson 	} else if (sxfer->entry.tqe_prev != NULL) {
1524390065b1SAlfred Perlstein 		/* we are lucky - transfer is on a queue */
1525390065b1SAlfred Perlstein 		TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
1526390065b1SAlfred Perlstein 		sxfer->entry.tqe_prev = NULL;
15274594d907SAndrew Thompson 		libusb10_complete_transfer(NULL,
15284594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1529390065b1SAlfred Perlstein 	} else if (pxfer0 == NULL || pxfer1 == NULL) {
1530390065b1SAlfred Perlstein 		/* not started */
15314594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
1532390065b1SAlfred Perlstein 	} else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) {
15334594d907SAndrew Thompson 		libusb10_complete_transfer(pxfer0,
15344594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1535390065b1SAlfred Perlstein 		libusb20_tr_stop(pxfer0);
1536390065b1SAlfred Perlstein 		/* make sure the queue doesn't stall */
1537390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(
1538390065b1SAlfred Perlstein 		    uxfer->dev_handle, endpoint);
1539390065b1SAlfred Perlstein 	} else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) {
15404594d907SAndrew Thompson 		libusb10_complete_transfer(pxfer1,
15414594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1542390065b1SAlfred Perlstein 		libusb20_tr_stop(pxfer1);
1543390065b1SAlfred Perlstein 		/* make sure the queue doesn't stall */
1544390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(
1545390065b1SAlfred Perlstein 		    uxfer->dev_handle, endpoint);
1546390065b1SAlfred Perlstein 	} else {
1547390065b1SAlfred Perlstein 		/* not started */
15484594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
1549c500e4ddSAndrew Thompson 	}
15508c8fff31SAndrew Thompson 
1551390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
15528c8fff31SAndrew Thompson 
1553390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave");
15548c8fff31SAndrew Thompson 
15554594d907SAndrew Thompson 	return (retval);
15568c8fff31SAndrew Thompson }
15578c8fff31SAndrew Thompson 
1558390065b1SAlfred Perlstein UNEXPORTED void
1559390065b1SAlfred Perlstein libusb10_cancel_all_transfer(libusb_device *dev)
15608c8fff31SAndrew Thompson {
156190988efdSHans Petter Selasky 	struct libusb20_device *pdev = dev->os_priv;
156290988efdSHans Petter Selasky 	unsigned x;
156390988efdSHans Petter Selasky 
156490988efdSHans Petter Selasky 	for (x = 0; x != LIBUSB_NUM_SW_ENDPOINTS; x++) {
156590988efdSHans Petter Selasky 		struct libusb20_transfer *xfer;
156690988efdSHans Petter Selasky 
156790988efdSHans Petter Selasky 		xfer = libusb20_tr_get_pointer(pdev, x);
156890988efdSHans Petter Selasky 		if (xfer == NULL)
156990988efdSHans Petter Selasky 			continue;
157090988efdSHans Petter Selasky 		libusb20_tr_close(xfer);
157190988efdSHans Petter Selasky 	}
15728c8fff31SAndrew Thompson }
1573ccef4ddfSAndrew Thompson 
1574ccef4ddfSAndrew Thompson uint16_t
1575ccef4ddfSAndrew Thompson libusb_cpu_to_le16(uint16_t x)
1576ccef4ddfSAndrew Thompson {
1577ccef4ddfSAndrew Thompson 	return (htole16(x));
1578ccef4ddfSAndrew Thompson }
1579ccef4ddfSAndrew Thompson 
1580ccef4ddfSAndrew Thompson uint16_t
1581ccef4ddfSAndrew Thompson libusb_le16_to_cpu(uint16_t x)
1582ccef4ddfSAndrew Thompson {
1583ccef4ddfSAndrew Thompson 	return (le16toh(x));
1584ccef4ddfSAndrew Thompson }
1585ccef4ddfSAndrew Thompson 
1586698e791aSHans Petter Selasky const char *
1587698e791aSHans Petter Selasky libusb_strerror(int code)
1588698e791aSHans Petter Selasky {
1589c61f2561SHans Petter Selasky 	switch (code) {
1590c61f2561SHans Petter Selasky 	case LIBUSB_SUCCESS:
1591c61f2561SHans Petter Selasky 		return ("Success");
1592c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_IO:
1593c61f2561SHans Petter Selasky 		return ("I/O error");
1594c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INVALID_PARAM:
1595c61f2561SHans Petter Selasky 		return ("Invalid parameter");
1596c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_ACCESS:
1597c61f2561SHans Petter Selasky 		return ("Permissions error");
1598c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_DEVICE:
1599c61f2561SHans Petter Selasky 		return ("No device");
1600c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_FOUND:
1601c61f2561SHans Petter Selasky 		return ("Not found");
1602c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_BUSY:
1603c61f2561SHans Petter Selasky 		return ("Device busy");
1604c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_TIMEOUT:
1605c61f2561SHans Petter Selasky 		return ("Timeout");
1606c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OVERFLOW:
1607c61f2561SHans Petter Selasky 		return ("Overflow");
1608c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_PIPE:
1609c61f2561SHans Petter Selasky 		return ("Pipe error");
1610c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INTERRUPTED:
1611c61f2561SHans Petter Selasky 		return ("Interrupted");
1612c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_MEM:
1613c61f2561SHans Petter Selasky 		return ("Out of memory");
1614c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_SUPPORTED:
1615c61f2561SHans Petter Selasky 		return ("Not supported");
1616c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OTHER:
1617c61f2561SHans Petter Selasky 		return ("Other error");
1618c61f2561SHans Petter Selasky 	default:
1619698e791aSHans Petter Selasky 		return ("Unknown error");
1620698e791aSHans Petter Selasky 	}
1621c61f2561SHans Petter Selasky }
1622c61f2561SHans Petter Selasky 
1623c61f2561SHans Petter Selasky const char *
1624c61f2561SHans Petter Selasky libusb_error_name(int code)
1625c61f2561SHans Petter Selasky {
1626c61f2561SHans Petter Selasky 	switch (code) {
1627c61f2561SHans Petter Selasky 	case LIBUSB_SUCCESS:
1628c61f2561SHans Petter Selasky 		return ("LIBUSB_SUCCESS");
1629c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_IO:
1630c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_IO");
1631c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INVALID_PARAM:
1632c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_INVALID_PARAM");
1633c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_ACCESS:
1634c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_ACCESS");
1635c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_DEVICE:
1636c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NO_DEVICE");
1637c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_FOUND:
1638c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NOT_FOUND");
1639c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_BUSY:
1640c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_BUSY");
1641c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_TIMEOUT:
1642c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_TIMEOUT");
1643c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OVERFLOW:
1644c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_OVERFLOW");
1645c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_PIPE:
1646c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_PIPE");
1647c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_INTERRUPTED:
1648c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_INTERRUPTED");
1649c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NO_MEM:
1650c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NO_MEM");
1651c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_NOT_SUPPORTED:
1652c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_NOT_SUPPORTED");
1653c61f2561SHans Petter Selasky 	case LIBUSB_ERROR_OTHER:
1654c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_OTHER");
1655c61f2561SHans Petter Selasky 	default:
1656c61f2561SHans Petter Selasky 		return ("LIBUSB_ERROR_UNKNOWN");
1657c61f2561SHans Petter Selasky 	}
1658c61f2561SHans Petter Selasky }
1659