libusb20.c revision 213852
1184610Salfred/* $FreeBSD: head/lib/libusb/libusb20.c 213852 2010-10-14 20:38:18Z hselasky $ */
2184610Salfred/*-
3199575Sthompsa * Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred *
14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184610Salfred * SUCH DAMAGE.
25184610Salfred */
26184610Salfred
27203815Swkoszek#include <sys/queue.h>
28203815Swkoszek
29203815Swkoszek#include <ctype.h>
30203815Swkoszek#include <poll.h>
31184610Salfred#include <stdio.h>
32184610Salfred#include <stdlib.h>
33184610Salfred#include <string.h>
34184610Salfred
35184610Salfred#include "libusb20.h"
36184610Salfred#include "libusb20_desc.h"
37184610Salfred#include "libusb20_int.h"
38184610Salfred
39184610Salfredstatic int
40184610Salfreddummy_int(void)
41184610Salfred{
42184610Salfred	return (LIBUSB20_ERROR_NOT_SUPPORTED);
43184610Salfred}
44184610Salfred
45184610Salfredstatic void
46184610Salfreddummy_void(void)
47184610Salfred{
48184610Salfred	return;
49184610Salfred}
50184610Salfred
51184610Salfredstatic void
52184610Salfreddummy_callback(struct libusb20_transfer *xfer)
53184610Salfred{
54184610Salfred	;				/* style fix */
55184610Salfred	switch (libusb20_tr_get_status(xfer)) {
56184610Salfred	case LIBUSB20_TRANSFER_START:
57184610Salfred		libusb20_tr_submit(xfer);
58184610Salfred		break;
59184610Salfred	default:
60184610Salfred		/* complete or error */
61184610Salfred		break;
62184610Salfred	}
63184610Salfred	return;
64184610Salfred}
65184610Salfred
66184610Salfred#define	dummy_get_config_desc_full (void *)dummy_int
67184610Salfred#define	dummy_get_config_index (void *)dummy_int
68184610Salfred#define	dummy_set_config_index (void *)dummy_int
69184610Salfred#define	dummy_set_alt_index (void *)dummy_int
70184610Salfred#define	dummy_reset_device (void *)dummy_int
71203147Sthompsa#define	dummy_check_connected (void *)dummy_int
72184610Salfred#define	dummy_set_power_mode (void *)dummy_int
73184610Salfred#define	dummy_get_power_mode (void *)dummy_int
74184610Salfred#define	dummy_kernel_driver_active (void *)dummy_int
75184610Salfred#define	dummy_detach_kernel_driver (void *)dummy_int
76184610Salfred#define	dummy_do_request_sync (void *)dummy_int
77184610Salfred#define	dummy_tr_open (void *)dummy_int
78184610Salfred#define	dummy_tr_close (void *)dummy_int
79184610Salfred#define	dummy_tr_clear_stall_sync (void *)dummy_int
80184610Salfred#define	dummy_process (void *)dummy_int
81188622Sthompsa#define	dummy_dev_info (void *)dummy_int
82188622Sthompsa#define	dummy_dev_get_iface_driver (void *)dummy_int
83184610Salfred
84184610Salfred#define	dummy_tr_submit (void *)dummy_void
85184610Salfred#define	dummy_tr_cancel_async (void *)dummy_void
86184610Salfred
87184610Salfredstatic const struct libusb20_device_methods libusb20_dummy_methods = {
88184610Salfred	LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy)
89184610Salfred};
90184610Salfred
91184610Salfredvoid
92184610Salfredlibusb20_tr_callback_wrapper(struct libusb20_transfer *xfer)
93184610Salfred{
94184610Salfred	;				/* style fix */
95184610Salfred
96184610Salfredrepeat:
97184610Salfred
98184610Salfred	if (!xfer->is_pending) {
99184610Salfred		xfer->status = LIBUSB20_TRANSFER_START;
100184610Salfred	} else {
101184610Salfred		xfer->is_pending = 0;
102184610Salfred	}
103184610Salfred
104188622Sthompsa	xfer->callback(xfer);
105184610Salfred
106184610Salfred	if (xfer->is_restart) {
107184610Salfred		xfer->is_restart = 0;
108184610Salfred		goto repeat;
109184610Salfred	}
110184610Salfred	if (xfer->is_draining &&
111184610Salfred	    (!xfer->is_pending)) {
112184610Salfred		xfer->is_draining = 0;
113184610Salfred		xfer->status = LIBUSB20_TRANSFER_DRAINED;
114188622Sthompsa		xfer->callback(xfer);
115184610Salfred	}
116184610Salfred	return;
117184610Salfred}
118184610Salfred
119184610Salfredint
120184610Salfredlibusb20_tr_close(struct libusb20_transfer *xfer)
121184610Salfred{
122184610Salfred	int error;
123184610Salfred
124184610Salfred	if (!xfer->is_opened) {
125184610Salfred		return (LIBUSB20_ERROR_OTHER);
126184610Salfred	}
127188622Sthompsa	error = xfer->pdev->methods->tr_close(xfer);
128184610Salfred
129184610Salfred	if (xfer->pLength) {
130184610Salfred		free(xfer->pLength);
131184610Salfred	}
132184610Salfred	if (xfer->ppBuffer) {
133184610Salfred		free(xfer->ppBuffer);
134184610Salfred	}
135202025Sthompsa	/* reset variable fields in case the transfer is opened again */
136202025Sthompsa	xfer->priv_sc0 = 0;
137202025Sthompsa	xfer->priv_sc1 = 0;
138184610Salfred	xfer->is_opened = 0;
139202025Sthompsa	xfer->is_pending = 0;
140202025Sthompsa	xfer->is_cancel = 0;
141202025Sthompsa	xfer->is_draining = 0;
142202025Sthompsa	xfer->is_restart = 0;
143202025Sthompsa	xfer->status = 0;
144202025Sthompsa	xfer->flags = 0;
145202025Sthompsa	xfer->nFrames = 0;
146202025Sthompsa	xfer->aFrames = 0;
147202025Sthompsa	xfer->timeout = 0;
148184610Salfred	xfer->maxFrames = 0;
149184610Salfred	xfer->maxTotalLength = 0;
150184610Salfred	xfer->maxPacketLen = 0;
151184610Salfred	return (error);
152184610Salfred}
153184610Salfred
154184610Salfredint
155184610Salfredlibusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
156184610Salfred    uint32_t MaxFrameCount, uint8_t ep_no)
157184610Salfred{
158184610Salfred	uint32_t size;
159184610Salfred	int error;
160184610Salfred
161184610Salfred	if (xfer->is_opened) {
162184610Salfred		return (LIBUSB20_ERROR_BUSY);
163184610Salfred	}
164184610Salfred	if (MaxFrameCount == 0) {
165184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
166184610Salfred	}
167184610Salfred	xfer->maxFrames = MaxFrameCount;
168184610Salfred
169184610Salfred	size = MaxFrameCount * sizeof(xfer->pLength[0]);
170184610Salfred	xfer->pLength = malloc(size);
171184610Salfred	if (xfer->pLength == NULL) {
172184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
173184610Salfred	}
174184610Salfred	memset(xfer->pLength, 0, size);
175184610Salfred
176184610Salfred	size = MaxFrameCount * sizeof(xfer->ppBuffer[0]);
177184610Salfred	xfer->ppBuffer = malloc(size);
178184610Salfred	if (xfer->ppBuffer == NULL) {
179184610Salfred		free(xfer->pLength);
180184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
181184610Salfred	}
182184610Salfred	memset(xfer->ppBuffer, 0, size);
183184610Salfred
184188622Sthompsa	error = xfer->pdev->methods->tr_open(xfer, MaxBufSize,
185184610Salfred	    MaxFrameCount, ep_no);
186184610Salfred
187184610Salfred	if (error) {
188184610Salfred		free(xfer->ppBuffer);
189184610Salfred		free(xfer->pLength);
190184610Salfred	} else {
191184610Salfred		xfer->is_opened = 1;
192184610Salfred	}
193184610Salfred	return (error);
194184610Salfred}
195184610Salfred
196184610Salfredstruct libusb20_transfer *
197184610Salfredlibusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex)
198184610Salfred{
199184610Salfred	if (trIndex >= pdev->nTransfer) {
200184610Salfred		return (NULL);
201184610Salfred	}
202184610Salfred	return (pdev->pTransfer + trIndex);
203184610Salfred}
204184610Salfred
205184610Salfreduint32_t
206184610Salfredlibusb20_tr_get_actual_frames(struct libusb20_transfer *xfer)
207184610Salfred{
208184610Salfred	return (xfer->aFrames);
209184610Salfred}
210184610Salfred
211184610Salfreduint16_t
212184610Salfredlibusb20_tr_get_time_complete(struct libusb20_transfer *xfer)
213184610Salfred{
214184610Salfred	return (xfer->timeComplete);
215184610Salfred}
216184610Salfred
217184610Salfreduint32_t
218184610Salfredlibusb20_tr_get_actual_length(struct libusb20_transfer *xfer)
219184610Salfred{
220184610Salfred	uint32_t x;
221184610Salfred	uint32_t actlen = 0;
222184610Salfred
223184610Salfred	for (x = 0; x != xfer->aFrames; x++) {
224184610Salfred		actlen += xfer->pLength[x];
225184610Salfred	}
226184610Salfred	return (actlen);
227184610Salfred}
228184610Salfred
229184610Salfreduint32_t
230184610Salfredlibusb20_tr_get_max_frames(struct libusb20_transfer *xfer)
231184610Salfred{
232184610Salfred	return (xfer->maxFrames);
233184610Salfred}
234184610Salfred
235184610Salfreduint32_t
236184610Salfredlibusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer)
237184610Salfred{
238184610Salfred	/*
239184610Salfred	 * Special Case NOTE: If the packet multiplier is non-zero for
240184610Salfred	 * High Speed USB, the value returned is equal to
241184610Salfred	 * "wMaxPacketSize * multiplier" !
242184610Salfred	 */
243184610Salfred	return (xfer->maxPacketLen);
244184610Salfred}
245184610Salfred
246184610Salfreduint32_t
247184610Salfredlibusb20_tr_get_max_total_length(struct libusb20_transfer *xfer)
248184610Salfred{
249184610Salfred	return (xfer->maxTotalLength);
250184610Salfred}
251184610Salfred
252184610Salfreduint8_t
253184610Salfredlibusb20_tr_get_status(struct libusb20_transfer *xfer)
254184610Salfred{
255184610Salfred	return (xfer->status);
256184610Salfred}
257184610Salfred
258184610Salfreduint8_t
259184610Salfredlibusb20_tr_pending(struct libusb20_transfer *xfer)
260184610Salfred{
261184610Salfred	return (xfer->is_pending);
262184610Salfred}
263184610Salfred
264184610Salfredvoid   *
265184610Salfredlibusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer)
266184610Salfred{
267184610Salfred	return (xfer->priv_sc0);
268184610Salfred}
269184610Salfred
270184610Salfredvoid   *
271184610Salfredlibusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer)
272184610Salfred{
273184610Salfred	return (xfer->priv_sc1);
274184610Salfred}
275184610Salfred
276184610Salfredvoid
277184610Salfredlibusb20_tr_stop(struct libusb20_transfer *xfer)
278184610Salfred{
279199575Sthompsa	if (!xfer->is_opened) {
280199575Sthompsa		/* transfer is not opened */
281199575Sthompsa		return;
282199575Sthompsa	}
283184610Salfred	if (!xfer->is_pending) {
284184610Salfred		/* transfer not pending */
285184610Salfred		return;
286184610Salfred	}
287184610Salfred	if (xfer->is_cancel) {
288184610Salfred		/* already cancelling */
289184610Salfred		return;
290184610Salfred	}
291184610Salfred	xfer->is_cancel = 1;		/* we are cancelling */
292184610Salfred
293188622Sthompsa	xfer->pdev->methods->tr_cancel_async(xfer);
294184610Salfred	return;
295184610Salfred}
296184610Salfred
297184610Salfredvoid
298184610Salfredlibusb20_tr_drain(struct libusb20_transfer *xfer)
299184610Salfred{
300199575Sthompsa	if (!xfer->is_opened) {
301199575Sthompsa		/* transfer is not opened */
302199575Sthompsa		return;
303199575Sthompsa	}
304184610Salfred	/* make sure that we are cancelling */
305184610Salfred	libusb20_tr_stop(xfer);
306184610Salfred
307184610Salfred	if (xfer->is_pending) {
308184610Salfred		xfer->is_draining = 1;
309184610Salfred	}
310184610Salfred	return;
311184610Salfred}
312184610Salfred
313184610Salfredvoid
314184610Salfredlibusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
315184610Salfred{
316188622Sthompsa	xfer->pdev->methods->tr_clear_stall_sync(xfer);
317184610Salfred	return;
318184610Salfred}
319184610Salfred
320184610Salfredvoid
321184610Salfredlibusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex)
322184610Salfred{
323213852Shselasky	xfer->ppBuffer[frIndex] = libusb20_pass_ptr(buffer);
324184610Salfred	return;
325184610Salfred}
326184610Salfred
327184610Salfredvoid
328184610Salfredlibusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb)
329184610Salfred{
330184610Salfred	xfer->callback = cb;
331184610Salfred	return;
332184610Salfred}
333184610Salfred
334184610Salfredvoid
335184610Salfredlibusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags)
336184610Salfred{
337184610Salfred	xfer->flags = flags;
338184610Salfred	return;
339184610Salfred}
340184610Salfred
341193313Sthompsauint32_t
342193313Sthompsalibusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t frIndex)
343193313Sthompsa{
344193313Sthompsa	return (xfer->pLength[frIndex]);
345193313Sthompsa}
346193313Sthompsa
347184610Salfredvoid
348184610Salfredlibusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex)
349184610Salfred{
350184610Salfred	xfer->pLength[frIndex] = length;
351184610Salfred	return;
352184610Salfred}
353184610Salfred
354184610Salfredvoid
355184610Salfredlibusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0)
356184610Salfred{
357184610Salfred	xfer->priv_sc0 = sc0;
358184610Salfred	return;
359184610Salfred}
360184610Salfred
361184610Salfredvoid
362184610Salfredlibusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1)
363184610Salfred{
364184610Salfred	xfer->priv_sc1 = sc1;
365184610Salfred	return;
366184610Salfred}
367184610Salfred
368184610Salfredvoid
369184610Salfredlibusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout)
370184610Salfred{
371184610Salfred	xfer->timeout = timeout;
372184610Salfred	return;
373184610Salfred}
374184610Salfred
375184610Salfredvoid
376184610Salfredlibusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames)
377184610Salfred{
378184610Salfred	if (nFrames > xfer->maxFrames) {
379184610Salfred		/* should not happen */
380184610Salfred		nFrames = xfer->maxFrames;
381184610Salfred	}
382184610Salfred	xfer->nFrames = nFrames;
383184610Salfred	return;
384184610Salfred}
385184610Salfred
386184610Salfredvoid
387184610Salfredlibusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout)
388184610Salfred{
389213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf);
390184610Salfred	xfer->pLength[0] = length;
391184610Salfred	xfer->timeout = timeout;
392184610Salfred	xfer->nFrames = 1;
393184610Salfred	return;
394184610Salfred}
395184610Salfred
396184610Salfredvoid
397184610Salfredlibusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout)
398184610Salfred{
399184610Salfred	uint16_t len;
400184610Salfred
401213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(psetup);
402184610Salfred	xfer->pLength[0] = 8;		/* fixed */
403184610Salfred	xfer->timeout = timeout;
404184610Salfred
405184610Salfred	len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8);
406184610Salfred
407184610Salfred	if (len != 0) {
408184610Salfred		xfer->nFrames = 2;
409213852Shselasky		xfer->ppBuffer[1] = libusb20_pass_ptr(pBuf);
410184610Salfred		xfer->pLength[1] = len;
411184610Salfred	} else {
412184610Salfred		xfer->nFrames = 1;
413184610Salfred	}
414184610Salfred	return;
415184610Salfred}
416184610Salfred
417184610Salfredvoid
418184610Salfredlibusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout)
419184610Salfred{
420213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf);
421184610Salfred	xfer->pLength[0] = length;
422184610Salfred	xfer->timeout = timeout;
423184610Salfred	xfer->nFrames = 1;
424184610Salfred	return;
425184610Salfred}
426184610Salfred
427184610Salfredvoid
428184610Salfredlibusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex)
429184610Salfred{
430184610Salfred	if (frIndex >= xfer->maxFrames) {
431184610Salfred		/* should not happen */
432184610Salfred		return;
433184610Salfred	}
434213852Shselasky	xfer->ppBuffer[frIndex] = libusb20_pass_ptr(pBuf);
435184610Salfred	xfer->pLength[frIndex] = length;
436184610Salfred	return;
437184610Salfred}
438184610Salfred
439199575Sthompsauint8_t
440199575Sthompsalibusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer,
441199575Sthompsa    void *pbuf, uint32_t length, uint32_t *pactlen,
442199575Sthompsa    uint32_t timeout)
443199575Sthompsa{
444199575Sthompsa	struct libusb20_device *pdev = xfer->pdev;
445199575Sthompsa	uint32_t transfer_max;
446199575Sthompsa	uint32_t transfer_act;
447199575Sthompsa	uint8_t retval;
448199575Sthompsa
449199575Sthompsa	/* set some sensible default value */
450199575Sthompsa	if (pactlen != NULL)
451199575Sthompsa		*pactlen = 0;
452199575Sthompsa
453199575Sthompsa	/* check for error condition */
454199575Sthompsa	if (libusb20_tr_pending(xfer))
455199575Sthompsa		return (LIBUSB20_ERROR_OTHER);
456199575Sthompsa
457199575Sthompsa	do {
458199575Sthompsa		/* compute maximum transfer length */
459199575Sthompsa		transfer_max =
460199575Sthompsa		    libusb20_tr_get_max_total_length(xfer);
461199575Sthompsa
462199575Sthompsa		if (transfer_max > length)
463199575Sthompsa			transfer_max = length;
464199575Sthompsa
465199575Sthompsa		/* setup bulk or interrupt transfer */
466199575Sthompsa		libusb20_tr_setup_bulk(xfer, pbuf,
467199575Sthompsa		    transfer_max, timeout);
468199575Sthompsa
469199575Sthompsa		/* start the transfer */
470199575Sthompsa		libusb20_tr_start(xfer);
471199575Sthompsa
472199575Sthompsa		/* wait for transfer completion */
473199575Sthompsa		while (libusb20_dev_process(pdev) == 0) {
474199575Sthompsa
475199575Sthompsa			if (libusb20_tr_pending(xfer) == 0)
476199575Sthompsa				break;
477199575Sthompsa
478199575Sthompsa			libusb20_dev_wait_process(pdev, -1);
479199575Sthompsa		}
480199575Sthompsa
481199575Sthompsa		transfer_act = libusb20_tr_get_actual_length(xfer);
482199575Sthompsa
483199575Sthompsa		/* update actual length, if any */
484199575Sthompsa		if (pactlen != NULL)
485199575Sthompsa			pactlen[0] += transfer_act;
486199575Sthompsa
487199575Sthompsa		/* check transfer status */
488199575Sthompsa		retval = libusb20_tr_get_status(xfer);
489199575Sthompsa		if (retval)
490199575Sthompsa			break;
491199575Sthompsa
492199575Sthompsa		/* check for short transfer */
493199575Sthompsa		if (transfer_act != transfer_max)
494199575Sthompsa			break;
495199575Sthompsa
496199575Sthompsa		/* update buffer pointer and length */
497199575Sthompsa		pbuf = ((uint8_t *)pbuf) + transfer_max;
498199575Sthompsa		length = length - transfer_max;
499199575Sthompsa
500199575Sthompsa	} while (length != 0);
501199575Sthompsa
502199575Sthompsa	return (retval);
503199575Sthompsa}
504199575Sthompsa
505184610Salfredvoid
506184610Salfredlibusb20_tr_submit(struct libusb20_transfer *xfer)
507184610Salfred{
508199575Sthompsa	if (!xfer->is_opened) {
509199575Sthompsa		/* transfer is not opened */
510199575Sthompsa		return;
511199575Sthompsa	}
512184610Salfred	if (xfer->is_pending) {
513184610Salfred		/* should not happen */
514184610Salfred		return;
515184610Salfred	}
516184610Salfred	xfer->is_pending = 1;		/* we are pending */
517184610Salfred	xfer->is_cancel = 0;		/* not cancelling */
518184610Salfred	xfer->is_restart = 0;		/* not restarting */
519184610Salfred
520188622Sthompsa	xfer->pdev->methods->tr_submit(xfer);
521184610Salfred	return;
522184610Salfred}
523184610Salfred
524184610Salfredvoid
525184610Salfredlibusb20_tr_start(struct libusb20_transfer *xfer)
526184610Salfred{
527199575Sthompsa	if (!xfer->is_opened) {
528199575Sthompsa		/* transfer is not opened */
529199575Sthompsa		return;
530199575Sthompsa	}
531184610Salfred	if (xfer->is_pending) {
532184610Salfred		if (xfer->is_cancel) {
533184610Salfred			/* cancelling - restart */
534184610Salfred			xfer->is_restart = 1;
535184610Salfred		}
536184610Salfred		/* transfer not pending */
537184610Salfred		return;
538184610Salfred	}
539184610Salfred	/* get into the callback */
540184610Salfred	libusb20_tr_callback_wrapper(xfer);
541184610Salfred	return;
542184610Salfred}
543184610Salfred
544184610Salfred/* USB device operations */
545184610Salfred
546184610Salfredint
547184610Salfredlibusb20_dev_close(struct libusb20_device *pdev)
548184610Salfred{
549184610Salfred	struct libusb20_transfer *xfer;
550184610Salfred	uint16_t x;
551184610Salfred	int error = 0;
552184610Salfred
553184610Salfred	if (!pdev->is_opened) {
554184610Salfred		return (LIBUSB20_ERROR_OTHER);
555184610Salfred	}
556184610Salfred	for (x = 0; x != pdev->nTransfer; x++) {
557184610Salfred		xfer = pdev->pTransfer + x;
558184610Salfred
559199575Sthompsa		if (!xfer->is_opened) {
560199575Sthompsa			/* transfer is not opened */
561199575Sthompsa			continue;
562199575Sthompsa		}
563199575Sthompsa
564184610Salfred		libusb20_tr_drain(xfer);
565199575Sthompsa
566199575Sthompsa		libusb20_tr_close(xfer);
567184610Salfred	}
568184610Salfred
569184610Salfred	if (pdev->pTransfer != NULL) {
570184610Salfred		free(pdev->pTransfer);
571184610Salfred		pdev->pTransfer = NULL;
572184610Salfred	}
573188622Sthompsa	error = pdev->beMethods->close_device(pdev);
574184610Salfred
575184610Salfred	pdev->methods = &libusb20_dummy_methods;
576184610Salfred
577184610Salfred	pdev->is_opened = 0;
578184610Salfred
579194069Sthompsa	/*
580194069Sthompsa	 * The following variable is only used by the libusb v0.1
581194069Sthompsa	 * compat layer:
582194069Sthompsa	 */
583194069Sthompsa	pdev->claimed_interface = 0;
584187184Sthompsa
585184610Salfred	return (error);
586184610Salfred}
587184610Salfred
588184610Salfredint
589184610Salfredlibusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex)
590184610Salfred{
591184610Salfred	int error;
592184610Salfred
593188622Sthompsa	error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex);
594184610Salfred	return (error);
595184610Salfred}
596184610Salfred
597184610Salfredstruct LIBUSB20_DEVICE_DESC_DECODED *
598184610Salfredlibusb20_dev_get_device_desc(struct libusb20_device *pdev)
599184610Salfred{
600184610Salfred	return (&(pdev->ddesc));
601184610Salfred}
602184610Salfred
603184610Salfredint
604184610Salfredlibusb20_dev_get_fd(struct libusb20_device *pdev)
605184610Salfred{
606184610Salfred	return (pdev->file);
607184610Salfred}
608184610Salfred
609184610Salfredint
610184610Salfredlibusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex)
611184610Salfred{
612184610Salfred	int error;
613184610Salfred
614188622Sthompsa	error = pdev->methods->kernel_driver_active(pdev, ifaceIndex);
615184610Salfred	return (error);
616184610Salfred}
617184610Salfred
618184610Salfredint
619184610Salfredlibusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax)
620184610Salfred{
621184610Salfred	struct libusb20_transfer *xfer;
622184610Salfred	uint32_t size;
623184610Salfred	uint16_t x;
624184610Salfred	int error;
625184610Salfred
626184610Salfred	if (pdev->is_opened) {
627184610Salfred		return (LIBUSB20_ERROR_BUSY);
628184610Salfred	}
629184610Salfred	if (nTransferMax >= 256) {
630184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
631184610Salfred	} else if (nTransferMax != 0) {
632184610Salfred		size = sizeof(pdev->pTransfer[0]) * nTransferMax;
633184610Salfred		pdev->pTransfer = malloc(size);
634184610Salfred		if (pdev->pTransfer == NULL) {
635184610Salfred			return (LIBUSB20_ERROR_NO_MEM);
636184610Salfred		}
637184610Salfred		memset(pdev->pTransfer, 0, size);
638184610Salfred	}
639184610Salfred	/* initialise all transfers */
640184610Salfred	for (x = 0; x != nTransferMax; x++) {
641184610Salfred
642184610Salfred		xfer = pdev->pTransfer + x;
643184610Salfred
644184610Salfred		xfer->pdev = pdev;
645184610Salfred		xfer->trIndex = x;
646184610Salfred		xfer->callback = &dummy_callback;
647184610Salfred	}
648184610Salfred
649185087Salfred	/* set "nTransfer" early */
650185087Salfred	pdev->nTransfer = nTransferMax;
651185087Salfred
652188622Sthompsa	error = pdev->beMethods->open_device(pdev, nTransferMax);
653184610Salfred
654184610Salfred	if (error) {
655184610Salfred		if (pdev->pTransfer != NULL) {
656184610Salfred			free(pdev->pTransfer);
657184610Salfred			pdev->pTransfer = NULL;
658184610Salfred		}
659184610Salfred		pdev->file = -1;
660184610Salfred		pdev->file_ctrl = -1;
661184610Salfred		pdev->nTransfer = 0;
662184610Salfred	} else {
663184610Salfred		pdev->is_opened = 1;
664184610Salfred	}
665184610Salfred	return (error);
666184610Salfred}
667184610Salfred
668184610Salfredint
669184610Salfredlibusb20_dev_reset(struct libusb20_device *pdev)
670184610Salfred{
671184610Salfred	int error;
672184610Salfred
673188622Sthompsa	error = pdev->methods->reset_device(pdev);
674184610Salfred	return (error);
675184610Salfred}
676184610Salfred
677184610Salfredint
678203147Sthompsalibusb20_dev_check_connected(struct libusb20_device *pdev)
679203147Sthompsa{
680203147Sthompsa	int error;
681203147Sthompsa
682203147Sthompsa	error = pdev->methods->check_connected(pdev);
683203147Sthompsa	return (error);
684203147Sthompsa}
685203147Sthompsa
686203147Sthompsaint
687184610Salfredlibusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
688184610Salfred{
689184610Salfred	int error;
690184610Salfred
691188622Sthompsa	error = pdev->methods->set_power_mode(pdev, power_mode);
692184610Salfred	return (error);
693184610Salfred}
694184610Salfred
695184610Salfreduint8_t
696184610Salfredlibusb20_dev_get_power_mode(struct libusb20_device *pdev)
697184610Salfred{
698184610Salfred	int error;
699184610Salfred	uint8_t power_mode;
700184610Salfred
701188622Sthompsa	error = pdev->methods->get_power_mode(pdev, &power_mode);
702184610Salfred	if (error)
703184610Salfred		power_mode = LIBUSB20_POWER_ON;	/* fake power mode */
704184610Salfred	return (power_mode);
705184610Salfred}
706184610Salfred
707184610Salfredint
708184610Salfredlibusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex)
709184610Salfred{
710184610Salfred	int error;
711184610Salfred
712188622Sthompsa	error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex);
713184610Salfred	return (error);
714184610Salfred}
715184610Salfred
716184610Salfredint
717184610Salfredlibusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex)
718184610Salfred{
719184610Salfred	int error;
720184610Salfred
721188622Sthompsa	error = pdev->methods->set_config_index(pdev, configIndex);
722184610Salfred	return (error);
723184610Salfred}
724184610Salfred
725184610Salfredint
726184610Salfredlibusb20_dev_request_sync(struct libusb20_device *pdev,
727184610Salfred    struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data,
728184610Salfred    uint16_t *pactlen, uint32_t timeout, uint8_t flags)
729184610Salfred{
730184610Salfred	int error;
731184610Salfred
732188622Sthompsa	error = pdev->methods->do_request_sync(pdev,
733184610Salfred	    setup, data, pactlen, timeout, flags);
734184610Salfred	return (error);
735184610Salfred}
736184610Salfred
737184610Salfredint
738184610Salfredlibusb20_dev_req_string_sync(struct libusb20_device *pdev,
739185087Salfred    uint8_t str_index, uint16_t langid, void *ptr, uint16_t len)
740184610Salfred{
741184610Salfred	struct LIBUSB20_CONTROL_SETUP_DECODED req;
742184610Salfred	int error;
743184610Salfred
744199055Sthompsa	/* make sure memory is initialised */
745199055Sthompsa	memset(ptr, 0, len);
746199055Sthompsa
747184610Salfred	if (len < 4) {
748184610Salfred		/* invalid length */
749184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
750184610Salfred	}
751184610Salfred	LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req);
752184610Salfred
753184610Salfred	/*
754184610Salfred	 * We need to read the USB string in two steps else some USB
755184610Salfred	 * devices will complain.
756184610Salfred	 */
757184610Salfred	req.bmRequestType =
758184610Salfred	    LIBUSB20_REQUEST_TYPE_STANDARD |
759184610Salfred	    LIBUSB20_RECIPIENT_DEVICE |
760184610Salfred	    LIBUSB20_ENDPOINT_IN;
761184610Salfred	req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR;
762185087Salfred	req.wValue = (LIBUSB20_DT_STRING << 8) | str_index;
763184610Salfred	req.wIndex = langid;
764184610Salfred	req.wLength = 4;		/* bytes */
765184610Salfred
766184610Salfred	error = libusb20_dev_request_sync(pdev, &req,
767184610Salfred	    ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK);
768184610Salfred	if (error) {
769184610Salfred		return (error);
770184610Salfred	}
771184610Salfred	req.wLength = *(uint8_t *)ptr;	/* bytes */
772184610Salfred	if (req.wLength > len) {
773184610Salfred		/* partial string read */
774184610Salfred		req.wLength = len;
775184610Salfred	}
776184610Salfred	error = libusb20_dev_request_sync(pdev, &req,
777184610Salfred	    ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK);
778184610Salfred
779184610Salfred	if (error) {
780184610Salfred		return (error);
781184610Salfred	}
782184610Salfred	if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING) {
783184610Salfred		return (LIBUSB20_ERROR_OTHER);
784184610Salfred	}
785184610Salfred	return (0);			/* success */
786184610Salfred}
787184610Salfred
788184610Salfredint
789184610Salfredlibusb20_dev_req_string_simple_sync(struct libusb20_device *pdev,
790185087Salfred    uint8_t str_index, void *ptr, uint16_t len)
791184610Salfred{
792184610Salfred	char *buf;
793184610Salfred	int error;
794184610Salfred	uint16_t langid;
795184610Salfred	uint16_t n;
796184610Salfred	uint16_t i;
797184610Salfred	uint16_t c;
798184610Salfred	uint8_t temp[255];
799184610Salfred	uint8_t swap;
800184610Salfred
801184610Salfred	/* the following code derives from the FreeBSD USB kernel */
802184610Salfred
803184610Salfred	if ((len < 1) || (ptr == NULL)) {
804184610Salfred		/* too short buffer */
805184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
806184610Salfred	}
807184610Salfred	error = libusb20_dev_req_string_sync(pdev,
808184610Salfred	    0, 0, temp, sizeof(temp));
809185087Salfred	if (error < 0) {
810185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
811184610Salfred		return (error);
812185087Salfred	}
813184610Salfred	langid = temp[2] | (temp[3] << 8);
814184610Salfred
815185087Salfred	error = libusb20_dev_req_string_sync(pdev, str_index,
816184610Salfred	    langid, temp, sizeof(temp));
817185087Salfred	if (error < 0) {
818185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
819184610Salfred		return (error);
820185087Salfred	}
821184610Salfred	if (temp[0] < 2) {
822184610Salfred		/* string length is too short */
823185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
824184610Salfred		return (LIBUSB20_ERROR_OTHER);
825184610Salfred	}
826184610Salfred	/* reserve one byte for terminating zero */
827184610Salfred	len--;
828184610Salfred
829184610Salfred	/* find maximum length */
830184610Salfred	n = (temp[0] / 2) - 1;
831184610Salfred	if (n > len) {
832184610Salfred		n = len;
833184610Salfred	}
834184610Salfred	/* reset swap state */
835184610Salfred	swap = 3;
836184610Salfred
837184610Salfred	/* setup output buffer pointer */
838184610Salfred	buf = ptr;
839184610Salfred
840184610Salfred	/* convert and filter */
841184610Salfred	for (i = 0; (i != n); i++) {
842184610Salfred		c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8);
843184610Salfred
844184610Salfred		/* convert from Unicode, handle buggy strings */
845184610Salfred		if (((c & 0xff00) == 0) && (swap & 1)) {
846184610Salfred			/* Little Endian, default */
847184610Salfred			*buf = c;
848184610Salfred			swap = 1;
849184610Salfred		} else if (((c & 0x00ff) == 0) && (swap & 2)) {
850184610Salfred			/* Big Endian */
851184610Salfred			*buf = c >> 8;
852184610Salfred			swap = 2;
853184610Salfred		} else {
854185087Salfred			/* skip invalid character */
855185087Salfred			continue;
856184610Salfred		}
857184610Salfred		/*
858184610Salfred		 * Filter by default - we don't allow greater and less than
859184610Salfred		 * signs because they might confuse the dmesg printouts!
860184610Salfred		 */
861184610Salfred		if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) {
862185087Salfred			/* skip invalid character */
863185087Salfred			continue;
864184610Salfred		}
865184610Salfred		buf++;
866184610Salfred	}
867184610Salfred	*buf = 0;			/* zero terminate string */
868184610Salfred
869184610Salfred	return (0);
870184610Salfred}
871184610Salfred
872184610Salfredstruct libusb20_config *
873184610Salfredlibusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex)
874184610Salfred{
875184610Salfred	struct libusb20_config *retval = NULL;
876184610Salfred	uint8_t *ptr;
877184610Salfred	uint16_t len;
878184610Salfred	uint8_t do_close;
879184610Salfred	int error;
880184610Salfred
881184610Salfred	if (!pdev->is_opened) {
882184610Salfred		error = libusb20_dev_open(pdev, 0);
883184610Salfred		if (error) {
884184610Salfred			return (NULL);
885184610Salfred		}
886184610Salfred		do_close = 1;
887184610Salfred	} else {
888184610Salfred		do_close = 0;
889184610Salfred	}
890188622Sthompsa	error = pdev->methods->get_config_desc_full(pdev,
891184610Salfred	    &ptr, &len, configIndex);
892184610Salfred
893184610Salfred	if (error) {
894184610Salfred		goto done;
895184610Salfred	}
896184610Salfred	/* parse new config descriptor */
897184610Salfred	retval = libusb20_parse_config_desc(ptr);
898184610Salfred
899184610Salfred	/* free config descriptor */
900184610Salfred	free(ptr);
901184610Salfred
902184610Salfreddone:
903184610Salfred	if (do_close) {
904184610Salfred		error = libusb20_dev_close(pdev);
905184610Salfred	}
906184610Salfred	return (retval);
907184610Salfred}
908184610Salfred
909184610Salfredstruct libusb20_device *
910184610Salfredlibusb20_dev_alloc(void)
911184610Salfred{
912184610Salfred	struct libusb20_device *pdev;
913184610Salfred
914184610Salfred	pdev = malloc(sizeof(*pdev));
915184610Salfred	if (pdev == NULL) {
916184610Salfred		return (NULL);
917184610Salfred	}
918184610Salfred	memset(pdev, 0, sizeof(*pdev));
919184610Salfred
920184610Salfred	pdev->file = -1;
921184610Salfred	pdev->file_ctrl = -1;
922184610Salfred	pdev->methods = &libusb20_dummy_methods;
923184610Salfred	return (pdev);
924184610Salfred}
925184610Salfred
926184610Salfreduint8_t
927184610Salfredlibusb20_dev_get_config_index(struct libusb20_device *pdev)
928184610Salfred{
929184610Salfred	int error;
930185087Salfred	uint8_t cfg_index;
931184610Salfred	uint8_t do_close;
932184610Salfred
933184610Salfred	if (!pdev->is_opened) {
934184610Salfred		error = libusb20_dev_open(pdev, 0);
935184610Salfred		if (error == 0) {
936184610Salfred			do_close = 1;
937184610Salfred		} else {
938184610Salfred			do_close = 0;
939184610Salfred		}
940184610Salfred	} else {
941184610Salfred		do_close = 0;
942184610Salfred	}
943184610Salfred
944188622Sthompsa	error = pdev->methods->get_config_index(pdev, &cfg_index);
945184610Salfred	if (error) {
946185087Salfred		cfg_index = 0 - 1;	/* current config index */
947184610Salfred	}
948184610Salfred	if (do_close) {
949184610Salfred		if (libusb20_dev_close(pdev)) {
950184610Salfred			/* ignore */
951184610Salfred		}
952184610Salfred	}
953185087Salfred	return (cfg_index);
954184610Salfred}
955184610Salfred
956184610Salfreduint8_t
957184610Salfredlibusb20_dev_get_mode(struct libusb20_device *pdev)
958184610Salfred{
959184610Salfred	return (pdev->usb_mode);
960184610Salfred}
961184610Salfred
962184610Salfreduint8_t
963184610Salfredlibusb20_dev_get_speed(struct libusb20_device *pdev)
964184610Salfred{
965184610Salfred	return (pdev->usb_speed);
966184610Salfred}
967184610Salfred
968184610Salfred/* if this function returns an error, the device is gone */
969184610Salfredint
970184610Salfredlibusb20_dev_process(struct libusb20_device *pdev)
971184610Salfred{
972184610Salfred	int error;
973184610Salfred
974188622Sthompsa	error = pdev->methods->process(pdev);
975184610Salfred	return (error);
976184610Salfred}
977184610Salfred
978184610Salfredvoid
979184610Salfredlibusb20_dev_wait_process(struct libusb20_device *pdev, int timeout)
980184610Salfred{
981185087Salfred	struct pollfd pfd[1];
982184610Salfred
983184610Salfred	if (!pdev->is_opened) {
984184610Salfred		return;
985184610Salfred	}
986184610Salfred	pfd[0].fd = pdev->file;
987184610Salfred	pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
988184610Salfred	pfd[0].revents = 0;
989184610Salfred
990185087Salfred	if (poll(pfd, 1, timeout)) {
991184610Salfred		/* ignore any error */
992184610Salfred	}
993184610Salfred	return;
994184610Salfred}
995184610Salfred
996184610Salfredvoid
997184610Salfredlibusb20_dev_free(struct libusb20_device *pdev)
998184610Salfred{
999184610Salfred	if (pdev == NULL) {
1000184610Salfred		/* be NULL safe */
1001184610Salfred		return;
1002184610Salfred	}
1003184610Salfred	if (pdev->is_opened) {
1004184610Salfred		if (libusb20_dev_close(pdev)) {
1005184610Salfred			/* ignore any errors */
1006184610Salfred		}
1007184610Salfred	}
1008184610Salfred	free(pdev);
1009184610Salfred	return;
1010184610Salfred}
1011184610Salfred
1012188622Sthompsaint
1013188622Sthompsalibusb20_dev_get_info(struct libusb20_device *pdev,
1014192984Sthompsa    struct usb_device_info *pinfo)
1015188622Sthompsa{
1016188622Sthompsa	if (pinfo == NULL)
1017188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
1018188622Sthompsa
1019188622Sthompsa	return (pdev->beMethods->dev_get_info(pdev, pinfo));
1020188622Sthompsa}
1021188622Sthompsa
1022184610Salfredconst char *
1023184610Salfredlibusb20_dev_get_backend_name(struct libusb20_device *pdev)
1024184610Salfred{
1025188622Sthompsa	return (pdev->beMethods->get_backend_name());
1026184610Salfred}
1027184610Salfred
1028184610Salfredconst char *
1029184610Salfredlibusb20_dev_get_desc(struct libusb20_device *pdev)
1030184610Salfred{
1031184610Salfred	return (pdev->usb_desc);
1032184610Salfred}
1033184610Salfred
1034184610Salfredvoid
1035184610Salfredlibusb20_dev_set_debug(struct libusb20_device *pdev, int debug)
1036184610Salfred{
1037184610Salfred	pdev->debug = debug;
1038184610Salfred	return;
1039184610Salfred}
1040184610Salfred
1041184610Salfredint
1042184610Salfredlibusb20_dev_get_debug(struct libusb20_device *pdev)
1043184610Salfred{
1044184610Salfred	return (pdev->debug);
1045184610Salfred}
1046184610Salfred
1047184610Salfreduint8_t
1048184610Salfredlibusb20_dev_get_address(struct libusb20_device *pdev)
1049184610Salfred{
1050184610Salfred	return (pdev->device_address);
1051184610Salfred}
1052184610Salfred
1053184610Salfreduint8_t
1054184610Salfredlibusb20_dev_get_bus_number(struct libusb20_device *pdev)
1055184610Salfred{
1056184610Salfred	return (pdev->bus_number);
1057184610Salfred}
1058184610Salfred
1059184610Salfredint
1060188622Sthompsalibusb20_dev_get_iface_desc(struct libusb20_device *pdev,
1061188622Sthompsa    uint8_t iface_index, char *buf, uint8_t len)
1062188622Sthompsa{
1063188622Sthompsa	if ((buf == NULL) || (len == 0))
1064188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
1065188622Sthompsa
1066188622Sthompsa	return (pdev->beMethods->dev_get_iface_desc(
1067188622Sthompsa	    pdev, iface_index, buf, len));
1068188622Sthompsa}
1069188622Sthompsa
1070184610Salfred/* USB backend operations */
1071184610Salfred
1072184610Salfredint
1073184610Salfredlibusb20_be_get_dev_quirk(struct libusb20_backend *pbe,
1074185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
1075184610Salfred{
1076188622Sthompsa	return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq));
1077184610Salfred}
1078184610Salfred
1079184610Salfredint
1080184610Salfredlibusb20_be_get_quirk_name(struct libusb20_backend *pbe,
1081185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
1082184610Salfred{
1083188622Sthompsa	return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq));
1084184610Salfred}
1085184610Salfred
1086184610Salfredint
1087184610Salfredlibusb20_be_add_dev_quirk(struct libusb20_backend *pbe,
1088184610Salfred    struct libusb20_quirk *pq)
1089184610Salfred{
1090188622Sthompsa	return (pbe->methods->root_add_dev_quirk(pbe, pq));
1091184610Salfred}
1092184610Salfred
1093184610Salfredint
1094184610Salfredlibusb20_be_remove_dev_quirk(struct libusb20_backend *pbe,
1095184610Salfred    struct libusb20_quirk *pq)
1096184610Salfred{
1097188622Sthompsa	return (pbe->methods->root_remove_dev_quirk(pbe, pq));
1098184610Salfred}
1099184610Salfred
1100184610Salfredint
1101188987Sthompsalibusb20_be_set_template(struct libusb20_backend *pbe, int temp)
1102188987Sthompsa{
1103188987Sthompsa	return (pbe->methods->root_set_template(pbe, temp));
1104188987Sthompsa}
1105188987Sthompsa
1106188987Sthompsaint
1107188987Sthompsalibusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp)
1108188987Sthompsa{
1109188987Sthompsa	int temp;
1110188987Sthompsa
1111188987Sthompsa	if (ptemp == NULL)
1112188987Sthompsa		ptemp = &temp;
1113188987Sthompsa
1114188987Sthompsa	return (pbe->methods->root_get_template(pbe, ptemp));
1115188987Sthompsa}
1116188987Sthompsa
1117184610Salfredstruct libusb20_device *
1118184610Salfredlibusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1119184610Salfred{
1120184610Salfred	if (pbe == NULL) {
1121184610Salfred		pdev = NULL;
1122184610Salfred	} else if (pdev == NULL) {
1123184610Salfred		pdev = TAILQ_FIRST(&(pbe->usb_devs));
1124184610Salfred	} else {
1125184610Salfred		pdev = TAILQ_NEXT(pdev, dev_entry);
1126184610Salfred	}
1127184610Salfred	return (pdev);
1128184610Salfred}
1129184610Salfred
1130184610Salfredstruct libusb20_backend *
1131184610Salfredlibusb20_be_alloc(const struct libusb20_backend_methods *methods)
1132184610Salfred{
1133184610Salfred	struct libusb20_backend *pbe;
1134184610Salfred
1135184610Salfred	pbe = malloc(sizeof(*pbe));
1136184610Salfred	if (pbe == NULL) {
1137184610Salfred		return (NULL);
1138184610Salfred	}
1139184610Salfred	memset(pbe, 0, sizeof(*pbe));
1140184610Salfred
1141184610Salfred	TAILQ_INIT(&(pbe->usb_devs));
1142184610Salfred
1143184610Salfred	pbe->methods = methods;		/* set backend methods */
1144184610Salfred
1145184610Salfred	/* do the initial device scan */
1146184610Salfred	if (pbe->methods->init_backend) {
1147188622Sthompsa		pbe->methods->init_backend(pbe);
1148184610Salfred	}
1149184610Salfred	return (pbe);
1150184610Salfred}
1151184610Salfred
1152184610Salfredstruct libusb20_backend *
1153184610Salfredlibusb20_be_alloc_linux(void)
1154184610Salfred{
1155184610Salfred	struct libusb20_backend *pbe;
1156184610Salfred
1157184610Salfred#ifdef __linux__
1158184610Salfred	pbe = libusb20_be_alloc(&libusb20_linux_backend);
1159184610Salfred#else
1160184610Salfred	pbe = NULL;
1161184610Salfred#endif
1162184610Salfred	return (pbe);
1163184610Salfred}
1164184610Salfred
1165184610Salfredstruct libusb20_backend *
1166184610Salfredlibusb20_be_alloc_ugen20(void)
1167184610Salfred{
1168184610Salfred	struct libusb20_backend *pbe;
1169184610Salfred
1170213852Shselasky#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1171184610Salfred	pbe = libusb20_be_alloc(&libusb20_ugen20_backend);
1172184610Salfred#else
1173184610Salfred	pbe = NULL;
1174184610Salfred#endif
1175184610Salfred	return (pbe);
1176184610Salfred}
1177184610Salfred
1178184610Salfredstruct libusb20_backend *
1179184610Salfredlibusb20_be_alloc_default(void)
1180184610Salfred{
1181184610Salfred	struct libusb20_backend *pbe;
1182184610Salfred
1183184610Salfred	pbe = libusb20_be_alloc_linux();
1184184610Salfred	if (pbe) {
1185184610Salfred		return (pbe);
1186184610Salfred	}
1187184610Salfred	pbe = libusb20_be_alloc_ugen20();
1188184610Salfred	if (pbe) {
1189184610Salfred		return (pbe);
1190184610Salfred	}
1191184610Salfred	return (NULL);			/* no backend found */
1192184610Salfred}
1193184610Salfred
1194184610Salfredvoid
1195184610Salfredlibusb20_be_free(struct libusb20_backend *pbe)
1196184610Salfred{
1197184610Salfred	struct libusb20_device *pdev;
1198184610Salfred
1199184610Salfred	if (pbe == NULL) {
1200184610Salfred		/* be NULL safe */
1201184610Salfred		return;
1202184610Salfred	}
1203184610Salfred	while ((pdev = libusb20_be_device_foreach(pbe, NULL))) {
1204184610Salfred		libusb20_be_dequeue_device(pbe, pdev);
1205184610Salfred		libusb20_dev_free(pdev);
1206184610Salfred	}
1207184610Salfred	if (pbe->methods->exit_backend) {
1208188622Sthompsa		pbe->methods->exit_backend(pbe);
1209184610Salfred	}
1210199055Sthompsa	/* free backend */
1211199055Sthompsa	free(pbe);
1212184610Salfred}
1213184610Salfred
1214184610Salfredvoid
1215184610Salfredlibusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1216184610Salfred{
1217184610Salfred	pdev->beMethods = pbe->methods;	/* copy backend methods */
1218184610Salfred	TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry);
1219184610Salfred}
1220184610Salfred
1221184610Salfredvoid
1222184610Salfredlibusb20_be_dequeue_device(struct libusb20_backend *pbe,
1223184610Salfred    struct libusb20_device *pdev)
1224184610Salfred{
1225184610Salfred	TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry);
1226184610Salfred}
1227