libusb20_ugen20.c revision 310280
1139749Simp/* $FreeBSD: stable/10/lib/libusb/libusb20_ugen20.c 310280 2016-12-19 18:26:26Z trasz $ */
2102682Sgibbs/*-
397883Sgibbs * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4133122Sgibbs *
5102682Sgibbs * Redistribution and use in source and binary forms, with or without
697883Sgibbs * modification, are permitted provided that the following conditions
797883Sgibbs * are met:
897883Sgibbs * 1. Redistributions of source code must retain the above copyright
997883Sgibbs *    notice, this list of conditions and the following disclaimer.
1097883Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1197883Sgibbs *    notice, this list of conditions and the following disclaimer in the
1297883Sgibbs *    documentation and/or other materials provided with the distribution.
1397883Sgibbs *
1497883Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1597883Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1697883Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1797883Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1897883Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1997883Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2097883Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2197883Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2297883Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2397883Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2497883Sgibbs * SUCH DAMAGE.
2597883Sgibbs */
2697883Sgibbs
2797883Sgibbs#ifdef LIBUSB_GLOBAL_INCLUDE_FILE
2897883Sgibbs#include LIBUSB_GLOBAL_INCLUDE_FILE
2997883Sgibbs#else
3097883Sgibbs#include <errno.h>
3197883Sgibbs#include <fcntl.h>
3297883Sgibbs#include <stdio.h>
3397883Sgibbs#include <stdlib.h>
3497883Sgibbs#include <string.h>
3597883Sgibbs#include <unistd.h>
3697883Sgibbs#include <time.h>
3797883Sgibbs#include <sys/queue.h>
3897883Sgibbs#include <sys/types.h>
3997883Sgibbs#endif
4097883Sgibbs
4197883Sgibbs#include <dev/usb/usb.h>
4297883Sgibbs#include <dev/usb/usbdi.h>
43129134Sgibbs#include <dev/usb/usb_ioctl.h>
4497883Sgibbs
45102682Sgibbs#include "libusb20.h"
4697883Sgibbs#include "libusb20_desc.h"
4797883Sgibbs#include "libusb20_int.h"
4897883Sgibbs
4997883Sgibbs#ifndef	IOUSB
50107441Sscottl#define IOUSB(a) a
51107441Sscottl#endif
52107441Sscottl
53107441Sscottlstatic libusb20_init_backend_t ugen20_init_backend;
54107441Sscottlstatic libusb20_open_device_t ugen20_open_device;
55107441Sscottlstatic libusb20_close_device_t ugen20_close_device;
5697883Sgibbsstatic libusb20_get_backend_name_t ugen20_get_backend_name;
57107441Sscottlstatic libusb20_exit_backend_t ugen20_exit_backend;
58107441Sscottlstatic libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc;
59107441Sscottlstatic libusb20_dev_get_info_t ugen20_dev_get_info;
60107441Sscottlstatic libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk;
61107441Sscottlstatic libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name;
62107441Sscottlstatic libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk;
63107441Sscottlstatic libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk;
64107441Sscottlstatic libusb20_root_set_template_t ugen20_root_set_template;
65107628Sscottlstatic libusb20_root_get_template_t ugen20_root_get_template;
66107441Sscottl
67107628Sscottlconst struct libusb20_backend_methods libusb20_ugen20_backend = {
68107441Sscottl	LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20)
69104023Sgibbs};
7097883Sgibbs
71125448Sgibbs/* USB device specific */
72125448Sgibbsstatic libusb20_get_config_desc_full_t ugen20_get_config_desc_full;
73125448Sgibbsstatic libusb20_get_config_index_t ugen20_get_config_index;
74125448Sgibbsstatic libusb20_set_config_index_t ugen20_set_config_index;
75125448Sgibbsstatic libusb20_set_alt_index_t ugen20_set_alt_index;
76125448Sgibbsstatic libusb20_reset_device_t ugen20_reset_device;
77125448Sgibbsstatic libusb20_check_connected_t ugen20_check_connected;
78125448Sgibbsstatic libusb20_set_power_mode_t ugen20_set_power_mode;
79125448Sgibbsstatic libusb20_get_power_mode_t ugen20_get_power_mode;
80125448Sgibbsstatic libusb20_get_port_path_t ugen20_get_port_path;
81125448Sgibbsstatic libusb20_get_power_usage_t ugen20_get_power_usage;
82125448Sgibbsstatic libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
83125448Sgibbsstatic libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
84125448Sgibbsstatic libusb20_do_request_sync_t ugen20_do_request_sync;
85125448Sgibbsstatic libusb20_process_t ugen20_process;
86125448Sgibbs
87125448Sgibbs/* USB transfer specific */
88125448Sgibbsstatic libusb20_tr_open_t ugen20_tr_open;
89125448Sgibbsstatic libusb20_tr_close_t ugen20_tr_close;
90125448Sgibbsstatic libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
91125448Sgibbsstatic libusb20_tr_submit_t ugen20_tr_submit;
92125448Sgibbsstatic libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
93125448Sgibbs
94125448Sgibbsstatic const struct libusb20_device_methods libusb20_ugen20_device_methods = {
95125448Sgibbs	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
96125448Sgibbs};
97125448Sgibbs
98125448Sgibbsstatic const char *
99125448Sgibbsugen20_get_backend_name(void)
100125448Sgibbs{
101125448Sgibbs	return ("FreeBSD UGEN 2.0");
102125448Sgibbs}
103125448Sgibbs
104125448Sgibbsstatic uint32_t
105125448Sgibbsugen20_path_convert_one(const char **pp)
106125448Sgibbs{
10797883Sgibbs	const char *ptr;
10897883Sgibbs	uint32_t temp = 0;
10997883Sgibbs
11097883Sgibbs	ptr = *pp;
11197883Sgibbs
11297883Sgibbs	while ((*ptr >= '0') && (*ptr <= '9')) {
11397883Sgibbs		temp *= 10;
11497883Sgibbs		temp += (*ptr - '0');
11597883Sgibbs		if (temp >= 1000000) {
11697883Sgibbs			/* catch overflow early */
11797883Sgibbs			return (0xFFFFFFFF);
11897883Sgibbs		}
11997883Sgibbs		ptr++;
12097883Sgibbs	}
12197883Sgibbs
122107441Sscottl	if (*ptr == '.') {
12397883Sgibbs		/* skip dot */
124114623Sgibbs		ptr++;
125123579Sgibbs	}
126123579Sgibbs	*pp = ptr;
127123579Sgibbs
128123579Sgibbs	return (temp);
129123579Sgibbs}
130123579Sgibbs
131123579Sgibbsstatic int
132114623Sgibbsugen20_enumerate(struct libusb20_device *pdev, const char *id)
133114623Sgibbs{
134114623Sgibbs	const char *tmp = id;
135114623Sgibbs	struct usb_device_descriptor ddesc;
136114623Sgibbs	struct usb_device_info devinfo;
137114623Sgibbs	uint32_t plugtime;
138107441Sscottl	char buf[64];
139107441Sscottl	int f;
140107441Sscottl	int error;
141107441Sscottl
142107441Sscottl	pdev->bus_number = ugen20_path_convert_one(&tmp);
143107441Sscottl	pdev->device_address = ugen20_path_convert_one(&tmp);
144107441Sscottl
145123579Sgibbs	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
146107441Sscottl	    pdev->bus_number, pdev->device_address);
147107441Sscottl
14897883Sgibbs	f = open(buf, O_RDWR);
14997883Sgibbs	if (f < 0) {
150114623Sgibbs		return (LIBUSB20_ERROR_OTHER);
151114623Sgibbs	}
15297883Sgibbs	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
15397883Sgibbs		error = LIBUSB20_ERROR_OTHER;
15497883Sgibbs		goto done;
15597883Sgibbs	}
156109588Sgibbs	/* store when the device was plugged */
157109588Sgibbs	pdev->session_data.plugtime = plugtime;
158109588Sgibbs
159109588Sgibbs	if (ioctl(f, IOUSB(USB_GET_DEVICE_DESC), &ddesc)) {
160109588Sgibbs		error = LIBUSB20_ERROR_OTHER;
161109588Sgibbs		goto done;
162109588Sgibbs	}
163109588Sgibbs	LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc));
164109588Sgibbs
165109588Sgibbs	libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc));
16697883Sgibbs
16797883Sgibbs	if (pdev->ddesc.bNumConfigurations == 0) {
16897883Sgibbs		error = LIBUSB20_ERROR_OTHER;
16997883Sgibbs		goto done;
17097883Sgibbs	} else if (pdev->ddesc.bNumConfigurations >= 8) {
171111653Sgibbs		error = LIBUSB20_ERROR_OTHER;
17297883Sgibbs		goto done;
17397883Sgibbs	}
174111653Sgibbs	if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) {
175111653Sgibbs		error = LIBUSB20_ERROR_OTHER;
17697883Sgibbs		goto done;
177114623Sgibbs	}
178114623Sgibbs	switch (devinfo.udi_mode) {
179114623Sgibbs	case USB_MODE_DEVICE:
18097883Sgibbs		pdev->usb_mode = LIBUSB20_MODE_DEVICE;
181111653Sgibbs		break;
18297883Sgibbs	default:
183111653Sgibbs		pdev->usb_mode = LIBUSB20_MODE_HOST;
18497883Sgibbs		break;
18597883Sgibbs	}
18697883Sgibbs
187114623Sgibbs	switch (devinfo.udi_speed) {
18897883Sgibbs	case USB_SPEED_LOW:
18997883Sgibbs		pdev->usb_speed = LIBUSB20_SPEED_LOW;
190107441Sscottl		break;
191107441Sscottl	case USB_SPEED_FULL:
19297883Sgibbs		pdev->usb_speed = LIBUSB20_SPEED_FULL;
193104023Sgibbs		break;
194123579Sgibbs	case USB_SPEED_HIGH:
19597883Sgibbs		pdev->usb_speed = LIBUSB20_SPEED_HIGH;
19697883Sgibbs		break;
197123579Sgibbs	case USB_SPEED_VARIABLE:
19897883Sgibbs		pdev->usb_speed = LIBUSB20_SPEED_VARIABLE;
199104023Sgibbs		break;
200123579Sgibbs	case USB_SPEED_SUPER:
201107441Sscottl		pdev->usb_speed = LIBUSB20_SPEED_SUPER;
202123579Sgibbs		break;
203114623Sgibbs	default:
204107441Sscottl		pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN;
20597883Sgibbs		break;
20697883Sgibbs	}
207104023Sgibbs
208109588Sgibbs	/* get parent HUB index and port */
209123579Sgibbs
210109588Sgibbs	pdev->parent_address = devinfo.udi_hubindex;
211109588Sgibbs	pdev->parent_port = devinfo.udi_hubport;
212104023Sgibbs
21397883Sgibbs	/* generate a nice description for printout */
21497883Sgibbs
21597883Sgibbs	snprintf(pdev->usb_desc, sizeof(pdev->usb_desc),
21697883Sgibbs	    USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number,
21797883Sgibbs	    pdev->device_address, devinfo.udi_vendor,
21897883Sgibbs	    devinfo.udi_product, pdev->bus_number);
21997883Sgibbs
220299375Spfg	error = 0;
221114623Sgibbsdone:
222114623Sgibbs	close(f);
223114623Sgibbs	return (error);
224114623Sgibbs}
225123579Sgibbs
22697883Sgibbsstruct ugen20_urd_state {
227125448Sgibbs	struct usb_read_dir urd;
228125448Sgibbs	uint32_t nparsed;
229125448Sgibbs	int	f;
230125448Sgibbs	uint8_t *ptr;
231123579Sgibbs	const char *src;
232125448Sgibbs	const char *dst;
233125448Sgibbs	uint8_t	buf[256];
234123579Sgibbs	uint8_t	dummy_zero[1];
235123579Sgibbs};
236123579Sgibbs
23797883Sgibbsstatic int
23897883Sgibbsugen20_readdir(struct ugen20_urd_state *st)
239109588Sgibbs{
24097883Sgibbs	;				/* style fix */
24197883Sgibbsrepeat:
24297883Sgibbs	if (st->ptr == NULL) {
243102682Sgibbs		st->urd.urd_startentry += st->nparsed;
244102682Sgibbs		st->urd.urd_data = libusb20_pass_ptr(st->buf);
245123579Sgibbs		st->urd.urd_maxlen = sizeof(st->buf);
24697883Sgibbs		st->nparsed = 0;
247109588Sgibbs
248109588Sgibbs		if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) {
249109588Sgibbs			return (EINVAL);
250115329Sgibbs		}
251109588Sgibbs		st->ptr = st->buf;
252109588Sgibbs	}
253115329Sgibbs	if (st->ptr[0] == 0) {
254115329Sgibbs		if (st->nparsed) {
255115329Sgibbs			st->ptr = NULL;
256109588Sgibbs			goto repeat;
257109588Sgibbs		} else {
258109588Sgibbs			return (ENXIO);
259109588Sgibbs		}
260109588Sgibbs	}
261115329Sgibbs	st->src = (void *)(st->ptr + 1);
262109588Sgibbs	st->dst = st->src + strlen(st->src) + 1;
263115329Sgibbs	st->ptr = st->ptr + st->ptr[0];
264109588Sgibbs	st->nparsed++;
265109588Sgibbs
266109588Sgibbs	if ((st->ptr < st->buf) ||
267109588Sgibbs	    (st->ptr > st->dummy_zero)) {
268115329Sgibbs		/* invalid entry */
269109588Sgibbs		return (EINVAL);
270109588Sgibbs	}
271115329Sgibbs	return (0);
272115329Sgibbs}
273115329Sgibbs
274109588Sgibbsstatic int
275109588Sgibbsugen20_init_backend(struct libusb20_backend *pbe)
276109588Sgibbs{
277109588Sgibbs	struct ugen20_urd_state state;
278109588Sgibbs	struct libusb20_device *pdev;
279109588Sgibbs
280115329Sgibbs	memset(&state, 0, sizeof(state));
281109588Sgibbs
282109588Sgibbs	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
283109588Sgibbs	if (state.f < 0)
284109588Sgibbs		return (LIBUSB20_ERROR_OTHER);
285109588Sgibbs
286109588Sgibbs	while (ugen20_readdir(&state) == 0) {
287115329Sgibbs
288109588Sgibbs		if ((state.src[0] != 'u') ||
289109588Sgibbs		    (state.src[1] != 'g') ||
290104023Sgibbs		    (state.src[2] != 'e') ||
29197883Sgibbs		    (state.src[3] != 'n')) {
29297883Sgibbs			continue;
29397883Sgibbs		}
29497883Sgibbs		pdev = libusb20_dev_alloc();
295109588Sgibbs		if (pdev == NULL) {
296109588Sgibbs			continue;
297109588Sgibbs		}
298115331Sgibbs		if (ugen20_enumerate(pdev, state.src + 4)) {
299115331Sgibbs			libusb20_dev_free(pdev);
300115331Sgibbs			continue;
301115331Sgibbs		}
302115331Sgibbs		/* put the device on the backend list */
303115331Sgibbs		libusb20_be_enqueue_device(pbe, pdev);
304115331Sgibbs	}
305115331Sgibbs	close(state.f);
306115331Sgibbs	return (0);			/* success */
307114623Sgibbs}
308114623Sgibbs
309114623Sgibbsstatic void
310114623Sgibbsugen20_tr_release(struct libusb20_device *pdev)
311114623Sgibbs{
312114623Sgibbs	struct usb_fs_uninit fs_uninit;
313116937Sgibbs
31497883Sgibbs	if (pdev->nTransfer == 0) {
31597883Sgibbs		return;
316133122Sgibbs	}
317133122Sgibbs	/* release all pending USB transfers */
318133122Sgibbs	if (pdev->privBeData != NULL) {
319133122Sgibbs		memset(&fs_uninit, 0, sizeof(fs_uninit));
32097883Sgibbs		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
32197883Sgibbs			/* ignore any errors of this kind */
322107441Sscottl		}
323107441Sscottl	}
324133122Sgibbs	return;
32597883Sgibbs}
326133122Sgibbs
327133122Sgibbsstatic int
328133122Sgibbsugen20_tr_renew(struct libusb20_device *pdev)
329133122Sgibbs{
330133122Sgibbs	struct usb_fs_init fs_init;
33197883Sgibbs	struct usb_fs_endpoint *pfse;
332133122Sgibbs	int error;
333133122Sgibbs	uint32_t size;
334133122Sgibbs	uint16_t nMaxTransfer;
335133122Sgibbs
336133122Sgibbs	nMaxTransfer = pdev->nTransfer;
337133122Sgibbs	error = 0;
338133122Sgibbs
33997883Sgibbs	if (nMaxTransfer == 0) {
34097883Sgibbs		goto done;
34197883Sgibbs	}
34297883Sgibbs	size = nMaxTransfer * sizeof(*pfse);
34397883Sgibbs
344133122Sgibbs	if (pdev->privBeData == NULL) {
345133122Sgibbs		pfse = malloc(size);
346133122Sgibbs		if (pfse == NULL) {
347133122Sgibbs			error = LIBUSB20_ERROR_NO_MEM;
34897883Sgibbs			goto done;
349133122Sgibbs		}
350133122Sgibbs		pdev->privBeData = pfse;
351133122Sgibbs	}
352133122Sgibbs	/* reset endpoint data */
353133122Sgibbs	memset(pdev->privBeData, 0, size);
35497883Sgibbs
355133122Sgibbs	memset(&fs_init, 0, sizeof(fs_init));
356133122Sgibbs
357133122Sgibbs	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
358133122Sgibbs	fs_init.ep_index_max = nMaxTransfer;
359133122Sgibbs
360133122Sgibbs	if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) {
361133122Sgibbs		error = LIBUSB20_ERROR_OTHER;
362133122Sgibbs		goto done;
363133122Sgibbs	}
364133122Sgibbsdone:
365133122Sgibbs	return (error);
366133122Sgibbs}
367133122Sgibbs
368133122Sgibbsstatic int
369133122Sgibbsugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
370133122Sgibbs{
371133122Sgibbs	uint32_t plugtime;
372133122Sgibbs	char buf[64];
373133122Sgibbs	int f;
374133122Sgibbs	int g;
375133122Sgibbs	int error;
376133122Sgibbs
377133122Sgibbs	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
378133122Sgibbs	    pdev->bus_number, pdev->device_address);
379133122Sgibbs
380133122Sgibbs	/*
381133122Sgibbs	 * We need two file handles, one for the control endpoint and one
382133122Sgibbs	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
383133122Sgibbs	 * kernel locking.
384133122Sgibbs	 */
385133122Sgibbs	g = open(buf, O_RDWR);
386133122Sgibbs	if (g < 0) {
387133122Sgibbs		return (LIBUSB20_ERROR_NO_DEVICE);
388133122Sgibbs	}
389133122Sgibbs	f = open(buf, O_RDWR);
390133122Sgibbs	if (f < 0) {
391133122Sgibbs		close(g);
392133122Sgibbs		return (LIBUSB20_ERROR_NO_DEVICE);
393133122Sgibbs	}
394133122Sgibbs	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
395133122Sgibbs		error = LIBUSB20_ERROR_OTHER;
396107441Sscottl		goto done;
397133122Sgibbs	}
39897883Sgibbs	/* check that the correct device is still plugged */
399133122Sgibbs	if (pdev->session_data.plugtime != plugtime) {
400133122Sgibbs		error = LIBUSB20_ERROR_NO_DEVICE;
401133122Sgibbs		goto done;
402133122Sgibbs	}
40397883Sgibbs	/* need to set this before "tr_renew()" */
40497883Sgibbs	pdev->file = f;
40597883Sgibbs	pdev->file_ctrl = g;
406107441Sscottl
40797883Sgibbs	/* renew all USB transfers */
408133122Sgibbs	error = ugen20_tr_renew(pdev);
409133122Sgibbs	if (error) {
410133122Sgibbs		goto done;
411133122Sgibbs	}
41297883Sgibbs	/* set methods */
413107441Sscottl	pdev->methods = &libusb20_ugen20_device_methods;
414104023Sgibbs
41597883Sgibbsdone:
41697883Sgibbs	if (error) {
41797883Sgibbs		if (pdev->privBeData) {
418133122Sgibbs			/* cleanup after "tr_renew()" */
419133122Sgibbs			free(pdev->privBeData);
420133122Sgibbs			pdev->privBeData = NULL;
42197883Sgibbs		}
422133122Sgibbs		pdev->file = -1;
423133122Sgibbs		pdev->file_ctrl = -1;
42497883Sgibbs		close(f);
42597883Sgibbs		close(g);
426133122Sgibbs	}
42797883Sgibbs	return (error);
42897883Sgibbs}
42997883Sgibbs
43097883Sgibbsstatic int
43197883Sgibbsugen20_close_device(struct libusb20_device *pdev)
43297883Sgibbs{
43397883Sgibbs	struct usb_fs_uninit fs_uninit;
43497883Sgibbs
43597883Sgibbs	if (pdev->privBeData) {
43697883Sgibbs		memset(&fs_uninit, 0, sizeof(fs_uninit));
43797883Sgibbs		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
438129134Sgibbs			/* ignore this error */
43997883Sgibbs		}
44097883Sgibbs		free(pdev->privBeData);
441125448Sgibbs	}
442125448Sgibbs	pdev->nTransfer = 0;
443125448Sgibbs	pdev->privBeData = NULL;
44497883Sgibbs	close(pdev->file);
445115329Sgibbs	close(pdev->file_ctrl);
446109588Sgibbs	pdev->file = -1;
447109588Sgibbs	pdev->file_ctrl = -1;
44897883Sgibbs	return (0);			/* success */
44997883Sgibbs}
450107441Sscottl
451129134Sgibbsstatic void
452129134Sgibbsugen20_exit_backend(struct libusb20_backend *pbe)
453129134Sgibbs{
454129134Sgibbs	return;				/* nothing to do */
455129134Sgibbs}
456129134Sgibbs
457129134Sgibbsstatic int
458129134Sgibbsugen20_get_config_desc_full(struct libusb20_device *pdev,
459129134Sgibbs    uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
460129134Sgibbs{
461129134Sgibbs	struct usb_gen_descriptor gen_desc;
462129134Sgibbs	struct usb_config_descriptor cdesc;
463107441Sscottl	uint8_t *ptr;
464107441Sscottl	uint16_t len;
46597883Sgibbs	int error;
46697883Sgibbs
46797883Sgibbs	/* make sure memory is initialised */
46897883Sgibbs	memset(&cdesc, 0, sizeof(cdesc));
46997883Sgibbs	memset(&gen_desc, 0, sizeof(gen_desc));
47097883Sgibbs
47197883Sgibbs	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
47297883Sgibbs	gen_desc.ugd_maxlen = sizeof(cdesc);
47397883Sgibbs	gen_desc.ugd_config_index = cfg_index;
47497883Sgibbs
47597883Sgibbs	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
47697883Sgibbs	if (error) {
477114623Sgibbs		return (LIBUSB20_ERROR_OTHER);
47897883Sgibbs	}
47997883Sgibbs	len = UGETW(cdesc.wTotalLength);
48097883Sgibbs	if (len < sizeof(cdesc)) {
48197883Sgibbs		/* corrupt descriptor */
48297883Sgibbs		return (LIBUSB20_ERROR_OTHER);
48397883Sgibbs	}
48497883Sgibbs	ptr = malloc(len);
48597883Sgibbs	if (!ptr) {
48697883Sgibbs		return (LIBUSB20_ERROR_NO_MEM);
48797883Sgibbs	}
48897883Sgibbs
48997883Sgibbs	/* make sure memory is initialised */
49097883Sgibbs	memset(ptr, 0, len);
49197883Sgibbs
492104023Sgibbs	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
49397883Sgibbs	gen_desc.ugd_maxlen = len;
49497883Sgibbs
495125448Sgibbs	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
496125448Sgibbs	if (error) {
497125448Sgibbs		free(ptr);
498125448Sgibbs		return (LIBUSB20_ERROR_OTHER);
499125448Sgibbs	}
500125448Sgibbs	/* make sure that the device doesn't fool us */
501125448Sgibbs	memcpy(ptr, &cdesc, sizeof(cdesc));
502125448Sgibbs
503125448Sgibbs	*ppbuf = ptr;
504125448Sgibbs	*plen = len;
505125448Sgibbs
506125448Sgibbs	return (0);			/* success */
50797883Sgibbs}
50897883Sgibbs
50997883Sgibbsstatic int
51097883Sgibbsugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
51197883Sgibbs{
51297883Sgibbs	int temp;
513107441Sscottl
51497883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) {
51597883Sgibbs		return (LIBUSB20_ERROR_OTHER);
51697883Sgibbs	}
51797883Sgibbs	*pindex = temp;
51897883Sgibbs
51997883Sgibbs	return (0);
520104023Sgibbs}
52197883Sgibbs
52297883Sgibbsstatic int
52397883Sgibbsugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
52497883Sgibbs{
525104023Sgibbs	int temp = cfg_index;
526104023Sgibbs
527107441Sscottl	/* release all active USB transfers */
528107441Sscottl	ugen20_tr_release(pdev);
529107441Sscottl
530107441Sscottl	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) {
531107441Sscottl		return (LIBUSB20_ERROR_OTHER);
532107441Sscottl	}
533107441Sscottl	return (ugen20_tr_renew(pdev));
534107441Sscottl}
53597883Sgibbs
53697883Sgibbsstatic int
53797883Sgibbsugen20_set_alt_index(struct libusb20_device *pdev,
53897883Sgibbs    uint8_t iface_index, uint8_t alt_index)
53997883Sgibbs{
54097883Sgibbs	struct usb_alt_interface alt_iface;
54197883Sgibbs
54297883Sgibbs	memset(&alt_iface, 0, sizeof(alt_iface));
54397883Sgibbs
54497883Sgibbs	alt_iface.uai_interface_index = iface_index;
54597883Sgibbs	alt_iface.uai_alt_index = alt_index;
546102682Sgibbs
54797883Sgibbs	/* release all active USB transfers */
54897883Sgibbs	ugen20_tr_release(pdev);
54997883Sgibbs
55097883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) {
55197883Sgibbs		return (LIBUSB20_ERROR_OTHER);
55297883Sgibbs	}
55397883Sgibbs	return (ugen20_tr_renew(pdev));
55497883Sgibbs}
55597883Sgibbs
55697883Sgibbsstatic int
55797883Sgibbsugen20_reset_device(struct libusb20_device *pdev)
55897883Sgibbs{
55997883Sgibbs	int temp = 0;
56097883Sgibbs
56197883Sgibbs	/* release all active USB transfers */
56297883Sgibbs	ugen20_tr_release(pdev);
56397883Sgibbs
56497883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) {
56597883Sgibbs		return (LIBUSB20_ERROR_OTHER);
56697883Sgibbs	}
56797883Sgibbs	return (ugen20_tr_renew(pdev));
56897883Sgibbs}
56997883Sgibbs
57097883Sgibbsstatic int
57197883Sgibbsugen20_check_connected(struct libusb20_device *pdev)
57297883Sgibbs{
57397883Sgibbs	uint32_t plugtime;
57497883Sgibbs	int error = 0;
57597883Sgibbs
57697883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
57797883Sgibbs		error = LIBUSB20_ERROR_NO_DEVICE;
57897883Sgibbs		goto done;
57997883Sgibbs	}
58097883Sgibbs
58197883Sgibbs	if (pdev->session_data.plugtime != plugtime) {
58297883Sgibbs		error = LIBUSB20_ERROR_NO_DEVICE;
58397883Sgibbs		goto done;
584107441Sscottl	}
585107441Sscottldone:
58697883Sgibbs	return (error);
58797883Sgibbs}
58897883Sgibbs
58997883Sgibbsstatic int
59097883Sgibbsugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
591107441Sscottl{
592104023Sgibbs	int temp;
59397883Sgibbs
594107441Sscottl	switch (power_mode) {
595107441Sscottl	case LIBUSB20_POWER_OFF:
596107441Sscottl		temp = USB_POWER_MODE_OFF;
597107441Sscottl		break;
598107441Sscottl	case LIBUSB20_POWER_ON:
599107441Sscottl		temp = USB_POWER_MODE_ON;
600107441Sscottl		break;
601107441Sscottl	case LIBUSB20_POWER_SAVE:
602107441Sscottl		temp = USB_POWER_MODE_SAVE;
603107441Sscottl		break;
604107441Sscottl	case LIBUSB20_POWER_SUSPEND:
605107441Sscottl		temp = USB_POWER_MODE_SUSPEND;
606107441Sscottl		break;
607107441Sscottl	case LIBUSB20_POWER_RESUME:
608104023Sgibbs		temp = USB_POWER_MODE_RESUME;
609104023Sgibbs		break;
61097883Sgibbs	default:
61197883Sgibbs		return (LIBUSB20_ERROR_INVALID_PARAM);
61297883Sgibbs	}
61397883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) {
61497883Sgibbs		return (LIBUSB20_ERROR_OTHER);
61597883Sgibbs	}
61697883Sgibbs	return (0);
61797883Sgibbs}
618123579Sgibbs
619123579Sgibbsstatic int
620123579Sgibbsugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
621123579Sgibbs{
622123579Sgibbs	int temp;
623123579Sgibbs
624123579Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) {
625123579Sgibbs		return (LIBUSB20_ERROR_OTHER);
626123579Sgibbs	}
627123579Sgibbs	switch (temp) {
628123579Sgibbs	case USB_POWER_MODE_OFF:
629123579Sgibbs		temp = LIBUSB20_POWER_OFF;
630123579Sgibbs		break;
631123579Sgibbs	case USB_POWER_MODE_ON:
632123579Sgibbs		temp = LIBUSB20_POWER_ON;
633104023Sgibbs		break;
634104023Sgibbs	case USB_POWER_MODE_SAVE:
635123579Sgibbs		temp = LIBUSB20_POWER_SAVE;
636123579Sgibbs		break;
637123579Sgibbs	case USB_POWER_MODE_SUSPEND:
638123579Sgibbs		temp = LIBUSB20_POWER_SUSPEND;
639123579Sgibbs		break;
640104023Sgibbs	case USB_POWER_MODE_RESUME:
641123579Sgibbs		temp = LIBUSB20_POWER_RESUME;
642104023Sgibbs		break;
643104023Sgibbs	default:
644104023Sgibbs		temp = LIBUSB20_POWER_ON;
64597883Sgibbs		break;
64697883Sgibbs	}
64797883Sgibbs	*power_mode = temp;
64897883Sgibbs	return (0);			/* success */
64997883Sgibbs}
65097883Sgibbs
65197883Sgibbsstatic int
65297883Sgibbsugen20_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
65397883Sgibbs{
65497883Sgibbs	struct usb_device_port_path udpp;
65597883Sgibbs
65697883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_DEV_PORT_PATH), &udpp))
65797883Sgibbs		return (LIBUSB20_ERROR_OTHER);
65897883Sgibbs
65997883Sgibbs	if (udpp.udp_port_level > bufsize)
66097883Sgibbs		return (LIBUSB20_ERROR_OVERFLOW);
66197883Sgibbs
66297883Sgibbs	memcpy(buf, udpp.udp_port_no, udpp.udp_port_level);
66397883Sgibbs
66497883Sgibbs	return (udpp.udp_port_level);	/* success */
66597883Sgibbs}
66697883Sgibbs
66797883Sgibbsstatic int
66897883Sgibbsugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
66997883Sgibbs{
67097883Sgibbs	int temp;
671123579Sgibbs
672123579Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) {
673123579Sgibbs		return (LIBUSB20_ERROR_OTHER);
674123579Sgibbs	}
675123579Sgibbs	*power_usage = temp;
676123579Sgibbs	return (0);			/* success */
677123579Sgibbs}
678123579Sgibbs
679123579Sgibbsstatic int
680123579Sgibbsugen20_kernel_driver_active(struct libusb20_device *pdev,
681123579Sgibbs    uint8_t iface_index)
682123579Sgibbs{
683123579Sgibbs	int temp = iface_index;
684123579Sgibbs
685123579Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) {
68697883Sgibbs		return (LIBUSB20_ERROR_OTHER);
68797883Sgibbs	}
68897883Sgibbs	return (0);			/* kernel driver is active */
68997883Sgibbs}
69097883Sgibbs
691111653Sgibbsstatic int
69297883Sgibbsugen20_detach_kernel_driver(struct libusb20_device *pdev,
69397883Sgibbs    uint8_t iface_index)
69497883Sgibbs{
69597883Sgibbs	int temp = iface_index;
69697883Sgibbs
69797883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) {
69897883Sgibbs		return (LIBUSB20_ERROR_OTHER);
69997883Sgibbs	}
70097883Sgibbs	return (0);			/* kernel driver is detached */
70197883Sgibbs}
70297883Sgibbs
70397883Sgibbsstatic int
70497883Sgibbsugen20_do_request_sync(struct libusb20_device *pdev,
70597883Sgibbs    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
70697883Sgibbs    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
70797883Sgibbs{
70897883Sgibbs	struct usb_ctl_request req;
70997883Sgibbs
71097883Sgibbs	memset(&req, 0, sizeof(req));
71197883Sgibbs
71297883Sgibbs	req.ucr_data = libusb20_pass_ptr(data);
71397883Sgibbs	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
71497883Sgibbs		req.ucr_flags |= USB_SHORT_XFER_OK;
71597883Sgibbs	}
71697883Sgibbs	if (libusb20_me_encode(&req.ucr_request,
71797883Sgibbs	    sizeof(req.ucr_request), setup)) {
71897883Sgibbs		/* ignore */
71997883Sgibbs	}
72097883Sgibbs	if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) {
72197883Sgibbs		return (LIBUSB20_ERROR_OTHER);
72297883Sgibbs	}
72397883Sgibbs	if (pactlen) {
72497883Sgibbs		/* get actual length */
72597883Sgibbs		*pactlen = req.ucr_actlen;
72697883Sgibbs	}
72797883Sgibbs	return (0);			/* request was successful */
72897883Sgibbs}
72997883Sgibbs
73097883Sgibbsstatic int
73197883Sgibbsugen20_process(struct libusb20_device *pdev)
73297883Sgibbs{
73397883Sgibbs	struct usb_fs_complete temp;
73497883Sgibbs	struct usb_fs_endpoint *fsep;
73597883Sgibbs	struct libusb20_transfer *xfer;
73697883Sgibbs
73797883Sgibbs	while (1) {
73897883Sgibbs
73997883Sgibbs	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
74097883Sgibbs			if (errno == EBUSY) {
74197883Sgibbs				break;
74297883Sgibbs			} else {
743107441Sscottl				/* device detached */
74497883Sgibbs				return (LIBUSB20_ERROR_OTHER);
745133122Sgibbs			}
746133122Sgibbs		}
74797883Sgibbs		fsep = pdev->privBeData;
748133122Sgibbs		xfer = pdev->pTransfer;
74997883Sgibbs		fsep += temp.ep_index;
75097883Sgibbs		xfer += temp.ep_index;
751133122Sgibbs
75297883Sgibbs		/* update transfer status */
753133122Sgibbs
754133122Sgibbs		if (fsep->status == 0) {
755133122Sgibbs			xfer->aFrames = fsep->aFrames;
75697883Sgibbs			xfer->timeComplete = fsep->isoc_time_complete;
75797883Sgibbs			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
758133122Sgibbs		} else if (fsep->status == USB_ERR_CANCELLED) {
75997883Sgibbs			xfer->aFrames = 0;
76097883Sgibbs			xfer->timeComplete = 0;
76197883Sgibbs			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
76297883Sgibbs		} else if (fsep->status == USB_ERR_STALLED) {
76397883Sgibbs			xfer->aFrames = 0;
764133122Sgibbs			xfer->timeComplete = 0;
76597883Sgibbs			xfer->status = LIBUSB20_TRANSFER_STALL;
766133122Sgibbs		} else if (fsep->status == USB_ERR_TIMEOUT) {
767133122Sgibbs			xfer->aFrames = 0;
76897883Sgibbs			xfer->timeComplete = 0;
769133122Sgibbs			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
770133122Sgibbs		} else {
771133122Sgibbs			xfer->aFrames = 0;
772133122Sgibbs			xfer->timeComplete = 0;
773133122Sgibbs			xfer->status = LIBUSB20_TRANSFER_ERROR;
774133122Sgibbs		}
775133122Sgibbs		libusb20_tr_callback_wrapper(xfer);
776133122Sgibbs	}
777133122Sgibbs	return (0);			/* done */
778133122Sgibbs}
779133122Sgibbs
78097883Sgibbsstatic int
78197883Sgibbsugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
78297883Sgibbs    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
78397883Sgibbs    uint8_t pre_scale)
78497883Sgibbs{
78597883Sgibbs	union {
78697883Sgibbs		struct usb_fs_open fs_open;
78797883Sgibbs		struct usb_fs_open_stream fs_open_stream;
78897883Sgibbs	} temp;
78997883Sgibbs	struct usb_fs_endpoint *fsep;
79097883Sgibbs
791133122Sgibbs	if (pre_scale)
792133122Sgibbs		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
79397883Sgibbs
79497883Sgibbs	memset(&temp, 0, sizeof(temp));
79597883Sgibbs
79697883Sgibbs	fsep = xfer->pdev->privBeData;
79797883Sgibbs	fsep += xfer->trIndex;
798109588Sgibbs
799109588Sgibbs	temp.fs_open.max_bufsize = MaxBufSize;
80097883Sgibbs	temp.fs_open.max_frames = MaxFrameCount;
80197883Sgibbs	temp.fs_open.ep_index = xfer->trIndex;
802109588Sgibbs	temp.fs_open.ep_no = ep_no;
803109588Sgibbs
804109588Sgibbs	if (stream_id != 0) {
805109588Sgibbs		temp.fs_open_stream.stream_id = stream_id;
806109588Sgibbs
80797883Sgibbs		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream))
80897883Sgibbs			return (LIBUSB20_ERROR_INVALID_PARAM);
80997883Sgibbs	} else {
81097883Sgibbs		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open))
811109588Sgibbs			return (LIBUSB20_ERROR_INVALID_PARAM);
812109588Sgibbs	}
813109588Sgibbs	/* maximums might have changed - update */
814109588Sgibbs	xfer->maxFrames = temp.fs_open.max_frames;
81597883Sgibbs
81697883Sgibbs	/* "max_bufsize" should be multiple of "max_packet_length" */
81797883Sgibbs	xfer->maxTotalLength = temp.fs_open.max_bufsize;
81897883Sgibbs	xfer->maxPacketLen = temp.fs_open.max_packet_length;
819104023Sgibbs
820104023Sgibbs	/* setup buffer and length lists using zero copy */
821123579Sgibbs	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
822123579Sgibbs	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
823123579Sgibbs
824123579Sgibbs	return (0);			/* success */
825123579Sgibbs}
826104023Sgibbs
827123579Sgibbsstatic int
828104023Sgibbsugen20_tr_close(struct libusb20_transfer *xfer)
829104023Sgibbs{
830104023Sgibbs	struct usb_fs_close temp;
83197883Sgibbs
83297883Sgibbs	memset(&temp, 0, sizeof(temp));
833109588Sgibbs
834109588Sgibbs	temp.ep_index = xfer->trIndex;
83597883Sgibbs
83697883Sgibbs	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) {
83797883Sgibbs		return (LIBUSB20_ERROR_INVALID_PARAM);
83897883Sgibbs	}
83997883Sgibbs	return (0);			/* success */
84097883Sgibbs}
84197883Sgibbs
84297883Sgibbsstatic int
84397883Sgibbsugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
84497883Sgibbs{
84597883Sgibbs	struct usb_fs_clear_stall_sync temp;
84697883Sgibbs
84797883Sgibbs	memset(&temp, 0, sizeof(temp));
84897883Sgibbs
84997883Sgibbs	/* if the transfer is active, an error will be returned */
85097883Sgibbs
85197883Sgibbs	temp.ep_index = xfer->trIndex;
85297883Sgibbs
85397883Sgibbs	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) {
85497883Sgibbs		return (LIBUSB20_ERROR_INVALID_PARAM);
85597883Sgibbs	}
85697883Sgibbs	return (0);			/* success */
85797883Sgibbs}
85897883Sgibbs
85997883Sgibbsstatic void
86097883Sgibbsugen20_tr_submit(struct libusb20_transfer *xfer)
861107441Sscottl{
86297883Sgibbs	struct usb_fs_start temp;
86397883Sgibbs	struct usb_fs_endpoint *fsep;
86497883Sgibbs
86597883Sgibbs	memset(&temp, 0, sizeof(temp));
86697883Sgibbs
86797883Sgibbs	fsep = xfer->pdev->privBeData;
868107441Sscottl	fsep += xfer->trIndex;
869107441Sscottl
870107441Sscottl	fsep->nFrames = xfer->nFrames;
87197883Sgibbs	fsep->flags = 0;
87297883Sgibbs	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
87397883Sgibbs		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
87497883Sgibbs	}
87597883Sgibbs	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
87697883Sgibbs		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
87797883Sgibbs	}
878107441Sscottl	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
879107441Sscottl		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
88097883Sgibbs	}
881107441Sscottl	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
88297883Sgibbs		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
883104023Sgibbs	}
88497883Sgibbs	/* NOTE: The "fsep->timeout" variable is 16-bit. */
885107441Sscottl	if (xfer->timeout > 65535)
886107441Sscottl		fsep->timeout = 65535;
88797883Sgibbs	else
88897883Sgibbs		fsep->timeout = xfer->timeout;
88997883Sgibbs
890111653Sgibbs	temp.ep_index = xfer->trIndex;
89197883Sgibbs
89297883Sgibbs	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) {
893107441Sscottl		/* ignore any errors - should never happen */
89497883Sgibbs	}
89597883Sgibbs	return;				/* success */
89697883Sgibbs}
89797883Sgibbs
898107441Sscottlstatic void
899107441Sscottlugen20_tr_cancel_async(struct libusb20_transfer *xfer)
900123579Sgibbs{
901123579Sgibbs	struct usb_fs_stop temp;
902123579Sgibbs
903123579Sgibbs	memset(&temp, 0, sizeof(temp));
904123579Sgibbs
905123579Sgibbs	temp.ep_index = xfer->trIndex;
906123579Sgibbs
907123579Sgibbs	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) {
908123579Sgibbs		/* ignore any errors - should never happen */
909123579Sgibbs	}
910123579Sgibbs	return;
911123579Sgibbs}
912123579Sgibbs
913123579Sgibbsstatic int
914123579Sgibbsugen20_be_ioctl(uint32_t cmd, void *data)
915123579Sgibbs{
916123579Sgibbs	int f;
917123579Sgibbs	int error;
918123579Sgibbs
919123579Sgibbs	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
920123579Sgibbs	if (f < 0)
921123579Sgibbs		return (LIBUSB20_ERROR_OTHER);
922123579Sgibbs	error = ioctl(f, cmd, data);
923123579Sgibbs	if (error == -1) {
924123579Sgibbs		if (errno == EPERM) {
92597883Sgibbs			error = LIBUSB20_ERROR_ACCESS;
92697883Sgibbs		} else {
92797883Sgibbs			error = LIBUSB20_ERROR_OTHER;
928107441Sscottl		}
92997883Sgibbs	}
93097883Sgibbs	close(f);
931123579Sgibbs	return (error);
93297883Sgibbs}
93397883Sgibbs
93497883Sgibbsstatic int
93597883Sgibbsugen20_dev_get_iface_desc(struct libusb20_device *pdev,
93697883Sgibbs    uint8_t iface_index, char *buf, uint8_t len)
93797883Sgibbs{
93897883Sgibbs	struct usb_gen_descriptor ugd;
93997883Sgibbs
94097883Sgibbs	memset(&ugd, 0, sizeof(ugd));
94197883Sgibbs
94297883Sgibbs	ugd.ugd_data = libusb20_pass_ptr(buf);
943107441Sscottl	ugd.ugd_maxlen = len;
94497883Sgibbs	ugd.ugd_iface_index = iface_index;
94597883Sgibbs
94697883Sgibbs	if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) {
94797883Sgibbs		return (LIBUSB20_ERROR_INVALID_PARAM);
94897883Sgibbs	}
94997883Sgibbs	return (0);
95097883Sgibbs}
95197883Sgibbs
95297883Sgibbsstatic int
95397883Sgibbsugen20_dev_get_info(struct libusb20_device *pdev,
95497883Sgibbs    struct usb_device_info *pinfo)
95597883Sgibbs{
95697883Sgibbs	if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) {
95797883Sgibbs		return (LIBUSB20_ERROR_INVALID_PARAM);
95897883Sgibbs	}
95997883Sgibbs	return (0);
96097883Sgibbs}
96197883Sgibbs
96297883Sgibbsstatic int
963299375Spfgugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
96497883Sgibbs    uint16_t quirk_index, struct libusb20_quirk *pq)
96597883Sgibbs{
96697883Sgibbs	struct usb_gen_quirk q;
96797883Sgibbs	int error;
96897883Sgibbs
96997883Sgibbs	memset(&q, 0, sizeof(q));
97097883Sgibbs
97197883Sgibbs	q.index = quirk_index;
97297883Sgibbs
97397883Sgibbs	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q);
97497883Sgibbs
97597883Sgibbs	if (error) {
97697883Sgibbs		if (errno == EINVAL) {
97797883Sgibbs			return (LIBUSB20_ERROR_NOT_FOUND);
97897883Sgibbs		}
97997883Sgibbs	} else {
98097883Sgibbs		pq->vid = q.vid;
98197883Sgibbs		pq->pid = q.pid;
98297883Sgibbs		pq->bcdDeviceLow = q.bcdDeviceLow;
98397883Sgibbs		pq->bcdDeviceHigh = q.bcdDeviceHigh;
98497883Sgibbs		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
98597883Sgibbs	}
98697883Sgibbs	return (error);
98797883Sgibbs}
98897883Sgibbs
98997883Sgibbsstatic int
99097883Sgibbsugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
99197883Sgibbs    struct libusb20_quirk *pq)
99297883Sgibbs{
99397883Sgibbs	struct usb_gen_quirk q;
99497883Sgibbs	int error;
99597883Sgibbs
996102682Sgibbs	memset(&q, 0, sizeof(q));
99797883Sgibbs
99897883Sgibbs	q.index = quirk_index;
99997883Sgibbs
100097883Sgibbs	error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q);
100197883Sgibbs
100297883Sgibbs	if (error) {
100397883Sgibbs		if (errno == EINVAL) {
100497883Sgibbs			return (LIBUSB20_ERROR_NOT_FOUND);
100597883Sgibbs		}
100697883Sgibbs	} else {
100797883Sgibbs		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
100897883Sgibbs	}
100997883Sgibbs	return (error);
101097883Sgibbs}
101197883Sgibbs
101297883Sgibbsstatic int
101397883Sgibbsugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
101497883Sgibbs    struct libusb20_quirk *pq)
101597883Sgibbs{
101697883Sgibbs	struct usb_gen_quirk q;
101797883Sgibbs	int error;
101897883Sgibbs
101997883Sgibbs	memset(&q, 0, sizeof(q));
102097883Sgibbs
102197883Sgibbs	q.vid = pq->vid;
102297883Sgibbs	q.pid = pq->pid;
102397883Sgibbs	q.bcdDeviceLow = pq->bcdDeviceLow;
102497883Sgibbs	q.bcdDeviceHigh = pq->bcdDeviceHigh;
102597883Sgibbs	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
102697883Sgibbs
102797883Sgibbs	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q);
102897883Sgibbs	if (error) {
102997883Sgibbs		if (errno == ENOMEM) {
103097883Sgibbs			return (LIBUSB20_ERROR_NO_MEM);
103197883Sgibbs		}
103297883Sgibbs	}
103397883Sgibbs	return (error);
103497883Sgibbs}
103597883Sgibbs
103697883Sgibbsstatic int
103797883Sgibbsugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
103897883Sgibbs    struct libusb20_quirk *pq)
103997883Sgibbs{
104097883Sgibbs	struct usb_gen_quirk q;
104197883Sgibbs	int error;
104297883Sgibbs
104397883Sgibbs	memset(&q, 0, sizeof(q));
104497883Sgibbs
104597883Sgibbs	q.vid = pq->vid;
104697883Sgibbs	q.pid = pq->pid;
104797883Sgibbs	q.bcdDeviceLow = pq->bcdDeviceLow;
1048107441Sscottl	q.bcdDeviceHigh = pq->bcdDeviceHigh;
104997883Sgibbs	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
105097883Sgibbs
105197883Sgibbs	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q);
105297883Sgibbs	if (error) {
105397883Sgibbs		if (errno == EINVAL) {
105497883Sgibbs			return (LIBUSB20_ERROR_NOT_FOUND);
105597883Sgibbs		}
105697883Sgibbs	}
105797883Sgibbs	return (error);
1058109588Sgibbs}
1059109588Sgibbs
106097883Sgibbsstatic int
1061109588Sgibbsugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1062109588Sgibbs{
1063109588Sgibbs	return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp));
106497883Sgibbs}
106597883Sgibbs
106697883Sgibbsstatic int
106797883Sgibbsugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1068115335Sgibbs{
1069115335Sgibbs	return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp));
107097883Sgibbs}
107197883Sgibbs