libusb20.c revision 188622
1/* $FreeBSD: head/lib/libusb20/libusb20.c 188622 2009-02-14 23:20:00Z thompsa $ */
2/*-
3 * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <poll.h>
31#include <ctype.h>
32#include <sys/queue.h>
33
34#include "libusb20.h"
35#include "libusb20_desc.h"
36#include "libusb20_int.h"
37
38static int
39dummy_int(void)
40{
41	return (LIBUSB20_ERROR_NOT_SUPPORTED);
42}
43
44static void
45dummy_void(void)
46{
47	return;
48}
49
50static void
51dummy_callback(struct libusb20_transfer *xfer)
52{
53	;				/* style fix */
54	switch (libusb20_tr_get_status(xfer)) {
55	case LIBUSB20_TRANSFER_START:
56		libusb20_tr_submit(xfer);
57		break;
58	default:
59		/* complete or error */
60		break;
61	}
62	return;
63}
64
65#define	dummy_get_config_desc_full (void *)dummy_int
66#define	dummy_get_config_index (void *)dummy_int
67#define	dummy_set_config_index (void *)dummy_int
68#define	dummy_claim_interface (void *)dummy_int
69#define	dummy_release_interface (void *)dummy_int
70#define	dummy_set_alt_index (void *)dummy_int
71#define	dummy_reset_device (void *)dummy_int
72#define	dummy_set_power_mode (void *)dummy_int
73#define	dummy_get_power_mode (void *)dummy_int
74#define	dummy_kernel_driver_active (void *)dummy_int
75#define	dummy_detach_kernel_driver (void *)dummy_int
76#define	dummy_do_request_sync (void *)dummy_int
77#define	dummy_tr_open (void *)dummy_int
78#define	dummy_tr_close (void *)dummy_int
79#define	dummy_tr_clear_stall_sync (void *)dummy_int
80#define	dummy_process (void *)dummy_int
81#define	dummy_dev_info (void *)dummy_int
82#define	dummy_dev_get_iface_driver (void *)dummy_int
83
84#define	dummy_tr_submit (void *)dummy_void
85#define	dummy_tr_cancel_async (void *)dummy_void
86
87static const struct libusb20_device_methods libusb20_dummy_methods = {
88	LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy)
89};
90
91void
92libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer)
93{
94	;				/* style fix */
95
96repeat:
97
98	if (!xfer->is_pending) {
99		xfer->status = LIBUSB20_TRANSFER_START;
100	} else {
101		xfer->is_pending = 0;
102	}
103
104	xfer->callback(xfer);
105
106	if (xfer->is_restart) {
107		xfer->is_restart = 0;
108		goto repeat;
109	}
110	if (xfer->is_draining &&
111	    (!xfer->is_pending)) {
112		xfer->is_draining = 0;
113		xfer->status = LIBUSB20_TRANSFER_DRAINED;
114		xfer->callback(xfer);
115	}
116	return;
117}
118
119int
120libusb20_tr_close(struct libusb20_transfer *xfer)
121{
122	int error;
123
124	if (!xfer->is_opened) {
125		return (LIBUSB20_ERROR_OTHER);
126	}
127	error = xfer->pdev->methods->tr_close(xfer);
128
129	if (xfer->pLength) {
130		free(xfer->pLength);
131	}
132	if (xfer->ppBuffer) {
133		free(xfer->ppBuffer);
134	}
135	/* clear some fields */
136	xfer->is_opened = 0;
137	xfer->maxFrames = 0;
138	xfer->maxTotalLength = 0;
139	xfer->maxPacketLen = 0;
140	return (error);
141}
142
143int
144libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
145    uint32_t MaxFrameCount, uint8_t ep_no)
146{
147	uint32_t size;
148	int error;
149
150	if (xfer->is_opened) {
151		return (LIBUSB20_ERROR_BUSY);
152	}
153	if (MaxFrameCount == 0) {
154		return (LIBUSB20_ERROR_INVALID_PARAM);
155	}
156	xfer->maxFrames = MaxFrameCount;
157
158	size = MaxFrameCount * sizeof(xfer->pLength[0]);
159	xfer->pLength = malloc(size);
160	if (xfer->pLength == NULL) {
161		return (LIBUSB20_ERROR_NO_MEM);
162	}
163	memset(xfer->pLength, 0, size);
164
165	size = MaxFrameCount * sizeof(xfer->ppBuffer[0]);
166	xfer->ppBuffer = malloc(size);
167	if (xfer->ppBuffer == NULL) {
168		free(xfer->pLength);
169		return (LIBUSB20_ERROR_NO_MEM);
170	}
171	memset(xfer->ppBuffer, 0, size);
172
173	error = xfer->pdev->methods->tr_open(xfer, MaxBufSize,
174	    MaxFrameCount, ep_no);
175
176	if (error) {
177		free(xfer->ppBuffer);
178		free(xfer->pLength);
179	} else {
180		xfer->is_opened = 1;
181	}
182	return (error);
183}
184
185struct libusb20_transfer *
186libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex)
187{
188	if (trIndex >= pdev->nTransfer) {
189		return (NULL);
190	}
191	return (pdev->pTransfer + trIndex);
192}
193
194uint32_t
195libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer)
196{
197	return (xfer->aFrames);
198}
199
200uint16_t
201libusb20_tr_get_time_complete(struct libusb20_transfer *xfer)
202{
203	return (xfer->timeComplete);
204}
205
206uint32_t
207libusb20_tr_get_actual_length(struct libusb20_transfer *xfer)
208{
209	uint32_t x;
210	uint32_t actlen = 0;
211
212	for (x = 0; x != xfer->aFrames; x++) {
213		actlen += xfer->pLength[x];
214	}
215	return (actlen);
216}
217
218uint32_t
219libusb20_tr_get_max_frames(struct libusb20_transfer *xfer)
220{
221	return (xfer->maxFrames);
222}
223
224uint32_t
225libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer)
226{
227	/*
228	 * Special Case NOTE: If the packet multiplier is non-zero for
229	 * High Speed USB, the value returned is equal to
230	 * "wMaxPacketSize * multiplier" !
231	 */
232	return (xfer->maxPacketLen);
233}
234
235uint32_t
236libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer)
237{
238	return (xfer->maxTotalLength);
239}
240
241uint8_t
242libusb20_tr_get_status(struct libusb20_transfer *xfer)
243{
244	return (xfer->status);
245}
246
247uint8_t
248libusb20_tr_pending(struct libusb20_transfer *xfer)
249{
250	return (xfer->is_pending);
251}
252
253void   *
254libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer)
255{
256	return (xfer->priv_sc0);
257}
258
259void   *
260libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer)
261{
262	return (xfer->priv_sc1);
263}
264
265void
266libusb20_tr_stop(struct libusb20_transfer *xfer)
267{
268	if (!xfer->is_pending) {
269		/* transfer not pending */
270		return;
271	}
272	if (xfer->is_cancel) {
273		/* already cancelling */
274		return;
275	}
276	xfer->is_cancel = 1;		/* we are cancelling */
277
278	xfer->pdev->methods->tr_cancel_async(xfer);
279	return;
280}
281
282void
283libusb20_tr_drain(struct libusb20_transfer *xfer)
284{
285	/* make sure that we are cancelling */
286	libusb20_tr_stop(xfer);
287
288	if (xfer->is_pending) {
289		xfer->is_draining = 1;
290	}
291	return;
292}
293
294void
295libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
296{
297	xfer->pdev->methods->tr_clear_stall_sync(xfer);
298	return;
299}
300
301void
302libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex)
303{
304	xfer->ppBuffer[frIndex] = buffer;
305	return;
306}
307
308void
309libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb)
310{
311	xfer->callback = cb;
312	return;
313}
314
315void
316libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags)
317{
318	xfer->flags = flags;
319	return;
320}
321
322void
323libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex)
324{
325	xfer->pLength[frIndex] = length;
326	return;
327}
328
329void
330libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0)
331{
332	xfer->priv_sc0 = sc0;
333	return;
334}
335
336void
337libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1)
338{
339	xfer->priv_sc1 = sc1;
340	return;
341}
342
343void
344libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout)
345{
346	xfer->timeout = timeout;
347	return;
348}
349
350void
351libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames)
352{
353	if (nFrames > xfer->maxFrames) {
354		/* should not happen */
355		nFrames = xfer->maxFrames;
356	}
357	xfer->nFrames = nFrames;
358	return;
359}
360
361void
362libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout)
363{
364	xfer->ppBuffer[0] = pBuf;
365	xfer->pLength[0] = length;
366	xfer->timeout = timeout;
367	xfer->nFrames = 1;
368	return;
369}
370
371void
372libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout)
373{
374	uint16_t len;
375
376	xfer->ppBuffer[0] = psetup;
377	xfer->pLength[0] = 8;		/* fixed */
378	xfer->timeout = timeout;
379
380	len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8);
381
382	if (len != 0) {
383		xfer->nFrames = 2;
384		xfer->ppBuffer[1] = pBuf;
385		xfer->pLength[1] = len;
386	} else {
387		xfer->nFrames = 1;
388	}
389	return;
390}
391
392void
393libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout)
394{
395	xfer->ppBuffer[0] = pBuf;
396	xfer->pLength[0] = length;
397	xfer->timeout = timeout;
398	xfer->nFrames = 1;
399	return;
400}
401
402void
403libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex)
404{
405	if (frIndex >= xfer->maxFrames) {
406		/* should not happen */
407		return;
408	}
409	xfer->ppBuffer[frIndex] = pBuf;
410	xfer->pLength[frIndex] = length;
411	return;
412}
413
414void
415libusb20_tr_submit(struct libusb20_transfer *xfer)
416{
417	if (xfer->is_pending) {
418		/* should not happen */
419		return;
420	}
421	xfer->is_pending = 1;		/* we are pending */
422	xfer->is_cancel = 0;		/* not cancelling */
423	xfer->is_restart = 0;		/* not restarting */
424
425	xfer->pdev->methods->tr_submit(xfer);
426	return;
427}
428
429void
430libusb20_tr_start(struct libusb20_transfer *xfer)
431{
432	if (xfer->is_pending) {
433		if (xfer->is_cancel) {
434			/* cancelling - restart */
435			xfer->is_restart = 1;
436		}
437		/* transfer not pending */
438		return;
439	}
440	/* get into the callback */
441	libusb20_tr_callback_wrapper(xfer);
442	return;
443}
444
445/* USB device operations */
446
447int
448libusb20_dev_claim_interface(struct libusb20_device *pdev, uint8_t ifaceIndex)
449{
450	int error;
451
452	if (ifaceIndex >= 32) {
453		error = LIBUSB20_ERROR_INVALID_PARAM;
454	} else if (pdev->claimed_interfaces & (1 << ifaceIndex)) {
455		error = LIBUSB20_ERROR_NOT_FOUND;
456	} else {
457		error = pdev->methods->claim_interface(pdev, ifaceIndex);
458	}
459	if (!error) {
460		pdev->claimed_interfaces |= (1 << ifaceIndex);
461	}
462	return (error);
463}
464
465int
466libusb20_dev_close(struct libusb20_device *pdev)
467{
468	struct libusb20_transfer *xfer;
469	uint16_t x;
470	int error = 0;
471
472	if (!pdev->is_opened) {
473		return (LIBUSB20_ERROR_OTHER);
474	}
475	for (x = 0; x != pdev->nTransfer; x++) {
476		xfer = pdev->pTransfer + x;
477
478		libusb20_tr_drain(xfer);
479	}
480
481	if (pdev->pTransfer != NULL) {
482		free(pdev->pTransfer);
483		pdev->pTransfer = NULL;
484	}
485	error = pdev->beMethods->close_device(pdev);
486
487	pdev->methods = &libusb20_dummy_methods;
488
489	pdev->is_opened = 0;
490
491	pdev->claimed_interfaces = 0;
492
493	return (error);
494}
495
496int
497libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex)
498{
499	int error;
500
501	error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex);
502	return (error);
503}
504
505struct LIBUSB20_DEVICE_DESC_DECODED *
506libusb20_dev_get_device_desc(struct libusb20_device *pdev)
507{
508	return (&(pdev->ddesc));
509}
510
511int
512libusb20_dev_get_fd(struct libusb20_device *pdev)
513{
514	return (pdev->file);
515}
516
517int
518libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex)
519{
520	int error;
521
522	error = pdev->methods->kernel_driver_active(pdev, ifaceIndex);
523	return (error);
524}
525
526int
527libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax)
528{
529	struct libusb20_transfer *xfer;
530	uint32_t size;
531	uint16_t x;
532	int error;
533
534	if (pdev->is_opened) {
535		return (LIBUSB20_ERROR_BUSY);
536	}
537	if (nTransferMax >= 256) {
538		return (LIBUSB20_ERROR_INVALID_PARAM);
539	} else if (nTransferMax != 0) {
540		size = sizeof(pdev->pTransfer[0]) * nTransferMax;
541		pdev->pTransfer = malloc(size);
542		if (pdev->pTransfer == NULL) {
543			return (LIBUSB20_ERROR_NO_MEM);
544		}
545		memset(pdev->pTransfer, 0, size);
546	}
547	/* initialise all transfers */
548	for (x = 0; x != nTransferMax; x++) {
549
550		xfer = pdev->pTransfer + x;
551
552		xfer->pdev = pdev;
553		xfer->trIndex = x;
554		xfer->callback = &dummy_callback;
555	}
556
557	/* set "nTransfer" early */
558	pdev->nTransfer = nTransferMax;
559
560	error = pdev->beMethods->open_device(pdev, nTransferMax);
561
562	if (error) {
563		if (pdev->pTransfer != NULL) {
564			free(pdev->pTransfer);
565			pdev->pTransfer = NULL;
566		}
567		pdev->file = -1;
568		pdev->file_ctrl = -1;
569		pdev->nTransfer = 0;
570	} else {
571		pdev->is_opened = 1;
572	}
573	return (error);
574}
575
576int
577libusb20_dev_release_interface(struct libusb20_device *pdev, uint8_t ifaceIndex)
578{
579	int error;
580
581	if (ifaceIndex >= 32) {
582		error = LIBUSB20_ERROR_INVALID_PARAM;
583	} else if (!(pdev->claimed_interfaces & (1 << ifaceIndex))) {
584		error = LIBUSB20_ERROR_NOT_FOUND;
585	} else {
586		error = pdev->methods->release_interface(pdev, ifaceIndex);
587	}
588	if (!error) {
589		pdev->claimed_interfaces &= ~(1 << ifaceIndex);
590	}
591	return (error);
592}
593
594int
595libusb20_dev_reset(struct libusb20_device *pdev)
596{
597	int error;
598
599	error = pdev->methods->reset_device(pdev);
600	return (error);
601}
602
603int
604libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
605{
606	int error;
607
608	error = pdev->methods->set_power_mode(pdev, power_mode);
609	return (error);
610}
611
612uint8_t
613libusb20_dev_get_power_mode(struct libusb20_device *pdev)
614{
615	int error;
616	uint8_t power_mode;
617
618	error = pdev->methods->get_power_mode(pdev, &power_mode);
619	if (error)
620		power_mode = LIBUSB20_POWER_ON;	/* fake power mode */
621	return (power_mode);
622}
623
624int
625libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex)
626{
627	int error;
628
629	error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex);
630	return (error);
631}
632
633int
634libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex)
635{
636	int error;
637
638	error = pdev->methods->set_config_index(pdev, configIndex);
639	return (error);
640}
641
642int
643libusb20_dev_request_sync(struct libusb20_device *pdev,
644    struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data,
645    uint16_t *pactlen, uint32_t timeout, uint8_t flags)
646{
647	int error;
648
649	error = pdev->methods->do_request_sync(pdev,
650	    setup, data, pactlen, timeout, flags);
651	return (error);
652}
653
654int
655libusb20_dev_req_string_sync(struct libusb20_device *pdev,
656    uint8_t str_index, uint16_t langid, void *ptr, uint16_t len)
657{
658	struct LIBUSB20_CONTROL_SETUP_DECODED req;
659	int error;
660
661	if (len < 4) {
662		/* invalid length */
663		return (LIBUSB20_ERROR_INVALID_PARAM);
664	}
665	LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req);
666
667	/*
668	 * We need to read the USB string in two steps else some USB
669	 * devices will complain.
670	 */
671	req.bmRequestType =
672	    LIBUSB20_REQUEST_TYPE_STANDARD |
673	    LIBUSB20_RECIPIENT_DEVICE |
674	    LIBUSB20_ENDPOINT_IN;
675	req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR;
676	req.wValue = (LIBUSB20_DT_STRING << 8) | str_index;
677	req.wIndex = langid;
678	req.wLength = 4;		/* bytes */
679
680	error = libusb20_dev_request_sync(pdev, &req,
681	    ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK);
682	if (error) {
683		return (error);
684	}
685	req.wLength = *(uint8_t *)ptr;	/* bytes */
686	if (req.wLength > len) {
687		/* partial string read */
688		req.wLength = len;
689	}
690	error = libusb20_dev_request_sync(pdev, &req,
691	    ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK);
692
693	if (error) {
694		return (error);
695	}
696	if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING) {
697		return (LIBUSB20_ERROR_OTHER);
698	}
699	return (0);			/* success */
700}
701
702int
703libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev,
704    uint8_t str_index, void *ptr, uint16_t len)
705{
706	char *buf;
707	int error;
708	uint16_t langid;
709	uint16_t n;
710	uint16_t i;
711	uint16_t c;
712	uint8_t temp[255];
713	uint8_t swap;
714
715	/* the following code derives from the FreeBSD USB kernel */
716
717	if ((len < 1) || (ptr == NULL)) {
718		/* too short buffer */
719		return (LIBUSB20_ERROR_INVALID_PARAM);
720	}
721	error = libusb20_dev_req_string_sync(pdev,
722	    0, 0, temp, sizeof(temp));
723	if (error < 0) {
724		*(uint8_t *)ptr = 0;	/* zero terminate */
725		return (error);
726	}
727	langid = temp[2] | (temp[3] << 8);
728
729	error = libusb20_dev_req_string_sync(pdev, str_index,
730	    langid, temp, sizeof(temp));
731	if (error < 0) {
732		*(uint8_t *)ptr = 0;	/* zero terminate */
733		return (error);
734	}
735	if (temp[0] < 2) {
736		/* string length is too short */
737		*(uint8_t *)ptr = 0;	/* zero terminate */
738		return (LIBUSB20_ERROR_OTHER);
739	}
740	/* reserve one byte for terminating zero */
741	len--;
742
743	/* find maximum length */
744	n = (temp[0] / 2) - 1;
745	if (n > len) {
746		n = len;
747	}
748	/* reset swap state */
749	swap = 3;
750
751	/* setup output buffer pointer */
752	buf = ptr;
753
754	/* convert and filter */
755	for (i = 0; (i != n); i++) {
756		c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8);
757
758		/* convert from Unicode, handle buggy strings */
759		if (((c & 0xff00) == 0) && (swap & 1)) {
760			/* Little Endian, default */
761			*buf = c;
762			swap = 1;
763		} else if (((c & 0x00ff) == 0) && (swap & 2)) {
764			/* Big Endian */
765			*buf = c >> 8;
766			swap = 2;
767		} else {
768			/* skip invalid character */
769			continue;
770		}
771		/*
772		 * Filter by default - we don't allow greater and less than
773		 * signs because they might confuse the dmesg printouts!
774		 */
775		if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) {
776			/* skip invalid character */
777			continue;
778		}
779		buf++;
780	}
781	*buf = 0;			/* zero terminate string */
782
783	return (0);
784}
785
786struct libusb20_config *
787libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex)
788{
789	struct libusb20_config *retval = NULL;
790	uint8_t *ptr;
791	uint16_t len;
792	uint8_t do_close;
793	int error;
794
795	if (!pdev->is_opened) {
796		error = libusb20_dev_open(pdev, 0);
797		if (error) {
798			return (NULL);
799		}
800		do_close = 1;
801	} else {
802		do_close = 0;
803	}
804	error = pdev->methods->get_config_desc_full(pdev,
805	    &ptr, &len, configIndex);
806
807	if (error) {
808		goto done;
809	}
810	/* parse new config descriptor */
811	retval = libusb20_parse_config_desc(ptr);
812
813	/* free config descriptor */
814	free(ptr);
815
816done:
817	if (do_close) {
818		error = libusb20_dev_close(pdev);
819	}
820	return (retval);
821}
822
823struct libusb20_device *
824libusb20_dev_alloc(void)
825{
826	struct libusb20_device *pdev;
827
828	pdev = malloc(sizeof(*pdev));
829	if (pdev == NULL) {
830		return (NULL);
831	}
832	memset(pdev, 0, sizeof(*pdev));
833
834	pdev->file = -1;
835	pdev->file_ctrl = -1;
836	pdev->methods = &libusb20_dummy_methods;
837	return (pdev);
838}
839
840uint8_t
841libusb20_dev_get_config_index(struct libusb20_device *pdev)
842{
843	int error;
844	uint8_t cfg_index;
845	uint8_t do_close;
846
847	if (!pdev->is_opened) {
848		error = libusb20_dev_open(pdev, 0);
849		if (error == 0) {
850			do_close = 1;
851		} else {
852			do_close = 0;
853		}
854	} else {
855		do_close = 0;
856	}
857
858	error = pdev->methods->get_config_index(pdev, &cfg_index);
859	if (error) {
860		cfg_index = 0 - 1;	/* current config index */
861	}
862	if (do_close) {
863		if (libusb20_dev_close(pdev)) {
864			/* ignore */
865		}
866	}
867	return (cfg_index);
868}
869
870uint8_t
871libusb20_dev_get_mode(struct libusb20_device *pdev)
872{
873	return (pdev->usb_mode);
874}
875
876uint8_t
877libusb20_dev_get_speed(struct libusb20_device *pdev)
878{
879	return (pdev->usb_speed);
880}
881
882/* if this function returns an error, the device is gone */
883int
884libusb20_dev_process(struct libusb20_device *pdev)
885{
886	int error;
887
888	error = pdev->methods->process(pdev);
889	return (error);
890}
891
892void
893libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout)
894{
895	struct pollfd pfd[1];
896
897	if (!pdev->is_opened) {
898		return;
899	}
900	pfd[0].fd = pdev->file;
901	pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
902	pfd[0].revents = 0;
903
904	if (poll(pfd, 1, timeout)) {
905		/* ignore any error */
906	}
907	return;
908}
909
910void
911libusb20_dev_free(struct libusb20_device *pdev)
912{
913	if (pdev == NULL) {
914		/* be NULL safe */
915		return;
916	}
917	if (pdev->is_opened) {
918		if (libusb20_dev_close(pdev)) {
919			/* ignore any errors */
920		}
921	}
922	free(pdev);
923	return;
924}
925
926int
927libusb20_dev_get_info(struct libusb20_device *pdev,
928    struct usb2_device_info *pinfo)
929{
930	if (pinfo == NULL)
931		return (LIBUSB20_ERROR_INVALID_PARAM);
932
933	return (pdev->beMethods->dev_get_info(pdev, pinfo));
934}
935
936const char *
937libusb20_dev_get_backend_name(struct libusb20_device *pdev)
938{
939	return (pdev->beMethods->get_backend_name());
940}
941
942const char *
943libusb20_dev_get_desc(struct libusb20_device *pdev)
944{
945	return (pdev->usb_desc);
946}
947
948void
949libusb20_dev_set_debug(struct libusb20_device *pdev, int debug)
950{
951	pdev->debug = debug;
952	return;
953}
954
955int
956libusb20_dev_get_debug(struct libusb20_device *pdev)
957{
958	return (pdev->debug);
959}
960
961uint8_t
962libusb20_dev_get_address(struct libusb20_device *pdev)
963{
964	return (pdev->device_address);
965}
966
967uint8_t
968libusb20_dev_get_bus_number(struct libusb20_device *pdev)
969{
970	return (pdev->bus_number);
971}
972
973int
974libusb20_dev_set_owner(struct libusb20_device *pdev, uid_t user, gid_t group)
975{
976	return (pdev->beMethods->dev_set_owner(pdev, user, group));
977}
978
979int
980libusb20_dev_set_perm(struct libusb20_device *pdev, mode_t mode)
981{
982	return (pdev->beMethods->dev_set_perm(pdev, mode));
983}
984
985int
986libusb20_dev_set_iface_owner(struct libusb20_device *pdev,
987    uint8_t iface_index, uid_t user, gid_t group)
988{
989	return (pdev->beMethods->dev_set_iface_owner(
990	    pdev, iface_index, user, group));
991}
992
993int
994libusb20_dev_set_iface_perm(struct libusb20_device *pdev,
995    uint8_t iface_index, mode_t mode)
996{
997	return (pdev->beMethods->dev_set_iface_perm(
998	    pdev, iface_index, mode));
999}
1000
1001int
1002libusb20_dev_get_owner(struct libusb20_device *pdev, uid_t *user, gid_t *group)
1003{
1004	uid_t a;
1005	gid_t b;
1006
1007	if (user == NULL)
1008		user = &a;
1009	if (group == NULL)
1010		group = &b;
1011
1012	return (pdev->beMethods->dev_get_owner(pdev, user, group));
1013}
1014
1015int
1016libusb20_dev_get_perm(struct libusb20_device *pdev, mode_t *mode)
1017{
1018	mode_t a;
1019
1020	if (mode == NULL)
1021		mode = &a;
1022	return (pdev->beMethods->dev_get_perm(pdev, mode));
1023}
1024
1025int
1026libusb20_dev_get_iface_owner(struct libusb20_device *pdev,
1027    uint8_t iface_index, uid_t *user, gid_t *group)
1028{
1029	uid_t a;
1030	gid_t b;
1031
1032	if (user == NULL)
1033		user = &a;
1034	if (group == NULL)
1035		group = &b;
1036
1037	return (pdev->beMethods->dev_get_iface_owner(
1038	    pdev, iface_index, user, group));
1039}
1040
1041int
1042libusb20_dev_get_iface_perm(struct libusb20_device *pdev,
1043    uint8_t iface_index, mode_t *mode)
1044{
1045	mode_t a;
1046
1047	if (mode == NULL)
1048		mode = &a;
1049	return (pdev->beMethods->dev_get_iface_perm(
1050	    pdev, iface_index, mode));
1051}
1052
1053int
1054libusb20_dev_get_iface_desc(struct libusb20_device *pdev,
1055    uint8_t iface_index, char *buf, uint8_t len)
1056{
1057	if ((buf == NULL) || (len == 0))
1058		return (LIBUSB20_ERROR_INVALID_PARAM);
1059
1060	return (pdev->beMethods->dev_get_iface_desc(
1061	    pdev, iface_index, buf, len));
1062}
1063
1064/* USB bus operations */
1065
1066int
1067libusb20_bus_set_owner(struct libusb20_backend *pbe,
1068    uint8_t bus, uid_t user, gid_t group)
1069{
1070	return (pbe->methods->bus_set_owner(pbe, bus, user, group));
1071}
1072
1073int
1074libusb20_bus_set_perm(struct libusb20_backend *pbe, uint8_t bus, mode_t mode)
1075{
1076	return (pbe->methods->bus_set_perm(pbe, bus, mode));
1077}
1078
1079int
1080libusb20_bus_get_owner(struct libusb20_backend *pbe,
1081    uint8_t bus, uid_t *user, gid_t *group)
1082{
1083	uid_t a;
1084	gid_t b;
1085
1086	if (user == NULL)
1087		user = &a;
1088	if (group == NULL)
1089		group = &b;
1090	return (pbe->methods->bus_get_owner(pbe, bus, user, group));
1091}
1092
1093int
1094libusb20_bus_get_perm(struct libusb20_backend *pbe, uint8_t bus, mode_t *mode)
1095{
1096	mode_t a;
1097
1098	if (mode == NULL)
1099		mode = &a;
1100	return (pbe->methods->bus_get_perm(pbe, bus, mode));
1101}
1102
1103/* USB backend operations */
1104
1105int
1106libusb20_be_get_dev_quirk(struct libusb20_backend *pbe,
1107    uint16_t quirk_index, struct libusb20_quirk *pq)
1108{
1109	return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq));
1110}
1111
1112int
1113libusb20_be_get_quirk_name(struct libusb20_backend *pbe,
1114    uint16_t quirk_index, struct libusb20_quirk *pq)
1115{
1116	return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq));
1117}
1118
1119int
1120libusb20_be_add_dev_quirk(struct libusb20_backend *pbe,
1121    struct libusb20_quirk *pq)
1122{
1123	return (pbe->methods->root_add_dev_quirk(pbe, pq));
1124}
1125
1126int
1127libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe,
1128    struct libusb20_quirk *pq)
1129{
1130	return (pbe->methods->root_remove_dev_quirk(pbe, pq));
1131}
1132
1133int
1134libusb20_be_set_owner(struct libusb20_backend *pbe, uid_t user, gid_t group)
1135{
1136	return (pbe->methods->root_set_owner(pbe, user, group));
1137}
1138
1139int
1140libusb20_be_set_perm(struct libusb20_backend *pbe, mode_t mode)
1141{
1142	return (pbe->methods->root_set_perm(pbe, mode));
1143}
1144
1145int
1146libusb20_be_get_owner(struct libusb20_backend *pbe, uid_t *user, gid_t *group)
1147{
1148	uid_t a;
1149	gid_t b;
1150
1151	if (user == NULL)
1152		user = &a;
1153	if (group == NULL)
1154		group = &b;
1155	return (pbe->methods->root_get_owner(pbe, user, group));
1156}
1157
1158int
1159libusb20_be_get_perm(struct libusb20_backend *pbe, mode_t *mode)
1160{
1161	mode_t a;
1162
1163	if (mode == NULL)
1164		mode = &a;
1165	return (pbe->methods->root_get_perm(pbe, mode));
1166}
1167
1168struct libusb20_device *
1169libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1170{
1171	if (pbe == NULL) {
1172		pdev = NULL;
1173	} else if (pdev == NULL) {
1174		pdev = TAILQ_FIRST(&(pbe->usb_devs));
1175	} else {
1176		pdev = TAILQ_NEXT(pdev, dev_entry);
1177	}
1178	return (pdev);
1179}
1180
1181struct libusb20_backend *
1182libusb20_be_alloc(const struct libusb20_backend_methods *methods)
1183{
1184	struct libusb20_backend *pbe;
1185
1186	pbe = malloc(sizeof(*pbe));
1187	if (pbe == NULL) {
1188		return (NULL);
1189	}
1190	memset(pbe, 0, sizeof(*pbe));
1191
1192	TAILQ_INIT(&(pbe->usb_devs));
1193
1194	pbe->methods = methods;		/* set backend methods */
1195
1196	/* do the initial device scan */
1197	if (pbe->methods->init_backend) {
1198		pbe->methods->init_backend(pbe);
1199	}
1200	return (pbe);
1201}
1202
1203struct libusb20_backend *
1204libusb20_be_alloc_linux(void)
1205{
1206	struct libusb20_backend *pbe;
1207
1208#ifdef __linux__
1209	pbe = libusb20_be_alloc(&libusb20_linux_backend);
1210#else
1211	pbe = NULL;
1212#endif
1213	return (pbe);
1214}
1215
1216struct libusb20_backend *
1217libusb20_be_alloc_ugen20(void)
1218{
1219	struct libusb20_backend *pbe;
1220
1221#ifdef __FreeBSD__
1222	pbe = libusb20_be_alloc(&libusb20_ugen20_backend);
1223#else
1224	pbe = NULL;
1225#endif
1226	return (pbe);
1227}
1228
1229struct libusb20_backend *
1230libusb20_be_alloc_default(void)
1231{
1232	struct libusb20_backend *pbe;
1233
1234	pbe = libusb20_be_alloc_linux();
1235	if (pbe) {
1236		return (pbe);
1237	}
1238	pbe = libusb20_be_alloc_ugen20();
1239	if (pbe) {
1240		return (pbe);
1241	}
1242	return (NULL);			/* no backend found */
1243}
1244
1245void
1246libusb20_be_free(struct libusb20_backend *pbe)
1247{
1248	struct libusb20_device *pdev;
1249
1250	if (pbe == NULL) {
1251		/* be NULL safe */
1252		return;
1253	}
1254	while ((pdev = libusb20_be_device_foreach(pbe, NULL))) {
1255		libusb20_be_dequeue_device(pbe, pdev);
1256		libusb20_dev_free(pdev);
1257	}
1258	if (pbe->methods->exit_backend) {
1259		pbe->methods->exit_backend(pbe);
1260	}
1261	return;
1262}
1263
1264void
1265libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1266{
1267	pdev->beMethods = pbe->methods;	/* copy backend methods */
1268	TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry);
1269	return;
1270}
1271
1272void
1273libusb20_be_dequeue_device(struct libusb20_backend *pbe,
1274    struct libusb20_device *pdev)
1275{
1276	TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry);
1277	return;
1278}
1279