1184610Salfred/* $FreeBSD: stable/10/lib/libusb/libusb20_ugen20.c 356399 2020-01-06 09:22:33Z 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
49253339Shselasky#ifndef	IOUSB
50253339Shselasky#define IOUSB(a) a
51253339Shselasky#endif
52253339Shselasky
53184610Salfredstatic libusb20_init_backend_t ugen20_init_backend;
54184610Salfredstatic libusb20_open_device_t ugen20_open_device;
55184610Salfredstatic libusb20_close_device_t ugen20_close_device;
56184610Salfredstatic libusb20_get_backend_name_t ugen20_get_backend_name;
57184610Salfredstatic libusb20_exit_backend_t ugen20_exit_backend;
58188622Sthompsastatic libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc;
59188622Sthompsastatic libusb20_dev_get_info_t ugen20_dev_get_info;
60184610Salfredstatic libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk;
61184610Salfredstatic libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name;
62184610Salfredstatic libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk;
63184610Salfredstatic libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk;
64188987Sthompsastatic libusb20_root_set_template_t ugen20_root_set_template;
65188987Sthompsastatic libusb20_root_get_template_t ugen20_root_get_template;
66184610Salfred
67184610Salfredconst struct libusb20_backend_methods libusb20_ugen20_backend = {
68184610Salfred	LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20)
69184610Salfred};
70184610Salfred
71184610Salfred/* USB device specific */
72184610Salfredstatic libusb20_get_config_desc_full_t ugen20_get_config_desc_full;
73184610Salfredstatic libusb20_get_config_index_t ugen20_get_config_index;
74184610Salfredstatic libusb20_set_config_index_t ugen20_set_config_index;
75184610Salfredstatic libusb20_set_alt_index_t ugen20_set_alt_index;
76184610Salfredstatic libusb20_reset_device_t ugen20_reset_device;
77203147Sthompsastatic libusb20_check_connected_t ugen20_check_connected;
78184610Salfredstatic libusb20_set_power_mode_t ugen20_set_power_mode;
79184610Salfredstatic libusb20_get_power_mode_t ugen20_get_power_mode;
80250201Shselaskystatic libusb20_get_port_path_t ugen20_get_port_path;
81246789Shselaskystatic libusb20_get_power_usage_t ugen20_get_power_usage;
82356399Shselaskystatic libusb20_get_stats_t ugen20_get_stats;
83184610Salfredstatic libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
84184610Salfredstatic libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
85184610Salfredstatic libusb20_do_request_sync_t ugen20_do_request_sync;
86184610Salfredstatic libusb20_process_t ugen20_process;
87184610Salfred
88184610Salfred/* USB transfer specific */
89184610Salfredstatic libusb20_tr_open_t ugen20_tr_open;
90184610Salfredstatic libusb20_tr_close_t ugen20_tr_close;
91184610Salfredstatic libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
92184610Salfredstatic libusb20_tr_submit_t ugen20_tr_submit;
93184610Salfredstatic libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
94184610Salfred
95184610Salfredstatic const struct libusb20_device_methods libusb20_ugen20_device_methods = {
96184610Salfred	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
97184610Salfred};
98184610Salfred
99184610Salfredstatic const char *
100184610Salfredugen20_get_backend_name(void)
101184610Salfred{
102184610Salfred	return ("FreeBSD UGEN 2.0");
103184610Salfred}
104184610Salfred
105184610Salfredstatic uint32_t
106184610Salfredugen20_path_convert_one(const char **pp)
107184610Salfred{
108184610Salfred	const char *ptr;
109184610Salfred	uint32_t temp = 0;
110184610Salfred
111184610Salfred	ptr = *pp;
112184610Salfred
113184610Salfred	while ((*ptr >= '0') && (*ptr <= '9')) {
114184610Salfred		temp *= 10;
115184610Salfred		temp += (*ptr - '0');
116184610Salfred		if (temp >= 1000000) {
117184610Salfred			/* catch overflow early */
118234491Shselasky			return (0xFFFFFFFF);
119184610Salfred		}
120184610Salfred		ptr++;
121184610Salfred	}
122184610Salfred
123184610Salfred	if (*ptr == '.') {
124184610Salfred		/* skip dot */
125184610Salfred		ptr++;
126184610Salfred	}
127184610Salfred	*pp = ptr;
128184610Salfred
129184610Salfred	return (temp);
130184610Salfred}
131184610Salfred
132184610Salfredstatic int
133184610Salfredugen20_enumerate(struct libusb20_device *pdev, const char *id)
134184610Salfred{
135184610Salfred	const char *tmp = id;
136192984Sthompsa	struct usb_device_descriptor ddesc;
137192984Sthompsa	struct usb_device_info devinfo;
138184610Salfred	uint32_t plugtime;
139184610Salfred	char buf[64];
140184610Salfred	int f;
141184610Salfred	int error;
142184610Salfred
143184610Salfred	pdev->bus_number = ugen20_path_convert_one(&tmp);
144184610Salfred	pdev->device_address = ugen20_path_convert_one(&tmp);
145184610Salfred
146189110Sthompsa	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
147184610Salfred	    pdev->bus_number, pdev->device_address);
148184610Salfred
149184610Salfred	f = open(buf, O_RDWR);
150184610Salfred	if (f < 0) {
151184610Salfred		return (LIBUSB20_ERROR_OTHER);
152184610Salfred	}
153253339Shselasky	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
154184610Salfred		error = LIBUSB20_ERROR_OTHER;
155184610Salfred		goto done;
156184610Salfred	}
157184610Salfred	/* store when the device was plugged */
158184610Salfred	pdev->session_data.plugtime = plugtime;
159184610Salfred
160253339Shselasky	if (ioctl(f, IOUSB(USB_GET_DEVICE_DESC), &ddesc)) {
161184610Salfred		error = LIBUSB20_ERROR_OTHER;
162184610Salfred		goto done;
163184610Salfred	}
164184610Salfred	LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc));
165184610Salfred
166184610Salfred	libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc));
167184610Salfred
168184610Salfred	if (pdev->ddesc.bNumConfigurations == 0) {
169184610Salfred		error = LIBUSB20_ERROR_OTHER;
170184610Salfred		goto done;
171184610Salfred	} else if (pdev->ddesc.bNumConfigurations >= 8) {
172184610Salfred		error = LIBUSB20_ERROR_OTHER;
173184610Salfred		goto done;
174184610Salfred	}
175253339Shselasky	if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) {
176184610Salfred		error = LIBUSB20_ERROR_OTHER;
177184610Salfred		goto done;
178184610Salfred	}
179184610Salfred	switch (devinfo.udi_mode) {
180184610Salfred	case USB_MODE_DEVICE:
181184610Salfred		pdev->usb_mode = LIBUSB20_MODE_DEVICE;
182184610Salfred		break;
183184610Salfred	default:
184184610Salfred		pdev->usb_mode = LIBUSB20_MODE_HOST;
185184610Salfred		break;
186184610Salfred	}
187184610Salfred
188184610Salfred	switch (devinfo.udi_speed) {
189184610Salfred	case USB_SPEED_LOW:
190184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_LOW;
191184610Salfred		break;
192184610Salfred	case USB_SPEED_FULL:
193184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_FULL;
194184610Salfred		break;
195184610Salfred	case USB_SPEED_HIGH:
196184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_HIGH;
197184610Salfred		break;
198184610Salfred	case USB_SPEED_VARIABLE:
199184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_VARIABLE;
200184610Salfred		break;
201184610Salfred	case USB_SPEED_SUPER:
202184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_SUPER;
203184610Salfred		break;
204184610Salfred	default:
205184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN;
206184610Salfred		break;
207184610Salfred	}
208184610Salfred
209223495Shselasky	/* get parent HUB index and port */
210223495Shselasky
211223495Shselasky	pdev->parent_address = devinfo.udi_hubindex;
212223495Shselasky	pdev->parent_port = devinfo.udi_hubport;
213223495Shselasky
214184610Salfred	/* generate a nice description for printout */
215184610Salfred
216184610Salfred	snprintf(pdev->usb_desc, sizeof(pdev->usb_desc),
217189110Sthompsa	    USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number,
218310280Strasz	    pdev->device_address, devinfo.udi_vendor,
219310280Strasz	    devinfo.udi_product, pdev->bus_number);
220184610Salfred
221184610Salfred	error = 0;
222184610Salfreddone:
223184610Salfred	close(f);
224184610Salfred	return (error);
225184610Salfred}
226184610Salfred
227184610Salfredstruct ugen20_urd_state {
228192984Sthompsa	struct usb_read_dir urd;
229184610Salfred	uint32_t nparsed;
230184610Salfred	int	f;
231184610Salfred	uint8_t *ptr;
232184610Salfred	const char *src;
233184610Salfred	const char *dst;
234184610Salfred	uint8_t	buf[256];
235184610Salfred	uint8_t	dummy_zero[1];
236184610Salfred};
237184610Salfred
238184610Salfredstatic int
239184610Salfredugen20_readdir(struct ugen20_urd_state *st)
240184610Salfred{
241184610Salfred	;				/* style fix */
242184610Salfredrepeat:
243184610Salfred	if (st->ptr == NULL) {
244184610Salfred		st->urd.urd_startentry += st->nparsed;
245213852Shselasky		st->urd.urd_data = libusb20_pass_ptr(st->buf);
246184610Salfred		st->urd.urd_maxlen = sizeof(st->buf);
247184610Salfred		st->nparsed = 0;
248184610Salfred
249253339Shselasky		if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) {
250184610Salfred			return (EINVAL);
251184610Salfred		}
252184610Salfred		st->ptr = st->buf;
253184610Salfred	}
254184610Salfred	if (st->ptr[0] == 0) {
255184610Salfred		if (st->nparsed) {
256184610Salfred			st->ptr = NULL;
257184610Salfred			goto repeat;
258184610Salfred		} else {
259184610Salfred			return (ENXIO);
260184610Salfred		}
261184610Salfred	}
262184610Salfred	st->src = (void *)(st->ptr + 1);
263184610Salfred	st->dst = st->src + strlen(st->src) + 1;
264184610Salfred	st->ptr = st->ptr + st->ptr[0];
265184610Salfred	st->nparsed++;
266184610Salfred
267184610Salfred	if ((st->ptr < st->buf) ||
268184610Salfred	    (st->ptr > st->dummy_zero)) {
269184610Salfred		/* invalid entry */
270184610Salfred		return (EINVAL);
271184610Salfred	}
272184610Salfred	return (0);
273184610Salfred}
274184610Salfred
275184610Salfredstatic int
276184610Salfredugen20_init_backend(struct libusb20_backend *pbe)
277184610Salfred{
278184610Salfred	struct ugen20_urd_state state;
279184610Salfred	struct libusb20_device *pdev;
280184610Salfred
281184610Salfred	memset(&state, 0, sizeof(state));
282184610Salfred
283189110Sthompsa	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
284184610Salfred	if (state.f < 0)
285184610Salfred		return (LIBUSB20_ERROR_OTHER);
286184610Salfred
287184610Salfred	while (ugen20_readdir(&state) == 0) {
288184610Salfred
289184610Salfred		if ((state.src[0] != 'u') ||
290184610Salfred		    (state.src[1] != 'g') ||
291184610Salfred		    (state.src[2] != 'e') ||
292184610Salfred		    (state.src[3] != 'n')) {
293184610Salfred			continue;
294184610Salfred		}
295184610Salfred		pdev = libusb20_dev_alloc();
296184610Salfred		if (pdev == NULL) {
297184610Salfred			continue;
298184610Salfred		}
299184610Salfred		if (ugen20_enumerate(pdev, state.src + 4)) {
300184610Salfred			libusb20_dev_free(pdev);
301184610Salfred			continue;
302184610Salfred		}
303184610Salfred		/* put the device on the backend list */
304184610Salfred		libusb20_be_enqueue_device(pbe, pdev);
305184610Salfred	}
306184610Salfred	close(state.f);
307184610Salfred	return (0);			/* success */
308184610Salfred}
309184610Salfred
310185290Salfredstatic void
311185290Salfredugen20_tr_release(struct libusb20_device *pdev)
312185290Salfred{
313192984Sthompsa	struct usb_fs_uninit fs_uninit;
314185290Salfred
315185290Salfred	if (pdev->nTransfer == 0) {
316185290Salfred		return;
317185290Salfred	}
318185290Salfred	/* release all pending USB transfers */
319185290Salfred	if (pdev->privBeData != NULL) {
320185290Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
321253339Shselasky		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
322185290Salfred			/* ignore any errors of this kind */
323185290Salfred		}
324185290Salfred	}
325185290Salfred	return;
326185290Salfred}
327185290Salfred
328184610Salfredstatic int
329185087Salfredugen20_tr_renew(struct libusb20_device *pdev)
330185087Salfred{
331192984Sthompsa	struct usb_fs_init fs_init;
332192984Sthompsa	struct usb_fs_endpoint *pfse;
333185087Salfred	int error;
334185087Salfred	uint32_t size;
335185087Salfred	uint16_t nMaxTransfer;
336185087Salfred
337185087Salfred	nMaxTransfer = pdev->nTransfer;
338185087Salfred	error = 0;
339185087Salfred
340185087Salfred	if (nMaxTransfer == 0) {
341185087Salfred		goto done;
342185087Salfred	}
343185087Salfred	size = nMaxTransfer * sizeof(*pfse);
344185087Salfred
345185290Salfred	if (pdev->privBeData == NULL) {
346185087Salfred		pfse = malloc(size);
347185087Salfred		if (pfse == NULL) {
348185087Salfred			error = LIBUSB20_ERROR_NO_MEM;
349185087Salfred			goto done;
350185087Salfred		}
351185087Salfred		pdev->privBeData = pfse;
352185087Salfred	}
353185087Salfred	/* reset endpoint data */
354185087Salfred	memset(pdev->privBeData, 0, size);
355185087Salfred
356185087Salfred	memset(&fs_init, 0, sizeof(fs_init));
357185087Salfred
358213852Shselasky	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
359185087Salfred	fs_init.ep_index_max = nMaxTransfer;
360185087Salfred
361253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) {
362185087Salfred		error = LIBUSB20_ERROR_OTHER;
363185087Salfred		goto done;
364185087Salfred	}
365185087Salfreddone:
366185087Salfred	return (error);
367185087Salfred}
368185087Salfred
369185087Salfredstatic int
370184610Salfredugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
371184610Salfred{
372184610Salfred	uint32_t plugtime;
373184610Salfred	char buf[64];
374184610Salfred	int f;
375184610Salfred	int g;
376184610Salfred	int error;
377184610Salfred
378189110Sthompsa	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
379184610Salfred	    pdev->bus_number, pdev->device_address);
380184610Salfred
381184610Salfred	/*
382184610Salfred	 * We need two file handles, one for the control endpoint and one
383184610Salfred	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
384184610Salfred	 * kernel locking.
385184610Salfred	 */
386184610Salfred	g = open(buf, O_RDWR);
387184610Salfred	if (g < 0) {
388184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
389184610Salfred	}
390184610Salfred	f = open(buf, O_RDWR);
391184610Salfred	if (f < 0) {
392184610Salfred		close(g);
393184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
394184610Salfred	}
395253339Shselasky	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
396184610Salfred		error = LIBUSB20_ERROR_OTHER;
397184610Salfred		goto done;
398184610Salfred	}
399184610Salfred	/* check that the correct device is still plugged */
400184610Salfred	if (pdev->session_data.plugtime != plugtime) {
401184610Salfred		error = LIBUSB20_ERROR_NO_DEVICE;
402184610Salfred		goto done;
403184610Salfred	}
404185087Salfred	/* need to set this before "tr_renew()" */
405185087Salfred	pdev->file = f;
406185087Salfred	pdev->file_ctrl = g;
407184610Salfred
408185087Salfred	/* renew all USB transfers */
409185087Salfred	error = ugen20_tr_renew(pdev);
410185087Salfred	if (error) {
411185087Salfred		goto done;
412184610Salfred	}
413184610Salfred	/* set methods */
414184610Salfred	pdev->methods = &libusb20_ugen20_device_methods;
415185087Salfred
416184610Salfreddone:
417184610Salfred	if (error) {
418185087Salfred		if (pdev->privBeData) {
419185087Salfred			/* cleanup after "tr_renew()" */
420185087Salfred			free(pdev->privBeData);
421185087Salfred			pdev->privBeData = NULL;
422184610Salfred		}
423185087Salfred		pdev->file = -1;
424185087Salfred		pdev->file_ctrl = -1;
425184610Salfred		close(f);
426184610Salfred		close(g);
427184610Salfred	}
428184610Salfred	return (error);
429184610Salfred}
430184610Salfred
431184610Salfredstatic int
432184610Salfredugen20_close_device(struct libusb20_device *pdev)
433184610Salfred{
434192984Sthompsa	struct usb_fs_uninit fs_uninit;
435184610Salfred
436184610Salfred	if (pdev->privBeData) {
437185087Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
438253339Shselasky		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
439185290Salfred			/* ignore this error */
440184610Salfred		}
441184610Salfred		free(pdev->privBeData);
442184610Salfred	}
443184610Salfred	pdev->nTransfer = 0;
444184610Salfred	pdev->privBeData = NULL;
445184610Salfred	close(pdev->file);
446184610Salfred	close(pdev->file_ctrl);
447184610Salfred	pdev->file = -1;
448184610Salfred	pdev->file_ctrl = -1;
449185290Salfred	return (0);			/* success */
450184610Salfred}
451184610Salfred
452184610Salfredstatic void
453184610Salfredugen20_exit_backend(struct libusb20_backend *pbe)
454184610Salfred{
455184610Salfred	return;				/* nothing to do */
456184610Salfred}
457184610Salfred
458184610Salfredstatic int
459184610Salfredugen20_get_config_desc_full(struct libusb20_device *pdev,
460185087Salfred    uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
461184610Salfred{
462192984Sthompsa	struct usb_gen_descriptor gen_desc;
463192984Sthompsa	struct usb_config_descriptor cdesc;
464184610Salfred	uint8_t *ptr;
465184610Salfred	uint16_t len;
466184610Salfred	int error;
467184610Salfred
468199055Sthompsa	/* make sure memory is initialised */
469199055Sthompsa	memset(&cdesc, 0, sizeof(cdesc));
470185087Salfred	memset(&gen_desc, 0, sizeof(gen_desc));
471185087Salfred
472213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
473184610Salfred	gen_desc.ugd_maxlen = sizeof(cdesc);
474185087Salfred	gen_desc.ugd_config_index = cfg_index;
475184610Salfred
476253339Shselasky	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
477184610Salfred	if (error) {
478184610Salfred		return (LIBUSB20_ERROR_OTHER);
479184610Salfred	}
480184610Salfred	len = UGETW(cdesc.wTotalLength);
481184610Salfred	if (len < sizeof(cdesc)) {
482184610Salfred		/* corrupt descriptor */
483184610Salfred		return (LIBUSB20_ERROR_OTHER);
484184610Salfred	}
485184610Salfred	ptr = malloc(len);
486184610Salfred	if (!ptr) {
487184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
488184610Salfred	}
489199055Sthompsa
490199055Sthompsa	/* make sure memory is initialised */
491199055Sthompsa	memset(ptr, 0, len);
492199055Sthompsa
493213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
494184610Salfred	gen_desc.ugd_maxlen = len;
495184610Salfred
496253339Shselasky	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
497184610Salfred	if (error) {
498184610Salfred		free(ptr);
499184610Salfred		return (LIBUSB20_ERROR_OTHER);
500184610Salfred	}
501184610Salfred	/* make sure that the device doesn't fool us */
502184610Salfred	memcpy(ptr, &cdesc, sizeof(cdesc));
503184610Salfred
504184610Salfred	*ppbuf = ptr;
505184610Salfred	*plen = len;
506184610Salfred
507184610Salfred	return (0);			/* success */
508184610Salfred}
509184610Salfred
510184610Salfredstatic int
511184610Salfredugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
512184610Salfred{
513184610Salfred	int temp;
514184610Salfred
515253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) {
516184610Salfred		return (LIBUSB20_ERROR_OTHER);
517184610Salfred	}
518184610Salfred	*pindex = temp;
519184610Salfred
520184610Salfred	return (0);
521184610Salfred}
522184610Salfred
523184610Salfredstatic int
524185087Salfredugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
525184610Salfred{
526185087Salfred	int temp = cfg_index;
527184610Salfred
528185290Salfred	/* release all active USB transfers */
529185290Salfred	ugen20_tr_release(pdev);
530185290Salfred
531253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) {
532184610Salfred		return (LIBUSB20_ERROR_OTHER);
533184610Salfred	}
534185087Salfred	return (ugen20_tr_renew(pdev));
535184610Salfred}
536184610Salfred
537184610Salfredstatic int
538184610Salfredugen20_set_alt_index(struct libusb20_device *pdev,
539184610Salfred    uint8_t iface_index, uint8_t alt_index)
540184610Salfred{
541192984Sthompsa	struct usb_alt_interface alt_iface;
542184610Salfred
543185087Salfred	memset(&alt_iface, 0, sizeof(alt_iface));
544185087Salfred
545184610Salfred	alt_iface.uai_interface_index = iface_index;
546184610Salfred	alt_iface.uai_alt_index = alt_index;
547184610Salfred
548185290Salfred	/* release all active USB transfers */
549185290Salfred	ugen20_tr_release(pdev);
550185290Salfred
551253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) {
552184610Salfred		return (LIBUSB20_ERROR_OTHER);
553184610Salfred	}
554185087Salfred	return (ugen20_tr_renew(pdev));
555184610Salfred}
556184610Salfred
557184610Salfredstatic int
558184610Salfredugen20_reset_device(struct libusb20_device *pdev)
559184610Salfred{
560184610Salfred	int temp = 0;
561184610Salfred
562185290Salfred	/* release all active USB transfers */
563185290Salfred	ugen20_tr_release(pdev);
564185290Salfred
565253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) {
566184610Salfred		return (LIBUSB20_ERROR_OTHER);
567184610Salfred	}
568185087Salfred	return (ugen20_tr_renew(pdev));
569184610Salfred}
570184610Salfred
571184610Salfredstatic int
572203147Sthompsaugen20_check_connected(struct libusb20_device *pdev)
573203147Sthompsa{
574203147Sthompsa	uint32_t plugtime;
575203147Sthompsa	int error = 0;
576203147Sthompsa
577253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
578203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
579203147Sthompsa		goto done;
580203147Sthompsa	}
581203147Sthompsa
582203147Sthompsa	if (pdev->session_data.plugtime != plugtime) {
583203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
584203147Sthompsa		goto done;
585203147Sthompsa	}
586203147Sthompsadone:
587203147Sthompsa	return (error);
588203147Sthompsa}
589203147Sthompsa
590203147Sthompsastatic int
591184610Salfredugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
592184610Salfred{
593184610Salfred	int temp;
594184610Salfred
595184610Salfred	switch (power_mode) {
596184610Salfred	case LIBUSB20_POWER_OFF:
597184610Salfred		temp = USB_POWER_MODE_OFF;
598184610Salfred		break;
599184610Salfred	case LIBUSB20_POWER_ON:
600184610Salfred		temp = USB_POWER_MODE_ON;
601184610Salfred		break;
602184610Salfred	case LIBUSB20_POWER_SAVE:
603184610Salfred		temp = USB_POWER_MODE_SAVE;
604184610Salfred		break;
605184610Salfred	case LIBUSB20_POWER_SUSPEND:
606184610Salfred		temp = USB_POWER_MODE_SUSPEND;
607184610Salfred		break;
608184610Salfred	case LIBUSB20_POWER_RESUME:
609184610Salfred		temp = USB_POWER_MODE_RESUME;
610184610Salfred		break;
611184610Salfred	default:
612184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
613184610Salfred	}
614253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) {
615184610Salfred		return (LIBUSB20_ERROR_OTHER);
616184610Salfred	}
617184610Salfred	return (0);
618184610Salfred}
619184610Salfred
620184610Salfredstatic int
621184610Salfredugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
622184610Salfred{
623184610Salfred	int temp;
624184610Salfred
625253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) {
626184610Salfred		return (LIBUSB20_ERROR_OTHER);
627184610Salfred	}
628184610Salfred	switch (temp) {
629184610Salfred	case USB_POWER_MODE_OFF:
630184610Salfred		temp = LIBUSB20_POWER_OFF;
631184610Salfred		break;
632184610Salfred	case USB_POWER_MODE_ON:
633184610Salfred		temp = LIBUSB20_POWER_ON;
634184610Salfred		break;
635184610Salfred	case USB_POWER_MODE_SAVE:
636184610Salfred		temp = LIBUSB20_POWER_SAVE;
637184610Salfred		break;
638184610Salfred	case USB_POWER_MODE_SUSPEND:
639184610Salfred		temp = LIBUSB20_POWER_SUSPEND;
640184610Salfred		break;
641184610Salfred	case USB_POWER_MODE_RESUME:
642184610Salfred		temp = LIBUSB20_POWER_RESUME;
643184610Salfred		break;
644184610Salfred	default:
645184610Salfred		temp = LIBUSB20_POWER_ON;
646184610Salfred		break;
647184610Salfred	}
648184610Salfred	*power_mode = temp;
649184610Salfred	return (0);			/* success */
650184610Salfred}
651184610Salfred
652184610Salfredstatic int
653250201Shselaskyugen20_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
654250201Shselasky{
655250201Shselasky	struct usb_device_port_path udpp;
656250201Shselasky
657253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_DEV_PORT_PATH), &udpp))
658250201Shselasky		return (LIBUSB20_ERROR_OTHER);
659250201Shselasky
660250201Shselasky	if (udpp.udp_port_level > bufsize)
661250201Shselasky		return (LIBUSB20_ERROR_OVERFLOW);
662250201Shselasky
663250201Shselasky	memcpy(buf, udpp.udp_port_no, udpp.udp_port_level);
664250201Shselasky
665250201Shselasky	return (udpp.udp_port_level);	/* success */
666250201Shselasky}
667250201Shselasky
668250201Shselaskystatic int
669246789Shselaskyugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
670246789Shselasky{
671246789Shselasky	int temp;
672246789Shselasky
673253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) {
674246789Shselasky		return (LIBUSB20_ERROR_OTHER);
675246789Shselasky	}
676246789Shselasky	*power_usage = temp;
677246789Shselasky	return (0);			/* success */
678246789Shselasky}
679246789Shselasky
680246789Shselaskystatic int
681356399Shselaskyugen20_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats)
682356399Shselasky{
683356399Shselasky	struct usb_device_stats st;
684356399Shselasky
685356399Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICESTATS), &st))
686356399Shselasky		return (LIBUSB20_ERROR_OTHER);
687356399Shselasky
688356399Shselasky	memset(pstats, 0, sizeof(*pstats));
689356399Shselasky
690356399Shselasky	pstats->xfer_ok[0] = st.uds_requests_ok[0];
691356399Shselasky	pstats->xfer_ok[1] = st.uds_requests_ok[1];
692356399Shselasky	pstats->xfer_ok[2] = st.uds_requests_ok[2];
693356399Shselasky	pstats->xfer_ok[3] = st.uds_requests_ok[3];
694356399Shselasky
695356399Shselasky	pstats->xfer_fail[0] = st.uds_requests_fail[0];
696356399Shselasky	pstats->xfer_fail[1] = st.uds_requests_fail[1];
697356399Shselasky	pstats->xfer_fail[2] = st.uds_requests_fail[2];
698356399Shselasky	pstats->xfer_fail[3] = st.uds_requests_fail[3];
699356399Shselasky
700356399Shselasky	return (0);			/* success */
701356399Shselasky}
702356399Shselasky
703356399Shselaskystatic int
704184610Salfredugen20_kernel_driver_active(struct libusb20_device *pdev,
705184610Salfred    uint8_t iface_index)
706184610Salfred{
707184610Salfred	int temp = iface_index;
708184610Salfred
709253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) {
710184610Salfred		return (LIBUSB20_ERROR_OTHER);
711184610Salfred	}
712184610Salfred	return (0);			/* kernel driver is active */
713184610Salfred}
714184610Salfred
715184610Salfredstatic int
716184610Salfredugen20_detach_kernel_driver(struct libusb20_device *pdev,
717184610Salfred    uint8_t iface_index)
718184610Salfred{
719184610Salfred	int temp = iface_index;
720184610Salfred
721253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) {
722184610Salfred		return (LIBUSB20_ERROR_OTHER);
723184610Salfred	}
724255242Shselasky	return (0);			/* kernel driver is detached */
725184610Salfred}
726184610Salfred
727184610Salfredstatic int
728184610Salfredugen20_do_request_sync(struct libusb20_device *pdev,
729184610Salfred    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
730184610Salfred    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
731184610Salfred{
732192984Sthompsa	struct usb_ctl_request req;
733184610Salfred
734185087Salfred	memset(&req, 0, sizeof(req));
735185087Salfred
736213852Shselasky	req.ucr_data = libusb20_pass_ptr(data);
737184610Salfred	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
738184610Salfred		req.ucr_flags |= USB_SHORT_XFER_OK;
739184610Salfred	}
740184610Salfred	if (libusb20_me_encode(&req.ucr_request,
741184610Salfred	    sizeof(req.ucr_request), setup)) {
742184610Salfred		/* ignore */
743184610Salfred	}
744253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) {
745184610Salfred		return (LIBUSB20_ERROR_OTHER);
746184610Salfred	}
747184610Salfred	if (pactlen) {
748184610Salfred		/* get actual length */
749184610Salfred		*pactlen = req.ucr_actlen;
750184610Salfred	}
751255242Shselasky	return (0);			/* request was successful */
752184610Salfred}
753184610Salfred
754184610Salfredstatic int
755184610Salfredugen20_process(struct libusb20_device *pdev)
756184610Salfred{
757192984Sthompsa	struct usb_fs_complete temp;
758192984Sthompsa	struct usb_fs_endpoint *fsep;
759184610Salfred	struct libusb20_transfer *xfer;
760184610Salfred
761184610Salfred	while (1) {
762184610Salfred
763253339Shselasky	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
764184610Salfred			if (errno == EBUSY) {
765184610Salfred				break;
766184610Salfred			} else {
767184610Salfred				/* device detached */
768184610Salfred				return (LIBUSB20_ERROR_OTHER);
769184610Salfred			}
770184610Salfred		}
771184610Salfred		fsep = pdev->privBeData;
772184610Salfred		xfer = pdev->pTransfer;
773184610Salfred		fsep += temp.ep_index;
774184610Salfred		xfer += temp.ep_index;
775184610Salfred
776184610Salfred		/* update transfer status */
777184610Salfred
778184610Salfred		if (fsep->status == 0) {
779184610Salfred			xfer->aFrames = fsep->aFrames;
780184610Salfred			xfer->timeComplete = fsep->isoc_time_complete;
781184610Salfred			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
782184610Salfred		} else if (fsep->status == USB_ERR_CANCELLED) {
783184610Salfred			xfer->aFrames = 0;
784184610Salfred			xfer->timeComplete = 0;
785184610Salfred			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
786184610Salfred		} else if (fsep->status == USB_ERR_STALLED) {
787184610Salfred			xfer->aFrames = 0;
788184610Salfred			xfer->timeComplete = 0;
789184610Salfred			xfer->status = LIBUSB20_TRANSFER_STALL;
790184610Salfred		} else if (fsep->status == USB_ERR_TIMEOUT) {
791184610Salfred			xfer->aFrames = 0;
792184610Salfred			xfer->timeComplete = 0;
793184610Salfred			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
794184610Salfred		} else {
795184610Salfred			xfer->aFrames = 0;
796184610Salfred			xfer->timeComplete = 0;
797184610Salfred			xfer->status = LIBUSB20_TRANSFER_ERROR;
798184610Salfred		}
799184610Salfred		libusb20_tr_callback_wrapper(xfer);
800184610Salfred	}
801184610Salfred	return (0);			/* done */
802184610Salfred}
803184610Salfred
804184610Salfredstatic int
805184610Salfredugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
806239239Shselasky    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
807239239Shselasky    uint8_t pre_scale)
808184610Salfred{
809239239Shselasky	union {
810239239Shselasky		struct usb_fs_open fs_open;
811239239Shselasky		struct usb_fs_open_stream fs_open_stream;
812239239Shselasky	} temp;
813192984Sthompsa	struct usb_fs_endpoint *fsep;
814184610Salfred
815219100Shselasky	if (pre_scale)
816219100Shselasky		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
817219100Shselasky
818185087Salfred	memset(&temp, 0, sizeof(temp));
819185087Salfred
820184610Salfred	fsep = xfer->pdev->privBeData;
821184610Salfred	fsep += xfer->trIndex;
822184610Salfred
823239239Shselasky	temp.fs_open.max_bufsize = MaxBufSize;
824239239Shselasky	temp.fs_open.max_frames = MaxFrameCount;
825239239Shselasky	temp.fs_open.ep_index = xfer->trIndex;
826239239Shselasky	temp.fs_open.ep_no = ep_no;
827184610Salfred
828239239Shselasky	if (stream_id != 0) {
829239239Shselasky		temp.fs_open_stream.stream_id = stream_id;
830239239Shselasky
831253339Shselasky		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream))
832239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
833239239Shselasky	} else {
834253339Shselasky		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open))
835239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
836184610Salfred	}
837184610Salfred	/* maximums might have changed - update */
838239239Shselasky	xfer->maxFrames = temp.fs_open.max_frames;
839184610Salfred
840184610Salfred	/* "max_bufsize" should be multiple of "max_packet_length" */
841239239Shselasky	xfer->maxTotalLength = temp.fs_open.max_bufsize;
842239239Shselasky	xfer->maxPacketLen = temp.fs_open.max_packet_length;
843184610Salfred
844213852Shselasky	/* setup buffer and length lists using zero copy */
845213852Shselasky	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
846213852Shselasky	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
847184610Salfred
848184610Salfred	return (0);			/* success */
849184610Salfred}
850184610Salfred
851184610Salfredstatic int
852184610Salfredugen20_tr_close(struct libusb20_transfer *xfer)
853184610Salfred{
854192984Sthompsa	struct usb_fs_close temp;
855184610Salfred
856185087Salfred	memset(&temp, 0, sizeof(temp));
857185087Salfred
858184610Salfred	temp.ep_index = xfer->trIndex;
859184610Salfred
860253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) {
861184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
862184610Salfred	}
863184610Salfred	return (0);			/* success */
864184610Salfred}
865184610Salfred
866184610Salfredstatic int
867184610Salfredugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
868184610Salfred{
869192984Sthompsa	struct usb_fs_clear_stall_sync temp;
870184610Salfred
871185087Salfred	memset(&temp, 0, sizeof(temp));
872185087Salfred
873184610Salfred	/* if the transfer is active, an error will be returned */
874184610Salfred
875184610Salfred	temp.ep_index = xfer->trIndex;
876184610Salfred
877253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) {
878184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
879184610Salfred	}
880184610Salfred	return (0);			/* success */
881184610Salfred}
882184610Salfred
883184610Salfredstatic void
884184610Salfredugen20_tr_submit(struct libusb20_transfer *xfer)
885184610Salfred{
886192984Sthompsa	struct usb_fs_start temp;
887192984Sthompsa	struct usb_fs_endpoint *fsep;
888184610Salfred
889185087Salfred	memset(&temp, 0, sizeof(temp));
890185087Salfred
891184610Salfred	fsep = xfer->pdev->privBeData;
892184610Salfred	fsep += xfer->trIndex;
893184610Salfred
894184610Salfred	fsep->nFrames = xfer->nFrames;
895184610Salfred	fsep->flags = 0;
896184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
897184610Salfred		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
898184610Salfred	}
899184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
900184610Salfred		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
901184610Salfred	}
902184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
903184610Salfred		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
904184610Salfred	}
905184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
906184610Salfred		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
907184610Salfred	}
908198376Sthompsa	/* NOTE: The "fsep->timeout" variable is 16-bit. */
909198376Sthompsa	if (xfer->timeout > 65535)
910198376Sthompsa		fsep->timeout = 65535;
911198376Sthompsa	else
912198376Sthompsa		fsep->timeout = xfer->timeout;
913184610Salfred
914184610Salfred	temp.ep_index = xfer->trIndex;
915184610Salfred
916253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) {
917184610Salfred		/* ignore any errors - should never happen */
918184610Salfred	}
919184610Salfred	return;				/* success */
920184610Salfred}
921184610Salfred
922184610Salfredstatic void
923184610Salfredugen20_tr_cancel_async(struct libusb20_transfer *xfer)
924184610Salfred{
925192984Sthompsa	struct usb_fs_stop temp;
926184610Salfred
927185087Salfred	memset(&temp, 0, sizeof(temp));
928185087Salfred
929184610Salfred	temp.ep_index = xfer->trIndex;
930184610Salfred
931253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) {
932184610Salfred		/* ignore any errors - should never happen */
933184610Salfred	}
934184610Salfred	return;
935184610Salfred}
936184610Salfred
937184610Salfredstatic int
938184610Salfredugen20_be_ioctl(uint32_t cmd, void *data)
939184610Salfred{
940184610Salfred	int f;
941185087Salfred	int error;
942184610Salfred
943189110Sthompsa	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
944184610Salfred	if (f < 0)
945184610Salfred		return (LIBUSB20_ERROR_OTHER);
946185087Salfred	error = ioctl(f, cmd, data);
947185087Salfred	if (error == -1) {
948184610Salfred		if (errno == EPERM) {
949185087Salfred			error = LIBUSB20_ERROR_ACCESS;
950184610Salfred		} else {
951185087Salfred			error = LIBUSB20_ERROR_OTHER;
952184610Salfred		}
953184610Salfred	}
954184610Salfred	close(f);
955185087Salfred	return (error);
956184610Salfred}
957184610Salfred
958184610Salfredstatic int
959188622Sthompsaugen20_dev_get_iface_desc(struct libusb20_device *pdev,
960188622Sthompsa    uint8_t iface_index, char *buf, uint8_t len)
961188622Sthompsa{
962192984Sthompsa	struct usb_gen_descriptor ugd;
963188622Sthompsa
964188622Sthompsa	memset(&ugd, 0, sizeof(ugd));
965188622Sthompsa
966213852Shselasky	ugd.ugd_data = libusb20_pass_ptr(buf);
967188622Sthompsa	ugd.ugd_maxlen = len;
968188622Sthompsa	ugd.ugd_iface_index = iface_index;
969188622Sthompsa
970253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) {
971188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
972188622Sthompsa	}
973188622Sthompsa	return (0);
974188622Sthompsa}
975188622Sthompsa
976188622Sthompsastatic int
977188622Sthompsaugen20_dev_get_info(struct libusb20_device *pdev,
978192984Sthompsa    struct usb_device_info *pinfo)
979188622Sthompsa{
980253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) {
981188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
982188622Sthompsa	}
983188622Sthompsa	return (0);
984188622Sthompsa}
985188622Sthompsa
986188622Sthompsastatic int
987184610Salfredugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
988185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
989184610Salfred{
990192984Sthompsa	struct usb_gen_quirk q;
991185087Salfred	int error;
992184610Salfred
993184610Salfred	memset(&q, 0, sizeof(q));
994184610Salfred
995185087Salfred	q.index = quirk_index;
996184610Salfred
997253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q);
998184610Salfred
999185087Salfred	if (error) {
1000184610Salfred		if (errno == EINVAL) {
1001184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1002184610Salfred		}
1003184610Salfred	} else {
1004184610Salfred		pq->vid = q.vid;
1005184610Salfred		pq->pid = q.pid;
1006184610Salfred		pq->bcdDeviceLow = q.bcdDeviceLow;
1007184610Salfred		pq->bcdDeviceHigh = q.bcdDeviceHigh;
1008184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1009184610Salfred	}
1010185087Salfred	return (error);
1011184610Salfred}
1012184610Salfred
1013184610Salfredstatic int
1014185087Salfredugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
1015184610Salfred    struct libusb20_quirk *pq)
1016184610Salfred{
1017192984Sthompsa	struct usb_gen_quirk q;
1018185087Salfred	int error;
1019184610Salfred
1020184610Salfred	memset(&q, 0, sizeof(q));
1021184610Salfred
1022185087Salfred	q.index = quirk_index;
1023184610Salfred
1024253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q);
1025184610Salfred
1026185087Salfred	if (error) {
1027184610Salfred		if (errno == EINVAL) {
1028184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1029184610Salfred		}
1030184610Salfred	} else {
1031184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1032184610Salfred	}
1033185087Salfred	return (error);
1034184610Salfred}
1035184610Salfred
1036184610Salfredstatic int
1037184610Salfredugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
1038184610Salfred    struct libusb20_quirk *pq)
1039184610Salfred{
1040192984Sthompsa	struct usb_gen_quirk q;
1041185087Salfred	int error;
1042184610Salfred
1043184610Salfred	memset(&q, 0, sizeof(q));
1044184610Salfred
1045184610Salfred	q.vid = pq->vid;
1046184610Salfred	q.pid = pq->pid;
1047184610Salfred	q.bcdDeviceLow = pq->bcdDeviceLow;
1048184610Salfred	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1049184610Salfred	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1050184610Salfred
1051253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q);
1052185087Salfred	if (error) {
1053184610Salfred		if (errno == ENOMEM) {
1054184610Salfred			return (LIBUSB20_ERROR_NO_MEM);
1055184610Salfred		}
1056184610Salfred	}
1057185087Salfred	return (error);
1058184610Salfred}
1059184610Salfred
1060184610Salfredstatic int
1061184610Salfredugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
1062184610Salfred    struct libusb20_quirk *pq)
1063184610Salfred{
1064192984Sthompsa	struct usb_gen_quirk q;
1065185087Salfred	int error;
1066184610Salfred
1067184610Salfred	memset(&q, 0, sizeof(q));
1068184610Salfred
1069184610Salfred	q.vid = pq->vid;
1070184610Salfred	q.pid = pq->pid;
1071184610Salfred	q.bcdDeviceLow = pq->bcdDeviceLow;
1072184610Salfred	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1073184610Salfred	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1074184610Salfred
1075253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q);
1076185087Salfred	if (error) {
1077184610Salfred		if (errno == EINVAL) {
1078184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1079184610Salfred		}
1080184610Salfred	}
1081185087Salfred	return (error);
1082184610Salfred}
1083184610Salfred
1084184610Salfredstatic int
1085188987Sthompsaugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1086188987Sthompsa{
1087253339Shselasky	return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp));
1088188987Sthompsa}
1089188987Sthompsa
1090188987Sthompsastatic int
1091188987Sthompsaugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1092188987Sthompsa{
1093253339Shselasky	return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp));
1094188987Sthompsa}
1095