xref: /freebsd/lib/libusb/libusb10.c (revision f1b5fa6e496ae0eb2a3a60ecd613ff92d432e5b9)
18c8fff31SAndrew Thompson /* $FreeBSD$ */
28c8fff31SAndrew Thompson /*-
38c8fff31SAndrew Thompson  * Copyright (c) 2009 Sylvestre Gallon. All rights reserved.
4390065b1SAlfred Perlstein  * Copyright (c) 2009 Hans Petter Selasky. All rights reserved.
58c8fff31SAndrew Thompson  *
68c8fff31SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
78c8fff31SAndrew Thompson  * modification, are permitted provided that the following conditions
88c8fff31SAndrew Thompson  * are met:
98c8fff31SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
108c8fff31SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
118c8fff31SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
128c8fff31SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
138c8fff31SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
148c8fff31SAndrew Thompson  *
158c8fff31SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
168c8fff31SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
178c8fff31SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
188c8fff31SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
198c8fff31SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
208c8fff31SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
218c8fff31SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
228c8fff31SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
238c8fff31SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
248c8fff31SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
258c8fff31SAndrew Thompson  * SUCH DAMAGE.
268c8fff31SAndrew Thompson  */
278c8fff31SAndrew Thompson 
28f3cba95cSWojciech A. Koszek #include <sys/fcntl.h>
29f3cba95cSWojciech A. Koszek #include <sys/ioctl.h>
30f3cba95cSWojciech A. Koszek #include <sys/queue.h>
31f3cba95cSWojciech A. Koszek 
32ac840bfcSWojciech A. Koszek #include <assert.h>
33f3cba95cSWojciech A. Koszek #include <errno.h>
348c8fff31SAndrew Thompson #include <poll.h>
358c8fff31SAndrew Thompson #include <pthread.h>
36f3cba95cSWojciech A. Koszek #include <stdio.h>
37f3cba95cSWojciech A. Koszek #include <stdlib.h>
38f3cba95cSWojciech A. Koszek #include <unistd.h>
398c8fff31SAndrew Thompson 
409c087c5aSAndrew Thompson #define	libusb_device_handle libusb20_device
419c087c5aSAndrew Thompson 
428c8fff31SAndrew Thompson #include "libusb20.h"
438c8fff31SAndrew Thompson #include "libusb20_desc.h"
448c8fff31SAndrew Thompson #include "libusb20_int.h"
458c8fff31SAndrew Thompson #include "libusb.h"
468c8fff31SAndrew Thompson #include "libusb10.h"
478c8fff31SAndrew Thompson 
488c8fff31SAndrew Thompson static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER;
498c8fff31SAndrew Thompson struct libusb_context *usbi_default_context = NULL;
50390065b1SAlfred Perlstein 
51390065b1SAlfred Perlstein /* Prototypes */
52390065b1SAlfred Perlstein 
53390065b1SAlfred Perlstein static struct libusb20_transfer *libusb10_get_transfer(struct libusb20_device *, uint8_t, uint8_t);
54390065b1SAlfred Perlstein static int libusb10_get_maxframe(struct libusb20_device *, libusb_transfer *);
55390065b1SAlfred Perlstein static int libusb10_get_buffsize(struct libusb20_device *, libusb_transfer *);
56390065b1SAlfred Perlstein static int libusb10_convert_error(uint8_t status);
57390065b1SAlfred Perlstein static void libusb10_complete_transfer(struct libusb20_transfer *, struct libusb_super_transfer *, int);
58390065b1SAlfred Perlstein static void libusb10_isoc_proxy(struct libusb20_transfer *);
59390065b1SAlfred Perlstein static void libusb10_bulk_intr_proxy(struct libusb20_transfer *);
60390065b1SAlfred Perlstein static void libusb10_ctrl_proxy(struct libusb20_transfer *);
61390065b1SAlfred Perlstein static void libusb10_submit_transfer_sub(struct libusb20_device *, uint8_t);
628c8fff31SAndrew Thompson 
638c8fff31SAndrew Thompson /*  Library initialisation / deinitialisation */
648c8fff31SAndrew Thompson 
658c8fff31SAndrew Thompson void
668c8fff31SAndrew Thompson libusb_set_debug(libusb_context *ctx, int level)
678c8fff31SAndrew Thompson {
68390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
698c8fff31SAndrew Thompson 	if (ctx)
708c8fff31SAndrew Thompson 		ctx->debug = level;
718c8fff31SAndrew Thompson }
728c8fff31SAndrew Thompson 
738c8fff31SAndrew Thompson int
748c8fff31SAndrew Thompson libusb_init(libusb_context **context)
758c8fff31SAndrew Thompson {
768c8fff31SAndrew Thompson 	struct libusb_context *ctx;
778c8fff31SAndrew Thompson 	char *debug;
78ac840bfcSWojciech A. Koszek 	int flag;
798c8fff31SAndrew Thompson 	int ret;
808c8fff31SAndrew Thompson 
818c8fff31SAndrew Thompson 	ctx = malloc(sizeof(*ctx));
828c8fff31SAndrew Thompson 	if (!ctx)
838c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
848c8fff31SAndrew Thompson 
858c8fff31SAndrew Thompson 	memset(ctx, 0, sizeof(*ctx));
868c8fff31SAndrew Thompson 
878c8fff31SAndrew Thompson 	debug = getenv("LIBUSB_DEBUG");
888c8fff31SAndrew Thompson 	if (debug != NULL) {
898c8fff31SAndrew Thompson 		ctx->debug = atoi(debug);
908c8fff31SAndrew Thompson 		if (ctx->debug != 0)
918c8fff31SAndrew Thompson 			ctx->debug_fixed = 1;
928c8fff31SAndrew Thompson 	}
93c500e4ddSAndrew Thompson 	TAILQ_INIT(&ctx->pollfds);
94390065b1SAlfred Perlstein 	TAILQ_INIT(&ctx->tr_done);
95390065b1SAlfred Perlstein 
96390065b1SAlfred Perlstein 	pthread_mutex_init(&ctx->ctx_lock, NULL);
97390065b1SAlfred Perlstein 	pthread_cond_init(&ctx->ctx_cond, NULL);
98390065b1SAlfred Perlstein 
99390065b1SAlfred Perlstein 	ctx->ctx_handler = NO_THREAD;
1008c8fff31SAndrew Thompson 
1018c8fff31SAndrew Thompson 	ret = pipe(ctx->ctrl_pipe);
1028c8fff31SAndrew Thompson 	if (ret < 0) {
103390065b1SAlfred Perlstein 		pthread_mutex_destroy(&ctx->ctx_lock);
104390065b1SAlfred Perlstein 		pthread_cond_destroy(&ctx->ctx_cond);
1058c8fff31SAndrew Thompson 		free(ctx);
1068c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_OTHER);
1078c8fff31SAndrew Thompson 	}
108390065b1SAlfred Perlstein 	/* set non-blocking mode on the control pipe to avoid deadlock */
109ac840bfcSWojciech A. Koszek 	flag = 1;
110ac840bfcSWojciech A. Koszek 	ret = fcntl(ctx->ctrl_pipe[0], O_NONBLOCK, &flag);
111ac840bfcSWojciech A. Koszek 	assert(ret != -1 && "Couldn't set O_NONBLOCK for ctx->ctrl_pipe[0]");
112ac840bfcSWojciech A. Koszek 	flag = 1;
113ac840bfcSWojciech A. Koszek 	ret = fcntl(ctx->ctrl_pipe[1], O_NONBLOCK, &flag);
114ac840bfcSWojciech A. Koszek 	assert(ret != -1 && "Couldn't set O_NONBLOCK for ctx->ctrl_pipe[1]");
1158c8fff31SAndrew Thompson 
116390065b1SAlfred Perlstein 	libusb10_add_pollfd(ctx, &ctx->ctx_poll, NULL, ctx->ctrl_pipe[0], POLLIN);
1178c8fff31SAndrew Thompson 
1188c8fff31SAndrew Thompson 	pthread_mutex_lock(&default_context_lock);
1198c8fff31SAndrew Thompson 	if (usbi_default_context == NULL) {
1208c8fff31SAndrew Thompson 		usbi_default_context = ctx;
1218c8fff31SAndrew Thompson 	}
1228c8fff31SAndrew Thompson 	pthread_mutex_unlock(&default_context_lock);
1238c8fff31SAndrew Thompson 
1248c8fff31SAndrew Thompson 	if (context)
1258c8fff31SAndrew Thompson 		*context = ctx;
1268c8fff31SAndrew Thompson 
127390065b1SAlfred Perlstein 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_init complete");
128390065b1SAlfred Perlstein 
1298c8fff31SAndrew Thompson 	return (0);
1308c8fff31SAndrew Thompson }
1318c8fff31SAndrew Thompson 
1328c8fff31SAndrew Thompson void
1338c8fff31SAndrew Thompson libusb_exit(libusb_context *ctx)
1348c8fff31SAndrew Thompson {
135390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
1368c8fff31SAndrew Thompson 
137390065b1SAlfred Perlstein 	if (ctx == NULL)
138390065b1SAlfred Perlstein 		return;
139390065b1SAlfred Perlstein 
140390065b1SAlfred Perlstein 	/* XXX cleanup devices */
141390065b1SAlfred Perlstein 
142390065b1SAlfred Perlstein 	libusb10_remove_pollfd(ctx, &ctx->ctx_poll);
1438c8fff31SAndrew Thompson 	close(ctx->ctrl_pipe[0]);
1448c8fff31SAndrew Thompson 	close(ctx->ctrl_pipe[1]);
145390065b1SAlfred Perlstein 	pthread_mutex_destroy(&ctx->ctx_lock);
146390065b1SAlfred Perlstein 	pthread_cond_destroy(&ctx->ctx_cond);
1478c8fff31SAndrew Thompson 
1488c8fff31SAndrew Thompson 	pthread_mutex_lock(&default_context_lock);
1498c8fff31SAndrew Thompson 	if (ctx == usbi_default_context) {
1508c8fff31SAndrew Thompson 		usbi_default_context = NULL;
1518c8fff31SAndrew Thompson 	}
1528c8fff31SAndrew Thompson 	pthread_mutex_unlock(&default_context_lock);
1538c8fff31SAndrew Thompson 
1548c8fff31SAndrew Thompson 	free(ctx);
1558c8fff31SAndrew Thompson }
1568c8fff31SAndrew Thompson 
1578c8fff31SAndrew Thompson /* Device handling and initialisation. */
1588c8fff31SAndrew Thompson 
1598c8fff31SAndrew Thompson ssize_t
1608c8fff31SAndrew Thompson libusb_get_device_list(libusb_context *ctx, libusb_device ***list)
1618c8fff31SAndrew Thompson {
1628c8fff31SAndrew Thompson 	struct libusb20_backend *usb_backend;
163390065b1SAlfred Perlstein 	struct libusb20_device *pdev;
164390065b1SAlfred Perlstein 	struct libusb_device *dev;
1658c8fff31SAndrew Thompson 	int i;
1668c8fff31SAndrew Thompson 
167390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
168390065b1SAlfred Perlstein 
169390065b1SAlfred Perlstein 	if (ctx == NULL)
170390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
171390065b1SAlfred Perlstein 
172390065b1SAlfred Perlstein 	if (list == NULL)
173390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
1748c8fff31SAndrew Thompson 
1758c8fff31SAndrew Thompson 	usb_backend = libusb20_be_alloc_default();
1768c8fff31SAndrew Thompson 	if (usb_backend == NULL)
177390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_NO_MEM);
1788c8fff31SAndrew Thompson 
179390065b1SAlfred Perlstein 	/* figure out how many USB devices are present */
1808c8fff31SAndrew Thompson 	pdev = NULL;
1818c8fff31SAndrew Thompson 	i = 0;
1828c8fff31SAndrew Thompson 	while ((pdev = libusb20_be_device_foreach(usb_backend, pdev)))
1838c8fff31SAndrew Thompson 		i++;
1848c8fff31SAndrew Thompson 
185390065b1SAlfred Perlstein 	/* allocate device pointer list */
1868c8fff31SAndrew Thompson 	*list = malloc((i + 1) * sizeof(void *));
1878c8fff31SAndrew Thompson 	if (*list == NULL) {
1888c8fff31SAndrew Thompson 		libusb20_be_free(usb_backend);
1898c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
1908c8fff31SAndrew Thompson 	}
191390065b1SAlfred Perlstein 	/* create libusb v1.0 compliant devices */
1928c8fff31SAndrew Thompson 	i = 0;
1938c8fff31SAndrew Thompson 	while ((pdev = libusb20_be_device_foreach(usb_backend, NULL))) {
1948c8fff31SAndrew Thompson 
1958c8fff31SAndrew Thompson 		dev = malloc(sizeof(*dev));
1968c8fff31SAndrew Thompson 		if (dev == NULL) {
197c500e4ddSAndrew Thompson 			while (i != 0) {
198c500e4ddSAndrew Thompson 				libusb_unref_device((*list)[i - 1]);
199c500e4ddSAndrew Thompson 				i--;
200c500e4ddSAndrew Thompson 			}
2018c8fff31SAndrew Thompson 			free(*list);
202390065b1SAlfred Perlstein 			*list = NULL;
2038c8fff31SAndrew Thompson 			libusb20_be_free(usb_backend);
2048c8fff31SAndrew Thompson 			return (LIBUSB_ERROR_NO_MEM);
2058c8fff31SAndrew Thompson 		}
206ccef4ddfSAndrew Thompson 
207ccef4ddfSAndrew Thompson 		/* get device into libUSB v1.0 list */
208ccef4ddfSAndrew Thompson 		libusb20_be_dequeue_device(usb_backend, pdev);
209ccef4ddfSAndrew Thompson 
2108c8fff31SAndrew Thompson 		memset(dev, 0, sizeof(*dev));
2118c8fff31SAndrew Thompson 
212390065b1SAlfred Perlstein 		/* init transfer queues */
213390065b1SAlfred Perlstein 		TAILQ_INIT(&dev->tr_head);
214390065b1SAlfred Perlstein 
215390065b1SAlfred Perlstein 		/* set context we belong to */
2168c8fff31SAndrew Thompson 		dev->ctx = ctx;
2178c8fff31SAndrew Thompson 
2188c8fff31SAndrew Thompson 		/* link together the two structures */
2198c8fff31SAndrew Thompson 		dev->os_priv = pdev;
220390065b1SAlfred Perlstein 		pdev->privLuData = dev;
2218c8fff31SAndrew Thompson 
2228c8fff31SAndrew Thompson 		(*list)[i] = libusb_ref_device(dev);
2238c8fff31SAndrew Thompson 		i++;
2248c8fff31SAndrew Thompson 	}
2258c8fff31SAndrew Thompson 	(*list)[i] = NULL;
2268c8fff31SAndrew Thompson 
2278c8fff31SAndrew Thompson 	libusb20_be_free(usb_backend);
2288c8fff31SAndrew Thompson 	return (i);
2298c8fff31SAndrew Thompson }
2308c8fff31SAndrew Thompson 
2318c8fff31SAndrew Thompson void
2328c8fff31SAndrew Thompson libusb_free_device_list(libusb_device **list, int unref_devices)
2338c8fff31SAndrew Thompson {
2348c8fff31SAndrew Thompson 	int i;
2358c8fff31SAndrew Thompson 
2368c8fff31SAndrew Thompson 	if (list == NULL)
237390065b1SAlfred Perlstein 		return;			/* be NULL safe */
2388c8fff31SAndrew Thompson 
2398c8fff31SAndrew Thompson 	if (unref_devices) {
2408c8fff31SAndrew Thompson 		for (i = 0; list[i] != NULL; i++)
2418c8fff31SAndrew Thompson 			libusb_unref_device(list[i]);
2428c8fff31SAndrew Thompson 	}
2438c8fff31SAndrew Thompson 	free(list);
2448c8fff31SAndrew Thompson }
2458c8fff31SAndrew Thompson 
2468c8fff31SAndrew Thompson uint8_t
2478c8fff31SAndrew Thompson libusb_get_bus_number(libusb_device *dev)
2488c8fff31SAndrew Thompson {
2498c8fff31SAndrew Thompson 	if (dev == NULL)
250390065b1SAlfred Perlstein 		return (0);		/* should not happen */
251390065b1SAlfred Perlstein 	return (libusb20_dev_get_bus_number(dev->os_priv));
2528c8fff31SAndrew Thompson }
2538c8fff31SAndrew Thompson 
2548c8fff31SAndrew Thompson uint8_t
2558c8fff31SAndrew Thompson libusb_get_device_address(libusb_device *dev)
2568c8fff31SAndrew Thompson {
2578c8fff31SAndrew Thompson 	if (dev == NULL)
258390065b1SAlfred Perlstein 		return (0);		/* should not happen */
259390065b1SAlfred Perlstein 	return (libusb20_dev_get_address(dev->os_priv));
2608c8fff31SAndrew Thompson }
2618c8fff31SAndrew Thompson 
2628c8fff31SAndrew Thompson int
263390065b1SAlfred Perlstein libusb_get_max_packet_size(libusb_device *dev, uint8_t endpoint)
2648c8fff31SAndrew Thompson {
2658c8fff31SAndrew Thompson 	struct libusb_config_descriptor *pdconf;
2668c8fff31SAndrew Thompson 	struct libusb_interface *pinf;
2678c8fff31SAndrew Thompson 	struct libusb_interface_descriptor *pdinf;
2688c8fff31SAndrew Thompson 	struct libusb_endpoint_descriptor *pdend;
269390065b1SAlfred Perlstein 	int i;
270390065b1SAlfred Perlstein 	int j;
271390065b1SAlfred Perlstein 	int k;
272390065b1SAlfred Perlstein 	int ret;
2738c8fff31SAndrew Thompson 
2748c8fff31SAndrew Thompson 	if (dev == NULL)
2758c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_DEVICE);
2768c8fff31SAndrew Thompson 
277390065b1SAlfred Perlstein 	ret = libusb_get_active_config_descriptor(dev, &pdconf);
278390065b1SAlfred Perlstein 	if (ret < 0)
279390065b1SAlfred Perlstein 		return (ret);
2808c8fff31SAndrew Thompson 
2818c8fff31SAndrew Thompson 	ret = LIBUSB_ERROR_NOT_FOUND;
2828c8fff31SAndrew Thompson 	for (i = 0; i < pdconf->bNumInterfaces; i++) {
2838c8fff31SAndrew Thompson 		pinf = &pdconf->interface[i];
2848c8fff31SAndrew Thompson 		for (j = 0; j < pinf->num_altsetting; j++) {
2858c8fff31SAndrew Thompson 			pdinf = &pinf->altsetting[j];
2868c8fff31SAndrew Thompson 			for (k = 0; k < pdinf->bNumEndpoints; k++) {
2878c8fff31SAndrew Thompson 				pdend = &pdinf->endpoint[k];
2888c8fff31SAndrew Thompson 				if (pdend->bEndpointAddress == endpoint) {
2898c8fff31SAndrew Thompson 					ret = pdend->wMaxPacketSize;
2908c8fff31SAndrew Thompson 					goto out;
2918c8fff31SAndrew Thompson 				}
2928c8fff31SAndrew Thompson 			}
2938c8fff31SAndrew Thompson 		}
2948c8fff31SAndrew Thompson 	}
2958c8fff31SAndrew Thompson 
2968c8fff31SAndrew Thompson out:
2978c8fff31SAndrew Thompson 	libusb_free_config_descriptor(pdconf);
2988c8fff31SAndrew Thompson 	return (ret);
2998c8fff31SAndrew Thompson }
3008c8fff31SAndrew Thompson 
3018c8fff31SAndrew Thompson libusb_device *
3028c8fff31SAndrew Thompson libusb_ref_device(libusb_device *dev)
3038c8fff31SAndrew Thompson {
3048c8fff31SAndrew Thompson 	if (dev == NULL)
305390065b1SAlfred Perlstein 		return (NULL);		/* be NULL safe */
3068c8fff31SAndrew Thompson 
307390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
3088c8fff31SAndrew Thompson 	dev->refcnt++;
309390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
3108c8fff31SAndrew Thompson 
3118c8fff31SAndrew Thompson 	return (dev);
3128c8fff31SAndrew Thompson }
3138c8fff31SAndrew Thompson 
3148c8fff31SAndrew Thompson void
3158c8fff31SAndrew Thompson libusb_unref_device(libusb_device *dev)
3168c8fff31SAndrew Thompson {
3178c8fff31SAndrew Thompson 	if (dev == NULL)
318390065b1SAlfred Perlstein 		return;			/* be NULL safe */
3198c8fff31SAndrew Thompson 
320390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
3218c8fff31SAndrew Thompson 	dev->refcnt--;
322390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
3238c8fff31SAndrew Thompson 
3248c8fff31SAndrew Thompson 	if (dev->refcnt == 0) {
3258c8fff31SAndrew Thompson 		libusb20_dev_free(dev->os_priv);
3268c8fff31SAndrew Thompson 		free(dev);
3278c8fff31SAndrew Thompson 	}
3288c8fff31SAndrew Thompson }
3298c8fff31SAndrew Thompson 
3308c8fff31SAndrew Thompson int
3318c8fff31SAndrew Thompson libusb_open(libusb_device *dev, libusb_device_handle **devh)
3328c8fff31SAndrew Thompson {
3338c8fff31SAndrew Thompson 	libusb_context *ctx = dev->ctx;
3348c8fff31SAndrew Thompson 	struct libusb20_device *pdev = dev->os_priv;
335390065b1SAlfred Perlstein 	uint8_t dummy;
3368c8fff31SAndrew Thompson 	int err;
3378c8fff31SAndrew Thompson 
3388c8fff31SAndrew Thompson 	if (devh == NULL)
3398c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
3408c8fff31SAndrew Thompson 
341390065b1SAlfred Perlstein 	/* set default device handle value */
342390065b1SAlfred Perlstein 	*devh = NULL;
343390065b1SAlfred Perlstein 
344390065b1SAlfred Perlstein 	dev = libusb_ref_device(dev);
345390065b1SAlfred Perlstein 	if (dev == NULL)
346390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
3478c8fff31SAndrew Thompson 
3488c8fff31SAndrew Thompson 	err = libusb20_dev_open(pdev, 16 * 4 /* number of endpoints */ );
3498c8fff31SAndrew Thompson 	if (err) {
350390065b1SAlfred Perlstein 		libusb_unref_device(dev);
3518c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
3528c8fff31SAndrew Thompson 	}
353390065b1SAlfred Perlstein 	libusb10_add_pollfd(ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN |
3548c8fff31SAndrew Thompson 	    POLLOUT | POLLRDNORM | POLLWRNORM);
3558c8fff31SAndrew Thompson 
356390065b1SAlfred Perlstein 	/* make sure our event loop detects the new device */
357390065b1SAlfred Perlstein 	dummy = 0;
3588c8fff31SAndrew Thompson 	err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
359390065b1SAlfred Perlstein 	if (err < sizeof(dummy)) {
360390065b1SAlfred Perlstein 		/* ignore error, if any */
361390065b1SAlfred Perlstein 		DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open write failed!");
3628c8fff31SAndrew Thompson 	}
363390065b1SAlfred Perlstein 	*devh = pdev;
3648c8fff31SAndrew Thompson 
3658c8fff31SAndrew Thompson 	return (0);
3668c8fff31SAndrew Thompson }
3678c8fff31SAndrew Thompson 
3688c8fff31SAndrew Thompson libusb_device_handle *
3698c8fff31SAndrew Thompson libusb_open_device_with_vid_pid(libusb_context *ctx, uint16_t vendor_id,
3708c8fff31SAndrew Thompson     uint16_t product_id)
3718c8fff31SAndrew Thompson {
3728c8fff31SAndrew Thompson 	struct libusb_device **devs;
3738c8fff31SAndrew Thompson 	struct libusb20_device *pdev;
3748c8fff31SAndrew Thompson 	struct LIBUSB20_DEVICE_DESC_DECODED *pdesc;
375390065b1SAlfred Perlstein 	int i;
376390065b1SAlfred Perlstein 	int j;
3778c8fff31SAndrew Thompson 
378390065b1SAlfred Perlstein 	ctx = GET_CONTEXT(ctx);
379390065b1SAlfred Perlstein 	if (ctx == NULL)
380390065b1SAlfred Perlstein 		return (NULL);		/* be NULL safe */
381390065b1SAlfred Perlstein 
382c500e4ddSAndrew Thompson 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid enter");
3838c8fff31SAndrew Thompson 
3848c8fff31SAndrew Thompson 	if ((i = libusb_get_device_list(ctx, &devs)) < 0)
3858c8fff31SAndrew Thompson 		return (NULL);
3868c8fff31SAndrew Thompson 
3878c8fff31SAndrew Thompson 	for (j = 0; j < i; j++) {
388390065b1SAlfred Perlstein 		pdev = devs[j]->os_priv;
3898c8fff31SAndrew Thompson 		pdesc = libusb20_dev_get_device_desc(pdev);
390390065b1SAlfred Perlstein 		/*
391390065b1SAlfred Perlstein 		 * NOTE: The USB library will automatically swap the
392390065b1SAlfred Perlstein 		 * fields in the device descriptor to be of host
393390065b1SAlfred Perlstein 		 * endian type!
394390065b1SAlfred Perlstein 		 */
3958c8fff31SAndrew Thompson 		if (pdesc->idVendor == vendor_id &&
396c500e4ddSAndrew Thompson 		    pdesc->idProduct == product_id) {
397390065b1SAlfred Perlstein 			if (libusb_open(devs[j], &pdev) < 0)
398390065b1SAlfred Perlstein 				pdev = NULL;
399c500e4ddSAndrew Thompson 			break;
400c500e4ddSAndrew Thompson 		}
4018c8fff31SAndrew Thompson 	}
402abdbb3feSSean Farley 	if (j == i)
403abdbb3feSSean Farley 		pdev = NULL;
4048c8fff31SAndrew Thompson 
4058c8fff31SAndrew Thompson 	libusb_free_device_list(devs, 1);
406c500e4ddSAndrew Thompson 	DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_open_device_width_vid_pid leave");
407390065b1SAlfred Perlstein 	return (pdev);
4088c8fff31SAndrew Thompson }
4098c8fff31SAndrew Thompson 
4108c8fff31SAndrew Thompson void
411390065b1SAlfred Perlstein libusb_close(struct libusb20_device *pdev)
4128c8fff31SAndrew Thompson {
4138c8fff31SAndrew Thompson 	libusb_context *ctx;
414390065b1SAlfred Perlstein 	struct libusb_device *dev;
415390065b1SAlfred Perlstein 	uint8_t dummy;
4168c8fff31SAndrew Thompson 	int err;
4178c8fff31SAndrew Thompson 
418390065b1SAlfred Perlstein 	if (pdev == NULL)
419390065b1SAlfred Perlstein 		return;			/* be NULL safe */
4208c8fff31SAndrew Thompson 
421390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
422390065b1SAlfred Perlstein 	ctx = dev->ctx;
4238c8fff31SAndrew Thompson 
424390065b1SAlfred Perlstein 	libusb10_remove_pollfd(ctx, &dev->dev_poll);
4258c8fff31SAndrew Thompson 
426390065b1SAlfred Perlstein 	libusb20_dev_close(pdev);
427ccef4ddfSAndrew Thompson 
428ccef4ddfSAndrew Thompson 	/* unref will free the "pdev" when the refcount reaches zero */
429390065b1SAlfred Perlstein 	libusb_unref_device(dev);
4308c8fff31SAndrew Thompson 
431390065b1SAlfred Perlstein 	/* make sure our event loop detects the closed device */
432390065b1SAlfred Perlstein 	dummy = 0;
4338c8fff31SAndrew Thompson 	err = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
434390065b1SAlfred Perlstein 	if (err < sizeof(dummy)) {
435390065b1SAlfred Perlstein 		/* ignore error, if any */
436390065b1SAlfred Perlstein 		DPRINTF(ctx, LIBUSB_DEBUG_FUNCTION, "libusb_close write failed!");
437c500e4ddSAndrew Thompson 	}
4388c8fff31SAndrew Thompson }
4398c8fff31SAndrew Thompson 
4408c8fff31SAndrew Thompson libusb_device *
441390065b1SAlfred Perlstein libusb_get_device(struct libusb20_device *pdev)
4428c8fff31SAndrew Thompson {
443390065b1SAlfred Perlstein 	if (pdev == NULL)
4448c8fff31SAndrew Thompson 		return (NULL);
445390065b1SAlfred Perlstein 	return ((libusb_device *)pdev->privLuData);
4468c8fff31SAndrew Thompson }
4478c8fff31SAndrew Thompson 
4488c8fff31SAndrew Thompson int
449390065b1SAlfred Perlstein libusb_get_configuration(struct libusb20_device *pdev, int *config)
4508c8fff31SAndrew Thompson {
451390065b1SAlfred Perlstein 	struct libusb20_config *pconf;
4528c8fff31SAndrew Thompson 
453390065b1SAlfred Perlstein 	if (pdev == NULL || config == NULL)
4548c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
4558c8fff31SAndrew Thompson 
456390065b1SAlfred Perlstein 	pconf = libusb20_dev_alloc_config(pdev, libusb20_dev_get_config_index(pdev));
457390065b1SAlfred Perlstein 	if (pconf == NULL)
458c500e4ddSAndrew Thompson 		return (LIBUSB_ERROR_NO_MEM);
4598c8fff31SAndrew Thompson 
460390065b1SAlfred Perlstein 	*config = pconf->desc.bConfigurationValue;
4618c8fff31SAndrew Thompson 
462390065b1SAlfred Perlstein 	free(pconf);
4638c8fff31SAndrew Thompson 
4648c8fff31SAndrew Thompson 	return (0);
4658c8fff31SAndrew Thompson }
4668c8fff31SAndrew Thompson 
4678c8fff31SAndrew Thompson int
468390065b1SAlfred Perlstein libusb_set_configuration(struct libusb20_device *pdev, int configuration)
4698c8fff31SAndrew Thompson {
470390065b1SAlfred Perlstein 	struct libusb20_config *pconf;
471390065b1SAlfred Perlstein 	struct libusb_device *dev;
472390065b1SAlfred Perlstein 	int err;
473390065b1SAlfred Perlstein 	uint8_t i;
4748c8fff31SAndrew Thompson 
475390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
4768c8fff31SAndrew Thompson 
4778c8fff31SAndrew Thompson 	if (dev == NULL)
478390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
479390065b1SAlfred Perlstein 
480390065b1SAlfred Perlstein 	if (configuration < 1) {
481390065b1SAlfred Perlstein 		/* unconfigure */
482390065b1SAlfred Perlstein 		i = 255;
483390065b1SAlfred Perlstein 	} else {
484390065b1SAlfred Perlstein 		for (i = 0; i != 255; i++) {
485390065b1SAlfred Perlstein 			uint8_t found;
486390065b1SAlfred Perlstein 
487390065b1SAlfred Perlstein 			pconf = libusb20_dev_alloc_config(pdev, i);
488390065b1SAlfred Perlstein 			if (pconf == NULL)
489390065b1SAlfred Perlstein 				return (LIBUSB_ERROR_INVALID_PARAM);
490390065b1SAlfred Perlstein 			found = (pconf->desc.bConfigurationValue
491390065b1SAlfred Perlstein 			    == configuration);
492390065b1SAlfred Perlstein 			free(pconf);
493390065b1SAlfred Perlstein 
494390065b1SAlfred Perlstein 			if (found)
495390065b1SAlfred Perlstein 				goto set_config;
496390065b1SAlfred Perlstein 		}
497390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
498390065b1SAlfred Perlstein 	}
499390065b1SAlfred Perlstein 
500390065b1SAlfred Perlstein set_config:
501390065b1SAlfred Perlstein 
502390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
503390065b1SAlfred Perlstein 
504390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
505390065b1SAlfred Perlstein 
506390065b1SAlfred Perlstein 	err = libusb20_dev_set_config_index(pdev, i);
507390065b1SAlfred Perlstein 
508390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll, pdev, libusb20_dev_get_fd(pdev), POLLIN |
509390065b1SAlfred Perlstein 	    POLLOUT | POLLRDNORM | POLLWRNORM);
510390065b1SAlfred Perlstein 
511390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_INVALID_PARAM : 0);
512390065b1SAlfred Perlstein }
513390065b1SAlfred Perlstein 
514390065b1SAlfred Perlstein int
515390065b1SAlfred Perlstein libusb_claim_interface(struct libusb20_device *pdev, int interface_number)
516390065b1SAlfred Perlstein {
517390065b1SAlfred Perlstein 	libusb_device *dev;
518390065b1SAlfred Perlstein 	int err = 0;
519390065b1SAlfred Perlstein 
520390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
521390065b1SAlfred Perlstein 	if (dev == NULL)
522390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
523390065b1SAlfred Perlstein 
524390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
525390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
526390065b1SAlfred Perlstein 
527390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
528390065b1SAlfred Perlstein 	if (dev->claimed_interfaces & (1 << interface_number))
529390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_BUSY;
530390065b1SAlfred Perlstein 
531390065b1SAlfred Perlstein 	if (!err)
532390065b1SAlfred Perlstein 		dev->claimed_interfaces |= (1 << interface_number);
533390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
534390065b1SAlfred Perlstein 	return (err);
535390065b1SAlfred Perlstein }
536390065b1SAlfred Perlstein 
537390065b1SAlfred Perlstein int
538390065b1SAlfred Perlstein libusb_release_interface(struct libusb20_device *pdev, int interface_number)
539390065b1SAlfred Perlstein {
540390065b1SAlfred Perlstein 	libusb_device *dev;
541390065b1SAlfred Perlstein 	int err = 0;
542390065b1SAlfred Perlstein 
543390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
544390065b1SAlfred Perlstein 	if (dev == NULL)
545390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
546390065b1SAlfred Perlstein 
547390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
548390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
549390065b1SAlfred Perlstein 
550390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
551390065b1SAlfred Perlstein 	if (!(dev->claimed_interfaces & (1 << interface_number)))
552390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_NOT_FOUND;
553390065b1SAlfred Perlstein 
554390065b1SAlfred Perlstein 	if (!err)
555390065b1SAlfred Perlstein 		dev->claimed_interfaces &= ~(1 << interface_number);
556390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
557390065b1SAlfred Perlstein 	return (err);
558390065b1SAlfred Perlstein }
559390065b1SAlfred Perlstein 
560390065b1SAlfred Perlstein int
561390065b1SAlfred Perlstein libusb_set_interface_alt_setting(struct libusb20_device *pdev,
562390065b1SAlfred Perlstein     int interface_number, int alternate_setting)
563390065b1SAlfred Perlstein {
564390065b1SAlfred Perlstein 	libusb_device *dev;
565390065b1SAlfred Perlstein 	int err = 0;
566390065b1SAlfred Perlstein 
567390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
568390065b1SAlfred Perlstein 	if (dev == NULL)
569390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
570390065b1SAlfred Perlstein 
571390065b1SAlfred Perlstein 	if (interface_number < 0 || interface_number > 31)
572390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
573390065b1SAlfred Perlstein 
574390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
575390065b1SAlfred Perlstein 	if (!(dev->claimed_interfaces & (1 << interface_number)))
576390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_NOT_FOUND;
577390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
578390065b1SAlfred Perlstein 
579390065b1SAlfred Perlstein 	if (err)
580390065b1SAlfred Perlstein 		return (err);
581390065b1SAlfred Perlstein 
582390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
583390065b1SAlfred Perlstein 
584390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
585390065b1SAlfred Perlstein 
586390065b1SAlfred Perlstein 	err = libusb20_dev_set_alt_index(pdev,
587390065b1SAlfred Perlstein 	    interface_number, alternate_setting);
588390065b1SAlfred Perlstein 
589390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll,
590390065b1SAlfred Perlstein 	    pdev, libusb20_dev_get_fd(pdev),
591390065b1SAlfred Perlstein 	    POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
592390065b1SAlfred Perlstein 
593390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_OTHER : 0);
594390065b1SAlfred Perlstein }
595390065b1SAlfred Perlstein 
596390065b1SAlfred Perlstein static struct libusb20_transfer *
597390065b1SAlfred Perlstein libusb10_get_transfer(struct libusb20_device *pdev,
598390065b1SAlfred Perlstein     uint8_t endpoint, uint8_t index)
599390065b1SAlfred Perlstein {
600390065b1SAlfred Perlstein 	index &= 1;			/* double buffering */
601390065b1SAlfred Perlstein 
602390065b1SAlfred Perlstein 	index |= (endpoint & LIBUSB20_ENDPOINT_ADDRESS_MASK) * 4;
603390065b1SAlfred Perlstein 
604390065b1SAlfred Perlstein 	if (endpoint & LIBUSB20_ENDPOINT_DIR_MASK) {
605390065b1SAlfred Perlstein 		/* this is an IN endpoint */
606390065b1SAlfred Perlstein 		index |= 2;
607390065b1SAlfred Perlstein 	}
608390065b1SAlfred Perlstein 	return (libusb20_tr_get_pointer(pdev, index));
609390065b1SAlfred Perlstein }
610390065b1SAlfred Perlstein 
611390065b1SAlfred Perlstein int
612390065b1SAlfred Perlstein libusb_clear_halt(struct libusb20_device *pdev, uint8_t endpoint)
613390065b1SAlfred Perlstein {
614390065b1SAlfred Perlstein 	struct libusb20_transfer *xfer;
615390065b1SAlfred Perlstein 	struct libusb_device *dev;
616390065b1SAlfred Perlstein 	int err;
617390065b1SAlfred Perlstein 
618390065b1SAlfred Perlstein 	xfer = libusb10_get_transfer(pdev, endpoint, 0);
619390065b1SAlfred Perlstein 	if (xfer == NULL)
620390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
621390065b1SAlfred Perlstein 
622390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
623390065b1SAlfred Perlstein 
624390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
625390065b1SAlfred Perlstein 	err = libusb20_tr_open(xfer, 0, 0, endpoint);
626390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
627390065b1SAlfred Perlstein 
628390065b1SAlfred Perlstein 	if (err != 0 && err != LIBUSB20_ERROR_BUSY)
629390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_OTHER);
630390065b1SAlfred Perlstein 
631390065b1SAlfred Perlstein 	libusb20_tr_clear_stall_sync(xfer);
632390065b1SAlfred Perlstein 
633390065b1SAlfred Perlstein 	/* check if we opened the transfer */
634390065b1SAlfred Perlstein 	if (err == 0) {
635390065b1SAlfred Perlstein 		CTX_LOCK(dev->ctx);
636390065b1SAlfred Perlstein 		libusb20_tr_close(xfer);
637390065b1SAlfred Perlstein 		CTX_UNLOCK(dev->ctx);
638390065b1SAlfred Perlstein 	}
639390065b1SAlfred Perlstein 	return (0);			/* success */
640390065b1SAlfred Perlstein }
641390065b1SAlfred Perlstein 
642390065b1SAlfred Perlstein int
643390065b1SAlfred Perlstein libusb_reset_device(struct libusb20_device *pdev)
644390065b1SAlfred Perlstein {
645390065b1SAlfred Perlstein 	libusb_device *dev;
646390065b1SAlfred Perlstein 	int err;
647390065b1SAlfred Perlstein 
648390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
649390065b1SAlfred Perlstein 	if (dev == NULL)
6508c8fff31SAndrew Thompson 		return (LIBUSB20_ERROR_INVALID_PARAM);
6518c8fff31SAndrew Thompson 
652390065b1SAlfred Perlstein 	libusb10_cancel_all_transfer(dev);
653390065b1SAlfred Perlstein 
654390065b1SAlfred Perlstein 	libusb10_remove_pollfd(dev->ctx, &dev->dev_poll);
655390065b1SAlfred Perlstein 
656390065b1SAlfred Perlstein 	err = libusb20_dev_reset(pdev);
657390065b1SAlfred Perlstein 
658390065b1SAlfred Perlstein 	libusb10_add_pollfd(dev->ctx, &dev->dev_poll,
659390065b1SAlfred Perlstein 	    pdev, libusb20_dev_get_fd(pdev),
660390065b1SAlfred Perlstein 	    POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
661390065b1SAlfred Perlstein 
662390065b1SAlfred Perlstein 	return (err ? LIBUSB_ERROR_OTHER : 0);
6638c8fff31SAndrew Thompson }
6648c8fff31SAndrew Thompson 
6658c8fff31SAndrew Thompson int
666*f1b5fa6eSHans Petter Selasky libusb_check_connected(struct libusb20_device *pdev)
667*f1b5fa6eSHans Petter Selasky {
668*f1b5fa6eSHans Petter Selasky 	libusb_device *dev;
669*f1b5fa6eSHans Petter Selasky 	int err;
670*f1b5fa6eSHans Petter Selasky 
671*f1b5fa6eSHans Petter Selasky 	dev = libusb_get_device(pdev);
672*f1b5fa6eSHans Petter Selasky 	if (dev == NULL)
673*f1b5fa6eSHans Petter Selasky 		return (LIBUSB_ERROR_INVALID_PARAM);
674*f1b5fa6eSHans Petter Selasky 
675*f1b5fa6eSHans Petter Selasky 	err = libusb20_dev_check_connected(pdev);
676*f1b5fa6eSHans Petter Selasky 
677*f1b5fa6eSHans Petter Selasky 	return (err ? LIBUSB_ERROR_NO_DEVICE : 0);
678*f1b5fa6eSHans Petter Selasky }
679*f1b5fa6eSHans Petter Selasky 
680*f1b5fa6eSHans Petter Selasky int
681390065b1SAlfred Perlstein libusb_kernel_driver_active(struct libusb20_device *pdev, int interface)
6828c8fff31SAndrew Thompson {
683390065b1SAlfred Perlstein 	if (pdev == NULL)
6848c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
6858c8fff31SAndrew Thompson 
686390065b1SAlfred Perlstein 	return (libusb20_dev_kernel_driver_active(
687390065b1SAlfred Perlstein 	    pdev, interface));
6888c8fff31SAndrew Thompson }
6898c8fff31SAndrew Thompson 
6908c8fff31SAndrew Thompson int
691390065b1SAlfred Perlstein libusb_detach_kernel_driver(struct libusb20_device *pdev, int interface)
6928c8fff31SAndrew Thompson {
693390065b1SAlfred Perlstein 	int err;
6948c8fff31SAndrew Thompson 
695390065b1SAlfred Perlstein 	if (pdev == NULL)
6968c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
6978c8fff31SAndrew Thompson 
698390065b1SAlfred Perlstein 	err = libusb20_dev_detach_kernel_driver(
699390065b1SAlfred Perlstein 	    pdev, interface);
7008c8fff31SAndrew Thompson 
701390065b1SAlfred Perlstein 	return (err ? LIBUSB20_ERROR_OTHER : 0);
7028c8fff31SAndrew Thompson }
7038c8fff31SAndrew Thompson 
7048c8fff31SAndrew Thompson int
705390065b1SAlfred Perlstein libusb_attach_kernel_driver(struct libusb20_device *pdev, int interface)
7068c8fff31SAndrew Thompson {
707390065b1SAlfred Perlstein 	if (pdev == NULL)
7088c8fff31SAndrew Thompson 		return (LIBUSB_ERROR_INVALID_PARAM);
709390065b1SAlfred Perlstein 	/* stub - currently not supported by libusb20 */
7108c8fff31SAndrew Thompson 	return (0);
7118c8fff31SAndrew Thompson }
7128c8fff31SAndrew Thompson 
7138c8fff31SAndrew Thompson /* Asynchronous device I/O */
7148c8fff31SAndrew Thompson 
7158c8fff31SAndrew Thompson struct libusb_transfer *
7168c8fff31SAndrew Thompson libusb_alloc_transfer(int iso_packets)
7178c8fff31SAndrew Thompson {
718390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
719390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
7208c8fff31SAndrew Thompson 	int len;
7218c8fff31SAndrew Thompson 
7228c8fff31SAndrew Thompson 	len = sizeof(struct libusb_transfer) +
723390065b1SAlfred Perlstein 	    sizeof(struct libusb_super_transfer) +
7248c8fff31SAndrew Thompson 	    (iso_packets * sizeof(libusb_iso_packet_descriptor));
7258c8fff31SAndrew Thompson 
726390065b1SAlfred Perlstein 	sxfer = malloc(len);
727390065b1SAlfred Perlstein 	if (sxfer == NULL)
7288c8fff31SAndrew Thompson 		return (NULL);
7298c8fff31SAndrew Thompson 
730390065b1SAlfred Perlstein 	memset(sxfer, 0, len);
7318c8fff31SAndrew Thompson 
732390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
733390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
7348c8fff31SAndrew Thompson 
735390065b1SAlfred Perlstein 	/* set default value */
736390065b1SAlfred Perlstein 	uxfer->num_iso_packets = iso_packets;
737390065b1SAlfred Perlstein 
738390065b1SAlfred Perlstein 	return (uxfer);
7398c8fff31SAndrew Thompson }
7408c8fff31SAndrew Thompson 
7418c8fff31SAndrew Thompson void
742390065b1SAlfred Perlstein libusb_free_transfer(struct libusb_transfer *uxfer)
7438c8fff31SAndrew Thompson {
744390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
7458c8fff31SAndrew Thompson 
746390065b1SAlfred Perlstein 	if (uxfer == NULL)
747390065b1SAlfred Perlstein 		return;			/* be NULL safe */
7488c8fff31SAndrew Thompson 
749390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
750390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
7518c8fff31SAndrew Thompson 
752390065b1SAlfred Perlstein 	free(sxfer);
7538c8fff31SAndrew Thompson }
7548c8fff31SAndrew Thompson 
7558c8fff31SAndrew Thompson static int
756390065b1SAlfred Perlstein libusb10_get_maxframe(struct libusb20_device *pdev, libusb_transfer *xfer)
7578c8fff31SAndrew Thompson {
7588c8fff31SAndrew Thompson 	int ret;
7598c8fff31SAndrew Thompson 	int usb_speed;
7608c8fff31SAndrew Thompson 
7618c8fff31SAndrew Thompson 	usb_speed = libusb20_dev_get_speed(pdev);
7628c8fff31SAndrew Thompson 
7638c8fff31SAndrew Thompson 	switch (xfer->type) {
7648c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
7658c8fff31SAndrew Thompson 		switch (usb_speed) {
7668c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_LOW:
7678c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_FULL:
7688c8fff31SAndrew Thompson 			ret = 60 * 1;
7698c8fff31SAndrew Thompson 			break;
7708c8fff31SAndrew Thompson 		default:
7718c8fff31SAndrew Thompson 			ret = 60 * 8;
7728c8fff31SAndrew Thompson 			break;
7738c8fff31SAndrew Thompson 		}
7748c8fff31SAndrew Thompson 		break;
7758c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_CONTROL:
7768c8fff31SAndrew Thompson 		ret = 2;
7778c8fff31SAndrew Thompson 		break;
7788c8fff31SAndrew Thompson 	default:
7798c8fff31SAndrew Thompson 		ret = 1;
7808c8fff31SAndrew Thompson 		break;
7818c8fff31SAndrew Thompson 	}
782390065b1SAlfred Perlstein 	return (ret);
7838c8fff31SAndrew Thompson }
7848c8fff31SAndrew Thompson 
7858c8fff31SAndrew Thompson static int
786390065b1SAlfred Perlstein libusb10_get_buffsize(struct libusb20_device *pdev, libusb_transfer *xfer)
7878c8fff31SAndrew Thompson {
7888c8fff31SAndrew Thompson 	int ret;
7898c8fff31SAndrew Thompson 	int usb_speed;
7908c8fff31SAndrew Thompson 
7918c8fff31SAndrew Thompson 	usb_speed = libusb20_dev_get_speed(pdev);
7928c8fff31SAndrew Thompson 
7938c8fff31SAndrew Thompson 	switch (xfer->type) {
7948c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
795390065b1SAlfred Perlstein 		ret = 0;		/* kernel will auto-select */
7968c8fff31SAndrew Thompson 		break;
7978c8fff31SAndrew Thompson 	case LIBUSB_TRANSFER_TYPE_CONTROL:
798390065b1SAlfred Perlstein 		ret = 1024;
7998c8fff31SAndrew Thompson 		break;
8008c8fff31SAndrew Thompson 	default:
8018c8fff31SAndrew Thompson 		switch (usb_speed) {
8028c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_LOW:
8038c8fff31SAndrew Thompson 			ret = 256;
8048c8fff31SAndrew Thompson 			break;
8058c8fff31SAndrew Thompson 		case LIBUSB20_SPEED_FULL:
8068c8fff31SAndrew Thompson 			ret = 4096;
8078c8fff31SAndrew Thompson 			break;
8088c8fff31SAndrew Thompson 		default:
8098c8fff31SAndrew Thompson 			ret = 16384;
8108c8fff31SAndrew Thompson 			break;
8118c8fff31SAndrew Thompson 		}
8128c8fff31SAndrew Thompson 		break;
8138c8fff31SAndrew Thompson 	}
814390065b1SAlfred Perlstein 	return (ret);
8158c8fff31SAndrew Thompson }
8168c8fff31SAndrew Thompson 
817390065b1SAlfred Perlstein static int
818390065b1SAlfred Perlstein libusb10_convert_error(uint8_t status)
819390065b1SAlfred Perlstein {
820390065b1SAlfred Perlstein 	;				/* indent fix */
821390065b1SAlfred Perlstein 
822390065b1SAlfred Perlstein 	switch (status) {
823390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
824390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
825390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_COMPLETED);
826390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_OVERFLOW:
827390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_OVERFLOW);
828390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_NO_DEVICE:
829390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_NO_DEVICE);
830390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_STALL:
831390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_STALL);
832390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_CANCELLED:
833390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_CANCELLED);
834390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_TIMED_OUT:
835390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_TIMED_OUT);
836390065b1SAlfred Perlstein 	default:
837390065b1SAlfred Perlstein 		return (LIBUSB_TRANSFER_ERROR);
838390065b1SAlfred Perlstein 	}
839390065b1SAlfred Perlstein }
840390065b1SAlfred Perlstein 
841390065b1SAlfred Perlstein /* This function must be called locked */
842390065b1SAlfred Perlstein 
843c500e4ddSAndrew Thompson static void
844390065b1SAlfred Perlstein libusb10_complete_transfer(struct libusb20_transfer *pxfer,
845390065b1SAlfred Perlstein     struct libusb_super_transfer *sxfer, int status)
846c500e4ddSAndrew Thompson {
847390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
848390065b1SAlfred Perlstein 	struct libusb_device *dev;
849c500e4ddSAndrew Thompson 
850390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
851390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
852390065b1SAlfred Perlstein 
853390065b1SAlfred Perlstein 	if (pxfer != NULL)
854390065b1SAlfred Perlstein 		libusb20_tr_set_priv_sc1(pxfer, NULL);
855390065b1SAlfred Perlstein 
8564594d907SAndrew Thompson 	/* set transfer status */
857390065b1SAlfred Perlstein 	uxfer->status = status;
858390065b1SAlfred Perlstein 
8594594d907SAndrew Thompson 	/* update super transfer state */
8604594d907SAndrew Thompson 	sxfer->state = LIBUSB_SUPER_XFER_ST_NONE;
8614594d907SAndrew Thompson 
862390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
863390065b1SAlfred Perlstein 
864390065b1SAlfred Perlstein 	TAILQ_INSERT_TAIL(&dev->ctx->tr_done, sxfer, entry);
865390065b1SAlfred Perlstein }
866390065b1SAlfred Perlstein 
867390065b1SAlfred Perlstein /* This function must be called locked */
868390065b1SAlfred Perlstein 
869390065b1SAlfred Perlstein static void
870390065b1SAlfred Perlstein libusb10_isoc_proxy(struct libusb20_transfer *pxfer)
871390065b1SAlfred Perlstein {
872390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
873390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
874390065b1SAlfred Perlstein 	uint32_t actlen;
875390065b1SAlfred Perlstein 	uint16_t iso_packets;
876390065b1SAlfred Perlstein 	uint16_t i;
877390065b1SAlfred Perlstein 	uint8_t status;
878390065b1SAlfred Perlstein 	uint8_t flags;
879390065b1SAlfred Perlstein 
880390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
881390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
882390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
883390065b1SAlfred Perlstein 	iso_packets = libusb20_tr_get_max_frames(pxfer);
884390065b1SAlfred Perlstein 
885390065b1SAlfred Perlstein 	if (sxfer == NULL)
886390065b1SAlfred Perlstein 		return;			/* cancelled - nothing to do */
887390065b1SAlfred Perlstein 
888390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
889390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
890390065b1SAlfred Perlstein 
891390065b1SAlfred Perlstein 	if (iso_packets > uxfer->num_iso_packets)
892390065b1SAlfred Perlstein 		iso_packets = uxfer->num_iso_packets;
893390065b1SAlfred Perlstein 
894390065b1SAlfred Perlstein 	if (iso_packets == 0)
895390065b1SAlfred Perlstein 		return;			/* nothing to do */
896390065b1SAlfred Perlstein 
897390065b1SAlfred Perlstein 	/* make sure that the number of ISOCHRONOUS packets is valid */
898390065b1SAlfred Perlstein 	uxfer->num_iso_packets = iso_packets;
899390065b1SAlfred Perlstein 
900390065b1SAlfred Perlstein 	flags = uxfer->flags;
901c500e4ddSAndrew Thompson 
902c500e4ddSAndrew Thompson 	switch (status) {
903c500e4ddSAndrew Thompson 	case LIBUSB20_TRANSFER_COMPLETED:
904c500e4ddSAndrew Thompson 
905390065b1SAlfred Perlstein 		/* update actual length */
906390065b1SAlfred Perlstein 		uxfer->actual_length = actlen;
907390065b1SAlfred Perlstein 		for (i = 0; i != iso_packets; i++) {
908390065b1SAlfred Perlstein 			uxfer->iso_packet_desc[i].actual_length =
909390065b1SAlfred Perlstein 			    libusb20_tr_get_length(pxfer, i);
910390065b1SAlfred Perlstein 		}
911390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
912c500e4ddSAndrew Thompson 		break;
913390065b1SAlfred Perlstein 
914c500e4ddSAndrew Thompson 	case LIBUSB20_TRANSFER_START:
915390065b1SAlfred Perlstein 
916390065b1SAlfred Perlstein 		/* setup length(s) */
917390065b1SAlfred Perlstein 		actlen = 0;
918390065b1SAlfred Perlstein 		for (i = 0; i != iso_packets; i++) {
919390065b1SAlfred Perlstein 			libusb20_tr_setup_isoc(pxfer,
920390065b1SAlfred Perlstein 			    &uxfer->buffer[actlen],
921390065b1SAlfred Perlstein 			    uxfer->iso_packet_desc[i].length, i);
922390065b1SAlfred Perlstein 			actlen += uxfer->iso_packet_desc[i].length;
923c500e4ddSAndrew Thompson 		}
924390065b1SAlfred Perlstein 
925390065b1SAlfred Perlstein 		/* no remainder */
926390065b1SAlfred Perlstein 		sxfer->rem_len = 0;
927390065b1SAlfred Perlstein 
928390065b1SAlfred Perlstein 		libusb20_tr_set_total_frames(pxfer, iso_packets);
929390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
930390065b1SAlfred Perlstein 
931390065b1SAlfred Perlstein 		/* fork another USB transfer, if any */
932390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
933c500e4ddSAndrew Thompson 		break;
934390065b1SAlfred Perlstein 
935390065b1SAlfred Perlstein 	default:
936390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
937c500e4ddSAndrew Thompson 		break;
938c500e4ddSAndrew Thompson 	}
939390065b1SAlfred Perlstein }
940390065b1SAlfred Perlstein 
941390065b1SAlfred Perlstein /* This function must be called locked */
942390065b1SAlfred Perlstein 
943390065b1SAlfred Perlstein static void
944390065b1SAlfred Perlstein libusb10_bulk_intr_proxy(struct libusb20_transfer *pxfer)
945390065b1SAlfred Perlstein {
946390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
947390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
948390065b1SAlfred Perlstein 	uint32_t max_bulk;
949390065b1SAlfred Perlstein 	uint32_t actlen;
950390065b1SAlfred Perlstein 	uint8_t status;
951390065b1SAlfred Perlstein 	uint8_t flags;
952390065b1SAlfred Perlstein 
953390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
954390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
955390065b1SAlfred Perlstein 	max_bulk = libusb20_tr_get_max_total_length(pxfer);
956390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
957390065b1SAlfred Perlstein 
958390065b1SAlfred Perlstein 	if (sxfer == NULL)
959390065b1SAlfred Perlstein 		return;			/* cancelled - nothing to do */
960390065b1SAlfred Perlstein 
961390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
962390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
963390065b1SAlfred Perlstein 
964390065b1SAlfred Perlstein 	flags = uxfer->flags;
965390065b1SAlfred Perlstein 
966390065b1SAlfred Perlstein 	switch (status) {
967390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
968390065b1SAlfred Perlstein 
969390065b1SAlfred Perlstein 		uxfer->actual_length += actlen;
970390065b1SAlfred Perlstein 
971390065b1SAlfred Perlstein 		/* check for short packet */
972390065b1SAlfred Perlstein 		if (sxfer->last_len != actlen) {
973390065b1SAlfred Perlstein 			if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
974390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR);
975390065b1SAlfred Perlstein 			} else {
976390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
977390065b1SAlfred Perlstein 			}
978390065b1SAlfred Perlstein 			break;
979390065b1SAlfred Perlstein 		}
980390065b1SAlfred Perlstein 		/* check for end of data */
981390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0) {
982390065b1SAlfred Perlstein 			libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
983390065b1SAlfred Perlstein 			break;
984390065b1SAlfred Perlstein 		}
985390065b1SAlfred Perlstein 		/* FALLTHROUGH */
986390065b1SAlfred Perlstein 
987390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
988390065b1SAlfred Perlstein 		if (max_bulk > sxfer->rem_len) {
989390065b1SAlfred Perlstein 			max_bulk = sxfer->rem_len;
990390065b1SAlfred Perlstein 		}
991390065b1SAlfred Perlstein 		/* setup new BULK or INTERRUPT transaction */
992390065b1SAlfred Perlstein 		libusb20_tr_setup_bulk(pxfer,
993390065b1SAlfred Perlstein 		    sxfer->curr_data, max_bulk, uxfer->timeout);
994390065b1SAlfred Perlstein 
995390065b1SAlfred Perlstein 		/* update counters */
996390065b1SAlfred Perlstein 		sxfer->last_len = max_bulk;
997390065b1SAlfred Perlstein 		sxfer->curr_data += max_bulk;
998390065b1SAlfred Perlstein 		sxfer->rem_len -= max_bulk;
999390065b1SAlfred Perlstein 
1000390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1001390065b1SAlfred Perlstein 
1002390065b1SAlfred Perlstein 		/* check if we can fork another USB transfer */
1003390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0)
1004390065b1SAlfred Perlstein 			libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1005390065b1SAlfred Perlstein 		break;
1006390065b1SAlfred Perlstein 
1007390065b1SAlfred Perlstein 	default:
1008390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1009390065b1SAlfred Perlstein 		break;
1010390065b1SAlfred Perlstein 	}
1011390065b1SAlfred Perlstein }
1012390065b1SAlfred Perlstein 
1013390065b1SAlfred Perlstein /* This function must be called locked */
1014390065b1SAlfred Perlstein 
1015390065b1SAlfred Perlstein static void
1016390065b1SAlfred Perlstein libusb10_ctrl_proxy(struct libusb20_transfer *pxfer)
1017390065b1SAlfred Perlstein {
1018390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1019390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1020390065b1SAlfred Perlstein 	uint32_t max_bulk;
1021390065b1SAlfred Perlstein 	uint32_t actlen;
1022390065b1SAlfred Perlstein 	uint8_t status;
1023390065b1SAlfred Perlstein 	uint8_t flags;
1024390065b1SAlfred Perlstein 
1025390065b1SAlfred Perlstein 	status = libusb20_tr_get_status(pxfer);
1026390065b1SAlfred Perlstein 	sxfer = libusb20_tr_get_priv_sc1(pxfer);
1027390065b1SAlfred Perlstein 	max_bulk = libusb20_tr_get_max_total_length(pxfer);
1028390065b1SAlfred Perlstein 	actlen = libusb20_tr_get_actual_length(pxfer);
1029390065b1SAlfred Perlstein 
1030390065b1SAlfred Perlstein 	if (sxfer == NULL)
1031390065b1SAlfred Perlstein 		return;			/* cancelled - nothing to do */
1032390065b1SAlfred Perlstein 
1033390065b1SAlfred Perlstein 	uxfer = (struct libusb_transfer *)(
1034390065b1SAlfred Perlstein 	    ((uint8_t *)sxfer) + sizeof(*sxfer));
1035390065b1SAlfred Perlstein 
1036390065b1SAlfred Perlstein 	flags = uxfer->flags;
1037390065b1SAlfred Perlstein 
1038390065b1SAlfred Perlstein 	switch (status) {
1039390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_COMPLETED:
1040390065b1SAlfred Perlstein 
1041390065b1SAlfred Perlstein 		uxfer->actual_length += actlen;
1042390065b1SAlfred Perlstein 
1043390065b1SAlfred Perlstein 		/* subtract length of SETUP packet, if any */
1044390065b1SAlfred Perlstein 		actlen -= libusb20_tr_get_length(pxfer, 0);
1045390065b1SAlfred Perlstein 
1046390065b1SAlfred Perlstein 		/* check for short packet */
1047390065b1SAlfred Perlstein 		if (sxfer->last_len != actlen) {
1048390065b1SAlfred Perlstein 			if (flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
1049390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_ERROR);
1050390065b1SAlfred Perlstein 			} else {
1051390065b1SAlfred Perlstein 				libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1052390065b1SAlfred Perlstein 			}
1053390065b1SAlfred Perlstein 			break;
1054390065b1SAlfred Perlstein 		}
1055390065b1SAlfred Perlstein 		/* check for end of data */
1056390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0) {
1057390065b1SAlfred Perlstein 			libusb10_complete_transfer(pxfer, sxfer, LIBUSB_TRANSFER_COMPLETED);
1058390065b1SAlfred Perlstein 			break;
1059390065b1SAlfred Perlstein 		}
1060390065b1SAlfred Perlstein 		/* FALLTHROUGH */
1061390065b1SAlfred Perlstein 
1062390065b1SAlfred Perlstein 	case LIBUSB20_TRANSFER_START:
1063390065b1SAlfred Perlstein 		if (max_bulk > sxfer->rem_len) {
1064390065b1SAlfred Perlstein 			max_bulk = sxfer->rem_len;
1065390065b1SAlfred Perlstein 		}
1066390065b1SAlfred Perlstein 		/* setup new CONTROL transaction */
1067390065b1SAlfred Perlstein 		if (status == LIBUSB20_TRANSFER_COMPLETED) {
1068390065b1SAlfred Perlstein 			/* next fragment - don't send SETUP packet */
1069390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, 0, 0);
1070390065b1SAlfred Perlstein 		} else {
1071390065b1SAlfred Perlstein 			/* first fragment - send SETUP packet */
1072390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, 8, 0);
1073390065b1SAlfred Perlstein 			libusb20_tr_set_buffer(pxfer, uxfer->buffer, 0);
1074390065b1SAlfred Perlstein 		}
1075390065b1SAlfred Perlstein 
1076390065b1SAlfred Perlstein 		if (max_bulk != 0) {
1077390065b1SAlfred Perlstein 			libusb20_tr_set_length(pxfer, max_bulk, 1);
1078390065b1SAlfred Perlstein 			libusb20_tr_set_buffer(pxfer, sxfer->curr_data, 1);
1079390065b1SAlfred Perlstein 			libusb20_tr_set_total_frames(pxfer, 2);
1080390065b1SAlfred Perlstein 		} else {
1081390065b1SAlfred Perlstein 			libusb20_tr_set_total_frames(pxfer, 1);
1082390065b1SAlfred Perlstein 		}
1083390065b1SAlfred Perlstein 
1084390065b1SAlfred Perlstein 		/* update counters */
1085390065b1SAlfred Perlstein 		sxfer->last_len = max_bulk;
1086390065b1SAlfred Perlstein 		sxfer->curr_data += max_bulk;
1087390065b1SAlfred Perlstein 		sxfer->rem_len -= max_bulk;
1088390065b1SAlfred Perlstein 
1089390065b1SAlfred Perlstein 		libusb20_tr_submit(pxfer);
1090390065b1SAlfred Perlstein 
1091390065b1SAlfred Perlstein 		/* check if we can fork another USB transfer */
1092390065b1SAlfred Perlstein 		if (sxfer->rem_len == 0)
1093390065b1SAlfred Perlstein 			libusb10_submit_transfer_sub(libusb20_tr_get_priv_sc0(pxfer), uxfer->endpoint);
1094390065b1SAlfred Perlstein 		break;
1095390065b1SAlfred Perlstein 
1096390065b1SAlfred Perlstein 	default:
1097390065b1SAlfred Perlstein 		libusb10_complete_transfer(pxfer, sxfer, libusb10_convert_error(status));
1098390065b1SAlfred Perlstein 		break;
1099390065b1SAlfred Perlstein 	}
1100390065b1SAlfred Perlstein }
1101390065b1SAlfred Perlstein 
1102390065b1SAlfred Perlstein /* The following function must be called locked */
1103390065b1SAlfred Perlstein 
1104390065b1SAlfred Perlstein static void
1105390065b1SAlfred Perlstein libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint)
1106390065b1SAlfred Perlstein {
1107390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1108390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1109390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1110390065b1SAlfred Perlstein 	struct libusb_transfer *uxfer;
1111390065b1SAlfred Perlstein 	struct libusb_device *dev;
1112390065b1SAlfred Perlstein 	int err;
1113390065b1SAlfred Perlstein 	int buffsize;
1114390065b1SAlfred Perlstein 	int maxframe;
1115390065b1SAlfred Perlstein 	int temp;
1116390065b1SAlfred Perlstein 	uint8_t dummy;
1117390065b1SAlfred Perlstein 
1118390065b1SAlfred Perlstein 	dev = libusb_get_device(pdev);
1119390065b1SAlfred Perlstein 
1120390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(pdev, endpoint, 0);
1121390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(pdev, endpoint, 1);
1122390065b1SAlfred Perlstein 
1123390065b1SAlfred Perlstein 	if (pxfer0 == NULL || pxfer1 == NULL)
1124390065b1SAlfred Perlstein 		return;			/* shouldn't happen */
1125390065b1SAlfred Perlstein 
1126390065b1SAlfred Perlstein 	temp = 0;
1127390065b1SAlfred Perlstein 	if (libusb20_tr_pending(pxfer0))
1128390065b1SAlfred Perlstein 		temp |= 1;
1129390065b1SAlfred Perlstein 	if (libusb20_tr_pending(pxfer1))
1130390065b1SAlfred Perlstein 		temp |= 2;
1131390065b1SAlfred Perlstein 
1132390065b1SAlfred Perlstein 	switch (temp) {
1133390065b1SAlfred Perlstein 	case 3:
1134390065b1SAlfred Perlstein 		/* wait till one of the transfers complete */
1135390065b1SAlfred Perlstein 		return;
1136390065b1SAlfred Perlstein 	case 2:
1137390065b1SAlfred Perlstein 		sxfer = libusb20_tr_get_priv_sc1(pxfer1);
11384594d907SAndrew Thompson 		if (sxfer == NULL)
11394594d907SAndrew Thompson 			return;		/* cancelling */
1140390065b1SAlfred Perlstein 		if (sxfer->rem_len)
1141390065b1SAlfred Perlstein 			return;		/* cannot queue another one */
1142390065b1SAlfred Perlstein 		/* swap transfers */
1143390065b1SAlfred Perlstein 		pxfer1 = pxfer0;
1144390065b1SAlfred Perlstein 		break;
1145390065b1SAlfred Perlstein 	case 1:
1146390065b1SAlfred Perlstein 		sxfer = libusb20_tr_get_priv_sc1(pxfer0);
11474594d907SAndrew Thompson 		if (sxfer == NULL)
11484594d907SAndrew Thompson 			return;		/* cancelling */
1149390065b1SAlfred Perlstein 		if (sxfer->rem_len)
1150390065b1SAlfred Perlstein 			return;		/* cannot queue another one */
1151390065b1SAlfred Perlstein 		/* swap transfers */
1152390065b1SAlfred Perlstein 		pxfer0 = pxfer1;
1153c500e4ddSAndrew Thompson 		break;
1154c500e4ddSAndrew Thompson 	default:
1155c500e4ddSAndrew Thompson 		break;
1156c500e4ddSAndrew Thompson 	}
1157c500e4ddSAndrew Thompson 
1158390065b1SAlfred Perlstein 	/* find next transfer on same endpoint */
1159390065b1SAlfred Perlstein 	TAILQ_FOREACH(sxfer, &dev->tr_head, entry) {
1160390065b1SAlfred Perlstein 
1161390065b1SAlfred Perlstein 		uxfer = (struct libusb_transfer *)(
1162390065b1SAlfred Perlstein 		    ((uint8_t *)sxfer) + sizeof(*sxfer));
1163390065b1SAlfred Perlstein 
1164390065b1SAlfred Perlstein 		if (uxfer->endpoint == endpoint) {
1165390065b1SAlfred Perlstein 			TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
1166390065b1SAlfred Perlstein 			sxfer->entry.tqe_prev = NULL;
1167390065b1SAlfred Perlstein 			goto found;
1168c500e4ddSAndrew Thompson 		}
1169c500e4ddSAndrew Thompson 	}
1170390065b1SAlfred Perlstein 	return;				/* success */
1171390065b1SAlfred Perlstein 
1172390065b1SAlfred Perlstein found:
1173390065b1SAlfred Perlstein 
1174390065b1SAlfred Perlstein 	libusb20_tr_set_priv_sc0(pxfer0, pdev);
1175390065b1SAlfred Perlstein 	libusb20_tr_set_priv_sc1(pxfer0, sxfer);
1176390065b1SAlfred Perlstein 
1177390065b1SAlfred Perlstein 	/* reset super transfer state */
1178390065b1SAlfred Perlstein 	sxfer->rem_len = uxfer->length;
1179390065b1SAlfred Perlstein 	sxfer->curr_data = uxfer->buffer;
1180390065b1SAlfred Perlstein 	uxfer->actual_length = 0;
1181390065b1SAlfred Perlstein 
1182390065b1SAlfred Perlstein 	switch (uxfer->type) {
1183390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
1184390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_isoc_proxy);
1185390065b1SAlfred Perlstein 		break;
1186390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_BULK:
1187390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
1188390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_bulk_intr_proxy);
1189390065b1SAlfred Perlstein 		break;
1190390065b1SAlfred Perlstein 	case LIBUSB_TRANSFER_TYPE_CONTROL:
1191390065b1SAlfred Perlstein 		libusb20_tr_set_callback(pxfer0, libusb10_ctrl_proxy);
1192390065b1SAlfred Perlstein 		if (sxfer->rem_len < 8)
1193390065b1SAlfred Perlstein 			goto failure;
1194390065b1SAlfred Perlstein 
1195390065b1SAlfred Perlstein 		/* remove SETUP packet from data */
1196390065b1SAlfred Perlstein 		sxfer->rem_len -= 8;
1197390065b1SAlfred Perlstein 		sxfer->curr_data += 8;
1198390065b1SAlfred Perlstein 		break;
1199390065b1SAlfred Perlstein 	default:
1200390065b1SAlfred Perlstein 		goto failure;
1201390065b1SAlfred Perlstein 	}
1202390065b1SAlfred Perlstein 
1203390065b1SAlfred Perlstein 	buffsize = libusb10_get_buffsize(pdev, uxfer);
1204390065b1SAlfred Perlstein 	maxframe = libusb10_get_maxframe(pdev, uxfer);
1205390065b1SAlfred Perlstein 
1206390065b1SAlfred Perlstein 	/* make sure the transfer is opened */
1207390065b1SAlfred Perlstein 	err = libusb20_tr_open(pxfer0, buffsize, maxframe, endpoint);
1208390065b1SAlfred Perlstein 	if (err && (err != LIBUSB20_ERROR_BUSY)) {
1209390065b1SAlfred Perlstein 		goto failure;
1210390065b1SAlfred Perlstein 	}
1211390065b1SAlfred Perlstein 	libusb20_tr_start(pxfer0);
1212390065b1SAlfred Perlstein 	return;
1213390065b1SAlfred Perlstein 
1214390065b1SAlfred Perlstein failure:
1215390065b1SAlfred Perlstein 	libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_ERROR);
1216390065b1SAlfred Perlstein 
1217390065b1SAlfred Perlstein 	/* make sure our event loop spins the done handler */
1218390065b1SAlfred Perlstein 	dummy = 0;
1219390065b1SAlfred Perlstein 	write(dev->ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
1220390065b1SAlfred Perlstein }
1221390065b1SAlfred Perlstein 
1222390065b1SAlfred Perlstein /* The following function must be called unlocked */
1223c500e4ddSAndrew Thompson 
12248c8fff31SAndrew Thompson int
1225390065b1SAlfred Perlstein libusb_submit_transfer(struct libusb_transfer *uxfer)
12268c8fff31SAndrew Thompson {
1227390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1228390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1229390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1230390065b1SAlfred Perlstein 	struct libusb_device *dev;
1231ccef4ddfSAndrew Thompson 	uint32_t endpoint;
1232390065b1SAlfred Perlstein 	int err;
12338c8fff31SAndrew Thompson 
1234390065b1SAlfred Perlstein 	if (uxfer == NULL)
1235390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
12368c8fff31SAndrew Thompson 
1237390065b1SAlfred Perlstein 	if (uxfer->dev_handle == NULL)
1238390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
12398c8fff31SAndrew Thompson 
1240390065b1SAlfred Perlstein 	endpoint = uxfer->endpoint;
12418c8fff31SAndrew Thompson 
1242390065b1SAlfred Perlstein 	if (endpoint > 255)
1243390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
12448c8fff31SAndrew Thompson 
1245390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
12468c8fff31SAndrew Thompson 
1247390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer enter");
12488c8fff31SAndrew Thompson 
1249390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
1250390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
1251390065b1SAlfred Perlstein 
1252390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
1253390065b1SAlfred Perlstein 
1254390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0);
1255390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1);
1256390065b1SAlfred Perlstein 
1257390065b1SAlfred Perlstein 	if (pxfer0 == NULL || pxfer1 == NULL) {
1258390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_OTHER;
1259390065b1SAlfred Perlstein 	} else if ((sxfer->entry.tqe_prev != NULL) ||
1260390065b1SAlfred Perlstein 	    (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) ||
1261390065b1SAlfred Perlstein 	    (libusb20_tr_get_priv_sc1(pxfer1) == sxfer)) {
1262390065b1SAlfred Perlstein 		err = LIBUSB_ERROR_BUSY;
1263390065b1SAlfred Perlstein 	} else {
12644594d907SAndrew Thompson 
12654594d907SAndrew Thompson 		/* set pending state */
12664594d907SAndrew Thompson 		sxfer->state = LIBUSB_SUPER_XFER_ST_PEND;
12674594d907SAndrew Thompson 
12684594d907SAndrew Thompson 		/* insert transfer into transfer head list */
1269390065b1SAlfred Perlstein 		TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry);
1270390065b1SAlfred Perlstein 
12714594d907SAndrew Thompson 		/* start work transfers */
1272390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(
1273390065b1SAlfred Perlstein 		    uxfer->dev_handle, endpoint);
1274390065b1SAlfred Perlstein 
1275390065b1SAlfred Perlstein 		err = 0;		/* success */
12768c8fff31SAndrew Thompson 	}
12778c8fff31SAndrew Thompson 
1278390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
1279390065b1SAlfred Perlstein 
1280390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_submit_transfer leave %d", err);
1281390065b1SAlfred Perlstein 
1282390065b1SAlfred Perlstein 	return (err);
12838c8fff31SAndrew Thompson }
12848c8fff31SAndrew Thompson 
1285390065b1SAlfred Perlstein /* Asynchronous transfer cancel */
12868c8fff31SAndrew Thompson 
1287390065b1SAlfred Perlstein int
1288390065b1SAlfred Perlstein libusb_cancel_transfer(struct libusb_transfer *uxfer)
1289390065b1SAlfred Perlstein {
1290390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer0;
1291390065b1SAlfred Perlstein 	struct libusb20_transfer *pxfer1;
1292390065b1SAlfred Perlstein 	struct libusb_super_transfer *sxfer;
1293390065b1SAlfred Perlstein 	struct libusb_device *dev;
1294ccef4ddfSAndrew Thompson 	uint32_t endpoint;
12954594d907SAndrew Thompson 	int retval;
12968c8fff31SAndrew Thompson 
1297390065b1SAlfred Perlstein 	if (uxfer == NULL)
1298390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
12998c8fff31SAndrew Thompson 
13004594d907SAndrew Thompson 	/* check if not initialised */
1301390065b1SAlfred Perlstein 	if (uxfer->dev_handle == NULL)
13024594d907SAndrew Thompson 		return (LIBUSB_ERROR_NOT_FOUND);
13038c8fff31SAndrew Thompson 
1304390065b1SAlfred Perlstein 	endpoint = uxfer->endpoint;
13058c8fff31SAndrew Thompson 
1306390065b1SAlfred Perlstein 	if (endpoint > 255)
1307390065b1SAlfred Perlstein 		return (LIBUSB_ERROR_INVALID_PARAM);
13088c8fff31SAndrew Thompson 
1309390065b1SAlfred Perlstein 	dev = libusb_get_device(uxfer->dev_handle);
13108c8fff31SAndrew Thompson 
1311390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer enter");
13128c8fff31SAndrew Thompson 
1313390065b1SAlfred Perlstein 	sxfer = (struct libusb_super_transfer *)(
1314390065b1SAlfred Perlstein 	    (uint8_t *)uxfer - sizeof(*sxfer));
1315390065b1SAlfred Perlstein 
13164594d907SAndrew Thompson 	retval = 0;
13174594d907SAndrew Thompson 
1318390065b1SAlfred Perlstein 	CTX_LOCK(dev->ctx);
1319390065b1SAlfred Perlstein 
1320390065b1SAlfred Perlstein 	pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0);
1321390065b1SAlfred Perlstein 	pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1);
1322390065b1SAlfred Perlstein 
13234594d907SAndrew Thompson 	if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) {
13244594d907SAndrew Thompson 		/* only update the transfer status */
13254594d907SAndrew Thompson 		uxfer->status = LIBUSB_TRANSFER_CANCELLED;
13264594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
13274594d907SAndrew Thompson 	} else if (sxfer->entry.tqe_prev != NULL) {
1328390065b1SAlfred Perlstein 		/* we are lucky - transfer is on a queue */
1329390065b1SAlfred Perlstein 		TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
1330390065b1SAlfred Perlstein 		sxfer->entry.tqe_prev = NULL;
13314594d907SAndrew Thompson 		libusb10_complete_transfer(NULL,
13324594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1333390065b1SAlfred Perlstein 	} else if (pxfer0 == NULL || pxfer1 == NULL) {
1334390065b1SAlfred Perlstein 		/* not started */
13354594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
1336390065b1SAlfred Perlstein 	} else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) {
13374594d907SAndrew Thompson 		libusb10_complete_transfer(pxfer0,
13384594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1339390065b1SAlfred Perlstein 		libusb20_tr_stop(pxfer0);
1340390065b1SAlfred Perlstein 		/* make sure the queue doesn't stall */
1341390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(
1342390065b1SAlfred Perlstein 		    uxfer->dev_handle, endpoint);
1343390065b1SAlfred Perlstein 	} else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) {
13444594d907SAndrew Thompson 		libusb10_complete_transfer(pxfer1,
13454594d907SAndrew Thompson 		    sxfer, LIBUSB_TRANSFER_CANCELLED);
1346390065b1SAlfred Perlstein 		libusb20_tr_stop(pxfer1);
1347390065b1SAlfred Perlstein 		/* make sure the queue doesn't stall */
1348390065b1SAlfred Perlstein 		libusb10_submit_transfer_sub(
1349390065b1SAlfred Perlstein 		    uxfer->dev_handle, endpoint);
1350390065b1SAlfred Perlstein 	} else {
1351390065b1SAlfred Perlstein 		/* not started */
13524594d907SAndrew Thompson 		retval = LIBUSB_ERROR_NOT_FOUND;
1353c500e4ddSAndrew Thompson 	}
13548c8fff31SAndrew Thompson 
1355390065b1SAlfred Perlstein 	CTX_UNLOCK(dev->ctx);
13568c8fff31SAndrew Thompson 
1357390065b1SAlfred Perlstein 	DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave");
13588c8fff31SAndrew Thompson 
13594594d907SAndrew Thompson 	return (retval);
13608c8fff31SAndrew Thompson }
13618c8fff31SAndrew Thompson 
1362390065b1SAlfred Perlstein UNEXPORTED void
1363390065b1SAlfred Perlstein libusb10_cancel_all_transfer(libusb_device *dev)
13648c8fff31SAndrew Thompson {
1365390065b1SAlfred Perlstein 	/* TODO */
13668c8fff31SAndrew Thompson }
1367ccef4ddfSAndrew Thompson 
1368ccef4ddfSAndrew Thompson uint16_t
1369ccef4ddfSAndrew Thompson libusb_cpu_to_le16(uint16_t x)
1370ccef4ddfSAndrew Thompson {
1371ccef4ddfSAndrew Thompson 	return (htole16(x));
1372ccef4ddfSAndrew Thompson }
1373ccef4ddfSAndrew Thompson 
1374ccef4ddfSAndrew Thompson uint16_t
1375ccef4ddfSAndrew Thompson libusb_le16_to_cpu(uint16_t x)
1376ccef4ddfSAndrew Thompson {
1377ccef4ddfSAndrew Thompson 	return (le16toh(x));
1378ccef4ddfSAndrew Thompson }
1379ccef4ddfSAndrew Thompson 
1380