libusb20_ugen20.c revision 250201
1184610Salfred/* $FreeBSD: head/lib/libusb/libusb20_ugen20.c 250201 2013-05-03 07:44:58Z hselasky $ */
2184610Salfred/*-
3184610Salfred * Copyright (c) 2008 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 <errno.h>
31203815Swkoszek#include <fcntl.h>
32184610Salfred#include <stdio.h>
33184610Salfred#include <stdlib.h>
34203815Swkoszek#include <string.h>
35184610Salfred#include <unistd.h>
36248236Shselasky#include <time.h>
37248236Shselasky#include <sys/queue.h>
38248236Shselasky#include <sys/types.h>
39248236Shselasky#endif
40184610Salfred
41248236Shselasky#include <dev/usb/usb.h>
42248236Shselasky#include <dev/usb/usbdi.h>
43248236Shselasky#include <dev/usb/usb_ioctl.h>
44248236Shselasky
45184610Salfred#include "libusb20.h"
46184610Salfred#include "libusb20_desc.h"
47184610Salfred#include "libusb20_int.h"
48184610Salfred
49184610Salfredstatic libusb20_init_backend_t ugen20_init_backend;
50184610Salfredstatic libusb20_open_device_t ugen20_open_device;
51184610Salfredstatic libusb20_close_device_t ugen20_close_device;
52184610Salfredstatic libusb20_get_backend_name_t ugen20_get_backend_name;
53184610Salfredstatic libusb20_exit_backend_t ugen20_exit_backend;
54188622Sthompsastatic libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc;
55188622Sthompsastatic libusb20_dev_get_info_t ugen20_dev_get_info;
56184610Salfredstatic libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk;
57184610Salfredstatic libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name;
58184610Salfredstatic libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk;
59184610Salfredstatic libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk;
60188987Sthompsastatic libusb20_root_set_template_t ugen20_root_set_template;
61188987Sthompsastatic libusb20_root_get_template_t ugen20_root_get_template;
62184610Salfred
63184610Salfredconst struct libusb20_backend_methods libusb20_ugen20_backend = {
64184610Salfred	LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20)
65184610Salfred};
66184610Salfred
67184610Salfred/* USB device specific */
68184610Salfredstatic libusb20_get_config_desc_full_t ugen20_get_config_desc_full;
69184610Salfredstatic libusb20_get_config_index_t ugen20_get_config_index;
70184610Salfredstatic libusb20_set_config_index_t ugen20_set_config_index;
71184610Salfredstatic libusb20_set_alt_index_t ugen20_set_alt_index;
72184610Salfredstatic libusb20_reset_device_t ugen20_reset_device;
73203147Sthompsastatic libusb20_check_connected_t ugen20_check_connected;
74184610Salfredstatic libusb20_set_power_mode_t ugen20_set_power_mode;
75184610Salfredstatic libusb20_get_power_mode_t ugen20_get_power_mode;
76250201Shselaskystatic libusb20_get_port_path_t ugen20_get_port_path;
77246789Shselaskystatic libusb20_get_power_usage_t ugen20_get_power_usage;
78184610Salfredstatic libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
79184610Salfredstatic libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
80184610Salfredstatic libusb20_do_request_sync_t ugen20_do_request_sync;
81184610Salfredstatic libusb20_process_t ugen20_process;
82184610Salfred
83184610Salfred/* USB transfer specific */
84184610Salfredstatic libusb20_tr_open_t ugen20_tr_open;
85184610Salfredstatic libusb20_tr_close_t ugen20_tr_close;
86184610Salfredstatic libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
87184610Salfredstatic libusb20_tr_submit_t ugen20_tr_submit;
88184610Salfredstatic libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
89184610Salfred
90184610Salfredstatic const struct libusb20_device_methods libusb20_ugen20_device_methods = {
91184610Salfred	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
92184610Salfred};
93184610Salfred
94184610Salfredstatic const char *
95184610Salfredugen20_get_backend_name(void)
96184610Salfred{
97184610Salfred	return ("FreeBSD UGEN 2.0");
98184610Salfred}
99184610Salfred
100184610Salfredstatic uint32_t
101184610Salfredugen20_path_convert_one(const char **pp)
102184610Salfred{
103184610Salfred	const char *ptr;
104184610Salfred	uint32_t temp = 0;
105184610Salfred
106184610Salfred	ptr = *pp;
107184610Salfred
108184610Salfred	while ((*ptr >= '0') && (*ptr <= '9')) {
109184610Salfred		temp *= 10;
110184610Salfred		temp += (*ptr - '0');
111184610Salfred		if (temp >= 1000000) {
112184610Salfred			/* catch overflow early */
113234491Shselasky			return (0xFFFFFFFF);
114184610Salfred		}
115184610Salfred		ptr++;
116184610Salfred	}
117184610Salfred
118184610Salfred	if (*ptr == '.') {
119184610Salfred		/* skip dot */
120184610Salfred		ptr++;
121184610Salfred	}
122184610Salfred	*pp = ptr;
123184610Salfred
124184610Salfred	return (temp);
125184610Salfred}
126184610Salfred
127184610Salfredstatic int
128184610Salfredugen20_enumerate(struct libusb20_device *pdev, const char *id)
129184610Salfred{
130184610Salfred	const char *tmp = id;
131192984Sthompsa	struct usb_device_descriptor ddesc;
132192984Sthompsa	struct usb_device_info devinfo;
133184610Salfred	uint32_t plugtime;
134184610Salfred	char buf[64];
135184610Salfred	int f;
136184610Salfred	int error;
137184610Salfred
138184610Salfred	pdev->bus_number = ugen20_path_convert_one(&tmp);
139184610Salfred	pdev->device_address = ugen20_path_convert_one(&tmp);
140184610Salfred
141189110Sthompsa	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
142184610Salfred	    pdev->bus_number, pdev->device_address);
143184610Salfred
144184610Salfred	f = open(buf, O_RDWR);
145184610Salfred	if (f < 0) {
146184610Salfred		return (LIBUSB20_ERROR_OTHER);
147184610Salfred	}
148184610Salfred	if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) {
149184610Salfred		error = LIBUSB20_ERROR_OTHER;
150184610Salfred		goto done;
151184610Salfred	}
152184610Salfred	/* store when the device was plugged */
153184610Salfred	pdev->session_data.plugtime = plugtime;
154184610Salfred
155184610Salfred	if (ioctl(f, USB_GET_DEVICE_DESC, &ddesc)) {
156184610Salfred		error = LIBUSB20_ERROR_OTHER;
157184610Salfred		goto done;
158184610Salfred	}
159184610Salfred	LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc));
160184610Salfred
161184610Salfred	libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc));
162184610Salfred
163184610Salfred	if (pdev->ddesc.bNumConfigurations == 0) {
164184610Salfred		error = LIBUSB20_ERROR_OTHER;
165184610Salfred		goto done;
166184610Salfred	} else if (pdev->ddesc.bNumConfigurations >= 8) {
167184610Salfred		error = LIBUSB20_ERROR_OTHER;
168184610Salfred		goto done;
169184610Salfred	}
170184610Salfred	if (ioctl(f, USB_GET_DEVICEINFO, &devinfo)) {
171184610Salfred		error = LIBUSB20_ERROR_OTHER;
172184610Salfred		goto done;
173184610Salfred	}
174184610Salfred	switch (devinfo.udi_mode) {
175184610Salfred	case USB_MODE_DEVICE:
176184610Salfred		pdev->usb_mode = LIBUSB20_MODE_DEVICE;
177184610Salfred		break;
178184610Salfred	default:
179184610Salfred		pdev->usb_mode = LIBUSB20_MODE_HOST;
180184610Salfred		break;
181184610Salfred	}
182184610Salfred
183184610Salfred	switch (devinfo.udi_speed) {
184184610Salfred	case USB_SPEED_LOW:
185184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_LOW;
186184610Salfred		break;
187184610Salfred	case USB_SPEED_FULL:
188184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_FULL;
189184610Salfred		break;
190184610Salfred	case USB_SPEED_HIGH:
191184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_HIGH;
192184610Salfred		break;
193184610Salfred	case USB_SPEED_VARIABLE:
194184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_VARIABLE;
195184610Salfred		break;
196184610Salfred	case USB_SPEED_SUPER:
197184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_SUPER;
198184610Salfred		break;
199184610Salfred	default:
200184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN;
201184610Salfred		break;
202184610Salfred	}
203184610Salfred
204223495Shselasky	/* get parent HUB index and port */
205223495Shselasky
206223495Shselasky	pdev->parent_address = devinfo.udi_hubindex;
207223495Shselasky	pdev->parent_port = devinfo.udi_hubport;
208223495Shselasky
209184610Salfred	/* generate a nice description for printout */
210184610Salfred
211184610Salfred	snprintf(pdev->usb_desc, sizeof(pdev->usb_desc),
212189110Sthompsa	    USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number,
213184610Salfred	    pdev->device_address, devinfo.udi_product,
214184610Salfred	    devinfo.udi_vendor, pdev->bus_number);
215184610Salfred
216184610Salfred	error = 0;
217184610Salfreddone:
218184610Salfred	close(f);
219184610Salfred	return (error);
220184610Salfred}
221184610Salfred
222184610Salfredstruct ugen20_urd_state {
223192984Sthompsa	struct usb_read_dir urd;
224184610Salfred	uint32_t nparsed;
225184610Salfred	int	f;
226184610Salfred	uint8_t *ptr;
227184610Salfred	const char *src;
228184610Salfred	const char *dst;
229184610Salfred	uint8_t	buf[256];
230184610Salfred	uint8_t	dummy_zero[1];
231184610Salfred};
232184610Salfred
233184610Salfredstatic int
234184610Salfredugen20_readdir(struct ugen20_urd_state *st)
235184610Salfred{
236184610Salfred	;				/* style fix */
237184610Salfredrepeat:
238184610Salfred	if (st->ptr == NULL) {
239184610Salfred		st->urd.urd_startentry += st->nparsed;
240213852Shselasky		st->urd.urd_data = libusb20_pass_ptr(st->buf);
241184610Salfred		st->urd.urd_maxlen = sizeof(st->buf);
242184610Salfred		st->nparsed = 0;
243184610Salfred
244184610Salfred		if (ioctl(st->f, USB_READ_DIR, &st->urd)) {
245184610Salfred			return (EINVAL);
246184610Salfred		}
247184610Salfred		st->ptr = st->buf;
248184610Salfred	}
249184610Salfred	if (st->ptr[0] == 0) {
250184610Salfred		if (st->nparsed) {
251184610Salfred			st->ptr = NULL;
252184610Salfred			goto repeat;
253184610Salfred		} else {
254184610Salfred			return (ENXIO);
255184610Salfred		}
256184610Salfred	}
257184610Salfred	st->src = (void *)(st->ptr + 1);
258184610Salfred	st->dst = st->src + strlen(st->src) + 1;
259184610Salfred	st->ptr = st->ptr + st->ptr[0];
260184610Salfred	st->nparsed++;
261184610Salfred
262184610Salfred	if ((st->ptr < st->buf) ||
263184610Salfred	    (st->ptr > st->dummy_zero)) {
264184610Salfred		/* invalid entry */
265184610Salfred		return (EINVAL);
266184610Salfred	}
267184610Salfred	return (0);
268184610Salfred}
269184610Salfred
270184610Salfredstatic int
271184610Salfredugen20_init_backend(struct libusb20_backend *pbe)
272184610Salfred{
273184610Salfred	struct ugen20_urd_state state;
274184610Salfred	struct libusb20_device *pdev;
275184610Salfred
276184610Salfred	memset(&state, 0, sizeof(state));
277184610Salfred
278189110Sthompsa	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
279184610Salfred	if (state.f < 0)
280184610Salfred		return (LIBUSB20_ERROR_OTHER);
281184610Salfred
282184610Salfred	while (ugen20_readdir(&state) == 0) {
283184610Salfred
284184610Salfred		if ((state.src[0] != 'u') ||
285184610Salfred		    (state.src[1] != 'g') ||
286184610Salfred		    (state.src[2] != 'e') ||
287184610Salfred		    (state.src[3] != 'n')) {
288184610Salfred			continue;
289184610Salfred		}
290184610Salfred		pdev = libusb20_dev_alloc();
291184610Salfred		if (pdev == NULL) {
292184610Salfred			continue;
293184610Salfred		}
294184610Salfred		if (ugen20_enumerate(pdev, state.src + 4)) {
295184610Salfred			libusb20_dev_free(pdev);
296184610Salfred			continue;
297184610Salfred		}
298184610Salfred		/* put the device on the backend list */
299184610Salfred		libusb20_be_enqueue_device(pbe, pdev);
300184610Salfred	}
301184610Salfred	close(state.f);
302184610Salfred	return (0);			/* success */
303184610Salfred}
304184610Salfred
305185290Salfredstatic void
306185290Salfredugen20_tr_release(struct libusb20_device *pdev)
307185290Salfred{
308192984Sthompsa	struct usb_fs_uninit fs_uninit;
309185290Salfred
310185290Salfred	if (pdev->nTransfer == 0) {
311185290Salfred		return;
312185290Salfred	}
313185290Salfred	/* release all pending USB transfers */
314185290Salfred	if (pdev->privBeData != NULL) {
315185290Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
316185290Salfred		if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) {
317185290Salfred			/* ignore any errors of this kind */
318185290Salfred		}
319185290Salfred	}
320185290Salfred	return;
321185290Salfred}
322185290Salfred
323184610Salfredstatic int
324185087Salfredugen20_tr_renew(struct libusb20_device *pdev)
325185087Salfred{
326192984Sthompsa	struct usb_fs_init fs_init;
327192984Sthompsa	struct usb_fs_endpoint *pfse;
328185087Salfred	int error;
329185087Salfred	uint32_t size;
330185087Salfred	uint16_t nMaxTransfer;
331185087Salfred
332185087Salfred	nMaxTransfer = pdev->nTransfer;
333185087Salfred	error = 0;
334185087Salfred
335185087Salfred	if (nMaxTransfer == 0) {
336185087Salfred		goto done;
337185087Salfred	}
338185087Salfred	size = nMaxTransfer * sizeof(*pfse);
339185087Salfred
340185290Salfred	if (pdev->privBeData == NULL) {
341185087Salfred		pfse = malloc(size);
342185087Salfred		if (pfse == NULL) {
343185087Salfred			error = LIBUSB20_ERROR_NO_MEM;
344185087Salfred			goto done;
345185087Salfred		}
346185087Salfred		pdev->privBeData = pfse;
347185087Salfred	}
348185087Salfred	/* reset endpoint data */
349185087Salfred	memset(pdev->privBeData, 0, size);
350185087Salfred
351185087Salfred	memset(&fs_init, 0, sizeof(fs_init));
352185087Salfred
353213852Shselasky	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
354185087Salfred	fs_init.ep_index_max = nMaxTransfer;
355185087Salfred
356185087Salfred	if (ioctl(pdev->file, USB_FS_INIT, &fs_init)) {
357185087Salfred		error = LIBUSB20_ERROR_OTHER;
358185087Salfred		goto done;
359185087Salfred	}
360185087Salfreddone:
361185087Salfred	return (error);
362185087Salfred}
363185087Salfred
364185087Salfredstatic int
365184610Salfredugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
366184610Salfred{
367184610Salfred	uint32_t plugtime;
368184610Salfred	char buf[64];
369184610Salfred	int f;
370184610Salfred	int g;
371184610Salfred	int error;
372184610Salfred
373189110Sthompsa	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
374184610Salfred	    pdev->bus_number, pdev->device_address);
375184610Salfred
376184610Salfred	/*
377184610Salfred	 * We need two file handles, one for the control endpoint and one
378184610Salfred	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
379184610Salfred	 * kernel locking.
380184610Salfred	 */
381184610Salfred	g = open(buf, O_RDWR);
382184610Salfred	if (g < 0) {
383184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
384184610Salfred	}
385184610Salfred	f = open(buf, O_RDWR);
386184610Salfred	if (f < 0) {
387184610Salfred		close(g);
388184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
389184610Salfred	}
390184610Salfred	if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) {
391184610Salfred		error = LIBUSB20_ERROR_OTHER;
392184610Salfred		goto done;
393184610Salfred	}
394184610Salfred	/* check that the correct device is still plugged */
395184610Salfred	if (pdev->session_data.plugtime != plugtime) {
396184610Salfred		error = LIBUSB20_ERROR_NO_DEVICE;
397184610Salfred		goto done;
398184610Salfred	}
399185087Salfred	/* need to set this before "tr_renew()" */
400185087Salfred	pdev->file = f;
401185087Salfred	pdev->file_ctrl = g;
402184610Salfred
403185087Salfred	/* renew all USB transfers */
404185087Salfred	error = ugen20_tr_renew(pdev);
405185087Salfred	if (error) {
406185087Salfred		goto done;
407184610Salfred	}
408184610Salfred	/* set methods */
409184610Salfred	pdev->methods = &libusb20_ugen20_device_methods;
410185087Salfred
411184610Salfreddone:
412184610Salfred	if (error) {
413185087Salfred		if (pdev->privBeData) {
414185087Salfred			/* cleanup after "tr_renew()" */
415185087Salfred			free(pdev->privBeData);
416185087Salfred			pdev->privBeData = NULL;
417184610Salfred		}
418185087Salfred		pdev->file = -1;
419185087Salfred		pdev->file_ctrl = -1;
420184610Salfred		close(f);
421184610Salfred		close(g);
422184610Salfred	}
423184610Salfred	return (error);
424184610Salfred}
425184610Salfred
426184610Salfredstatic int
427184610Salfredugen20_close_device(struct libusb20_device *pdev)
428184610Salfred{
429192984Sthompsa	struct usb_fs_uninit fs_uninit;
430184610Salfred
431184610Salfred	if (pdev->privBeData) {
432185087Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
433184610Salfred		if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) {
434185290Salfred			/* ignore this error */
435184610Salfred		}
436184610Salfred		free(pdev->privBeData);
437184610Salfred	}
438184610Salfred	pdev->nTransfer = 0;
439184610Salfred	pdev->privBeData = NULL;
440184610Salfred	close(pdev->file);
441184610Salfred	close(pdev->file_ctrl);
442184610Salfred	pdev->file = -1;
443184610Salfred	pdev->file_ctrl = -1;
444185290Salfred	return (0);			/* success */
445184610Salfred}
446184610Salfred
447184610Salfredstatic void
448184610Salfredugen20_exit_backend(struct libusb20_backend *pbe)
449184610Salfred{
450184610Salfred	return;				/* nothing to do */
451184610Salfred}
452184610Salfred
453184610Salfredstatic int
454184610Salfredugen20_get_config_desc_full(struct libusb20_device *pdev,
455185087Salfred    uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
456184610Salfred{
457192984Sthompsa	struct usb_gen_descriptor gen_desc;
458192984Sthompsa	struct usb_config_descriptor cdesc;
459184610Salfred	uint8_t *ptr;
460184610Salfred	uint16_t len;
461184610Salfred	int error;
462184610Salfred
463199055Sthompsa	/* make sure memory is initialised */
464199055Sthompsa	memset(&cdesc, 0, sizeof(cdesc));
465185087Salfred	memset(&gen_desc, 0, sizeof(gen_desc));
466185087Salfred
467213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
468184610Salfred	gen_desc.ugd_maxlen = sizeof(cdesc);
469185087Salfred	gen_desc.ugd_config_index = cfg_index;
470184610Salfred
471184610Salfred	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
472184610Salfred	if (error) {
473184610Salfred		return (LIBUSB20_ERROR_OTHER);
474184610Salfred	}
475184610Salfred	len = UGETW(cdesc.wTotalLength);
476184610Salfred	if (len < sizeof(cdesc)) {
477184610Salfred		/* corrupt descriptor */
478184610Salfred		return (LIBUSB20_ERROR_OTHER);
479184610Salfred	}
480184610Salfred	ptr = malloc(len);
481184610Salfred	if (!ptr) {
482184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
483184610Salfred	}
484199055Sthompsa
485199055Sthompsa	/* make sure memory is initialised */
486199055Sthompsa	memset(ptr, 0, len);
487199055Sthompsa
488213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
489184610Salfred	gen_desc.ugd_maxlen = len;
490184610Salfred
491184610Salfred	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
492184610Salfred	if (error) {
493184610Salfred		free(ptr);
494184610Salfred		return (LIBUSB20_ERROR_OTHER);
495184610Salfred	}
496184610Salfred	/* make sure that the device doesn't fool us */
497184610Salfred	memcpy(ptr, &cdesc, sizeof(cdesc));
498184610Salfred
499184610Salfred	*ppbuf = ptr;
500184610Salfred	*plen = len;
501184610Salfred
502184610Salfred	return (0);			/* success */
503184610Salfred}
504184610Salfred
505184610Salfredstatic int
506184610Salfredugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
507184610Salfred{
508184610Salfred	int temp;
509184610Salfred
510184610Salfred	if (ioctl(pdev->file_ctrl, USB_GET_CONFIG, &temp)) {
511184610Salfred		return (LIBUSB20_ERROR_OTHER);
512184610Salfred	}
513184610Salfred	*pindex = temp;
514184610Salfred
515184610Salfred	return (0);
516184610Salfred}
517184610Salfred
518184610Salfredstatic int
519185087Salfredugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
520184610Salfred{
521185087Salfred	int temp = cfg_index;
522184610Salfred
523185290Salfred	/* release all active USB transfers */
524185290Salfred	ugen20_tr_release(pdev);
525185290Salfred
526184610Salfred	if (ioctl(pdev->file_ctrl, USB_SET_CONFIG, &temp)) {
527184610Salfred		return (LIBUSB20_ERROR_OTHER);
528184610Salfred	}
529185087Salfred	return (ugen20_tr_renew(pdev));
530184610Salfred}
531184610Salfred
532184610Salfredstatic int
533184610Salfredugen20_set_alt_index(struct libusb20_device *pdev,
534184610Salfred    uint8_t iface_index, uint8_t alt_index)
535184610Salfred{
536192984Sthompsa	struct usb_alt_interface alt_iface;
537184610Salfred
538185087Salfred	memset(&alt_iface, 0, sizeof(alt_iface));
539185087Salfred
540184610Salfred	alt_iface.uai_interface_index = iface_index;
541184610Salfred	alt_iface.uai_alt_index = alt_index;
542184610Salfred
543185290Salfred	/* release all active USB transfers */
544185290Salfred	ugen20_tr_release(pdev);
545185290Salfred
546184610Salfred	if (ioctl(pdev->file_ctrl, USB_SET_ALTINTERFACE, &alt_iface)) {
547184610Salfred		return (LIBUSB20_ERROR_OTHER);
548184610Salfred	}
549185087Salfred	return (ugen20_tr_renew(pdev));
550184610Salfred}
551184610Salfred
552184610Salfredstatic int
553184610Salfredugen20_reset_device(struct libusb20_device *pdev)
554184610Salfred{
555184610Salfred	int temp = 0;
556184610Salfred
557185290Salfred	/* release all active USB transfers */
558185290Salfred	ugen20_tr_release(pdev);
559185290Salfred
560184610Salfred	if (ioctl(pdev->file_ctrl, USB_DEVICEENUMERATE, &temp)) {
561184610Salfred		return (LIBUSB20_ERROR_OTHER);
562184610Salfred	}
563185087Salfred	return (ugen20_tr_renew(pdev));
564184610Salfred}
565184610Salfred
566184610Salfredstatic int
567203147Sthompsaugen20_check_connected(struct libusb20_device *pdev)
568203147Sthompsa{
569203147Sthompsa	uint32_t plugtime;
570203147Sthompsa	int error = 0;
571203147Sthompsa
572203147Sthompsa	if (ioctl(pdev->file_ctrl, USB_GET_PLUGTIME, &plugtime)) {
573203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
574203147Sthompsa		goto done;
575203147Sthompsa	}
576203147Sthompsa
577203147Sthompsa	if (pdev->session_data.plugtime != plugtime) {
578203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
579203147Sthompsa		goto done;
580203147Sthompsa	}
581203147Sthompsadone:
582203147Sthompsa	return (error);
583203147Sthompsa}
584203147Sthompsa
585203147Sthompsastatic int
586184610Salfredugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
587184610Salfred{
588184610Salfred	int temp;
589184610Salfred
590184610Salfred	switch (power_mode) {
591184610Salfred	case LIBUSB20_POWER_OFF:
592184610Salfred		temp = USB_POWER_MODE_OFF;
593184610Salfred		break;
594184610Salfred	case LIBUSB20_POWER_ON:
595184610Salfred		temp = USB_POWER_MODE_ON;
596184610Salfred		break;
597184610Salfred	case LIBUSB20_POWER_SAVE:
598184610Salfred		temp = USB_POWER_MODE_SAVE;
599184610Salfred		break;
600184610Salfred	case LIBUSB20_POWER_SUSPEND:
601184610Salfred		temp = USB_POWER_MODE_SUSPEND;
602184610Salfred		break;
603184610Salfred	case LIBUSB20_POWER_RESUME:
604184610Salfred		temp = USB_POWER_MODE_RESUME;
605184610Salfred		break;
606184610Salfred	default:
607184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
608184610Salfred	}
609184610Salfred	if (ioctl(pdev->file_ctrl, USB_SET_POWER_MODE, &temp)) {
610184610Salfred		return (LIBUSB20_ERROR_OTHER);
611184610Salfred	}
612184610Salfred	return (0);
613184610Salfred}
614184610Salfred
615184610Salfredstatic int
616184610Salfredugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
617184610Salfred{
618184610Salfred	int temp;
619184610Salfred
620184610Salfred	if (ioctl(pdev->file_ctrl, USB_GET_POWER_MODE, &temp)) {
621184610Salfred		return (LIBUSB20_ERROR_OTHER);
622184610Salfred	}
623184610Salfred	switch (temp) {
624184610Salfred	case USB_POWER_MODE_OFF:
625184610Salfred		temp = LIBUSB20_POWER_OFF;
626184610Salfred		break;
627184610Salfred	case USB_POWER_MODE_ON:
628184610Salfred		temp = LIBUSB20_POWER_ON;
629184610Salfred		break;
630184610Salfred	case USB_POWER_MODE_SAVE:
631184610Salfred		temp = LIBUSB20_POWER_SAVE;
632184610Salfred		break;
633184610Salfred	case USB_POWER_MODE_SUSPEND:
634184610Salfred		temp = LIBUSB20_POWER_SUSPEND;
635184610Salfred		break;
636184610Salfred	case USB_POWER_MODE_RESUME:
637184610Salfred		temp = LIBUSB20_POWER_RESUME;
638184610Salfred		break;
639184610Salfred	default:
640184610Salfred		temp = LIBUSB20_POWER_ON;
641184610Salfred		break;
642184610Salfred	}
643184610Salfred	*power_mode = temp;
644184610Salfred	return (0);			/* success */
645184610Salfred}
646184610Salfred
647184610Salfredstatic int
648250201Shselaskyugen20_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
649250201Shselasky{
650250201Shselasky	struct usb_device_port_path udpp;
651250201Shselasky
652250201Shselasky	if (ioctl(pdev->file_ctrl, USB_GET_DEV_PORT_PATH, &udpp))
653250201Shselasky		return (LIBUSB20_ERROR_OTHER);
654250201Shselasky
655250201Shselasky	if (udpp.udp_port_level > bufsize)
656250201Shselasky		return (LIBUSB20_ERROR_OVERFLOW);
657250201Shselasky
658250201Shselasky	memcpy(buf, udpp.udp_port_no, udpp.udp_port_level);
659250201Shselasky
660250201Shselasky	return (udpp.udp_port_level);	/* success */
661250201Shselasky}
662250201Shselasky
663250201Shselaskystatic int
664246789Shselaskyugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
665246789Shselasky{
666246789Shselasky	int temp;
667246789Shselasky
668246789Shselasky	if (ioctl(pdev->file_ctrl, USB_GET_POWER_USAGE, &temp)) {
669246789Shselasky		return (LIBUSB20_ERROR_OTHER);
670246789Shselasky	}
671246789Shselasky	*power_usage = temp;
672246789Shselasky	return (0);			/* success */
673246789Shselasky}
674246789Shselasky
675246789Shselaskystatic int
676184610Salfredugen20_kernel_driver_active(struct libusb20_device *pdev,
677184610Salfred    uint8_t iface_index)
678184610Salfred{
679184610Salfred	int temp = iface_index;
680184610Salfred
681184610Salfred	if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_ACTIVE, &temp)) {
682184610Salfred		return (LIBUSB20_ERROR_OTHER);
683184610Salfred	}
684184610Salfred	return (0);			/* kernel driver is active */
685184610Salfred}
686184610Salfred
687184610Salfredstatic int
688184610Salfredugen20_detach_kernel_driver(struct libusb20_device *pdev,
689184610Salfred    uint8_t iface_index)
690184610Salfred{
691184610Salfred	int temp = iface_index;
692184610Salfred
693184610Salfred	if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_DETACH, &temp)) {
694184610Salfred		return (LIBUSB20_ERROR_OTHER);
695184610Salfred	}
696184610Salfred	return (0);			/* kernel driver is active */
697184610Salfred}
698184610Salfred
699184610Salfredstatic int
700184610Salfredugen20_do_request_sync(struct libusb20_device *pdev,
701184610Salfred    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
702184610Salfred    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
703184610Salfred{
704192984Sthompsa	struct usb_ctl_request req;
705184610Salfred
706185087Salfred	memset(&req, 0, sizeof(req));
707185087Salfred
708213852Shselasky	req.ucr_data = libusb20_pass_ptr(data);
709184610Salfred	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
710184610Salfred		req.ucr_flags |= USB_SHORT_XFER_OK;
711184610Salfred	}
712184610Salfred	if (libusb20_me_encode(&req.ucr_request,
713184610Salfred	    sizeof(req.ucr_request), setup)) {
714184610Salfred		/* ignore */
715184610Salfred	}
716184610Salfred	if (ioctl(pdev->file_ctrl, USB_DO_REQUEST, &req)) {
717184610Salfred		return (LIBUSB20_ERROR_OTHER);
718184610Salfred	}
719184610Salfred	if (pactlen) {
720184610Salfred		/* get actual length */
721184610Salfred		*pactlen = req.ucr_actlen;
722184610Salfred	}
723184610Salfred	return (0);			/* kernel driver is active */
724184610Salfred}
725184610Salfred
726184610Salfredstatic int
727184610Salfredugen20_process(struct libusb20_device *pdev)
728184610Salfred{
729192984Sthompsa	struct usb_fs_complete temp;
730192984Sthompsa	struct usb_fs_endpoint *fsep;
731184610Salfred	struct libusb20_transfer *xfer;
732184610Salfred
733184610Salfred	while (1) {
734184610Salfred
735184610Salfred		if (ioctl(pdev->file, USB_FS_COMPLETE, &temp)) {
736184610Salfred			if (errno == EBUSY) {
737184610Salfred				break;
738184610Salfred			} else {
739184610Salfred				/* device detached */
740184610Salfred				return (LIBUSB20_ERROR_OTHER);
741184610Salfred			}
742184610Salfred		}
743184610Salfred		fsep = pdev->privBeData;
744184610Salfred		xfer = pdev->pTransfer;
745184610Salfred		fsep += temp.ep_index;
746184610Salfred		xfer += temp.ep_index;
747184610Salfred
748184610Salfred		/* update transfer status */
749184610Salfred
750184610Salfred		if (fsep->status == 0) {
751184610Salfred			xfer->aFrames = fsep->aFrames;
752184610Salfred			xfer->timeComplete = fsep->isoc_time_complete;
753184610Salfred			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
754184610Salfred		} else if (fsep->status == USB_ERR_CANCELLED) {
755184610Salfred			xfer->aFrames = 0;
756184610Salfred			xfer->timeComplete = 0;
757184610Salfred			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
758184610Salfred		} else if (fsep->status == USB_ERR_STALLED) {
759184610Salfred			xfer->aFrames = 0;
760184610Salfred			xfer->timeComplete = 0;
761184610Salfred			xfer->status = LIBUSB20_TRANSFER_STALL;
762184610Salfred		} else if (fsep->status == USB_ERR_TIMEOUT) {
763184610Salfred			xfer->aFrames = 0;
764184610Salfred			xfer->timeComplete = 0;
765184610Salfred			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
766184610Salfred		} else {
767184610Salfred			xfer->aFrames = 0;
768184610Salfred			xfer->timeComplete = 0;
769184610Salfred			xfer->status = LIBUSB20_TRANSFER_ERROR;
770184610Salfred		}
771184610Salfred		libusb20_tr_callback_wrapper(xfer);
772184610Salfred	}
773184610Salfred	return (0);			/* done */
774184610Salfred}
775184610Salfred
776184610Salfredstatic int
777184610Salfredugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
778239239Shselasky    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
779239239Shselasky    uint8_t pre_scale)
780184610Salfred{
781239239Shselasky	union {
782239239Shselasky		struct usb_fs_open fs_open;
783239239Shselasky		struct usb_fs_open_stream fs_open_stream;
784239239Shselasky	} temp;
785192984Sthompsa	struct usb_fs_endpoint *fsep;
786184610Salfred
787219100Shselasky	if (pre_scale)
788219100Shselasky		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
789219100Shselasky
790185087Salfred	memset(&temp, 0, sizeof(temp));
791185087Salfred
792184610Salfred	fsep = xfer->pdev->privBeData;
793184610Salfred	fsep += xfer->trIndex;
794184610Salfred
795239239Shselasky	temp.fs_open.max_bufsize = MaxBufSize;
796239239Shselasky	temp.fs_open.max_frames = MaxFrameCount;
797239239Shselasky	temp.fs_open.ep_index = xfer->trIndex;
798239239Shselasky	temp.fs_open.ep_no = ep_no;
799184610Salfred
800239239Shselasky	if (stream_id != 0) {
801239239Shselasky		temp.fs_open_stream.stream_id = stream_id;
802239239Shselasky
803239239Shselasky		if (ioctl(xfer->pdev->file, USB_FS_OPEN_STREAM, &temp.fs_open_stream))
804239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
805239239Shselasky	} else {
806239239Shselasky		if (ioctl(xfer->pdev->file, USB_FS_OPEN, &temp.fs_open))
807239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
808184610Salfred	}
809184610Salfred	/* maximums might have changed - update */
810239239Shselasky	xfer->maxFrames = temp.fs_open.max_frames;
811184610Salfred
812184610Salfred	/* "max_bufsize" should be multiple of "max_packet_length" */
813239239Shselasky	xfer->maxTotalLength = temp.fs_open.max_bufsize;
814239239Shselasky	xfer->maxPacketLen = temp.fs_open.max_packet_length;
815184610Salfred
816213852Shselasky	/* setup buffer and length lists using zero copy */
817213852Shselasky	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
818213852Shselasky	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
819184610Salfred
820184610Salfred	return (0);			/* success */
821184610Salfred}
822184610Salfred
823184610Salfredstatic int
824184610Salfredugen20_tr_close(struct libusb20_transfer *xfer)
825184610Salfred{
826192984Sthompsa	struct usb_fs_close temp;
827184610Salfred
828185087Salfred	memset(&temp, 0, sizeof(temp));
829185087Salfred
830184610Salfred	temp.ep_index = xfer->trIndex;
831184610Salfred
832184610Salfred	if (ioctl(xfer->pdev->file, USB_FS_CLOSE, &temp)) {
833184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
834184610Salfred	}
835184610Salfred	return (0);			/* success */
836184610Salfred}
837184610Salfred
838184610Salfredstatic int
839184610Salfredugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
840184610Salfred{
841192984Sthompsa	struct usb_fs_clear_stall_sync temp;
842184610Salfred
843185087Salfred	memset(&temp, 0, sizeof(temp));
844185087Salfred
845184610Salfred	/* if the transfer is active, an error will be returned */
846184610Salfred
847184610Salfred	temp.ep_index = xfer->trIndex;
848184610Salfred
849184610Salfred	if (ioctl(xfer->pdev->file, USB_FS_CLEAR_STALL_SYNC, &temp)) {
850184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
851184610Salfred	}
852184610Salfred	return (0);			/* success */
853184610Salfred}
854184610Salfred
855184610Salfredstatic void
856184610Salfredugen20_tr_submit(struct libusb20_transfer *xfer)
857184610Salfred{
858192984Sthompsa	struct usb_fs_start temp;
859192984Sthompsa	struct usb_fs_endpoint *fsep;
860184610Salfred
861185087Salfred	memset(&temp, 0, sizeof(temp));
862185087Salfred
863184610Salfred	fsep = xfer->pdev->privBeData;
864184610Salfred	fsep += xfer->trIndex;
865184610Salfred
866184610Salfred	fsep->nFrames = xfer->nFrames;
867184610Salfred	fsep->flags = 0;
868184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
869184610Salfred		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
870184610Salfred	}
871184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
872184610Salfred		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
873184610Salfred	}
874184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
875184610Salfred		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
876184610Salfred	}
877184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
878184610Salfred		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
879184610Salfred	}
880198376Sthompsa	/* NOTE: The "fsep->timeout" variable is 16-bit. */
881198376Sthompsa	if (xfer->timeout > 65535)
882198376Sthompsa		fsep->timeout = 65535;
883198376Sthompsa	else
884198376Sthompsa		fsep->timeout = xfer->timeout;
885184610Salfred
886184610Salfred	temp.ep_index = xfer->trIndex;
887184610Salfred
888184610Salfred	if (ioctl(xfer->pdev->file, USB_FS_START, &temp)) {
889184610Salfred		/* ignore any errors - should never happen */
890184610Salfred	}
891184610Salfred	return;				/* success */
892184610Salfred}
893184610Salfred
894184610Salfredstatic void
895184610Salfredugen20_tr_cancel_async(struct libusb20_transfer *xfer)
896184610Salfred{
897192984Sthompsa	struct usb_fs_stop temp;
898184610Salfred
899185087Salfred	memset(&temp, 0, sizeof(temp));
900185087Salfred
901184610Salfred	temp.ep_index = xfer->trIndex;
902184610Salfred
903184610Salfred	if (ioctl(xfer->pdev->file, USB_FS_STOP, &temp)) {
904184610Salfred		/* ignore any errors - should never happen */
905184610Salfred	}
906184610Salfred	return;
907184610Salfred}
908184610Salfred
909184610Salfredstatic int
910184610Salfredugen20_be_ioctl(uint32_t cmd, void *data)
911184610Salfred{
912184610Salfred	int f;
913185087Salfred	int error;
914184610Salfred
915189110Sthompsa	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
916184610Salfred	if (f < 0)
917184610Salfred		return (LIBUSB20_ERROR_OTHER);
918185087Salfred	error = ioctl(f, cmd, data);
919185087Salfred	if (error == -1) {
920184610Salfred		if (errno == EPERM) {
921185087Salfred			error = LIBUSB20_ERROR_ACCESS;
922184610Salfred		} else {
923185087Salfred			error = LIBUSB20_ERROR_OTHER;
924184610Salfred		}
925184610Salfred	}
926184610Salfred	close(f);
927185087Salfred	return (error);
928184610Salfred}
929184610Salfred
930184610Salfredstatic int
931188622Sthompsaugen20_dev_get_iface_desc(struct libusb20_device *pdev,
932188622Sthompsa    uint8_t iface_index, char *buf, uint8_t len)
933188622Sthompsa{
934192984Sthompsa	struct usb_gen_descriptor ugd;
935188622Sthompsa
936188622Sthompsa	memset(&ugd, 0, sizeof(ugd));
937188622Sthompsa
938213852Shselasky	ugd.ugd_data = libusb20_pass_ptr(buf);
939188622Sthompsa	ugd.ugd_maxlen = len;
940188622Sthompsa	ugd.ugd_iface_index = iface_index;
941188622Sthompsa
942188622Sthompsa	if (ioctl(pdev->file, USB_GET_IFACE_DRIVER, &ugd)) {
943188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
944188622Sthompsa	}
945188622Sthompsa	return (0);
946188622Sthompsa}
947188622Sthompsa
948188622Sthompsastatic int
949188622Sthompsaugen20_dev_get_info(struct libusb20_device *pdev,
950192984Sthompsa    struct usb_device_info *pinfo)
951188622Sthompsa{
952188622Sthompsa	if (ioctl(pdev->file, USB_GET_DEVICEINFO, pinfo)) {
953188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
954188622Sthompsa	}
955188622Sthompsa	return (0);
956188622Sthompsa}
957188622Sthompsa
958188622Sthompsastatic int
959184610Salfredugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
960185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
961184610Salfred{
962192984Sthompsa	struct usb_gen_quirk q;
963185087Salfred	int error;
964184610Salfred
965184610Salfred	memset(&q, 0, sizeof(q));
966184610Salfred
967185087Salfred	q.index = quirk_index;
968184610Salfred
969185087Salfred	error = ugen20_be_ioctl(USB_DEV_QUIRK_GET, &q);
970184610Salfred
971185087Salfred	if (error) {
972184610Salfred		if (errno == EINVAL) {
973184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
974184610Salfred		}
975184610Salfred	} else {
976184610Salfred		pq->vid = q.vid;
977184610Salfred		pq->pid = q.pid;
978184610Salfred		pq->bcdDeviceLow = q.bcdDeviceLow;
979184610Salfred		pq->bcdDeviceHigh = q.bcdDeviceHigh;
980184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
981184610Salfred	}
982185087Salfred	return (error);
983184610Salfred}
984184610Salfred
985184610Salfredstatic int
986185087Salfredugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
987184610Salfred    struct libusb20_quirk *pq)
988184610Salfred{
989192984Sthompsa	struct usb_gen_quirk q;
990185087Salfred	int error;
991184610Salfred
992184610Salfred	memset(&q, 0, sizeof(q));
993184610Salfred
994185087Salfred	q.index = quirk_index;
995184610Salfred
996185087Salfred	error = ugen20_be_ioctl(USB_QUIRK_NAME_GET, &q);
997184610Salfred
998185087Salfred	if (error) {
999184610Salfred		if (errno == EINVAL) {
1000184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1001184610Salfred		}
1002184610Salfred	} else {
1003184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1004184610Salfred	}
1005185087Salfred	return (error);
1006184610Salfred}
1007184610Salfred
1008184610Salfredstatic int
1009184610Salfredugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
1010184610Salfred    struct libusb20_quirk *pq)
1011184610Salfred{
1012192984Sthompsa	struct usb_gen_quirk q;
1013185087Salfred	int error;
1014184610Salfred
1015184610Salfred	memset(&q, 0, sizeof(q));
1016184610Salfred
1017184610Salfred	q.vid = pq->vid;
1018184610Salfred	q.pid = pq->pid;
1019184610Salfred	q.bcdDeviceLow = pq->bcdDeviceLow;
1020184610Salfred	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1021184610Salfred	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1022184610Salfred
1023185087Salfred	error = ugen20_be_ioctl(USB_DEV_QUIRK_ADD, &q);
1024185087Salfred	if (error) {
1025184610Salfred		if (errno == ENOMEM) {
1026184610Salfred			return (LIBUSB20_ERROR_NO_MEM);
1027184610Salfred		}
1028184610Salfred	}
1029185087Salfred	return (error);
1030184610Salfred}
1031184610Salfred
1032184610Salfredstatic int
1033184610Salfredugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
1034184610Salfred    struct libusb20_quirk *pq)
1035184610Salfred{
1036192984Sthompsa	struct usb_gen_quirk q;
1037185087Salfred	int error;
1038184610Salfred
1039184610Salfred	memset(&q, 0, sizeof(q));
1040184610Salfred
1041184610Salfred	q.vid = pq->vid;
1042184610Salfred	q.pid = pq->pid;
1043184610Salfred	q.bcdDeviceLow = pq->bcdDeviceLow;
1044184610Salfred	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1045184610Salfred	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1046184610Salfred
1047185087Salfred	error = ugen20_be_ioctl(USB_DEV_QUIRK_REMOVE, &q);
1048185087Salfred	if (error) {
1049184610Salfred		if (errno == EINVAL) {
1050184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1051184610Salfred		}
1052184610Salfred	}
1053185087Salfred	return (error);
1054184610Salfred}
1055184610Salfred
1056184610Salfredstatic int
1057188987Sthompsaugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1058188987Sthompsa{
1059188987Sthompsa	return (ugen20_be_ioctl(USB_SET_TEMPLATE, &temp));
1060188987Sthompsa}
1061188987Sthompsa
1062188987Sthompsastatic int
1063188987Sthompsaugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1064188987Sthompsa{
1065188987Sthompsa	return (ugen20_be_ioctl(USB_GET_TEMPLATE, ptemp));
1066188987Sthompsa}
1067