1184610Salfred/* $FreeBSD: stable/10/lib/libusb/libusb20.c 356399 2020-01-06 09:22:33Z 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
27248236Shselasky#ifdef LIBUSB_GLOBAL_INCLUDE_FILE
28248236Shselasky#include LIBUSB_GLOBAL_INCLUDE_FILE
29248236Shselasky#else
30203815Swkoszek#include <ctype.h>
31203815Swkoszek#include <poll.h>
32184610Salfred#include <stdio.h>
33184610Salfred#include <stdlib.h>
34184610Salfred#include <string.h>
35248236Shselasky#include <time.h>
36248236Shselasky#include <sys/queue.h>
37248236Shselasky#endif
38184610Salfred
39184610Salfred#include "libusb20.h"
40184610Salfred#include "libusb20_desc.h"
41184610Salfred#include "libusb20_int.h"
42184610Salfred
43184610Salfredstatic int
44184610Salfreddummy_int(void)
45184610Salfred{
46184610Salfred	return (LIBUSB20_ERROR_NOT_SUPPORTED);
47184610Salfred}
48184610Salfred
49184610Salfredstatic void
50184610Salfreddummy_void(void)
51184610Salfred{
52184610Salfred	return;
53184610Salfred}
54184610Salfred
55184610Salfredstatic void
56184610Salfreddummy_callback(struct libusb20_transfer *xfer)
57184610Salfred{
58184610Salfred	;				/* style fix */
59184610Salfred	switch (libusb20_tr_get_status(xfer)) {
60184610Salfred	case LIBUSB20_TRANSFER_START:
61184610Salfred		libusb20_tr_submit(xfer);
62184610Salfred		break;
63184610Salfred	default:
64184610Salfred		/* complete or error */
65184610Salfred		break;
66184610Salfred	}
67184610Salfred	return;
68184610Salfred}
69184610Salfred
70184610Salfred#define	dummy_get_config_desc_full (void *)dummy_int
71184610Salfred#define	dummy_get_config_index (void *)dummy_int
72184610Salfred#define	dummy_set_config_index (void *)dummy_int
73184610Salfred#define	dummy_set_alt_index (void *)dummy_int
74184610Salfred#define	dummy_reset_device (void *)dummy_int
75203147Sthompsa#define	dummy_check_connected (void *)dummy_int
76184610Salfred#define	dummy_set_power_mode (void *)dummy_int
77184610Salfred#define	dummy_get_power_mode (void *)dummy_int
78250201Shselasky#define	dummy_get_port_path (void *)dummy_int
79246789Shselasky#define	dummy_get_power_usage (void *)dummy_int
80356399Shselasky#define	dummy_get_stats (void *)dummy_int
81184610Salfred#define	dummy_kernel_driver_active (void *)dummy_int
82184610Salfred#define	dummy_detach_kernel_driver (void *)dummy_int
83184610Salfred#define	dummy_do_request_sync (void *)dummy_int
84184610Salfred#define	dummy_tr_open (void *)dummy_int
85184610Salfred#define	dummy_tr_close (void *)dummy_int
86184610Salfred#define	dummy_tr_clear_stall_sync (void *)dummy_int
87184610Salfred#define	dummy_process (void *)dummy_int
88188622Sthompsa#define	dummy_dev_info (void *)dummy_int
89188622Sthompsa#define	dummy_dev_get_iface_driver (void *)dummy_int
90184610Salfred
91184610Salfred#define	dummy_tr_submit (void *)dummy_void
92184610Salfred#define	dummy_tr_cancel_async (void *)dummy_void
93184610Salfred
94184610Salfredstatic const struct libusb20_device_methods libusb20_dummy_methods = {
95184610Salfred	LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy)
96184610Salfred};
97184610Salfred
98184610Salfredvoid
99184610Salfredlibusb20_tr_callback_wrapper(struct libusb20_transfer *xfer)
100184610Salfred{
101184610Salfred	;				/* style fix */
102184610Salfred
103184610Salfredrepeat:
104184610Salfred
105184610Salfred	if (!xfer->is_pending) {
106184610Salfred		xfer->status = LIBUSB20_TRANSFER_START;
107184610Salfred	} else {
108184610Salfred		xfer->is_pending = 0;
109184610Salfred	}
110184610Salfred
111188622Sthompsa	xfer->callback(xfer);
112184610Salfred
113184610Salfred	if (xfer->is_restart) {
114184610Salfred		xfer->is_restart = 0;
115184610Salfred		goto repeat;
116184610Salfred	}
117184610Salfred	if (xfer->is_draining &&
118184610Salfred	    (!xfer->is_pending)) {
119184610Salfred		xfer->is_draining = 0;
120184610Salfred		xfer->status = LIBUSB20_TRANSFER_DRAINED;
121188622Sthompsa		xfer->callback(xfer);
122184610Salfred	}
123184610Salfred	return;
124184610Salfred}
125184610Salfred
126184610Salfredint
127184610Salfredlibusb20_tr_close(struct libusb20_transfer *xfer)
128184610Salfred{
129184610Salfred	int error;
130184610Salfred
131184610Salfred	if (!xfer->is_opened) {
132184610Salfred		return (LIBUSB20_ERROR_OTHER);
133184610Salfred	}
134188622Sthompsa	error = xfer->pdev->methods->tr_close(xfer);
135184610Salfred
136184610Salfred	if (xfer->pLength) {
137184610Salfred		free(xfer->pLength);
138184610Salfred	}
139184610Salfred	if (xfer->ppBuffer) {
140184610Salfred		free(xfer->ppBuffer);
141184610Salfred	}
142202025Sthompsa	/* reset variable fields in case the transfer is opened again */
143202025Sthompsa	xfer->priv_sc0 = 0;
144202025Sthompsa	xfer->priv_sc1 = 0;
145184610Salfred	xfer->is_opened = 0;
146202025Sthompsa	xfer->is_pending = 0;
147202025Sthompsa	xfer->is_cancel = 0;
148202025Sthompsa	xfer->is_draining = 0;
149202025Sthompsa	xfer->is_restart = 0;
150202025Sthompsa	xfer->status = 0;
151202025Sthompsa	xfer->flags = 0;
152202025Sthompsa	xfer->nFrames = 0;
153202025Sthompsa	xfer->aFrames = 0;
154202025Sthompsa	xfer->timeout = 0;
155184610Salfred	xfer->maxFrames = 0;
156184610Salfred	xfer->maxTotalLength = 0;
157184610Salfred	xfer->maxPacketLen = 0;
158184610Salfred	return (error);
159184610Salfred}
160184610Salfred
161184610Salfredint
162184610Salfredlibusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
163184610Salfred    uint32_t MaxFrameCount, uint8_t ep_no)
164184610Salfred{
165239239Shselasky	return (libusb20_tr_open_stream(xfer, MaxBufSize, MaxFrameCount, ep_no, 0));
166239239Shselasky}
167239239Shselasky
168239239Shselaskyint
169239239Shselaskylibusb20_tr_open_stream(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
170239239Shselasky    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id)
171239239Shselasky{
172184610Salfred	uint32_t size;
173219100Shselasky	uint8_t pre_scale;
174184610Salfred	int error;
175184610Salfred
176219100Shselasky	if (xfer->is_opened)
177184610Salfred		return (LIBUSB20_ERROR_BUSY);
178219100Shselasky	if (MaxFrameCount & LIBUSB20_MAX_FRAME_PRE_SCALE) {
179219100Shselasky		MaxFrameCount &= ~LIBUSB20_MAX_FRAME_PRE_SCALE;
180305641Shselasky		/*
181305641Shselasky		 * The kernel can setup 8 times more frames when
182305641Shselasky		 * pre-scaling ISOCHRONOUS transfers. Make sure the
183305641Shselasky		 * length and pointer buffers are big enough:
184305641Shselasky		 */
185305641Shselasky		MaxFrameCount *= 8;
186219100Shselasky		pre_scale = 1;
187219100Shselasky	} else {
188219100Shselasky		pre_scale = 0;
189184610Salfred	}
190219100Shselasky	if (MaxFrameCount == 0)
191184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
192219100Shselasky
193184610Salfred	xfer->maxFrames = MaxFrameCount;
194184610Salfred
195184610Salfred	size = MaxFrameCount * sizeof(xfer->pLength[0]);
196184610Salfred	xfer->pLength = malloc(size);
197184610Salfred	if (xfer->pLength == NULL) {
198184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
199184610Salfred	}
200184610Salfred	memset(xfer->pLength, 0, size);
201184610Salfred
202184610Salfred	size = MaxFrameCount * sizeof(xfer->ppBuffer[0]);
203184610Salfred	xfer->ppBuffer = malloc(size);
204184610Salfred	if (xfer->ppBuffer == NULL) {
205184610Salfred		free(xfer->pLength);
206184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
207184610Salfred	}
208184610Salfred	memset(xfer->ppBuffer, 0, size);
209184610Salfred
210305641Shselasky	if (pre_scale) {
211305641Shselasky		error = xfer->pdev->methods->tr_open(xfer, MaxBufSize,
212305641Shselasky		    MaxFrameCount / 8, ep_no, stream_id, 1);
213305641Shselasky	} else {
214305641Shselasky		error = xfer->pdev->methods->tr_open(xfer, MaxBufSize,
215305641Shselasky		    MaxFrameCount, ep_no, stream_id, 0);
216305641Shselasky	}
217184610Salfred
218184610Salfred	if (error) {
219184610Salfred		free(xfer->ppBuffer);
220184610Salfred		free(xfer->pLength);
221184610Salfred	} else {
222184610Salfred		xfer->is_opened = 1;
223184610Salfred	}
224184610Salfred	return (error);
225184610Salfred}
226184610Salfred
227184610Salfredstruct libusb20_transfer *
228184610Salfredlibusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex)
229184610Salfred{
230184610Salfred	if (trIndex >= pdev->nTransfer) {
231184610Salfred		return (NULL);
232184610Salfred	}
233184610Salfred	return (pdev->pTransfer + trIndex);
234184610Salfred}
235184610Salfred
236184610Salfreduint32_t
237184610Salfredlibusb20_tr_get_actual_frames(struct libusb20_transfer *xfer)
238184610Salfred{
239184610Salfred	return (xfer->aFrames);
240184610Salfred}
241184610Salfred
242184610Salfreduint16_t
243184610Salfredlibusb20_tr_get_time_complete(struct libusb20_transfer *xfer)
244184610Salfred{
245184610Salfred	return (xfer->timeComplete);
246184610Salfred}
247184610Salfred
248184610Salfreduint32_t
249184610Salfredlibusb20_tr_get_actual_length(struct libusb20_transfer *xfer)
250184610Salfred{
251184610Salfred	uint32_t x;
252184610Salfred	uint32_t actlen = 0;
253184610Salfred
254184610Salfred	for (x = 0; x != xfer->aFrames; x++) {
255184610Salfred		actlen += xfer->pLength[x];
256184610Salfred	}
257184610Salfred	return (actlen);
258184610Salfred}
259184610Salfred
260184610Salfreduint32_t
261184610Salfredlibusb20_tr_get_max_frames(struct libusb20_transfer *xfer)
262184610Salfred{
263184610Salfred	return (xfer->maxFrames);
264184610Salfred}
265184610Salfred
266184610Salfreduint32_t
267184610Salfredlibusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer)
268184610Salfred{
269184610Salfred	/*
270184610Salfred	 * Special Case NOTE: If the packet multiplier is non-zero for
271184610Salfred	 * High Speed USB, the value returned is equal to
272184610Salfred	 * "wMaxPacketSize * multiplier" !
273184610Salfred	 */
274184610Salfred	return (xfer->maxPacketLen);
275184610Salfred}
276184610Salfred
277184610Salfreduint32_t
278184610Salfredlibusb20_tr_get_max_total_length(struct libusb20_transfer *xfer)
279184610Salfred{
280184610Salfred	return (xfer->maxTotalLength);
281184610Salfred}
282184610Salfred
283184610Salfreduint8_t
284184610Salfredlibusb20_tr_get_status(struct libusb20_transfer *xfer)
285184610Salfred{
286184610Salfred	return (xfer->status);
287184610Salfred}
288184610Salfred
289184610Salfreduint8_t
290184610Salfredlibusb20_tr_pending(struct libusb20_transfer *xfer)
291184610Salfred{
292184610Salfred	return (xfer->is_pending);
293184610Salfred}
294184610Salfred
295184610Salfredvoid   *
296184610Salfredlibusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer)
297184610Salfred{
298184610Salfred	return (xfer->priv_sc0);
299184610Salfred}
300184610Salfred
301184610Salfredvoid   *
302184610Salfredlibusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer)
303184610Salfred{
304184610Salfred	return (xfer->priv_sc1);
305184610Salfred}
306184610Salfred
307184610Salfredvoid
308184610Salfredlibusb20_tr_stop(struct libusb20_transfer *xfer)
309184610Salfred{
310199575Sthompsa	if (!xfer->is_opened) {
311199575Sthompsa		/* transfer is not opened */
312199575Sthompsa		return;
313199575Sthompsa	}
314184610Salfred	if (!xfer->is_pending) {
315184610Salfred		/* transfer not pending */
316184610Salfred		return;
317184610Salfred	}
318184610Salfred	if (xfer->is_cancel) {
319184610Salfred		/* already cancelling */
320184610Salfred		return;
321184610Salfred	}
322184610Salfred	xfer->is_cancel = 1;		/* we are cancelling */
323184610Salfred
324188622Sthompsa	xfer->pdev->methods->tr_cancel_async(xfer);
325184610Salfred	return;
326184610Salfred}
327184610Salfred
328184610Salfredvoid
329184610Salfredlibusb20_tr_drain(struct libusb20_transfer *xfer)
330184610Salfred{
331199575Sthompsa	if (!xfer->is_opened) {
332199575Sthompsa		/* transfer is not opened */
333199575Sthompsa		return;
334199575Sthompsa	}
335184610Salfred	/* make sure that we are cancelling */
336184610Salfred	libusb20_tr_stop(xfer);
337184610Salfred
338184610Salfred	if (xfer->is_pending) {
339184610Salfred		xfer->is_draining = 1;
340184610Salfred	}
341184610Salfred	return;
342184610Salfred}
343184610Salfred
344184610Salfredvoid
345184610Salfredlibusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
346184610Salfred{
347188622Sthompsa	xfer->pdev->methods->tr_clear_stall_sync(xfer);
348184610Salfred	return;
349184610Salfred}
350184610Salfred
351184610Salfredvoid
352184610Salfredlibusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex)
353184610Salfred{
354213852Shselasky	xfer->ppBuffer[frIndex] = libusb20_pass_ptr(buffer);
355184610Salfred	return;
356184610Salfred}
357184610Salfred
358184610Salfredvoid
359184610Salfredlibusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb)
360184610Salfred{
361184610Salfred	xfer->callback = cb;
362184610Salfred	return;
363184610Salfred}
364184610Salfred
365184610Salfredvoid
366184610Salfredlibusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags)
367184610Salfred{
368184610Salfred	xfer->flags = flags;
369184610Salfred	return;
370184610Salfred}
371184610Salfred
372193313Sthompsauint32_t
373193313Sthompsalibusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t frIndex)
374193313Sthompsa{
375193313Sthompsa	return (xfer->pLength[frIndex]);
376193313Sthompsa}
377193313Sthompsa
378184610Salfredvoid
379184610Salfredlibusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex)
380184610Salfred{
381184610Salfred	xfer->pLength[frIndex] = length;
382184610Salfred	return;
383184610Salfred}
384184610Salfred
385184610Salfredvoid
386184610Salfredlibusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0)
387184610Salfred{
388184610Salfred	xfer->priv_sc0 = sc0;
389184610Salfred	return;
390184610Salfred}
391184610Salfred
392184610Salfredvoid
393184610Salfredlibusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1)
394184610Salfred{
395184610Salfred	xfer->priv_sc1 = sc1;
396184610Salfred	return;
397184610Salfred}
398184610Salfred
399184610Salfredvoid
400184610Salfredlibusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout)
401184610Salfred{
402184610Salfred	xfer->timeout = timeout;
403184610Salfred	return;
404184610Salfred}
405184610Salfred
406184610Salfredvoid
407184610Salfredlibusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames)
408184610Salfred{
409184610Salfred	if (nFrames > xfer->maxFrames) {
410184610Salfred		/* should not happen */
411184610Salfred		nFrames = xfer->maxFrames;
412184610Salfred	}
413184610Salfred	xfer->nFrames = nFrames;
414184610Salfred	return;
415184610Salfred}
416184610Salfred
417184610Salfredvoid
418184610Salfredlibusb20_tr_setup_bulk(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_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout)
429184610Salfred{
430184610Salfred	uint16_t len;
431184610Salfred
432213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(psetup);
433184610Salfred	xfer->pLength[0] = 8;		/* fixed */
434184610Salfred	xfer->timeout = timeout;
435184610Salfred
436184610Salfred	len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8);
437184610Salfred
438184610Salfred	if (len != 0) {
439184610Salfred		xfer->nFrames = 2;
440213852Shselasky		xfer->ppBuffer[1] = libusb20_pass_ptr(pBuf);
441184610Salfred		xfer->pLength[1] = len;
442184610Salfred	} else {
443184610Salfred		xfer->nFrames = 1;
444184610Salfred	}
445184610Salfred	return;
446184610Salfred}
447184610Salfred
448184610Salfredvoid
449184610Salfredlibusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout)
450184610Salfred{
451213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf);
452184610Salfred	xfer->pLength[0] = length;
453184610Salfred	xfer->timeout = timeout;
454184610Salfred	xfer->nFrames = 1;
455184610Salfred	return;
456184610Salfred}
457184610Salfred
458184610Salfredvoid
459184610Salfredlibusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex)
460184610Salfred{
461184610Salfred	if (frIndex >= xfer->maxFrames) {
462184610Salfred		/* should not happen */
463184610Salfred		return;
464184610Salfred	}
465213852Shselasky	xfer->ppBuffer[frIndex] = libusb20_pass_ptr(pBuf);
466184610Salfred	xfer->pLength[frIndex] = length;
467184610Salfred	return;
468184610Salfred}
469184610Salfred
470199575Sthompsauint8_t
471199575Sthompsalibusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer,
472199575Sthompsa    void *pbuf, uint32_t length, uint32_t *pactlen,
473199575Sthompsa    uint32_t timeout)
474199575Sthompsa{
475199575Sthompsa	struct libusb20_device *pdev = xfer->pdev;
476199575Sthompsa	uint32_t transfer_max;
477199575Sthompsa	uint32_t transfer_act;
478199575Sthompsa	uint8_t retval;
479199575Sthompsa
480199575Sthompsa	/* set some sensible default value */
481199575Sthompsa	if (pactlen != NULL)
482199575Sthompsa		*pactlen = 0;
483199575Sthompsa
484199575Sthompsa	/* check for error condition */
485199575Sthompsa	if (libusb20_tr_pending(xfer))
486199575Sthompsa		return (LIBUSB20_ERROR_OTHER);
487199575Sthompsa
488199575Sthompsa	do {
489199575Sthompsa		/* compute maximum transfer length */
490199575Sthompsa		transfer_max =
491199575Sthompsa		    libusb20_tr_get_max_total_length(xfer);
492199575Sthompsa
493199575Sthompsa		if (transfer_max > length)
494199575Sthompsa			transfer_max = length;
495199575Sthompsa
496199575Sthompsa		/* setup bulk or interrupt transfer */
497199575Sthompsa		libusb20_tr_setup_bulk(xfer, pbuf,
498199575Sthompsa		    transfer_max, timeout);
499199575Sthompsa
500199575Sthompsa		/* start the transfer */
501199575Sthompsa		libusb20_tr_start(xfer);
502199575Sthompsa
503199575Sthompsa		/* wait for transfer completion */
504199575Sthompsa		while (libusb20_dev_process(pdev) == 0) {
505199575Sthompsa
506199575Sthompsa			if (libusb20_tr_pending(xfer) == 0)
507199575Sthompsa				break;
508199575Sthompsa
509199575Sthompsa			libusb20_dev_wait_process(pdev, -1);
510199575Sthompsa		}
511199575Sthompsa
512199575Sthompsa		transfer_act = libusb20_tr_get_actual_length(xfer);
513199575Sthompsa
514199575Sthompsa		/* update actual length, if any */
515199575Sthompsa		if (pactlen != NULL)
516199575Sthompsa			pactlen[0] += transfer_act;
517199575Sthompsa
518199575Sthompsa		/* check transfer status */
519199575Sthompsa		retval = libusb20_tr_get_status(xfer);
520199575Sthompsa		if (retval)
521199575Sthompsa			break;
522199575Sthompsa
523199575Sthompsa		/* check for short transfer */
524199575Sthompsa		if (transfer_act != transfer_max)
525199575Sthompsa			break;
526199575Sthompsa
527199575Sthompsa		/* update buffer pointer and length */
528199575Sthompsa		pbuf = ((uint8_t *)pbuf) + transfer_max;
529199575Sthompsa		length = length - transfer_max;
530199575Sthompsa
531199575Sthompsa	} while (length != 0);
532199575Sthompsa
533199575Sthompsa	return (retval);
534199575Sthompsa}
535199575Sthompsa
536184610Salfredvoid
537184610Salfredlibusb20_tr_submit(struct libusb20_transfer *xfer)
538184610Salfred{
539199575Sthompsa	if (!xfer->is_opened) {
540199575Sthompsa		/* transfer is not opened */
541199575Sthompsa		return;
542199575Sthompsa	}
543184610Salfred	if (xfer->is_pending) {
544184610Salfred		/* should not happen */
545184610Salfred		return;
546184610Salfred	}
547184610Salfred	xfer->is_pending = 1;		/* we are pending */
548184610Salfred	xfer->is_cancel = 0;		/* not cancelling */
549184610Salfred	xfer->is_restart = 0;		/* not restarting */
550184610Salfred
551188622Sthompsa	xfer->pdev->methods->tr_submit(xfer);
552184610Salfred	return;
553184610Salfred}
554184610Salfred
555184610Salfredvoid
556184610Salfredlibusb20_tr_start(struct libusb20_transfer *xfer)
557184610Salfred{
558199575Sthompsa	if (!xfer->is_opened) {
559199575Sthompsa		/* transfer is not opened */
560199575Sthompsa		return;
561199575Sthompsa	}
562184610Salfred	if (xfer->is_pending) {
563184610Salfred		if (xfer->is_cancel) {
564184610Salfred			/* cancelling - restart */
565184610Salfred			xfer->is_restart = 1;
566184610Salfred		}
567184610Salfred		/* transfer not pending */
568184610Salfred		return;
569184610Salfred	}
570184610Salfred	/* get into the callback */
571184610Salfred	libusb20_tr_callback_wrapper(xfer);
572184610Salfred	return;
573184610Salfred}
574184610Salfred
575184610Salfred/* USB device operations */
576184610Salfred
577184610Salfredint
578184610Salfredlibusb20_dev_close(struct libusb20_device *pdev)
579184610Salfred{
580184610Salfred	struct libusb20_transfer *xfer;
581184610Salfred	uint16_t x;
582184610Salfred	int error = 0;
583184610Salfred
584184610Salfred	if (!pdev->is_opened) {
585184610Salfred		return (LIBUSB20_ERROR_OTHER);
586184610Salfred	}
587184610Salfred	for (x = 0; x != pdev->nTransfer; x++) {
588184610Salfred		xfer = pdev->pTransfer + x;
589184610Salfred
590199575Sthompsa		if (!xfer->is_opened) {
591199575Sthompsa			/* transfer is not opened */
592199575Sthompsa			continue;
593199575Sthompsa		}
594199575Sthompsa
595184610Salfred		libusb20_tr_drain(xfer);
596199575Sthompsa
597199575Sthompsa		libusb20_tr_close(xfer);
598184610Salfred	}
599184610Salfred
600184610Salfred	if (pdev->pTransfer != NULL) {
601184610Salfred		free(pdev->pTransfer);
602184610Salfred		pdev->pTransfer = NULL;
603184610Salfred	}
604188622Sthompsa	error = pdev->beMethods->close_device(pdev);
605184610Salfred
606184610Salfred	pdev->methods = &libusb20_dummy_methods;
607184610Salfred
608184610Salfred	pdev->is_opened = 0;
609184610Salfred
610194069Sthompsa	/*
611194069Sthompsa	 * The following variable is only used by the libusb v0.1
612194069Sthompsa	 * compat layer:
613194069Sthompsa	 */
614194069Sthompsa	pdev->claimed_interface = 0;
615187184Sthompsa
616302275Shselasky	/*
617302275Shselasky	 * The following variable is only used by the libusb v1.0
618302275Shselasky	 * compat layer:
619302275Shselasky	 */
620302275Shselasky	pdev->auto_detach = 0;
621302275Shselasky
622184610Salfred	return (error);
623184610Salfred}
624184610Salfred
625184610Salfredint
626184610Salfredlibusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex)
627184610Salfred{
628184610Salfred	int error;
629184610Salfred
630188622Sthompsa	error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex);
631184610Salfred	return (error);
632184610Salfred}
633184610Salfred
634184610Salfredstruct LIBUSB20_DEVICE_DESC_DECODED *
635184610Salfredlibusb20_dev_get_device_desc(struct libusb20_device *pdev)
636184610Salfred{
637184610Salfred	return (&(pdev->ddesc));
638184610Salfred}
639184610Salfred
640184610Salfredint
641184610Salfredlibusb20_dev_get_fd(struct libusb20_device *pdev)
642184610Salfred{
643184610Salfred	return (pdev->file);
644184610Salfred}
645184610Salfred
646184610Salfredint
647184610Salfredlibusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex)
648184610Salfred{
649184610Salfred	int error;
650184610Salfred
651188622Sthompsa	error = pdev->methods->kernel_driver_active(pdev, ifaceIndex);
652184610Salfred	return (error);
653184610Salfred}
654184610Salfred
655184610Salfredint
656184610Salfredlibusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax)
657184610Salfred{
658184610Salfred	struct libusb20_transfer *xfer;
659184610Salfred	uint32_t size;
660184610Salfred	uint16_t x;
661184610Salfred	int error;
662184610Salfred
663184610Salfred	if (pdev->is_opened) {
664184610Salfred		return (LIBUSB20_ERROR_BUSY);
665184610Salfred	}
666184610Salfred	if (nTransferMax >= 256) {
667184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
668184610Salfred	} else if (nTransferMax != 0) {
669184610Salfred		size = sizeof(pdev->pTransfer[0]) * nTransferMax;
670184610Salfred		pdev->pTransfer = malloc(size);
671184610Salfred		if (pdev->pTransfer == NULL) {
672184610Salfred			return (LIBUSB20_ERROR_NO_MEM);
673184610Salfred		}
674184610Salfred		memset(pdev->pTransfer, 0, size);
675184610Salfred	}
676184610Salfred	/* initialise all transfers */
677184610Salfred	for (x = 0; x != nTransferMax; x++) {
678184610Salfred
679184610Salfred		xfer = pdev->pTransfer + x;
680184610Salfred
681184610Salfred		xfer->pdev = pdev;
682184610Salfred		xfer->trIndex = x;
683184610Salfred		xfer->callback = &dummy_callback;
684184610Salfred	}
685184610Salfred
686185087Salfred	/* set "nTransfer" early */
687185087Salfred	pdev->nTransfer = nTransferMax;
688185087Salfred
689188622Sthompsa	error = pdev->beMethods->open_device(pdev, nTransferMax);
690184610Salfred
691184610Salfred	if (error) {
692184610Salfred		if (pdev->pTransfer != NULL) {
693184610Salfred			free(pdev->pTransfer);
694184610Salfred			pdev->pTransfer = NULL;
695184610Salfred		}
696184610Salfred		pdev->file = -1;
697184610Salfred		pdev->file_ctrl = -1;
698184610Salfred		pdev->nTransfer = 0;
699184610Salfred	} else {
700184610Salfred		pdev->is_opened = 1;
701184610Salfred	}
702184610Salfred	return (error);
703184610Salfred}
704184610Salfred
705184610Salfredint
706184610Salfredlibusb20_dev_reset(struct libusb20_device *pdev)
707184610Salfred{
708184610Salfred	int error;
709184610Salfred
710188622Sthompsa	error = pdev->methods->reset_device(pdev);
711184610Salfred	return (error);
712184610Salfred}
713184610Salfred
714184610Salfredint
715203147Sthompsalibusb20_dev_check_connected(struct libusb20_device *pdev)
716203147Sthompsa{
717203147Sthompsa	int error;
718203147Sthompsa
719203147Sthompsa	error = pdev->methods->check_connected(pdev);
720203147Sthompsa	return (error);
721203147Sthompsa}
722203147Sthompsa
723203147Sthompsaint
724184610Salfredlibusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
725184610Salfred{
726184610Salfred	int error;
727184610Salfred
728188622Sthompsa	error = pdev->methods->set_power_mode(pdev, power_mode);
729184610Salfred	return (error);
730184610Salfred}
731184610Salfred
732184610Salfreduint8_t
733184610Salfredlibusb20_dev_get_power_mode(struct libusb20_device *pdev)
734184610Salfred{
735184610Salfred	int error;
736184610Salfred	uint8_t power_mode;
737184610Salfred
738188622Sthompsa	error = pdev->methods->get_power_mode(pdev, &power_mode);
739184610Salfred	if (error)
740184610Salfred		power_mode = LIBUSB20_POWER_ON;	/* fake power mode */
741184610Salfred	return (power_mode);
742184610Salfred}
743184610Salfred
744250201Shselaskyint
745250201Shselaskylibusb20_dev_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
746250201Shselasky{
747250201Shselasky	return (pdev->methods->get_port_path(pdev, buf, bufsize));
748250201Shselasky}
749250201Shselasky
750246789Shselaskyuint16_t
751246789Shselaskylibusb20_dev_get_power_usage(struct libusb20_device *pdev)
752246789Shselasky{
753246789Shselasky	int error;
754246789Shselasky	uint16_t power_usage;
755246789Shselasky
756246789Shselasky	error = pdev->methods->get_power_usage(pdev, &power_usage);
757246789Shselasky	if (error)
758246789Shselasky		power_usage = 0;
759246789Shselasky	return (power_usage);
760246789Shselasky}
761246789Shselasky
762184610Salfredint
763184610Salfredlibusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex)
764184610Salfred{
765184610Salfred	int error;
766184610Salfred
767188622Sthompsa	error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex);
768184610Salfred	return (error);
769184610Salfred}
770184610Salfred
771184610Salfredint
772184610Salfredlibusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex)
773184610Salfred{
774184610Salfred	int error;
775184610Salfred
776188622Sthompsa	error = pdev->methods->set_config_index(pdev, configIndex);
777184610Salfred	return (error);
778184610Salfred}
779184610Salfred
780184610Salfredint
781184610Salfredlibusb20_dev_request_sync(struct libusb20_device *pdev,
782184610Salfred    struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data,
783184610Salfred    uint16_t *pactlen, uint32_t timeout, uint8_t flags)
784184610Salfred{
785184610Salfred	int error;
786184610Salfred
787188622Sthompsa	error = pdev->methods->do_request_sync(pdev,
788184610Salfred	    setup, data, pactlen, timeout, flags);
789184610Salfred	return (error);
790184610Salfred}
791184610Salfred
792184610Salfredint
793184610Salfredlibusb20_dev_req_string_sync(struct libusb20_device *pdev,
794185087Salfred    uint8_t str_index, uint16_t langid, void *ptr, uint16_t len)
795184610Salfred{
796184610Salfred	struct LIBUSB20_CONTROL_SETUP_DECODED req;
797184610Salfred	int error;
798336884Shselasky	int flags;
799184610Salfred
800199055Sthompsa	/* make sure memory is initialised */
801199055Sthompsa	memset(ptr, 0, len);
802199055Sthompsa
803184610Salfred	if (len < 4) {
804184610Salfred		/* invalid length */
805184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
806184610Salfred	}
807184610Salfred	LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req);
808184610Salfred
809184610Salfred	/*
810184610Salfred	 * We need to read the USB string in two steps else some USB
811184610Salfred	 * devices will complain.
812184610Salfred	 */
813184610Salfred	req.bmRequestType =
814184610Salfred	    LIBUSB20_REQUEST_TYPE_STANDARD |
815184610Salfred	    LIBUSB20_RECIPIENT_DEVICE |
816184610Salfred	    LIBUSB20_ENDPOINT_IN;
817184610Salfred	req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR;
818185087Salfred	req.wValue = (LIBUSB20_DT_STRING << 8) | str_index;
819184610Salfred	req.wIndex = langid;
820184610Salfred	req.wLength = 4;		/* bytes */
821184610Salfred
822184610Salfred	error = libusb20_dev_request_sync(pdev, &req,
823184610Salfred	    ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK);
824184610Salfred	if (error) {
825336884Shselasky		/* try to request full string */
826336884Shselasky		req.wLength = 255;
827336884Shselasky		flags = 0;
828336884Shselasky	} else {
829336884Shselasky		/* extract length and request full string */
830336884Shselasky		req.wLength = *(uint8_t *)ptr;
831336884Shselasky		flags = LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK;
832184610Salfred	}
833184610Salfred	if (req.wLength > len) {
834184610Salfred		/* partial string read */
835184610Salfred		req.wLength = len;
836184610Salfred	}
837336884Shselasky	error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, flags);
838336884Shselasky	if (error)
839336884Shselasky		return (error);
840184610Salfred
841336884Shselasky	if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING)
842184610Salfred		return (LIBUSB20_ERROR_OTHER);
843184610Salfred	return (0);			/* success */
844184610Salfred}
845184610Salfred
846184610Salfredint
847184610Salfredlibusb20_dev_req_string_simple_sync(struct libusb20_device *pdev,
848185087Salfred    uint8_t str_index, void *ptr, uint16_t len)
849184610Salfred{
850184610Salfred	char *buf;
851184610Salfred	int error;
852184610Salfred	uint16_t langid;
853184610Salfred	uint16_t n;
854184610Salfred	uint16_t i;
855184610Salfred	uint16_t c;
856184610Salfred	uint8_t temp[255];
857184610Salfred	uint8_t swap;
858184610Salfred
859184610Salfred	/* the following code derives from the FreeBSD USB kernel */
860184610Salfred
861184610Salfred	if ((len < 1) || (ptr == NULL)) {
862184610Salfred		/* too short buffer */
863184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
864184610Salfred	}
865184610Salfred	error = libusb20_dev_req_string_sync(pdev,
866184610Salfred	    0, 0, temp, sizeof(temp));
867185087Salfred	if (error < 0) {
868185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
869184610Salfred		return (error);
870185087Salfred	}
871184610Salfred	langid = temp[2] | (temp[3] << 8);
872184610Salfred
873185087Salfred	error = libusb20_dev_req_string_sync(pdev, str_index,
874184610Salfred	    langid, temp, sizeof(temp));
875185087Salfred	if (error < 0) {
876185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
877184610Salfred		return (error);
878185087Salfred	}
879184610Salfred	if (temp[0] < 2) {
880184610Salfred		/* string length is too short */
881185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
882184610Salfred		return (LIBUSB20_ERROR_OTHER);
883184610Salfred	}
884184610Salfred	/* reserve one byte for terminating zero */
885184610Salfred	len--;
886184610Salfred
887184610Salfred	/* find maximum length */
888184610Salfred	n = (temp[0] / 2) - 1;
889184610Salfred	if (n > len) {
890184610Salfred		n = len;
891184610Salfred	}
892184610Salfred	/* reset swap state */
893184610Salfred	swap = 3;
894184610Salfred
895184610Salfred	/* setup output buffer pointer */
896184610Salfred	buf = ptr;
897184610Salfred
898184610Salfred	/* convert and filter */
899184610Salfred	for (i = 0; (i != n); i++) {
900184610Salfred		c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8);
901184610Salfred
902184610Salfred		/* convert from Unicode, handle buggy strings */
903184610Salfred		if (((c & 0xff00) == 0) && (swap & 1)) {
904184610Salfred			/* Little Endian, default */
905184610Salfred			*buf = c;
906184610Salfred			swap = 1;
907184610Salfred		} else if (((c & 0x00ff) == 0) && (swap & 2)) {
908184610Salfred			/* Big Endian */
909184610Salfred			*buf = c >> 8;
910184610Salfred			swap = 2;
911184610Salfred		} else {
912185087Salfred			/* skip invalid character */
913185087Salfred			continue;
914184610Salfred		}
915184610Salfred		/*
916184610Salfred		 * Filter by default - we don't allow greater and less than
917184610Salfred		 * signs because they might confuse the dmesg printouts!
918184610Salfred		 */
919184610Salfred		if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) {
920185087Salfred			/* skip invalid character */
921185087Salfred			continue;
922184610Salfred		}
923184610Salfred		buf++;
924184610Salfred	}
925184610Salfred	*buf = 0;			/* zero terminate string */
926184610Salfred
927184610Salfred	return (0);
928184610Salfred}
929184610Salfred
930184610Salfredstruct libusb20_config *
931184610Salfredlibusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex)
932184610Salfred{
933184610Salfred	struct libusb20_config *retval = NULL;
934184610Salfred	uint8_t *ptr;
935184610Salfred	uint16_t len;
936184610Salfred	uint8_t do_close;
937184610Salfred	int error;
938184610Salfred
939348895Shselasky	/*
940348895Shselasky	 * Catch invalid configuration descriptor reads early on to
941348895Shselasky	 * avoid issues with devices that don't check for a valid USB
942348895Shselasky	 * configuration read request.
943348895Shselasky	 */
944348895Shselasky	if (configIndex >= pdev->ddesc.bNumConfigurations)
945348895Shselasky		return (NULL);
946348895Shselasky
947184610Salfred	if (!pdev->is_opened) {
948184610Salfred		error = libusb20_dev_open(pdev, 0);
949184610Salfred		if (error) {
950184610Salfred			return (NULL);
951184610Salfred		}
952184610Salfred		do_close = 1;
953184610Salfred	} else {
954184610Salfred		do_close = 0;
955184610Salfred	}
956188622Sthompsa	error = pdev->methods->get_config_desc_full(pdev,
957184610Salfred	    &ptr, &len, configIndex);
958184610Salfred
959184610Salfred	if (error) {
960184610Salfred		goto done;
961184610Salfred	}
962184610Salfred	/* parse new config descriptor */
963184610Salfred	retval = libusb20_parse_config_desc(ptr);
964184610Salfred
965184610Salfred	/* free config descriptor */
966184610Salfred	free(ptr);
967184610Salfred
968184610Salfreddone:
969184610Salfred	if (do_close) {
970184610Salfred		error = libusb20_dev_close(pdev);
971184610Salfred	}
972184610Salfred	return (retval);
973184610Salfred}
974184610Salfred
975184610Salfredstruct libusb20_device *
976184610Salfredlibusb20_dev_alloc(void)
977184610Salfred{
978184610Salfred	struct libusb20_device *pdev;
979184610Salfred
980184610Salfred	pdev = malloc(sizeof(*pdev));
981184610Salfred	if (pdev == NULL) {
982184610Salfred		return (NULL);
983184610Salfred	}
984184610Salfred	memset(pdev, 0, sizeof(*pdev));
985184610Salfred
986184610Salfred	pdev->file = -1;
987184610Salfred	pdev->file_ctrl = -1;
988184610Salfred	pdev->methods = &libusb20_dummy_methods;
989184610Salfred	return (pdev);
990184610Salfred}
991184610Salfred
992184610Salfreduint8_t
993184610Salfredlibusb20_dev_get_config_index(struct libusb20_device *pdev)
994184610Salfred{
995184610Salfred	int error;
996185087Salfred	uint8_t cfg_index;
997184610Salfred	uint8_t do_close;
998184610Salfred
999184610Salfred	if (!pdev->is_opened) {
1000184610Salfred		error = libusb20_dev_open(pdev, 0);
1001184610Salfred		if (error == 0) {
1002184610Salfred			do_close = 1;
1003184610Salfred		} else {
1004184610Salfred			do_close = 0;
1005184610Salfred		}
1006184610Salfred	} else {
1007184610Salfred		do_close = 0;
1008184610Salfred	}
1009184610Salfred
1010188622Sthompsa	error = pdev->methods->get_config_index(pdev, &cfg_index);
1011234491Shselasky	if (error)
1012234491Shselasky		cfg_index = 0xFF;	/* current config index */
1013184610Salfred	if (do_close) {
1014184610Salfred		if (libusb20_dev_close(pdev)) {
1015184610Salfred			/* ignore */
1016184610Salfred		}
1017184610Salfred	}
1018185087Salfred	return (cfg_index);
1019184610Salfred}
1020184610Salfred
1021184610Salfreduint8_t
1022184610Salfredlibusb20_dev_get_mode(struct libusb20_device *pdev)
1023184610Salfred{
1024184610Salfred	return (pdev->usb_mode);
1025184610Salfred}
1026184610Salfred
1027184610Salfreduint8_t
1028184610Salfredlibusb20_dev_get_speed(struct libusb20_device *pdev)
1029184610Salfred{
1030184610Salfred	return (pdev->usb_speed);
1031184610Salfred}
1032184610Salfred
1033356399Shselaskyint
1034356399Shselaskylibusb20_dev_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats)
1035356399Shselasky{
1036356399Shselasky	uint8_t do_close;
1037356399Shselasky	int error;
1038356399Shselasky
1039356399Shselasky	if (!pdev->is_opened) {
1040356399Shselasky		error = libusb20_dev_open(pdev, 0);
1041356399Shselasky		if (error == 0) {
1042356399Shselasky			do_close = 1;
1043356399Shselasky		} else {
1044356399Shselasky			do_close = 0;
1045356399Shselasky		}
1046356399Shselasky	} else {
1047356399Shselasky		do_close = 0;
1048356399Shselasky	}
1049356399Shselasky
1050356399Shselasky	error = pdev->methods->get_stats(pdev, pstats);
1051356399Shselasky
1052356399Shselasky	if (do_close)
1053356399Shselasky		(void) libusb20_dev_close(pdev);
1054356399Shselasky
1055356399Shselasky	return (error);
1056356399Shselasky}
1057356399Shselasky
1058184610Salfred/* if this function returns an error, the device is gone */
1059184610Salfredint
1060184610Salfredlibusb20_dev_process(struct libusb20_device *pdev)
1061184610Salfred{
1062184610Salfred	int error;
1063184610Salfred
1064188622Sthompsa	error = pdev->methods->process(pdev);
1065184610Salfred	return (error);
1066184610Salfred}
1067184610Salfred
1068184610Salfredvoid
1069184610Salfredlibusb20_dev_wait_process(struct libusb20_device *pdev, int timeout)
1070184610Salfred{
1071185087Salfred	struct pollfd pfd[1];
1072184610Salfred
1073184610Salfred	if (!pdev->is_opened) {
1074184610Salfred		return;
1075184610Salfred	}
1076184610Salfred	pfd[0].fd = pdev->file;
1077184610Salfred	pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
1078184610Salfred	pfd[0].revents = 0;
1079184610Salfred
1080185087Salfred	if (poll(pfd, 1, timeout)) {
1081184610Salfred		/* ignore any error */
1082184610Salfred	}
1083184610Salfred	return;
1084184610Salfred}
1085184610Salfred
1086184610Salfredvoid
1087184610Salfredlibusb20_dev_free(struct libusb20_device *pdev)
1088184610Salfred{
1089184610Salfred	if (pdev == NULL) {
1090184610Salfred		/* be NULL safe */
1091184610Salfred		return;
1092184610Salfred	}
1093184610Salfred	if (pdev->is_opened) {
1094184610Salfred		if (libusb20_dev_close(pdev)) {
1095184610Salfred			/* ignore any errors */
1096184610Salfred		}
1097184610Salfred	}
1098184610Salfred	free(pdev);
1099184610Salfred	return;
1100184610Salfred}
1101184610Salfred
1102188622Sthompsaint
1103188622Sthompsalibusb20_dev_get_info(struct libusb20_device *pdev,
1104192984Sthompsa    struct usb_device_info *pinfo)
1105188622Sthompsa{
1106188622Sthompsa	if (pinfo == NULL)
1107188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
1108188622Sthompsa
1109188622Sthompsa	return (pdev->beMethods->dev_get_info(pdev, pinfo));
1110188622Sthompsa}
1111188622Sthompsa
1112184610Salfredconst char *
1113184610Salfredlibusb20_dev_get_backend_name(struct libusb20_device *pdev)
1114184610Salfred{
1115188622Sthompsa	return (pdev->beMethods->get_backend_name());
1116184610Salfred}
1117184610Salfred
1118184610Salfredconst char *
1119184610Salfredlibusb20_dev_get_desc(struct libusb20_device *pdev)
1120184610Salfred{
1121184610Salfred	return (pdev->usb_desc);
1122184610Salfred}
1123184610Salfred
1124184610Salfredvoid
1125184610Salfredlibusb20_dev_set_debug(struct libusb20_device *pdev, int debug)
1126184610Salfred{
1127184610Salfred	pdev->debug = debug;
1128184610Salfred	return;
1129184610Salfred}
1130184610Salfred
1131184610Salfredint
1132184610Salfredlibusb20_dev_get_debug(struct libusb20_device *pdev)
1133184610Salfred{
1134184610Salfred	return (pdev->debug);
1135184610Salfred}
1136184610Salfred
1137184610Salfreduint8_t
1138184610Salfredlibusb20_dev_get_address(struct libusb20_device *pdev)
1139184610Salfred{
1140184610Salfred	return (pdev->device_address);
1141184610Salfred}
1142184610Salfred
1143184610Salfreduint8_t
1144223495Shselaskylibusb20_dev_get_parent_address(struct libusb20_device *pdev)
1145223495Shselasky{
1146223495Shselasky	return (pdev->parent_address);
1147223495Shselasky}
1148223495Shselasky
1149223495Shselaskyuint8_t
1150223495Shselaskylibusb20_dev_get_parent_port(struct libusb20_device *pdev)
1151223495Shselasky{
1152223495Shselasky	return (pdev->parent_port);
1153223495Shselasky}
1154223495Shselasky
1155223495Shselaskyuint8_t
1156184610Salfredlibusb20_dev_get_bus_number(struct libusb20_device *pdev)
1157184610Salfred{
1158184610Salfred	return (pdev->bus_number);
1159184610Salfred}
1160184610Salfred
1161184610Salfredint
1162188622Sthompsalibusb20_dev_get_iface_desc(struct libusb20_device *pdev,
1163188622Sthompsa    uint8_t iface_index, char *buf, uint8_t len)
1164188622Sthompsa{
1165188622Sthompsa	if ((buf == NULL) || (len == 0))
1166188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
1167188622Sthompsa
1168224085Shselasky	buf[0] = 0;		/* set default string value */
1169224085Shselasky
1170188622Sthompsa	return (pdev->beMethods->dev_get_iface_desc(
1171188622Sthompsa	    pdev, iface_index, buf, len));
1172188622Sthompsa}
1173188622Sthompsa
1174184610Salfred/* USB backend operations */
1175184610Salfred
1176184610Salfredint
1177184610Salfredlibusb20_be_get_dev_quirk(struct libusb20_backend *pbe,
1178185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
1179184610Salfred{
1180188622Sthompsa	return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq));
1181184610Salfred}
1182184610Salfred
1183184610Salfredint
1184184610Salfredlibusb20_be_get_quirk_name(struct libusb20_backend *pbe,
1185185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
1186184610Salfred{
1187188622Sthompsa	return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq));
1188184610Salfred}
1189184610Salfred
1190184610Salfredint
1191184610Salfredlibusb20_be_add_dev_quirk(struct libusb20_backend *pbe,
1192184610Salfred    struct libusb20_quirk *pq)
1193184610Salfred{
1194188622Sthompsa	return (pbe->methods->root_add_dev_quirk(pbe, pq));
1195184610Salfred}
1196184610Salfred
1197184610Salfredint
1198184610Salfredlibusb20_be_remove_dev_quirk(struct libusb20_backend *pbe,
1199184610Salfred    struct libusb20_quirk *pq)
1200184610Salfred{
1201188622Sthompsa	return (pbe->methods->root_remove_dev_quirk(pbe, pq));
1202184610Salfred}
1203184610Salfred
1204184610Salfredint
1205188987Sthompsalibusb20_be_set_template(struct libusb20_backend *pbe, int temp)
1206188987Sthompsa{
1207188987Sthompsa	return (pbe->methods->root_set_template(pbe, temp));
1208188987Sthompsa}
1209188987Sthompsa
1210188987Sthompsaint
1211188987Sthompsalibusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp)
1212188987Sthompsa{
1213188987Sthompsa	int temp;
1214188987Sthompsa
1215188987Sthompsa	if (ptemp == NULL)
1216188987Sthompsa		ptemp = &temp;
1217188987Sthompsa
1218188987Sthompsa	return (pbe->methods->root_get_template(pbe, ptemp));
1219188987Sthompsa}
1220188987Sthompsa
1221184610Salfredstruct libusb20_device *
1222184610Salfredlibusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1223184610Salfred{
1224184610Salfred	if (pbe == NULL) {
1225184610Salfred		pdev = NULL;
1226184610Salfred	} else if (pdev == NULL) {
1227184610Salfred		pdev = TAILQ_FIRST(&(pbe->usb_devs));
1228184610Salfred	} else {
1229184610Salfred		pdev = TAILQ_NEXT(pdev, dev_entry);
1230184610Salfred	}
1231184610Salfred	return (pdev);
1232184610Salfred}
1233184610Salfred
1234184610Salfredstruct libusb20_backend *
1235184610Salfredlibusb20_be_alloc(const struct libusb20_backend_methods *methods)
1236184610Salfred{
1237184610Salfred	struct libusb20_backend *pbe;
1238184610Salfred
1239184610Salfred	pbe = malloc(sizeof(*pbe));
1240184610Salfred	if (pbe == NULL) {
1241184610Salfred		return (NULL);
1242184610Salfred	}
1243184610Salfred	memset(pbe, 0, sizeof(*pbe));
1244184610Salfred
1245184610Salfred	TAILQ_INIT(&(pbe->usb_devs));
1246184610Salfred
1247184610Salfred	pbe->methods = methods;		/* set backend methods */
1248184610Salfred
1249184610Salfred	/* do the initial device scan */
1250184610Salfred	if (pbe->methods->init_backend) {
1251188622Sthompsa		pbe->methods->init_backend(pbe);
1252184610Salfred	}
1253184610Salfred	return (pbe);
1254184610Salfred}
1255184610Salfred
1256184610Salfredstruct libusb20_backend *
1257184610Salfredlibusb20_be_alloc_linux(void)
1258184610Salfred{
1259253339Shselasky	return (NULL);
1260184610Salfred}
1261184610Salfred
1262184610Salfredstruct libusb20_backend *
1263184610Salfredlibusb20_be_alloc_ugen20(void)
1264184610Salfred{
1265253339Shselasky	return (libusb20_be_alloc(&libusb20_ugen20_backend));
1266184610Salfred}
1267184610Salfred
1268184610Salfredstruct libusb20_backend *
1269184610Salfredlibusb20_be_alloc_default(void)
1270184610Salfred{
1271184610Salfred	struct libusb20_backend *pbe;
1272184610Salfred
1273253339Shselasky#ifdef __linux__
1274184610Salfred	pbe = libusb20_be_alloc_linux();
1275184610Salfred	if (pbe) {
1276184610Salfred		return (pbe);
1277184610Salfred	}
1278253339Shselasky#endif
1279184610Salfred	pbe = libusb20_be_alloc_ugen20();
1280184610Salfred	if (pbe) {
1281184610Salfred		return (pbe);
1282184610Salfred	}
1283184610Salfred	return (NULL);			/* no backend found */
1284184610Salfred}
1285184610Salfred
1286184610Salfredvoid
1287184610Salfredlibusb20_be_free(struct libusb20_backend *pbe)
1288184610Salfred{
1289184610Salfred	struct libusb20_device *pdev;
1290184610Salfred
1291184610Salfred	if (pbe == NULL) {
1292184610Salfred		/* be NULL safe */
1293184610Salfred		return;
1294184610Salfred	}
1295184610Salfred	while ((pdev = libusb20_be_device_foreach(pbe, NULL))) {
1296184610Salfred		libusb20_be_dequeue_device(pbe, pdev);
1297184610Salfred		libusb20_dev_free(pdev);
1298184610Salfred	}
1299184610Salfred	if (pbe->methods->exit_backend) {
1300188622Sthompsa		pbe->methods->exit_backend(pbe);
1301184610Salfred	}
1302199055Sthompsa	/* free backend */
1303199055Sthompsa	free(pbe);
1304184610Salfred}
1305184610Salfred
1306184610Salfredvoid
1307184610Salfredlibusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1308184610Salfred{
1309184610Salfred	pdev->beMethods = pbe->methods;	/* copy backend methods */
1310184610Salfred	TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry);
1311184610Salfred}
1312184610Salfred
1313184610Salfredvoid
1314184610Salfredlibusb20_be_dequeue_device(struct libusb20_backend *pbe,
1315184610Salfred    struct libusb20_device *pdev)
1316184610Salfred{
1317184610Salfred	TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry);
1318184610Salfred}
1319225659Shselasky
1320225659Shselaskyconst char *
1321225659Shselaskylibusb20_strerror(int code)
1322225659Shselasky{
1323225659Shselasky	switch (code) {
1324225659Shselasky	case LIBUSB20_SUCCESS:
1325225659Shselasky		return ("Success");
1326225659Shselasky	case LIBUSB20_ERROR_IO:
1327225659Shselasky		return ("I/O error");
1328225659Shselasky	case LIBUSB20_ERROR_INVALID_PARAM:
1329225659Shselasky		return ("Invalid parameter");
1330225659Shselasky	case LIBUSB20_ERROR_ACCESS:
1331225659Shselasky		return ("Permissions error");
1332225659Shselasky	case LIBUSB20_ERROR_NO_DEVICE:
1333225659Shselasky		return ("No device");
1334225659Shselasky	case LIBUSB20_ERROR_NOT_FOUND:
1335225659Shselasky		return ("Not found");
1336225659Shselasky	case LIBUSB20_ERROR_BUSY:
1337225659Shselasky		return ("Device busy");
1338225659Shselasky	case LIBUSB20_ERROR_TIMEOUT:
1339225659Shselasky		return ("Timeout");
1340225659Shselasky	case LIBUSB20_ERROR_OVERFLOW:
1341225659Shselasky		return ("Overflow");
1342225659Shselasky	case LIBUSB20_ERROR_PIPE:
1343225659Shselasky		return ("Pipe error");
1344225659Shselasky	case LIBUSB20_ERROR_INTERRUPTED:
1345225659Shselasky		return ("Interrupted");
1346225659Shselasky	case LIBUSB20_ERROR_NO_MEM:
1347225659Shselasky		return ("Out of memory");
1348225659Shselasky	case LIBUSB20_ERROR_NOT_SUPPORTED:
1349225659Shselasky		return ("Not supported");
1350225659Shselasky	case LIBUSB20_ERROR_OTHER:
1351225659Shselasky		return ("Other error");
1352225659Shselasky	default:
1353225659Shselasky		return ("Unknown error");
1354225659Shselasky	}
1355225659Shselasky}
1356225659Shselasky
1357225659Shselaskyconst char *
1358225659Shselaskylibusb20_error_name(int code)
1359225659Shselasky{
1360225659Shselasky	switch (code) {
1361225659Shselasky	case LIBUSB20_SUCCESS:
1362225659Shselasky		return ("LIBUSB20_SUCCESS");
1363225659Shselasky	case LIBUSB20_ERROR_IO:
1364225659Shselasky		return ("LIBUSB20_ERROR_IO");
1365225659Shselasky	case LIBUSB20_ERROR_INVALID_PARAM:
1366225659Shselasky		return ("LIBUSB20_ERROR_INVALID_PARAM");
1367225659Shselasky	case LIBUSB20_ERROR_ACCESS:
1368225659Shselasky		return ("LIBUSB20_ERROR_ACCESS");
1369225659Shselasky	case LIBUSB20_ERROR_NO_DEVICE:
1370225659Shselasky		return ("LIBUSB20_ERROR_NO_DEVICE");
1371225659Shselasky	case LIBUSB20_ERROR_NOT_FOUND:
1372225659Shselasky		return ("LIBUSB20_ERROR_NOT_FOUND");
1373225659Shselasky	case LIBUSB20_ERROR_BUSY:
1374225659Shselasky		return ("LIBUSB20_ERROR_BUSY");
1375225659Shselasky	case LIBUSB20_ERROR_TIMEOUT:
1376225659Shselasky		return ("LIBUSB20_ERROR_TIMEOUT");
1377225659Shselasky	case LIBUSB20_ERROR_OVERFLOW:
1378225659Shselasky		return ("LIBUSB20_ERROR_OVERFLOW");
1379225659Shselasky	case LIBUSB20_ERROR_PIPE:
1380225659Shselasky		return ("LIBUSB20_ERROR_PIPE");
1381225659Shselasky	case LIBUSB20_ERROR_INTERRUPTED:
1382225659Shselasky		return ("LIBUSB20_ERROR_INTERRUPTED");
1383225659Shselasky	case LIBUSB20_ERROR_NO_MEM:
1384225659Shselasky		return ("LIBUSB20_ERROR_NO_MEM");
1385225659Shselasky	case LIBUSB20_ERROR_NOT_SUPPORTED:
1386225659Shselasky		return ("LIBUSB20_ERROR_NOT_SUPPORTED");
1387225659Shselasky	case LIBUSB20_ERROR_OTHER:
1388225659Shselasky		return ("LIBUSB20_ERROR_OTHER");
1389225659Shselasky	default:
1390225659Shselasky		return ("LIBUSB20_ERROR_UNKNOWN");
1391225659Shselasky	}
1392225659Shselasky}
1393