1184610Salfred/* $FreeBSD: stable/11/lib/libusb/libusb20.c 356398 2020-01-06 09:21:15Z 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
78246789Shselasky#define	dummy_get_power_usage (void *)dummy_int
79356398Shselasky#define	dummy_get_stats (void *)dummy_int
80184610Salfred#define	dummy_kernel_driver_active (void *)dummy_int
81184610Salfred#define	dummy_detach_kernel_driver (void *)dummy_int
82184610Salfred#define	dummy_do_request_sync (void *)dummy_int
83184610Salfred#define	dummy_tr_open (void *)dummy_int
84184610Salfred#define	dummy_tr_close (void *)dummy_int
85184610Salfred#define	dummy_tr_clear_stall_sync (void *)dummy_int
86184610Salfred#define	dummy_process (void *)dummy_int
87188622Sthompsa#define	dummy_dev_info (void *)dummy_int
88188622Sthompsa#define	dummy_dev_get_iface_driver (void *)dummy_int
89184610Salfred
90184610Salfred#define	dummy_tr_submit (void *)dummy_void
91184610Salfred#define	dummy_tr_cancel_async (void *)dummy_void
92184610Salfred
93184610Salfredstatic const struct libusb20_device_methods libusb20_dummy_methods = {
94184610Salfred	LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy)
95184610Salfred};
96184610Salfred
97184610Salfredvoid
98184610Salfredlibusb20_tr_callback_wrapper(struct libusb20_transfer *xfer)
99184610Salfred{
100184610Salfred	;				/* style fix */
101184610Salfred
102184610Salfredrepeat:
103184610Salfred
104184610Salfred	if (!xfer->is_pending) {
105184610Salfred		xfer->status = LIBUSB20_TRANSFER_START;
106184610Salfred	} else {
107184610Salfred		xfer->is_pending = 0;
108184610Salfred	}
109184610Salfred
110188622Sthompsa	xfer->callback(xfer);
111184610Salfred
112184610Salfred	if (xfer->is_restart) {
113184610Salfred		xfer->is_restart = 0;
114184610Salfred		goto repeat;
115184610Salfred	}
116184610Salfred	if (xfer->is_draining &&
117184610Salfred	    (!xfer->is_pending)) {
118184610Salfred		xfer->is_draining = 0;
119184610Salfred		xfer->status = LIBUSB20_TRANSFER_DRAINED;
120188622Sthompsa		xfer->callback(xfer);
121184610Salfred	}
122184610Salfred	return;
123184610Salfred}
124184610Salfred
125184610Salfredint
126184610Salfredlibusb20_tr_close(struct libusb20_transfer *xfer)
127184610Salfred{
128184610Salfred	int error;
129184610Salfred
130184610Salfred	if (!xfer->is_opened) {
131184610Salfred		return (LIBUSB20_ERROR_OTHER);
132184610Salfred	}
133188622Sthompsa	error = xfer->pdev->methods->tr_close(xfer);
134184610Salfred
135184610Salfred	if (xfer->pLength) {
136184610Salfred		free(xfer->pLength);
137184610Salfred	}
138184610Salfred	if (xfer->ppBuffer) {
139184610Salfred		free(xfer->ppBuffer);
140184610Salfred	}
141202025Sthompsa	/* reset variable fields in case the transfer is opened again */
142297764Spfg	xfer->priv_sc0 = NULL;
143297764Spfg	xfer->priv_sc1 = NULL;
144184610Salfred	xfer->is_opened = 0;
145202025Sthompsa	xfer->is_pending = 0;
146202025Sthompsa	xfer->is_cancel = 0;
147202025Sthompsa	xfer->is_draining = 0;
148202025Sthompsa	xfer->is_restart = 0;
149202025Sthompsa	xfer->status = 0;
150202025Sthompsa	xfer->flags = 0;
151202025Sthompsa	xfer->nFrames = 0;
152202025Sthompsa	xfer->aFrames = 0;
153202025Sthompsa	xfer->timeout = 0;
154184610Salfred	xfer->maxFrames = 0;
155184610Salfred	xfer->maxTotalLength = 0;
156184610Salfred	xfer->maxPacketLen = 0;
157184610Salfred	return (error);
158184610Salfred}
159184610Salfred
160184610Salfredint
161184610Salfredlibusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
162184610Salfred    uint32_t MaxFrameCount, uint8_t ep_no)
163184610Salfred{
164239239Shselasky	return (libusb20_tr_open_stream(xfer, MaxBufSize, MaxFrameCount, ep_no, 0));
165239239Shselasky}
166239239Shselasky
167239239Shselaskyint
168239239Shselaskylibusb20_tr_open_stream(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
169239239Shselasky    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id)
170239239Shselasky{
171184610Salfred	uint32_t size;
172219100Shselasky	uint8_t pre_scale;
173184610Salfred	int error;
174184610Salfred
175219100Shselasky	if (xfer->is_opened)
176184610Salfred		return (LIBUSB20_ERROR_BUSY);
177219100Shselasky	if (MaxFrameCount & LIBUSB20_MAX_FRAME_PRE_SCALE) {
178219100Shselasky		MaxFrameCount &= ~LIBUSB20_MAX_FRAME_PRE_SCALE;
179305640Shselasky		/*
180305640Shselasky		 * The kernel can setup 8 times more frames when
181305640Shselasky		 * pre-scaling ISOCHRONOUS transfers. Make sure the
182305640Shselasky		 * length and pointer buffers are big enough:
183305640Shselasky		 */
184305640Shselasky		MaxFrameCount *= 8;
185219100Shselasky		pre_scale = 1;
186219100Shselasky	} else {
187219100Shselasky		pre_scale = 0;
188184610Salfred	}
189219100Shselasky	if (MaxFrameCount == 0)
190184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
191219100Shselasky
192184610Salfred	xfer->maxFrames = MaxFrameCount;
193184610Salfred
194184610Salfred	size = MaxFrameCount * sizeof(xfer->pLength[0]);
195184610Salfred	xfer->pLength = malloc(size);
196184610Salfred	if (xfer->pLength == NULL) {
197184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
198184610Salfred	}
199184610Salfred	memset(xfer->pLength, 0, size);
200184610Salfred
201184610Salfred	size = MaxFrameCount * sizeof(xfer->ppBuffer[0]);
202184610Salfred	xfer->ppBuffer = malloc(size);
203184610Salfred	if (xfer->ppBuffer == NULL) {
204184610Salfred		free(xfer->pLength);
205184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
206184610Salfred	}
207184610Salfred	memset(xfer->ppBuffer, 0, size);
208184610Salfred
209305640Shselasky	if (pre_scale) {
210305640Shselasky		error = xfer->pdev->methods->tr_open(xfer, MaxBufSize,
211305640Shselasky		    MaxFrameCount / 8, ep_no, stream_id, 1);
212305640Shselasky	} else {
213305640Shselasky		error = xfer->pdev->methods->tr_open(xfer, MaxBufSize,
214305640Shselasky		    MaxFrameCount, ep_no, stream_id, 0);
215305640Shselasky	}
216184610Salfred
217184610Salfred	if (error) {
218184610Salfred		free(xfer->ppBuffer);
219184610Salfred		free(xfer->pLength);
220184610Salfred	} else {
221184610Salfred		xfer->is_opened = 1;
222184610Salfred	}
223184610Salfred	return (error);
224184610Salfred}
225184610Salfred
226184610Salfredstruct libusb20_transfer *
227184610Salfredlibusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex)
228184610Salfred{
229184610Salfred	if (trIndex >= pdev->nTransfer) {
230184610Salfred		return (NULL);
231184610Salfred	}
232184610Salfred	return (pdev->pTransfer + trIndex);
233184610Salfred}
234184610Salfred
235184610Salfreduint32_t
236184610Salfredlibusb20_tr_get_actual_frames(struct libusb20_transfer *xfer)
237184610Salfred{
238184610Salfred	return (xfer->aFrames);
239184610Salfred}
240184610Salfred
241184610Salfreduint16_t
242184610Salfredlibusb20_tr_get_time_complete(struct libusb20_transfer *xfer)
243184610Salfred{
244184610Salfred	return (xfer->timeComplete);
245184610Salfred}
246184610Salfred
247184610Salfreduint32_t
248184610Salfredlibusb20_tr_get_actual_length(struct libusb20_transfer *xfer)
249184610Salfred{
250184610Salfred	uint32_t x;
251184610Salfred	uint32_t actlen = 0;
252184610Salfred
253184610Salfred	for (x = 0; x != xfer->aFrames; x++) {
254184610Salfred		actlen += xfer->pLength[x];
255184610Salfred	}
256184610Salfred	return (actlen);
257184610Salfred}
258184610Salfred
259184610Salfreduint32_t
260184610Salfredlibusb20_tr_get_max_frames(struct libusb20_transfer *xfer)
261184610Salfred{
262184610Salfred	return (xfer->maxFrames);
263184610Salfred}
264184610Salfred
265184610Salfreduint32_t
266184610Salfredlibusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer)
267184610Salfred{
268184610Salfred	/*
269184610Salfred	 * Special Case NOTE: If the packet multiplier is non-zero for
270184610Salfred	 * High Speed USB, the value returned is equal to
271184610Salfred	 * "wMaxPacketSize * multiplier" !
272184610Salfred	 */
273184610Salfred	return (xfer->maxPacketLen);
274184610Salfred}
275184610Salfred
276184610Salfreduint32_t
277184610Salfredlibusb20_tr_get_max_total_length(struct libusb20_transfer *xfer)
278184610Salfred{
279184610Salfred	return (xfer->maxTotalLength);
280184610Salfred}
281184610Salfred
282184610Salfreduint8_t
283184610Salfredlibusb20_tr_get_status(struct libusb20_transfer *xfer)
284184610Salfred{
285184610Salfred	return (xfer->status);
286184610Salfred}
287184610Salfred
288184610Salfreduint8_t
289184610Salfredlibusb20_tr_pending(struct libusb20_transfer *xfer)
290184610Salfred{
291184610Salfred	return (xfer->is_pending);
292184610Salfred}
293184610Salfred
294184610Salfredvoid   *
295184610Salfredlibusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer)
296184610Salfred{
297184610Salfred	return (xfer->priv_sc0);
298184610Salfred}
299184610Salfred
300184610Salfredvoid   *
301184610Salfredlibusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer)
302184610Salfred{
303184610Salfred	return (xfer->priv_sc1);
304184610Salfred}
305184610Salfred
306184610Salfredvoid
307184610Salfredlibusb20_tr_stop(struct libusb20_transfer *xfer)
308184610Salfred{
309199575Sthompsa	if (!xfer->is_opened) {
310199575Sthompsa		/* transfer is not opened */
311199575Sthompsa		return;
312199575Sthompsa	}
313184610Salfred	if (!xfer->is_pending) {
314184610Salfred		/* transfer not pending */
315184610Salfred		return;
316184610Salfred	}
317184610Salfred	if (xfer->is_cancel) {
318184610Salfred		/* already cancelling */
319184610Salfred		return;
320184610Salfred	}
321184610Salfred	xfer->is_cancel = 1;		/* we are cancelling */
322184610Salfred
323188622Sthompsa	xfer->pdev->methods->tr_cancel_async(xfer);
324184610Salfred	return;
325184610Salfred}
326184610Salfred
327184610Salfredvoid
328184610Salfredlibusb20_tr_drain(struct libusb20_transfer *xfer)
329184610Salfred{
330199575Sthompsa	if (!xfer->is_opened) {
331199575Sthompsa		/* transfer is not opened */
332199575Sthompsa		return;
333199575Sthompsa	}
334184610Salfred	/* make sure that we are cancelling */
335184610Salfred	libusb20_tr_stop(xfer);
336184610Salfred
337184610Salfred	if (xfer->is_pending) {
338184610Salfred		xfer->is_draining = 1;
339184610Salfred	}
340184610Salfred	return;
341184610Salfred}
342184610Salfred
343184610Salfredvoid
344184610Salfredlibusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
345184610Salfred{
346188622Sthompsa	xfer->pdev->methods->tr_clear_stall_sync(xfer);
347184610Salfred	return;
348184610Salfred}
349184610Salfred
350184610Salfredvoid
351184610Salfredlibusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex)
352184610Salfred{
353213852Shselasky	xfer->ppBuffer[frIndex] = libusb20_pass_ptr(buffer);
354184610Salfred	return;
355184610Salfred}
356184610Salfred
357184610Salfredvoid
358184610Salfredlibusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb)
359184610Salfred{
360184610Salfred	xfer->callback = cb;
361184610Salfred	return;
362184610Salfred}
363184610Salfred
364184610Salfredvoid
365184610Salfredlibusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags)
366184610Salfred{
367184610Salfred	xfer->flags = flags;
368184610Salfred	return;
369184610Salfred}
370184610Salfred
371193313Sthompsauint32_t
372193313Sthompsalibusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t frIndex)
373193313Sthompsa{
374193313Sthompsa	return (xfer->pLength[frIndex]);
375193313Sthompsa}
376193313Sthompsa
377184610Salfredvoid
378184610Salfredlibusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex)
379184610Salfred{
380184610Salfred	xfer->pLength[frIndex] = length;
381184610Salfred	return;
382184610Salfred}
383184610Salfred
384184610Salfredvoid
385184610Salfredlibusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0)
386184610Salfred{
387184610Salfred	xfer->priv_sc0 = sc0;
388184610Salfred	return;
389184610Salfred}
390184610Salfred
391184610Salfredvoid
392184610Salfredlibusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1)
393184610Salfred{
394184610Salfred	xfer->priv_sc1 = sc1;
395184610Salfred	return;
396184610Salfred}
397184610Salfred
398184610Salfredvoid
399184610Salfredlibusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout)
400184610Salfred{
401184610Salfred	xfer->timeout = timeout;
402184610Salfred	return;
403184610Salfred}
404184610Salfred
405184610Salfredvoid
406184610Salfredlibusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames)
407184610Salfred{
408184610Salfred	if (nFrames > xfer->maxFrames) {
409184610Salfred		/* should not happen */
410184610Salfred		nFrames = xfer->maxFrames;
411184610Salfred	}
412184610Salfred	xfer->nFrames = nFrames;
413184610Salfred	return;
414184610Salfred}
415184610Salfred
416184610Salfredvoid
417184610Salfredlibusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout)
418184610Salfred{
419213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf);
420184610Salfred	xfer->pLength[0] = length;
421184610Salfred	xfer->timeout = timeout;
422184610Salfred	xfer->nFrames = 1;
423184610Salfred	return;
424184610Salfred}
425184610Salfred
426184610Salfredvoid
427184610Salfredlibusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout)
428184610Salfred{
429184610Salfred	uint16_t len;
430184610Salfred
431213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(psetup);
432184610Salfred	xfer->pLength[0] = 8;		/* fixed */
433184610Salfred	xfer->timeout = timeout;
434184610Salfred
435184610Salfred	len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8);
436184610Salfred
437184610Salfred	if (len != 0) {
438184610Salfred		xfer->nFrames = 2;
439213852Shselasky		xfer->ppBuffer[1] = libusb20_pass_ptr(pBuf);
440184610Salfred		xfer->pLength[1] = len;
441184610Salfred	} else {
442184610Salfred		xfer->nFrames = 1;
443184610Salfred	}
444184610Salfred	return;
445184610Salfred}
446184610Salfred
447184610Salfredvoid
448184610Salfredlibusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout)
449184610Salfred{
450213852Shselasky	xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf);
451184610Salfred	xfer->pLength[0] = length;
452184610Salfred	xfer->timeout = timeout;
453184610Salfred	xfer->nFrames = 1;
454184610Salfred	return;
455184610Salfred}
456184610Salfred
457184610Salfredvoid
458184610Salfredlibusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex)
459184610Salfred{
460184610Salfred	if (frIndex >= xfer->maxFrames) {
461184610Salfred		/* should not happen */
462184610Salfred		return;
463184610Salfred	}
464213852Shselasky	xfer->ppBuffer[frIndex] = libusb20_pass_ptr(pBuf);
465184610Salfred	xfer->pLength[frIndex] = length;
466184610Salfred	return;
467184610Salfred}
468184610Salfred
469199575Sthompsauint8_t
470199575Sthompsalibusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer,
471199575Sthompsa    void *pbuf, uint32_t length, uint32_t *pactlen,
472199575Sthompsa    uint32_t timeout)
473199575Sthompsa{
474199575Sthompsa	struct libusb20_device *pdev = xfer->pdev;
475199575Sthompsa	uint32_t transfer_max;
476199575Sthompsa	uint32_t transfer_act;
477199575Sthompsa	uint8_t retval;
478199575Sthompsa
479199575Sthompsa	/* set some sensible default value */
480199575Sthompsa	if (pactlen != NULL)
481199575Sthompsa		*pactlen = 0;
482199575Sthompsa
483199575Sthompsa	/* check for error condition */
484199575Sthompsa	if (libusb20_tr_pending(xfer))
485199575Sthompsa		return (LIBUSB20_ERROR_OTHER);
486199575Sthompsa
487199575Sthompsa	do {
488199575Sthompsa		/* compute maximum transfer length */
489199575Sthompsa		transfer_max =
490199575Sthompsa		    libusb20_tr_get_max_total_length(xfer);
491199575Sthompsa
492199575Sthompsa		if (transfer_max > length)
493199575Sthompsa			transfer_max = length;
494199575Sthompsa
495199575Sthompsa		/* setup bulk or interrupt transfer */
496199575Sthompsa		libusb20_tr_setup_bulk(xfer, pbuf,
497199575Sthompsa		    transfer_max, timeout);
498199575Sthompsa
499199575Sthompsa		/* start the transfer */
500199575Sthompsa		libusb20_tr_start(xfer);
501199575Sthompsa
502199575Sthompsa		/* wait for transfer completion */
503199575Sthompsa		while (libusb20_dev_process(pdev) == 0) {
504199575Sthompsa
505199575Sthompsa			if (libusb20_tr_pending(xfer) == 0)
506199575Sthompsa				break;
507199575Sthompsa
508199575Sthompsa			libusb20_dev_wait_process(pdev, -1);
509199575Sthompsa		}
510199575Sthompsa
511199575Sthompsa		transfer_act = libusb20_tr_get_actual_length(xfer);
512199575Sthompsa
513199575Sthompsa		/* update actual length, if any */
514199575Sthompsa		if (pactlen != NULL)
515199575Sthompsa			pactlen[0] += transfer_act;
516199575Sthompsa
517199575Sthompsa		/* check transfer status */
518199575Sthompsa		retval = libusb20_tr_get_status(xfer);
519199575Sthompsa		if (retval)
520199575Sthompsa			break;
521199575Sthompsa
522199575Sthompsa		/* check for short transfer */
523199575Sthompsa		if (transfer_act != transfer_max)
524199575Sthompsa			break;
525199575Sthompsa
526199575Sthompsa		/* update buffer pointer and length */
527199575Sthompsa		pbuf = ((uint8_t *)pbuf) + transfer_max;
528199575Sthompsa		length = length - transfer_max;
529199575Sthompsa
530199575Sthompsa	} while (length != 0);
531199575Sthompsa
532199575Sthompsa	return (retval);
533199575Sthompsa}
534199575Sthompsa
535184610Salfredvoid
536184610Salfredlibusb20_tr_submit(struct libusb20_transfer *xfer)
537184610Salfred{
538199575Sthompsa	if (!xfer->is_opened) {
539199575Sthompsa		/* transfer is not opened */
540199575Sthompsa		return;
541199575Sthompsa	}
542184610Salfred	if (xfer->is_pending) {
543184610Salfred		/* should not happen */
544184610Salfred		return;
545184610Salfred	}
546184610Salfred	xfer->is_pending = 1;		/* we are pending */
547184610Salfred	xfer->is_cancel = 0;		/* not cancelling */
548184610Salfred	xfer->is_restart = 0;		/* not restarting */
549184610Salfred
550188622Sthompsa	xfer->pdev->methods->tr_submit(xfer);
551184610Salfred	return;
552184610Salfred}
553184610Salfred
554184610Salfredvoid
555184610Salfredlibusb20_tr_start(struct libusb20_transfer *xfer)
556184610Salfred{
557199575Sthompsa	if (!xfer->is_opened) {
558199575Sthompsa		/* transfer is not opened */
559199575Sthompsa		return;
560199575Sthompsa	}
561184610Salfred	if (xfer->is_pending) {
562184610Salfred		if (xfer->is_cancel) {
563184610Salfred			/* cancelling - restart */
564184610Salfred			xfer->is_restart = 1;
565184610Salfred		}
566184610Salfred		/* transfer not pending */
567184610Salfred		return;
568184610Salfred	}
569184610Salfred	/* get into the callback */
570184610Salfred	libusb20_tr_callback_wrapper(xfer);
571184610Salfred	return;
572184610Salfred}
573184610Salfred
574184610Salfred/* USB device operations */
575184610Salfred
576184610Salfredint
577184610Salfredlibusb20_dev_close(struct libusb20_device *pdev)
578184610Salfred{
579184610Salfred	struct libusb20_transfer *xfer;
580184610Salfred	uint16_t x;
581184610Salfred	int error = 0;
582184610Salfred
583184610Salfred	if (!pdev->is_opened) {
584184610Salfred		return (LIBUSB20_ERROR_OTHER);
585184610Salfred	}
586184610Salfred	for (x = 0; x != pdev->nTransfer; x++) {
587184610Salfred		xfer = pdev->pTransfer + x;
588184610Salfred
589199575Sthompsa		if (!xfer->is_opened) {
590199575Sthompsa			/* transfer is not opened */
591199575Sthompsa			continue;
592199575Sthompsa		}
593199575Sthompsa
594184610Salfred		libusb20_tr_drain(xfer);
595199575Sthompsa
596199575Sthompsa		libusb20_tr_close(xfer);
597184610Salfred	}
598184610Salfred
599184610Salfred	if (pdev->pTransfer != NULL) {
600184610Salfred		free(pdev->pTransfer);
601184610Salfred		pdev->pTransfer = NULL;
602184610Salfred	}
603188622Sthompsa	error = pdev->beMethods->close_device(pdev);
604184610Salfred
605184610Salfred	pdev->methods = &libusb20_dummy_methods;
606184610Salfred
607184610Salfred	pdev->is_opened = 0;
608184610Salfred
609194069Sthompsa	/*
610194069Sthompsa	 * The following variable is only used by the libusb v0.1
611194069Sthompsa	 * compat layer:
612194069Sthompsa	 */
613194069Sthompsa	pdev->claimed_interface = 0;
614187184Sthompsa
615301966Shselasky	/*
616301966Shselasky	 * The following variable is only used by the libusb v1.0
617301966Shselasky	 * compat layer:
618301966Shselasky	 */
619301966Shselasky	pdev->auto_detach = 0;
620301966Shselasky
621184610Salfred	return (error);
622184610Salfred}
623184610Salfred
624184610Salfredint
625184610Salfredlibusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex)
626184610Salfred{
627184610Salfred	int error;
628184610Salfred
629188622Sthompsa	error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex);
630184610Salfred	return (error);
631184610Salfred}
632184610Salfred
633184610Salfredstruct LIBUSB20_DEVICE_DESC_DECODED *
634184610Salfredlibusb20_dev_get_device_desc(struct libusb20_device *pdev)
635184610Salfred{
636184610Salfred	return (&(pdev->ddesc));
637184610Salfred}
638184610Salfred
639184610Salfredint
640184610Salfredlibusb20_dev_get_fd(struct libusb20_device *pdev)
641184610Salfred{
642184610Salfred	return (pdev->file);
643184610Salfred}
644184610Salfred
645184610Salfredint
646184610Salfredlibusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex)
647184610Salfred{
648184610Salfred	int error;
649184610Salfred
650188622Sthompsa	error = pdev->methods->kernel_driver_active(pdev, ifaceIndex);
651184610Salfred	return (error);
652184610Salfred}
653184610Salfred
654184610Salfredint
655184610Salfredlibusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax)
656184610Salfred{
657184610Salfred	struct libusb20_transfer *xfer;
658184610Salfred	uint32_t size;
659184610Salfred	uint16_t x;
660184610Salfred	int error;
661184610Salfred
662184610Salfred	if (pdev->is_opened) {
663184610Salfred		return (LIBUSB20_ERROR_BUSY);
664184610Salfred	}
665184610Salfred	if (nTransferMax >= 256) {
666184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
667184610Salfred	} else if (nTransferMax != 0) {
668184610Salfred		size = sizeof(pdev->pTransfer[0]) * nTransferMax;
669184610Salfred		pdev->pTransfer = malloc(size);
670184610Salfred		if (pdev->pTransfer == NULL) {
671184610Salfred			return (LIBUSB20_ERROR_NO_MEM);
672184610Salfred		}
673184610Salfred		memset(pdev->pTransfer, 0, size);
674184610Salfred	}
675184610Salfred	/* initialise all transfers */
676184610Salfred	for (x = 0; x != nTransferMax; x++) {
677184610Salfred
678184610Salfred		xfer = pdev->pTransfer + x;
679184610Salfred
680184610Salfred		xfer->pdev = pdev;
681184610Salfred		xfer->trIndex = x;
682184610Salfred		xfer->callback = &dummy_callback;
683184610Salfred	}
684184610Salfred
685185087Salfred	/* set "nTransfer" early */
686185087Salfred	pdev->nTransfer = nTransferMax;
687185087Salfred
688188622Sthompsa	error = pdev->beMethods->open_device(pdev, nTransferMax);
689184610Salfred
690184610Salfred	if (error) {
691184610Salfred		if (pdev->pTransfer != NULL) {
692184610Salfred			free(pdev->pTransfer);
693184610Salfred			pdev->pTransfer = NULL;
694184610Salfred		}
695184610Salfred		pdev->file = -1;
696184610Salfred		pdev->file_ctrl = -1;
697184610Salfred		pdev->nTransfer = 0;
698184610Salfred	} else {
699184610Salfred		pdev->is_opened = 1;
700184610Salfred	}
701184610Salfred	return (error);
702184610Salfred}
703184610Salfred
704184610Salfredint
705184610Salfredlibusb20_dev_reset(struct libusb20_device *pdev)
706184610Salfred{
707184610Salfred	int error;
708184610Salfred
709188622Sthompsa	error = pdev->methods->reset_device(pdev);
710184610Salfred	return (error);
711184610Salfred}
712184610Salfred
713184610Salfredint
714203147Sthompsalibusb20_dev_check_connected(struct libusb20_device *pdev)
715203147Sthompsa{
716203147Sthompsa	int error;
717203147Sthompsa
718203147Sthompsa	error = pdev->methods->check_connected(pdev);
719203147Sthompsa	return (error);
720203147Sthompsa}
721203147Sthompsa
722203147Sthompsaint
723184610Salfredlibusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
724184610Salfred{
725184610Salfred	int error;
726184610Salfred
727188622Sthompsa	error = pdev->methods->set_power_mode(pdev, power_mode);
728184610Salfred	return (error);
729184610Salfred}
730184610Salfred
731184610Salfreduint8_t
732184610Salfredlibusb20_dev_get_power_mode(struct libusb20_device *pdev)
733184610Salfred{
734184610Salfred	int error;
735184610Salfred	uint8_t power_mode;
736184610Salfred
737188622Sthompsa	error = pdev->methods->get_power_mode(pdev, &power_mode);
738184610Salfred	if (error)
739184610Salfred		power_mode = LIBUSB20_POWER_ON;	/* fake power mode */
740184610Salfred	return (power_mode);
741184610Salfred}
742184610Salfred
743250201Shselaskyint
744250201Shselaskylibusb20_dev_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
745250201Shselasky{
746331771Shselasky
747331771Shselasky	if (pdev->port_level == 0) {
748331771Shselasky		/*
749331771Shselasky		 * Fallback for backends without port path:
750331771Shselasky		 */
751331771Shselasky		if (bufsize < 2)
752331771Shselasky			return (LIBUSB20_ERROR_OVERFLOW);
753331771Shselasky		buf[0] = pdev->parent_address;
754331771Shselasky		buf[1] = pdev->parent_port;
755331771Shselasky		return (2);
756331771Shselasky	}
757331771Shselasky
758331771Shselasky	/* check if client buffer is too small */
759331771Shselasky	if (pdev->port_level > bufsize)
760331771Shselasky		return (LIBUSB20_ERROR_OVERFLOW);
761331771Shselasky
762331771Shselasky	/* copy port number information */
763331771Shselasky	memcpy(buf, pdev->port_path, pdev->port_level);
764331771Shselasky
765331771Shselasky	return (pdev->port_level);	/* success */
766250201Shselasky}
767250201Shselasky
768246789Shselaskyuint16_t
769246789Shselaskylibusb20_dev_get_power_usage(struct libusb20_device *pdev)
770246789Shselasky{
771246789Shselasky	int error;
772246789Shselasky	uint16_t power_usage;
773246789Shselasky
774246789Shselasky	error = pdev->methods->get_power_usage(pdev, &power_usage);
775246789Shselasky	if (error)
776246789Shselasky		power_usage = 0;
777246789Shselasky	return (power_usage);
778246789Shselasky}
779246789Shselasky
780184610Salfredint
781184610Salfredlibusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex)
782184610Salfred{
783184610Salfred	int error;
784184610Salfred
785188622Sthompsa	error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex);
786184610Salfred	return (error);
787184610Salfred}
788184610Salfred
789184610Salfredint
790184610Salfredlibusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex)
791184610Salfred{
792184610Salfred	int error;
793184610Salfred
794188622Sthompsa	error = pdev->methods->set_config_index(pdev, configIndex);
795184610Salfred	return (error);
796184610Salfred}
797184610Salfred
798184610Salfredint
799184610Salfredlibusb20_dev_request_sync(struct libusb20_device *pdev,
800184610Salfred    struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data,
801184610Salfred    uint16_t *pactlen, uint32_t timeout, uint8_t flags)
802184610Salfred{
803184610Salfred	int error;
804184610Salfred
805188622Sthompsa	error = pdev->methods->do_request_sync(pdev,
806184610Salfred	    setup, data, pactlen, timeout, flags);
807184610Salfred	return (error);
808184610Salfred}
809184610Salfred
810184610Salfredint
811184610Salfredlibusb20_dev_req_string_sync(struct libusb20_device *pdev,
812185087Salfred    uint8_t str_index, uint16_t langid, void *ptr, uint16_t len)
813184610Salfred{
814184610Salfred	struct LIBUSB20_CONTROL_SETUP_DECODED req;
815184610Salfred	int error;
816336883Shselasky	int flags;
817184610Salfred
818199055Sthompsa	/* make sure memory is initialised */
819199055Sthompsa	memset(ptr, 0, len);
820199055Sthompsa
821184610Salfred	if (len < 4) {
822184610Salfred		/* invalid length */
823184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
824184610Salfred	}
825184610Salfred	LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req);
826184610Salfred
827184610Salfred	/*
828184610Salfred	 * We need to read the USB string in two steps else some USB
829184610Salfred	 * devices will complain.
830184610Salfred	 */
831184610Salfred	req.bmRequestType =
832184610Salfred	    LIBUSB20_REQUEST_TYPE_STANDARD |
833184610Salfred	    LIBUSB20_RECIPIENT_DEVICE |
834184610Salfred	    LIBUSB20_ENDPOINT_IN;
835184610Salfred	req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR;
836185087Salfred	req.wValue = (LIBUSB20_DT_STRING << 8) | str_index;
837184610Salfred	req.wIndex = langid;
838184610Salfred	req.wLength = 4;		/* bytes */
839184610Salfred
840184610Salfred	error = libusb20_dev_request_sync(pdev, &req,
841184610Salfred	    ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK);
842184610Salfred	if (error) {
843336883Shselasky		/* try to request full string */
844336883Shselasky		req.wLength = 255;
845336883Shselasky		flags = 0;
846336883Shselasky	} else {
847336883Shselasky		/* extract length and request full string */
848336883Shselasky		req.wLength = *(uint8_t *)ptr;
849336883Shselasky		flags = LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK;
850184610Salfred	}
851184610Salfred	if (req.wLength > len) {
852184610Salfred		/* partial string read */
853184610Salfred		req.wLength = len;
854184610Salfred	}
855336883Shselasky	error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, flags);
856336883Shselasky	if (error)
857336883Shselasky		return (error);
858184610Salfred
859336883Shselasky	if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING)
860184610Salfred		return (LIBUSB20_ERROR_OTHER);
861184610Salfred	return (0);			/* success */
862184610Salfred}
863184610Salfred
864184610Salfredint
865184610Salfredlibusb20_dev_req_string_simple_sync(struct libusb20_device *pdev,
866185087Salfred    uint8_t str_index, void *ptr, uint16_t len)
867184610Salfred{
868184610Salfred	char *buf;
869184610Salfred	int error;
870184610Salfred	uint16_t langid;
871184610Salfred	uint16_t n;
872184610Salfred	uint16_t i;
873184610Salfred	uint16_t c;
874184610Salfred	uint8_t temp[255];
875184610Salfred	uint8_t swap;
876184610Salfred
877184610Salfred	/* the following code derives from the FreeBSD USB kernel */
878184610Salfred
879184610Salfred	if ((len < 1) || (ptr == NULL)) {
880184610Salfred		/* too short buffer */
881184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
882184610Salfred	}
883184610Salfred	error = libusb20_dev_req_string_sync(pdev,
884184610Salfred	    0, 0, temp, sizeof(temp));
885185087Salfred	if (error < 0) {
886185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
887184610Salfred		return (error);
888185087Salfred	}
889184610Salfred	langid = temp[2] | (temp[3] << 8);
890184610Salfred
891185087Salfred	error = libusb20_dev_req_string_sync(pdev, str_index,
892184610Salfred	    langid, temp, sizeof(temp));
893185087Salfred	if (error < 0) {
894185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
895184610Salfred		return (error);
896185087Salfred	}
897184610Salfred	if (temp[0] < 2) {
898184610Salfred		/* string length is too short */
899185087Salfred		*(uint8_t *)ptr = 0;	/* zero terminate */
900184610Salfred		return (LIBUSB20_ERROR_OTHER);
901184610Salfred	}
902184610Salfred	/* reserve one byte for terminating zero */
903184610Salfred	len--;
904184610Salfred
905184610Salfred	/* find maximum length */
906184610Salfred	n = (temp[0] / 2) - 1;
907184610Salfred	if (n > len) {
908184610Salfred		n = len;
909184610Salfred	}
910184610Salfred	/* reset swap state */
911184610Salfred	swap = 3;
912184610Salfred
913184610Salfred	/* setup output buffer pointer */
914184610Salfred	buf = ptr;
915184610Salfred
916184610Salfred	/* convert and filter */
917184610Salfred	for (i = 0; (i != n); i++) {
918184610Salfred		c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8);
919184610Salfred
920184610Salfred		/* convert from Unicode, handle buggy strings */
921184610Salfred		if (((c & 0xff00) == 0) && (swap & 1)) {
922184610Salfred			/* Little Endian, default */
923184610Salfred			*buf = c;
924184610Salfred			swap = 1;
925184610Salfred		} else if (((c & 0x00ff) == 0) && (swap & 2)) {
926184610Salfred			/* Big Endian */
927184610Salfred			*buf = c >> 8;
928184610Salfred			swap = 2;
929184610Salfred		} else {
930185087Salfred			/* skip invalid character */
931185087Salfred			continue;
932184610Salfred		}
933184610Salfred		/*
934184610Salfred		 * Filter by default - we don't allow greater and less than
935184610Salfred		 * signs because they might confuse the dmesg printouts!
936184610Salfred		 */
937184610Salfred		if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) {
938185087Salfred			/* skip invalid character */
939185087Salfred			continue;
940184610Salfred		}
941184610Salfred		buf++;
942184610Salfred	}
943184610Salfred	*buf = 0;			/* zero terminate string */
944184610Salfred
945184610Salfred	return (0);
946184610Salfred}
947184610Salfred
948184610Salfredstruct libusb20_config *
949184610Salfredlibusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex)
950184610Salfred{
951184610Salfred	struct libusb20_config *retval = NULL;
952184610Salfred	uint8_t *ptr;
953184610Salfred	uint16_t len;
954184610Salfred	uint8_t do_close;
955184610Salfred	int error;
956184610Salfred
957348893Shselasky	/*
958348893Shselasky	 * Catch invalid configuration descriptor reads early on to
959348893Shselasky	 * avoid issues with devices that don't check for a valid USB
960348893Shselasky	 * configuration read request.
961348893Shselasky	 */
962348893Shselasky	if (configIndex >= pdev->ddesc.bNumConfigurations)
963348893Shselasky		return (NULL);
964348893Shselasky
965184610Salfred	if (!pdev->is_opened) {
966184610Salfred		error = libusb20_dev_open(pdev, 0);
967184610Salfred		if (error) {
968184610Salfred			return (NULL);
969184610Salfred		}
970184610Salfred		do_close = 1;
971184610Salfred	} else {
972184610Salfred		do_close = 0;
973184610Salfred	}
974188622Sthompsa	error = pdev->methods->get_config_desc_full(pdev,
975184610Salfred	    &ptr, &len, configIndex);
976184610Salfred
977184610Salfred	if (error) {
978184610Salfred		goto done;
979184610Salfred	}
980184610Salfred	/* parse new config descriptor */
981184610Salfred	retval = libusb20_parse_config_desc(ptr);
982184610Salfred
983184610Salfred	/* free config descriptor */
984184610Salfred	free(ptr);
985184610Salfred
986184610Salfreddone:
987184610Salfred	if (do_close) {
988184610Salfred		error = libusb20_dev_close(pdev);
989184610Salfred	}
990184610Salfred	return (retval);
991184610Salfred}
992184610Salfred
993184610Salfredstruct libusb20_device *
994184610Salfredlibusb20_dev_alloc(void)
995184610Salfred{
996184610Salfred	struct libusb20_device *pdev;
997184610Salfred
998184610Salfred	pdev = malloc(sizeof(*pdev));
999184610Salfred	if (pdev == NULL) {
1000184610Salfred		return (NULL);
1001184610Salfred	}
1002184610Salfred	memset(pdev, 0, sizeof(*pdev));
1003184610Salfred
1004184610Salfred	pdev->file = -1;
1005184610Salfred	pdev->file_ctrl = -1;
1006184610Salfred	pdev->methods = &libusb20_dummy_methods;
1007184610Salfred	return (pdev);
1008184610Salfred}
1009184610Salfred
1010184610Salfreduint8_t
1011184610Salfredlibusb20_dev_get_config_index(struct libusb20_device *pdev)
1012184610Salfred{
1013184610Salfred	int error;
1014185087Salfred	uint8_t cfg_index;
1015184610Salfred	uint8_t do_close;
1016184610Salfred
1017184610Salfred	if (!pdev->is_opened) {
1018184610Salfred		error = libusb20_dev_open(pdev, 0);
1019184610Salfred		if (error == 0) {
1020184610Salfred			do_close = 1;
1021184610Salfred		} else {
1022184610Salfred			do_close = 0;
1023184610Salfred		}
1024184610Salfred	} else {
1025184610Salfred		do_close = 0;
1026184610Salfred	}
1027184610Salfred
1028188622Sthompsa	error = pdev->methods->get_config_index(pdev, &cfg_index);
1029234491Shselasky	if (error)
1030234491Shselasky		cfg_index = 0xFF;	/* current config index */
1031184610Salfred	if (do_close) {
1032184610Salfred		if (libusb20_dev_close(pdev)) {
1033184610Salfred			/* ignore */
1034184610Salfred		}
1035184610Salfred	}
1036185087Salfred	return (cfg_index);
1037184610Salfred}
1038184610Salfred
1039184610Salfreduint8_t
1040184610Salfredlibusb20_dev_get_mode(struct libusb20_device *pdev)
1041184610Salfred{
1042184610Salfred	return (pdev->usb_mode);
1043184610Salfred}
1044184610Salfred
1045184610Salfreduint8_t
1046184610Salfredlibusb20_dev_get_speed(struct libusb20_device *pdev)
1047184610Salfred{
1048184610Salfred	return (pdev->usb_speed);
1049184610Salfred}
1050184610Salfred
1051356398Shselaskyint
1052356398Shselaskylibusb20_dev_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats)
1053356398Shselasky{
1054356398Shselasky	uint8_t do_close;
1055356398Shselasky	int error;
1056356398Shselasky
1057356398Shselasky	if (!pdev->is_opened) {
1058356398Shselasky		error = libusb20_dev_open(pdev, 0);
1059356398Shselasky		if (error == 0) {
1060356398Shselasky			do_close = 1;
1061356398Shselasky		} else {
1062356398Shselasky			do_close = 0;
1063356398Shselasky		}
1064356398Shselasky	} else {
1065356398Shselasky		do_close = 0;
1066356398Shselasky	}
1067356398Shselasky
1068356398Shselasky	error = pdev->methods->get_stats(pdev, pstats);
1069356398Shselasky
1070356398Shselasky	if (do_close)
1071356398Shselasky		(void) libusb20_dev_close(pdev);
1072356398Shselasky
1073356398Shselasky	return (error);
1074356398Shselasky}
1075356398Shselasky
1076184610Salfred/* if this function returns an error, the device is gone */
1077184610Salfredint
1078184610Salfredlibusb20_dev_process(struct libusb20_device *pdev)
1079184610Salfred{
1080184610Salfred	int error;
1081184610Salfred
1082188622Sthompsa	error = pdev->methods->process(pdev);
1083184610Salfred	return (error);
1084184610Salfred}
1085184610Salfred
1086184610Salfredvoid
1087184610Salfredlibusb20_dev_wait_process(struct libusb20_device *pdev, int timeout)
1088184610Salfred{
1089185087Salfred	struct pollfd pfd[1];
1090184610Salfred
1091184610Salfred	if (!pdev->is_opened) {
1092184610Salfred		return;
1093184610Salfred	}
1094184610Salfred	pfd[0].fd = pdev->file;
1095184610Salfred	pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
1096184610Salfred	pfd[0].revents = 0;
1097184610Salfred
1098185087Salfred	if (poll(pfd, 1, timeout)) {
1099184610Salfred		/* ignore any error */
1100184610Salfred	}
1101184610Salfred	return;
1102184610Salfred}
1103184610Salfred
1104184610Salfredvoid
1105184610Salfredlibusb20_dev_free(struct libusb20_device *pdev)
1106184610Salfred{
1107184610Salfred	if (pdev == NULL) {
1108184610Salfred		/* be NULL safe */
1109184610Salfred		return;
1110184610Salfred	}
1111184610Salfred	if (pdev->is_opened) {
1112184610Salfred		if (libusb20_dev_close(pdev)) {
1113184610Salfred			/* ignore any errors */
1114184610Salfred		}
1115184610Salfred	}
1116184610Salfred	free(pdev);
1117184610Salfred	return;
1118184610Salfred}
1119184610Salfred
1120188622Sthompsaint
1121188622Sthompsalibusb20_dev_get_info(struct libusb20_device *pdev,
1122192984Sthompsa    struct usb_device_info *pinfo)
1123188622Sthompsa{
1124188622Sthompsa	if (pinfo == NULL)
1125188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
1126188622Sthompsa
1127188622Sthompsa	return (pdev->beMethods->dev_get_info(pdev, pinfo));
1128188622Sthompsa}
1129188622Sthompsa
1130184610Salfredconst char *
1131184610Salfredlibusb20_dev_get_backend_name(struct libusb20_device *pdev)
1132184610Salfred{
1133188622Sthompsa	return (pdev->beMethods->get_backend_name());
1134184610Salfred}
1135184610Salfred
1136184610Salfredconst char *
1137184610Salfredlibusb20_dev_get_desc(struct libusb20_device *pdev)
1138184610Salfred{
1139184610Salfred	return (pdev->usb_desc);
1140184610Salfred}
1141184610Salfred
1142184610Salfredvoid
1143184610Salfredlibusb20_dev_set_debug(struct libusb20_device *pdev, int debug)
1144184610Salfred{
1145184610Salfred	pdev->debug = debug;
1146184610Salfred	return;
1147184610Salfred}
1148184610Salfred
1149184610Salfredint
1150184610Salfredlibusb20_dev_get_debug(struct libusb20_device *pdev)
1151184610Salfred{
1152184610Salfred	return (pdev->debug);
1153184610Salfred}
1154184610Salfred
1155184610Salfreduint8_t
1156184610Salfredlibusb20_dev_get_address(struct libusb20_device *pdev)
1157184610Salfred{
1158184610Salfred	return (pdev->device_address);
1159184610Salfred}
1160184610Salfred
1161184610Salfreduint8_t
1162223495Shselaskylibusb20_dev_get_parent_address(struct libusb20_device *pdev)
1163223495Shselasky{
1164223495Shselasky	return (pdev->parent_address);
1165223495Shselasky}
1166223495Shselasky
1167223495Shselaskyuint8_t
1168223495Shselaskylibusb20_dev_get_parent_port(struct libusb20_device *pdev)
1169223495Shselasky{
1170223495Shselasky	return (pdev->parent_port);
1171223495Shselasky}
1172223495Shselasky
1173223495Shselaskyuint8_t
1174184610Salfredlibusb20_dev_get_bus_number(struct libusb20_device *pdev)
1175184610Salfred{
1176184610Salfred	return (pdev->bus_number);
1177184610Salfred}
1178184610Salfred
1179184610Salfredint
1180188622Sthompsalibusb20_dev_get_iface_desc(struct libusb20_device *pdev,
1181188622Sthompsa    uint8_t iface_index, char *buf, uint8_t len)
1182188622Sthompsa{
1183188622Sthompsa	if ((buf == NULL) || (len == 0))
1184188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
1185188622Sthompsa
1186224085Shselasky	buf[0] = 0;		/* set default string value */
1187224085Shselasky
1188188622Sthompsa	return (pdev->beMethods->dev_get_iface_desc(
1189188622Sthompsa	    pdev, iface_index, buf, len));
1190188622Sthompsa}
1191188622Sthompsa
1192184610Salfred/* USB backend operations */
1193184610Salfred
1194184610Salfredint
1195184610Salfredlibusb20_be_get_dev_quirk(struct libusb20_backend *pbe,
1196185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
1197184610Salfred{
1198188622Sthompsa	return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq));
1199184610Salfred}
1200184610Salfred
1201184610Salfredint
1202184610Salfredlibusb20_be_get_quirk_name(struct libusb20_backend *pbe,
1203185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
1204184610Salfred{
1205188622Sthompsa	return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq));
1206184610Salfred}
1207184610Salfred
1208184610Salfredint
1209184610Salfredlibusb20_be_add_dev_quirk(struct libusb20_backend *pbe,
1210184610Salfred    struct libusb20_quirk *pq)
1211184610Salfred{
1212188622Sthompsa	return (pbe->methods->root_add_dev_quirk(pbe, pq));
1213184610Salfred}
1214184610Salfred
1215184610Salfredint
1216184610Salfredlibusb20_be_remove_dev_quirk(struct libusb20_backend *pbe,
1217184610Salfred    struct libusb20_quirk *pq)
1218184610Salfred{
1219188622Sthompsa	return (pbe->methods->root_remove_dev_quirk(pbe, pq));
1220184610Salfred}
1221184610Salfred
1222184610Salfredint
1223188987Sthompsalibusb20_be_set_template(struct libusb20_backend *pbe, int temp)
1224188987Sthompsa{
1225188987Sthompsa	return (pbe->methods->root_set_template(pbe, temp));
1226188987Sthompsa}
1227188987Sthompsa
1228188987Sthompsaint
1229188987Sthompsalibusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp)
1230188987Sthompsa{
1231188987Sthompsa	int temp;
1232188987Sthompsa
1233188987Sthompsa	if (ptemp == NULL)
1234188987Sthompsa		ptemp = &temp;
1235188987Sthompsa
1236188987Sthompsa	return (pbe->methods->root_get_template(pbe, ptemp));
1237188987Sthompsa}
1238188987Sthompsa
1239184610Salfredstruct libusb20_device *
1240184610Salfredlibusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1241184610Salfred{
1242184610Salfred	if (pbe == NULL) {
1243184610Salfred		pdev = NULL;
1244184610Salfred	} else if (pdev == NULL) {
1245184610Salfred		pdev = TAILQ_FIRST(&(pbe->usb_devs));
1246184610Salfred	} else {
1247184610Salfred		pdev = TAILQ_NEXT(pdev, dev_entry);
1248184610Salfred	}
1249184610Salfred	return (pdev);
1250184610Salfred}
1251184610Salfred
1252184610Salfredstruct libusb20_backend *
1253184610Salfredlibusb20_be_alloc(const struct libusb20_backend_methods *methods)
1254184610Salfred{
1255184610Salfred	struct libusb20_backend *pbe;
1256184610Salfred
1257184610Salfred	pbe = malloc(sizeof(*pbe));
1258184610Salfred	if (pbe == NULL) {
1259184610Salfred		return (NULL);
1260184610Salfred	}
1261184610Salfred	memset(pbe, 0, sizeof(*pbe));
1262184610Salfred
1263184610Salfred	TAILQ_INIT(&(pbe->usb_devs));
1264184610Salfred
1265184610Salfred	pbe->methods = methods;		/* set backend methods */
1266184610Salfred
1267184610Salfred	/* do the initial device scan */
1268184610Salfred	if (pbe->methods->init_backend) {
1269188622Sthompsa		pbe->methods->init_backend(pbe);
1270184610Salfred	}
1271184610Salfred	return (pbe);
1272184610Salfred}
1273184610Salfred
1274184610Salfredstruct libusb20_backend *
1275184610Salfredlibusb20_be_alloc_linux(void)
1276184610Salfred{
1277253339Shselasky	return (NULL);
1278184610Salfred}
1279184610Salfred
1280184610Salfredstruct libusb20_backend *
1281184610Salfredlibusb20_be_alloc_ugen20(void)
1282184610Salfred{
1283253339Shselasky	return (libusb20_be_alloc(&libusb20_ugen20_backend));
1284184610Salfred}
1285184610Salfred
1286184610Salfredstruct libusb20_backend *
1287184610Salfredlibusb20_be_alloc_default(void)
1288184610Salfred{
1289184610Salfred	struct libusb20_backend *pbe;
1290184610Salfred
1291253339Shselasky#ifdef __linux__
1292184610Salfred	pbe = libusb20_be_alloc_linux();
1293184610Salfred	if (pbe) {
1294184610Salfred		return (pbe);
1295184610Salfred	}
1296253339Shselasky#endif
1297184610Salfred	pbe = libusb20_be_alloc_ugen20();
1298184610Salfred	if (pbe) {
1299184610Salfred		return (pbe);
1300184610Salfred	}
1301184610Salfred	return (NULL);			/* no backend found */
1302184610Salfred}
1303184610Salfred
1304184610Salfredvoid
1305184610Salfredlibusb20_be_free(struct libusb20_backend *pbe)
1306184610Salfred{
1307184610Salfred	struct libusb20_device *pdev;
1308184610Salfred
1309184610Salfred	if (pbe == NULL) {
1310184610Salfred		/* be NULL safe */
1311184610Salfred		return;
1312184610Salfred	}
1313184610Salfred	while ((pdev = libusb20_be_device_foreach(pbe, NULL))) {
1314184610Salfred		libusb20_be_dequeue_device(pbe, pdev);
1315184610Salfred		libusb20_dev_free(pdev);
1316184610Salfred	}
1317184610Salfred	if (pbe->methods->exit_backend) {
1318188622Sthompsa		pbe->methods->exit_backend(pbe);
1319184610Salfred	}
1320199055Sthompsa	/* free backend */
1321199055Sthompsa	free(pbe);
1322184610Salfred}
1323184610Salfred
1324184610Salfredvoid
1325184610Salfredlibusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev)
1326184610Salfred{
1327184610Salfred	pdev->beMethods = pbe->methods;	/* copy backend methods */
1328184610Salfred	TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry);
1329184610Salfred}
1330184610Salfred
1331184610Salfredvoid
1332184610Salfredlibusb20_be_dequeue_device(struct libusb20_backend *pbe,
1333184610Salfred    struct libusb20_device *pdev)
1334184610Salfred{
1335184610Salfred	TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry);
1336184610Salfred}
1337225659Shselasky
1338225659Shselaskyconst char *
1339225659Shselaskylibusb20_strerror(int code)
1340225659Shselasky{
1341225659Shselasky	switch (code) {
1342225659Shselasky	case LIBUSB20_SUCCESS:
1343225659Shselasky		return ("Success");
1344225659Shselasky	case LIBUSB20_ERROR_IO:
1345225659Shselasky		return ("I/O error");
1346225659Shselasky	case LIBUSB20_ERROR_INVALID_PARAM:
1347225659Shselasky		return ("Invalid parameter");
1348225659Shselasky	case LIBUSB20_ERROR_ACCESS:
1349225659Shselasky		return ("Permissions error");
1350225659Shselasky	case LIBUSB20_ERROR_NO_DEVICE:
1351225659Shselasky		return ("No device");
1352225659Shselasky	case LIBUSB20_ERROR_NOT_FOUND:
1353225659Shselasky		return ("Not found");
1354225659Shselasky	case LIBUSB20_ERROR_BUSY:
1355225659Shselasky		return ("Device busy");
1356225659Shselasky	case LIBUSB20_ERROR_TIMEOUT:
1357225659Shselasky		return ("Timeout");
1358225659Shselasky	case LIBUSB20_ERROR_OVERFLOW:
1359225659Shselasky		return ("Overflow");
1360225659Shselasky	case LIBUSB20_ERROR_PIPE:
1361225659Shselasky		return ("Pipe error");
1362225659Shselasky	case LIBUSB20_ERROR_INTERRUPTED:
1363225659Shselasky		return ("Interrupted");
1364225659Shselasky	case LIBUSB20_ERROR_NO_MEM:
1365225659Shselasky		return ("Out of memory");
1366225659Shselasky	case LIBUSB20_ERROR_NOT_SUPPORTED:
1367225659Shselasky		return ("Not supported");
1368225659Shselasky	case LIBUSB20_ERROR_OTHER:
1369225659Shselasky		return ("Other error");
1370225659Shselasky	default:
1371225659Shselasky		return ("Unknown error");
1372225659Shselasky	}
1373225659Shselasky}
1374225659Shselasky
1375225659Shselaskyconst char *
1376225659Shselaskylibusb20_error_name(int code)
1377225659Shselasky{
1378225659Shselasky	switch (code) {
1379225659Shselasky	case LIBUSB20_SUCCESS:
1380225659Shselasky		return ("LIBUSB20_SUCCESS");
1381225659Shselasky	case LIBUSB20_ERROR_IO:
1382225659Shselasky		return ("LIBUSB20_ERROR_IO");
1383225659Shselasky	case LIBUSB20_ERROR_INVALID_PARAM:
1384225659Shselasky		return ("LIBUSB20_ERROR_INVALID_PARAM");
1385225659Shselasky	case LIBUSB20_ERROR_ACCESS:
1386225659Shselasky		return ("LIBUSB20_ERROR_ACCESS");
1387225659Shselasky	case LIBUSB20_ERROR_NO_DEVICE:
1388225659Shselasky		return ("LIBUSB20_ERROR_NO_DEVICE");
1389225659Shselasky	case LIBUSB20_ERROR_NOT_FOUND:
1390225659Shselasky		return ("LIBUSB20_ERROR_NOT_FOUND");
1391225659Shselasky	case LIBUSB20_ERROR_BUSY:
1392225659Shselasky		return ("LIBUSB20_ERROR_BUSY");
1393225659Shselasky	case LIBUSB20_ERROR_TIMEOUT:
1394225659Shselasky		return ("LIBUSB20_ERROR_TIMEOUT");
1395225659Shselasky	case LIBUSB20_ERROR_OVERFLOW:
1396225659Shselasky		return ("LIBUSB20_ERROR_OVERFLOW");
1397225659Shselasky	case LIBUSB20_ERROR_PIPE:
1398225659Shselasky		return ("LIBUSB20_ERROR_PIPE");
1399225659Shselasky	case LIBUSB20_ERROR_INTERRUPTED:
1400225659Shselasky		return ("LIBUSB20_ERROR_INTERRUPTED");
1401225659Shselasky	case LIBUSB20_ERROR_NO_MEM:
1402225659Shselasky		return ("LIBUSB20_ERROR_NO_MEM");
1403225659Shselasky	case LIBUSB20_ERROR_NOT_SUPPORTED:
1404225659Shselasky		return ("LIBUSB20_ERROR_NOT_SUPPORTED");
1405225659Shselasky	case LIBUSB20_ERROR_OTHER:
1406225659Shselasky		return ("LIBUSB20_ERROR_OTHER");
1407225659Shselasky	default:
1408225659Shselasky		return ("LIBUSB20_ERROR_UNKNOWN");
1409225659Shselasky	}
1410225659Shselasky}
1411