libusb20_ugen20.c revision 253339
1184610Salfred/* $FreeBSD: head/lib/libusb/libusb20_ugen20.c 253339 2013-07-14 10:22:00Z 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;
82184610Salfredstatic libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
83184610Salfredstatic libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
84184610Salfredstatic libusb20_do_request_sync_t ugen20_do_request_sync;
85184610Salfredstatic libusb20_process_t ugen20_process;
86184610Salfred
87184610Salfred/* USB transfer specific */
88184610Salfredstatic libusb20_tr_open_t ugen20_tr_open;
89184610Salfredstatic libusb20_tr_close_t ugen20_tr_close;
90184610Salfredstatic libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
91184610Salfredstatic libusb20_tr_submit_t ugen20_tr_submit;
92184610Salfredstatic libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
93184610Salfred
94184610Salfredstatic const struct libusb20_device_methods libusb20_ugen20_device_methods = {
95184610Salfred	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
96184610Salfred};
97184610Salfred
98184610Salfredstatic const char *
99184610Salfredugen20_get_backend_name(void)
100184610Salfred{
101184610Salfred	return ("FreeBSD UGEN 2.0");
102184610Salfred}
103184610Salfred
104184610Salfredstatic uint32_t
105184610Salfredugen20_path_convert_one(const char **pp)
106184610Salfred{
107184610Salfred	const char *ptr;
108184610Salfred	uint32_t temp = 0;
109184610Salfred
110184610Salfred	ptr = *pp;
111184610Salfred
112184610Salfred	while ((*ptr >= '0') && (*ptr <= '9')) {
113184610Salfred		temp *= 10;
114184610Salfred		temp += (*ptr - '0');
115184610Salfred		if (temp >= 1000000) {
116184610Salfred			/* catch overflow early */
117234491Shselasky			return (0xFFFFFFFF);
118184610Salfred		}
119184610Salfred		ptr++;
120184610Salfred	}
121184610Salfred
122184610Salfred	if (*ptr == '.') {
123184610Salfred		/* skip dot */
124184610Salfred		ptr++;
125184610Salfred	}
126184610Salfred	*pp = ptr;
127184610Salfred
128184610Salfred	return (temp);
129184610Salfred}
130184610Salfred
131184610Salfredstatic int
132184610Salfredugen20_enumerate(struct libusb20_device *pdev, const char *id)
133184610Salfred{
134184610Salfred	const char *tmp = id;
135192984Sthompsa	struct usb_device_descriptor ddesc;
136192984Sthompsa	struct usb_device_info devinfo;
137184610Salfred	uint32_t plugtime;
138184610Salfred	char buf[64];
139184610Salfred	int f;
140184610Salfred	int error;
141184610Salfred
142184610Salfred	pdev->bus_number = ugen20_path_convert_one(&tmp);
143184610Salfred	pdev->device_address = ugen20_path_convert_one(&tmp);
144184610Salfred
145189110Sthompsa	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
146184610Salfred	    pdev->bus_number, pdev->device_address);
147184610Salfred
148184610Salfred	f = open(buf, O_RDWR);
149184610Salfred	if (f < 0) {
150184610Salfred		return (LIBUSB20_ERROR_OTHER);
151184610Salfred	}
152253339Shselasky	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
153184610Salfred		error = LIBUSB20_ERROR_OTHER;
154184610Salfred		goto done;
155184610Salfred	}
156184610Salfred	/* store when the device was plugged */
157184610Salfred	pdev->session_data.plugtime = plugtime;
158184610Salfred
159253339Shselasky	if (ioctl(f, IOUSB(USB_GET_DEVICE_DESC), &ddesc)) {
160184610Salfred		error = LIBUSB20_ERROR_OTHER;
161184610Salfred		goto done;
162184610Salfred	}
163184610Salfred	LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc));
164184610Salfred
165184610Salfred	libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc));
166184610Salfred
167184610Salfred	if (pdev->ddesc.bNumConfigurations == 0) {
168184610Salfred		error = LIBUSB20_ERROR_OTHER;
169184610Salfred		goto done;
170184610Salfred	} else if (pdev->ddesc.bNumConfigurations >= 8) {
171184610Salfred		error = LIBUSB20_ERROR_OTHER;
172184610Salfred		goto done;
173184610Salfred	}
174253339Shselasky	if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) {
175184610Salfred		error = LIBUSB20_ERROR_OTHER;
176184610Salfred		goto done;
177184610Salfred	}
178184610Salfred	switch (devinfo.udi_mode) {
179184610Salfred	case USB_MODE_DEVICE:
180184610Salfred		pdev->usb_mode = LIBUSB20_MODE_DEVICE;
181184610Salfred		break;
182184610Salfred	default:
183184610Salfred		pdev->usb_mode = LIBUSB20_MODE_HOST;
184184610Salfred		break;
185184610Salfred	}
186184610Salfred
187184610Salfred	switch (devinfo.udi_speed) {
188184610Salfred	case USB_SPEED_LOW:
189184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_LOW;
190184610Salfred		break;
191184610Salfred	case USB_SPEED_FULL:
192184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_FULL;
193184610Salfred		break;
194184610Salfred	case USB_SPEED_HIGH:
195184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_HIGH;
196184610Salfred		break;
197184610Salfred	case USB_SPEED_VARIABLE:
198184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_VARIABLE;
199184610Salfred		break;
200184610Salfred	case USB_SPEED_SUPER:
201184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_SUPER;
202184610Salfred		break;
203184610Salfred	default:
204184610Salfred		pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN;
205184610Salfred		break;
206184610Salfred	}
207184610Salfred
208223495Shselasky	/* get parent HUB index and port */
209223495Shselasky
210223495Shselasky	pdev->parent_address = devinfo.udi_hubindex;
211223495Shselasky	pdev->parent_port = devinfo.udi_hubport;
212223495Shselasky
213184610Salfred	/* generate a nice description for printout */
214184610Salfred
215184610Salfred	snprintf(pdev->usb_desc, sizeof(pdev->usb_desc),
216189110Sthompsa	    USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number,
217184610Salfred	    pdev->device_address, devinfo.udi_product,
218184610Salfred	    devinfo.udi_vendor, pdev->bus_number);
219184610Salfred
220184610Salfred	error = 0;
221184610Salfreddone:
222184610Salfred	close(f);
223184610Salfred	return (error);
224184610Salfred}
225184610Salfred
226184610Salfredstruct ugen20_urd_state {
227192984Sthompsa	struct usb_read_dir urd;
228184610Salfred	uint32_t nparsed;
229184610Salfred	int	f;
230184610Salfred	uint8_t *ptr;
231184610Salfred	const char *src;
232184610Salfred	const char *dst;
233184610Salfred	uint8_t	buf[256];
234184610Salfred	uint8_t	dummy_zero[1];
235184610Salfred};
236184610Salfred
237184610Salfredstatic int
238184610Salfredugen20_readdir(struct ugen20_urd_state *st)
239184610Salfred{
240184610Salfred	;				/* style fix */
241184610Salfredrepeat:
242184610Salfred	if (st->ptr == NULL) {
243184610Salfred		st->urd.urd_startentry += st->nparsed;
244213852Shselasky		st->urd.urd_data = libusb20_pass_ptr(st->buf);
245184610Salfred		st->urd.urd_maxlen = sizeof(st->buf);
246184610Salfred		st->nparsed = 0;
247184610Salfred
248253339Shselasky		if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) {
249184610Salfred			return (EINVAL);
250184610Salfred		}
251184610Salfred		st->ptr = st->buf;
252184610Salfred	}
253184610Salfred	if (st->ptr[0] == 0) {
254184610Salfred		if (st->nparsed) {
255184610Salfred			st->ptr = NULL;
256184610Salfred			goto repeat;
257184610Salfred		} else {
258184610Salfred			return (ENXIO);
259184610Salfred		}
260184610Salfred	}
261184610Salfred	st->src = (void *)(st->ptr + 1);
262184610Salfred	st->dst = st->src + strlen(st->src) + 1;
263184610Salfred	st->ptr = st->ptr + st->ptr[0];
264184610Salfred	st->nparsed++;
265184610Salfred
266184610Salfred	if ((st->ptr < st->buf) ||
267184610Salfred	    (st->ptr > st->dummy_zero)) {
268184610Salfred		/* invalid entry */
269184610Salfred		return (EINVAL);
270184610Salfred	}
271184610Salfred	return (0);
272184610Salfred}
273184610Salfred
274184610Salfredstatic int
275184610Salfredugen20_init_backend(struct libusb20_backend *pbe)
276184610Salfred{
277184610Salfred	struct ugen20_urd_state state;
278184610Salfred	struct libusb20_device *pdev;
279184610Salfred
280184610Salfred	memset(&state, 0, sizeof(state));
281184610Salfred
282189110Sthompsa	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
283184610Salfred	if (state.f < 0)
284184610Salfred		return (LIBUSB20_ERROR_OTHER);
285184610Salfred
286184610Salfred	while (ugen20_readdir(&state) == 0) {
287184610Salfred
288184610Salfred		if ((state.src[0] != 'u') ||
289184610Salfred		    (state.src[1] != 'g') ||
290184610Salfred		    (state.src[2] != 'e') ||
291184610Salfred		    (state.src[3] != 'n')) {
292184610Salfred			continue;
293184610Salfred		}
294184610Salfred		pdev = libusb20_dev_alloc();
295184610Salfred		if (pdev == NULL) {
296184610Salfred			continue;
297184610Salfred		}
298184610Salfred		if (ugen20_enumerate(pdev, state.src + 4)) {
299184610Salfred			libusb20_dev_free(pdev);
300184610Salfred			continue;
301184610Salfred		}
302184610Salfred		/* put the device on the backend list */
303184610Salfred		libusb20_be_enqueue_device(pbe, pdev);
304184610Salfred	}
305184610Salfred	close(state.f);
306184610Salfred	return (0);			/* success */
307184610Salfred}
308184610Salfred
309185290Salfredstatic void
310185290Salfredugen20_tr_release(struct libusb20_device *pdev)
311185290Salfred{
312192984Sthompsa	struct usb_fs_uninit fs_uninit;
313185290Salfred
314185290Salfred	if (pdev->nTransfer == 0) {
315185290Salfred		return;
316185290Salfred	}
317185290Salfred	/* release all pending USB transfers */
318185290Salfred	if (pdev->privBeData != NULL) {
319185290Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
320253339Shselasky		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
321185290Salfred			/* ignore any errors of this kind */
322185290Salfred		}
323185290Salfred	}
324185290Salfred	return;
325185290Salfred}
326185290Salfred
327184610Salfredstatic int
328185087Salfredugen20_tr_renew(struct libusb20_device *pdev)
329185087Salfred{
330192984Sthompsa	struct usb_fs_init fs_init;
331192984Sthompsa	struct usb_fs_endpoint *pfse;
332185087Salfred	int error;
333185087Salfred	uint32_t size;
334185087Salfred	uint16_t nMaxTransfer;
335185087Salfred
336185087Salfred	nMaxTransfer = pdev->nTransfer;
337185087Salfred	error = 0;
338185087Salfred
339185087Salfred	if (nMaxTransfer == 0) {
340185087Salfred		goto done;
341185087Salfred	}
342185087Salfred	size = nMaxTransfer * sizeof(*pfse);
343185087Salfred
344185290Salfred	if (pdev->privBeData == NULL) {
345185087Salfred		pfse = malloc(size);
346185087Salfred		if (pfse == NULL) {
347185087Salfred			error = LIBUSB20_ERROR_NO_MEM;
348185087Salfred			goto done;
349185087Salfred		}
350185087Salfred		pdev->privBeData = pfse;
351185087Salfred	}
352185087Salfred	/* reset endpoint data */
353185087Salfred	memset(pdev->privBeData, 0, size);
354185087Salfred
355185087Salfred	memset(&fs_init, 0, sizeof(fs_init));
356185087Salfred
357213852Shselasky	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
358185087Salfred	fs_init.ep_index_max = nMaxTransfer;
359185087Salfred
360253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) {
361185087Salfred		error = LIBUSB20_ERROR_OTHER;
362185087Salfred		goto done;
363185087Salfred	}
364185087Salfreddone:
365185087Salfred	return (error);
366185087Salfred}
367185087Salfred
368185087Salfredstatic int
369184610Salfredugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
370184610Salfred{
371184610Salfred	uint32_t plugtime;
372184610Salfred	char buf[64];
373184610Salfred	int f;
374184610Salfred	int g;
375184610Salfred	int error;
376184610Salfred
377189110Sthompsa	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
378184610Salfred	    pdev->bus_number, pdev->device_address);
379184610Salfred
380184610Salfred	/*
381184610Salfred	 * We need two file handles, one for the control endpoint and one
382184610Salfred	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
383184610Salfred	 * kernel locking.
384184610Salfred	 */
385184610Salfred	g = open(buf, O_RDWR);
386184610Salfred	if (g < 0) {
387184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
388184610Salfred	}
389184610Salfred	f = open(buf, O_RDWR);
390184610Salfred	if (f < 0) {
391184610Salfred		close(g);
392184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
393184610Salfred	}
394253339Shselasky	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
395184610Salfred		error = LIBUSB20_ERROR_OTHER;
396184610Salfred		goto done;
397184610Salfred	}
398184610Salfred	/* check that the correct device is still plugged */
399184610Salfred	if (pdev->session_data.plugtime != plugtime) {
400184610Salfred		error = LIBUSB20_ERROR_NO_DEVICE;
401184610Salfred		goto done;
402184610Salfred	}
403185087Salfred	/* need to set this before "tr_renew()" */
404185087Salfred	pdev->file = f;
405185087Salfred	pdev->file_ctrl = g;
406184610Salfred
407185087Salfred	/* renew all USB transfers */
408185087Salfred	error = ugen20_tr_renew(pdev);
409185087Salfred	if (error) {
410185087Salfred		goto done;
411184610Salfred	}
412184610Salfred	/* set methods */
413184610Salfred	pdev->methods = &libusb20_ugen20_device_methods;
414185087Salfred
415184610Salfreddone:
416184610Salfred	if (error) {
417185087Salfred		if (pdev->privBeData) {
418185087Salfred			/* cleanup after "tr_renew()" */
419185087Salfred			free(pdev->privBeData);
420185087Salfred			pdev->privBeData = NULL;
421184610Salfred		}
422185087Salfred		pdev->file = -1;
423185087Salfred		pdev->file_ctrl = -1;
424184610Salfred		close(f);
425184610Salfred		close(g);
426184610Salfred	}
427184610Salfred	return (error);
428184610Salfred}
429184610Salfred
430184610Salfredstatic int
431184610Salfredugen20_close_device(struct libusb20_device *pdev)
432184610Salfred{
433192984Sthompsa	struct usb_fs_uninit fs_uninit;
434184610Salfred
435184610Salfred	if (pdev->privBeData) {
436185087Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
437253339Shselasky		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
438185290Salfred			/* ignore this error */
439184610Salfred		}
440184610Salfred		free(pdev->privBeData);
441184610Salfred	}
442184610Salfred	pdev->nTransfer = 0;
443184610Salfred	pdev->privBeData = NULL;
444184610Salfred	close(pdev->file);
445184610Salfred	close(pdev->file_ctrl);
446184610Salfred	pdev->file = -1;
447184610Salfred	pdev->file_ctrl = -1;
448185290Salfred	return (0);			/* success */
449184610Salfred}
450184610Salfred
451184610Salfredstatic void
452184610Salfredugen20_exit_backend(struct libusb20_backend *pbe)
453184610Salfred{
454184610Salfred	return;				/* nothing to do */
455184610Salfred}
456184610Salfred
457184610Salfredstatic int
458184610Salfredugen20_get_config_desc_full(struct libusb20_device *pdev,
459185087Salfred    uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
460184610Salfred{
461192984Sthompsa	struct usb_gen_descriptor gen_desc;
462192984Sthompsa	struct usb_config_descriptor cdesc;
463184610Salfred	uint8_t *ptr;
464184610Salfred	uint16_t len;
465184610Salfred	int error;
466184610Salfred
467199055Sthompsa	/* make sure memory is initialised */
468199055Sthompsa	memset(&cdesc, 0, sizeof(cdesc));
469185087Salfred	memset(&gen_desc, 0, sizeof(gen_desc));
470185087Salfred
471213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
472184610Salfred	gen_desc.ugd_maxlen = sizeof(cdesc);
473185087Salfred	gen_desc.ugd_config_index = cfg_index;
474184610Salfred
475253339Shselasky	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
476184610Salfred	if (error) {
477184610Salfred		return (LIBUSB20_ERROR_OTHER);
478184610Salfred	}
479184610Salfred	len = UGETW(cdesc.wTotalLength);
480184610Salfred	if (len < sizeof(cdesc)) {
481184610Salfred		/* corrupt descriptor */
482184610Salfred		return (LIBUSB20_ERROR_OTHER);
483184610Salfred	}
484184610Salfred	ptr = malloc(len);
485184610Salfred	if (!ptr) {
486184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
487184610Salfred	}
488199055Sthompsa
489199055Sthompsa	/* make sure memory is initialised */
490199055Sthompsa	memset(ptr, 0, len);
491199055Sthompsa
492213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
493184610Salfred	gen_desc.ugd_maxlen = len;
494184610Salfred
495253339Shselasky	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
496184610Salfred	if (error) {
497184610Salfred		free(ptr);
498184610Salfred		return (LIBUSB20_ERROR_OTHER);
499184610Salfred	}
500184610Salfred	/* make sure that the device doesn't fool us */
501184610Salfred	memcpy(ptr, &cdesc, sizeof(cdesc));
502184610Salfred
503184610Salfred	*ppbuf = ptr;
504184610Salfred	*plen = len;
505184610Salfred
506184610Salfred	return (0);			/* success */
507184610Salfred}
508184610Salfred
509184610Salfredstatic int
510184610Salfredugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
511184610Salfred{
512184610Salfred	int temp;
513184610Salfred
514253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) {
515184610Salfred		return (LIBUSB20_ERROR_OTHER);
516184610Salfred	}
517184610Salfred	*pindex = temp;
518184610Salfred
519184610Salfred	return (0);
520184610Salfred}
521184610Salfred
522184610Salfredstatic int
523185087Salfredugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
524184610Salfred{
525185087Salfred	int temp = cfg_index;
526184610Salfred
527185290Salfred	/* release all active USB transfers */
528185290Salfred	ugen20_tr_release(pdev);
529185290Salfred
530253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) {
531184610Salfred		return (LIBUSB20_ERROR_OTHER);
532184610Salfred	}
533185087Salfred	return (ugen20_tr_renew(pdev));
534184610Salfred}
535184610Salfred
536184610Salfredstatic int
537184610Salfredugen20_set_alt_index(struct libusb20_device *pdev,
538184610Salfred    uint8_t iface_index, uint8_t alt_index)
539184610Salfred{
540192984Sthompsa	struct usb_alt_interface alt_iface;
541184610Salfred
542185087Salfred	memset(&alt_iface, 0, sizeof(alt_iface));
543185087Salfred
544184610Salfred	alt_iface.uai_interface_index = iface_index;
545184610Salfred	alt_iface.uai_alt_index = alt_index;
546184610Salfred
547185290Salfred	/* release all active USB transfers */
548185290Salfred	ugen20_tr_release(pdev);
549185290Salfred
550253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) {
551184610Salfred		return (LIBUSB20_ERROR_OTHER);
552184610Salfred	}
553185087Salfred	return (ugen20_tr_renew(pdev));
554184610Salfred}
555184610Salfred
556184610Salfredstatic int
557184610Salfredugen20_reset_device(struct libusb20_device *pdev)
558184610Salfred{
559184610Salfred	int temp = 0;
560184610Salfred
561185290Salfred	/* release all active USB transfers */
562185290Salfred	ugen20_tr_release(pdev);
563185290Salfred
564253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) {
565184610Salfred		return (LIBUSB20_ERROR_OTHER);
566184610Salfred	}
567185087Salfred	return (ugen20_tr_renew(pdev));
568184610Salfred}
569184610Salfred
570184610Salfredstatic int
571203147Sthompsaugen20_check_connected(struct libusb20_device *pdev)
572203147Sthompsa{
573203147Sthompsa	uint32_t plugtime;
574203147Sthompsa	int error = 0;
575203147Sthompsa
576253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
577203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
578203147Sthompsa		goto done;
579203147Sthompsa	}
580203147Sthompsa
581203147Sthompsa	if (pdev->session_data.plugtime != plugtime) {
582203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
583203147Sthompsa		goto done;
584203147Sthompsa	}
585203147Sthompsadone:
586203147Sthompsa	return (error);
587203147Sthompsa}
588203147Sthompsa
589203147Sthompsastatic int
590184610Salfredugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
591184610Salfred{
592184610Salfred	int temp;
593184610Salfred
594184610Salfred	switch (power_mode) {
595184610Salfred	case LIBUSB20_POWER_OFF:
596184610Salfred		temp = USB_POWER_MODE_OFF;
597184610Salfred		break;
598184610Salfred	case LIBUSB20_POWER_ON:
599184610Salfred		temp = USB_POWER_MODE_ON;
600184610Salfred		break;
601184610Salfred	case LIBUSB20_POWER_SAVE:
602184610Salfred		temp = USB_POWER_MODE_SAVE;
603184610Salfred		break;
604184610Salfred	case LIBUSB20_POWER_SUSPEND:
605184610Salfred		temp = USB_POWER_MODE_SUSPEND;
606184610Salfred		break;
607184610Salfred	case LIBUSB20_POWER_RESUME:
608184610Salfred		temp = USB_POWER_MODE_RESUME;
609184610Salfred		break;
610184610Salfred	default:
611184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
612184610Salfred	}
613253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) {
614184610Salfred		return (LIBUSB20_ERROR_OTHER);
615184610Salfred	}
616184610Salfred	return (0);
617184610Salfred}
618184610Salfred
619184610Salfredstatic int
620184610Salfredugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
621184610Salfred{
622184610Salfred	int temp;
623184610Salfred
624253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) {
625184610Salfred		return (LIBUSB20_ERROR_OTHER);
626184610Salfred	}
627184610Salfred	switch (temp) {
628184610Salfred	case USB_POWER_MODE_OFF:
629184610Salfred		temp = LIBUSB20_POWER_OFF;
630184610Salfred		break;
631184610Salfred	case USB_POWER_MODE_ON:
632184610Salfred		temp = LIBUSB20_POWER_ON;
633184610Salfred		break;
634184610Salfred	case USB_POWER_MODE_SAVE:
635184610Salfred		temp = LIBUSB20_POWER_SAVE;
636184610Salfred		break;
637184610Salfred	case USB_POWER_MODE_SUSPEND:
638184610Salfred		temp = LIBUSB20_POWER_SUSPEND;
639184610Salfred		break;
640184610Salfred	case USB_POWER_MODE_RESUME:
641184610Salfred		temp = LIBUSB20_POWER_RESUME;
642184610Salfred		break;
643184610Salfred	default:
644184610Salfred		temp = LIBUSB20_POWER_ON;
645184610Salfred		break;
646184610Salfred	}
647184610Salfred	*power_mode = temp;
648184610Salfred	return (0);			/* success */
649184610Salfred}
650184610Salfred
651184610Salfredstatic int
652250201Shselaskyugen20_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
653250201Shselasky{
654250201Shselasky	struct usb_device_port_path udpp;
655250201Shselasky
656253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_DEV_PORT_PATH), &udpp))
657250201Shselasky		return (LIBUSB20_ERROR_OTHER);
658250201Shselasky
659250201Shselasky	if (udpp.udp_port_level > bufsize)
660250201Shselasky		return (LIBUSB20_ERROR_OVERFLOW);
661250201Shselasky
662250201Shselasky	memcpy(buf, udpp.udp_port_no, udpp.udp_port_level);
663250201Shselasky
664250201Shselasky	return (udpp.udp_port_level);	/* success */
665250201Shselasky}
666250201Shselasky
667250201Shselaskystatic int
668246789Shselaskyugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
669246789Shselasky{
670246789Shselasky	int temp;
671246789Shselasky
672253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) {
673246789Shselasky		return (LIBUSB20_ERROR_OTHER);
674246789Shselasky	}
675246789Shselasky	*power_usage = temp;
676246789Shselasky	return (0);			/* success */
677246789Shselasky}
678246789Shselasky
679246789Shselaskystatic int
680184610Salfredugen20_kernel_driver_active(struct libusb20_device *pdev,
681184610Salfred    uint8_t iface_index)
682184610Salfred{
683184610Salfred	int temp = iface_index;
684184610Salfred
685253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) {
686184610Salfred		return (LIBUSB20_ERROR_OTHER);
687184610Salfred	}
688184610Salfred	return (0);			/* kernel driver is active */
689184610Salfred}
690184610Salfred
691184610Salfredstatic int
692184610Salfredugen20_detach_kernel_driver(struct libusb20_device *pdev,
693184610Salfred    uint8_t iface_index)
694184610Salfred{
695184610Salfred	int temp = iface_index;
696184610Salfred
697253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) {
698184610Salfred		return (LIBUSB20_ERROR_OTHER);
699184610Salfred	}
700184610Salfred	return (0);			/* kernel driver is active */
701184610Salfred}
702184610Salfred
703184610Salfredstatic int
704184610Salfredugen20_do_request_sync(struct libusb20_device *pdev,
705184610Salfred    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
706184610Salfred    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
707184610Salfred{
708192984Sthompsa	struct usb_ctl_request req;
709184610Salfred
710185087Salfred	memset(&req, 0, sizeof(req));
711185087Salfred
712213852Shselasky	req.ucr_data = libusb20_pass_ptr(data);
713184610Salfred	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
714184610Salfred		req.ucr_flags |= USB_SHORT_XFER_OK;
715184610Salfred	}
716184610Salfred	if (libusb20_me_encode(&req.ucr_request,
717184610Salfred	    sizeof(req.ucr_request), setup)) {
718184610Salfred		/* ignore */
719184610Salfred	}
720253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) {
721184610Salfred		return (LIBUSB20_ERROR_OTHER);
722184610Salfred	}
723184610Salfred	if (pactlen) {
724184610Salfred		/* get actual length */
725184610Salfred		*pactlen = req.ucr_actlen;
726184610Salfred	}
727184610Salfred	return (0);			/* kernel driver is active */
728184610Salfred}
729184610Salfred
730184610Salfredstatic int
731184610Salfredugen20_process(struct libusb20_device *pdev)
732184610Salfred{
733192984Sthompsa	struct usb_fs_complete temp;
734192984Sthompsa	struct usb_fs_endpoint *fsep;
735184610Salfred	struct libusb20_transfer *xfer;
736184610Salfred
737184610Salfred	while (1) {
738184610Salfred
739253339Shselasky	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
740184610Salfred			if (errno == EBUSY) {
741184610Salfred				break;
742184610Salfred			} else {
743184610Salfred				/* device detached */
744184610Salfred				return (LIBUSB20_ERROR_OTHER);
745184610Salfred			}
746184610Salfred		}
747184610Salfred		fsep = pdev->privBeData;
748184610Salfred		xfer = pdev->pTransfer;
749184610Salfred		fsep += temp.ep_index;
750184610Salfred		xfer += temp.ep_index;
751184610Salfred
752184610Salfred		/* update transfer status */
753184610Salfred
754184610Salfred		if (fsep->status == 0) {
755184610Salfred			xfer->aFrames = fsep->aFrames;
756184610Salfred			xfer->timeComplete = fsep->isoc_time_complete;
757184610Salfred			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
758184610Salfred		} else if (fsep->status == USB_ERR_CANCELLED) {
759184610Salfred			xfer->aFrames = 0;
760184610Salfred			xfer->timeComplete = 0;
761184610Salfred			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
762184610Salfred		} else if (fsep->status == USB_ERR_STALLED) {
763184610Salfred			xfer->aFrames = 0;
764184610Salfred			xfer->timeComplete = 0;
765184610Salfred			xfer->status = LIBUSB20_TRANSFER_STALL;
766184610Salfred		} else if (fsep->status == USB_ERR_TIMEOUT) {
767184610Salfred			xfer->aFrames = 0;
768184610Salfred			xfer->timeComplete = 0;
769184610Salfred			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
770184610Salfred		} else {
771184610Salfred			xfer->aFrames = 0;
772184610Salfred			xfer->timeComplete = 0;
773184610Salfred			xfer->status = LIBUSB20_TRANSFER_ERROR;
774184610Salfred		}
775184610Salfred		libusb20_tr_callback_wrapper(xfer);
776184610Salfred	}
777184610Salfred	return (0);			/* done */
778184610Salfred}
779184610Salfred
780184610Salfredstatic int
781184610Salfredugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
782239239Shselasky    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
783239239Shselasky    uint8_t pre_scale)
784184610Salfred{
785239239Shselasky	union {
786239239Shselasky		struct usb_fs_open fs_open;
787239239Shselasky		struct usb_fs_open_stream fs_open_stream;
788239239Shselasky	} temp;
789192984Sthompsa	struct usb_fs_endpoint *fsep;
790184610Salfred
791219100Shselasky	if (pre_scale)
792219100Shselasky		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
793219100Shselasky
794185087Salfred	memset(&temp, 0, sizeof(temp));
795185087Salfred
796184610Salfred	fsep = xfer->pdev->privBeData;
797184610Salfred	fsep += xfer->trIndex;
798184610Salfred
799239239Shselasky	temp.fs_open.max_bufsize = MaxBufSize;
800239239Shselasky	temp.fs_open.max_frames = MaxFrameCount;
801239239Shselasky	temp.fs_open.ep_index = xfer->trIndex;
802239239Shselasky	temp.fs_open.ep_no = ep_no;
803184610Salfred
804239239Shselasky	if (stream_id != 0) {
805239239Shselasky		temp.fs_open_stream.stream_id = stream_id;
806239239Shselasky
807253339Shselasky		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream))
808239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
809239239Shselasky	} else {
810253339Shselasky		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open))
811239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
812184610Salfred	}
813184610Salfred	/* maximums might have changed - update */
814239239Shselasky	xfer->maxFrames = temp.fs_open.max_frames;
815184610Salfred
816184610Salfred	/* "max_bufsize" should be multiple of "max_packet_length" */
817239239Shselasky	xfer->maxTotalLength = temp.fs_open.max_bufsize;
818239239Shselasky	xfer->maxPacketLen = temp.fs_open.max_packet_length;
819184610Salfred
820213852Shselasky	/* setup buffer and length lists using zero copy */
821213852Shselasky	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
822213852Shselasky	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
823184610Salfred
824184610Salfred	return (0);			/* success */
825184610Salfred}
826184610Salfred
827184610Salfredstatic int
828184610Salfredugen20_tr_close(struct libusb20_transfer *xfer)
829184610Salfred{
830192984Sthompsa	struct usb_fs_close temp;
831184610Salfred
832185087Salfred	memset(&temp, 0, sizeof(temp));
833185087Salfred
834184610Salfred	temp.ep_index = xfer->trIndex;
835184610Salfred
836253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) {
837184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
838184610Salfred	}
839184610Salfred	return (0);			/* success */
840184610Salfred}
841184610Salfred
842184610Salfredstatic int
843184610Salfredugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
844184610Salfred{
845192984Sthompsa	struct usb_fs_clear_stall_sync temp;
846184610Salfred
847185087Salfred	memset(&temp, 0, sizeof(temp));
848185087Salfred
849184610Salfred	/* if the transfer is active, an error will be returned */
850184610Salfred
851184610Salfred	temp.ep_index = xfer->trIndex;
852184610Salfred
853253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) {
854184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
855184610Salfred	}
856184610Salfred	return (0);			/* success */
857184610Salfred}
858184610Salfred
859184610Salfredstatic void
860184610Salfredugen20_tr_submit(struct libusb20_transfer *xfer)
861184610Salfred{
862192984Sthompsa	struct usb_fs_start temp;
863192984Sthompsa	struct usb_fs_endpoint *fsep;
864184610Salfred
865185087Salfred	memset(&temp, 0, sizeof(temp));
866185087Salfred
867184610Salfred	fsep = xfer->pdev->privBeData;
868184610Salfred	fsep += xfer->trIndex;
869184610Salfred
870184610Salfred	fsep->nFrames = xfer->nFrames;
871184610Salfred	fsep->flags = 0;
872184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
873184610Salfred		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
874184610Salfred	}
875184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
876184610Salfred		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
877184610Salfred	}
878184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
879184610Salfred		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
880184610Salfred	}
881184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
882184610Salfred		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
883184610Salfred	}
884198376Sthompsa	/* NOTE: The "fsep->timeout" variable is 16-bit. */
885198376Sthompsa	if (xfer->timeout > 65535)
886198376Sthompsa		fsep->timeout = 65535;
887198376Sthompsa	else
888198376Sthompsa		fsep->timeout = xfer->timeout;
889184610Salfred
890184610Salfred	temp.ep_index = xfer->trIndex;
891184610Salfred
892253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) {
893184610Salfred		/* ignore any errors - should never happen */
894184610Salfred	}
895184610Salfred	return;				/* success */
896184610Salfred}
897184610Salfred
898184610Salfredstatic void
899184610Salfredugen20_tr_cancel_async(struct libusb20_transfer *xfer)
900184610Salfred{
901192984Sthompsa	struct usb_fs_stop temp;
902184610Salfred
903185087Salfred	memset(&temp, 0, sizeof(temp));
904185087Salfred
905184610Salfred	temp.ep_index = xfer->trIndex;
906184610Salfred
907253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) {
908184610Salfred		/* ignore any errors - should never happen */
909184610Salfred	}
910184610Salfred	return;
911184610Salfred}
912184610Salfred
913184610Salfredstatic int
914184610Salfredugen20_be_ioctl(uint32_t cmd, void *data)
915184610Salfred{
916184610Salfred	int f;
917185087Salfred	int error;
918184610Salfred
919189110Sthompsa	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
920184610Salfred	if (f < 0)
921184610Salfred		return (LIBUSB20_ERROR_OTHER);
922185087Salfred	error = ioctl(f, cmd, data);
923185087Salfred	if (error == -1) {
924184610Salfred		if (errno == EPERM) {
925185087Salfred			error = LIBUSB20_ERROR_ACCESS;
926184610Salfred		} else {
927185087Salfred			error = LIBUSB20_ERROR_OTHER;
928184610Salfred		}
929184610Salfred	}
930184610Salfred	close(f);
931185087Salfred	return (error);
932184610Salfred}
933184610Salfred
934184610Salfredstatic int
935188622Sthompsaugen20_dev_get_iface_desc(struct libusb20_device *pdev,
936188622Sthompsa    uint8_t iface_index, char *buf, uint8_t len)
937188622Sthompsa{
938192984Sthompsa	struct usb_gen_descriptor ugd;
939188622Sthompsa
940188622Sthompsa	memset(&ugd, 0, sizeof(ugd));
941188622Sthompsa
942213852Shselasky	ugd.ugd_data = libusb20_pass_ptr(buf);
943188622Sthompsa	ugd.ugd_maxlen = len;
944188622Sthompsa	ugd.ugd_iface_index = iface_index;
945188622Sthompsa
946253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) {
947188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
948188622Sthompsa	}
949188622Sthompsa	return (0);
950188622Sthompsa}
951188622Sthompsa
952188622Sthompsastatic int
953188622Sthompsaugen20_dev_get_info(struct libusb20_device *pdev,
954192984Sthompsa    struct usb_device_info *pinfo)
955188622Sthompsa{
956253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) {
957188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
958188622Sthompsa	}
959188622Sthompsa	return (0);
960188622Sthompsa}
961188622Sthompsa
962188622Sthompsastatic int
963184610Salfredugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
964185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
965184610Salfred{
966192984Sthompsa	struct usb_gen_quirk q;
967185087Salfred	int error;
968184610Salfred
969184610Salfred	memset(&q, 0, sizeof(q));
970184610Salfred
971185087Salfred	q.index = quirk_index;
972184610Salfred
973253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q);
974184610Salfred
975185087Salfred	if (error) {
976184610Salfred		if (errno == EINVAL) {
977184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
978184610Salfred		}
979184610Salfred	} else {
980184610Salfred		pq->vid = q.vid;
981184610Salfred		pq->pid = q.pid;
982184610Salfred		pq->bcdDeviceLow = q.bcdDeviceLow;
983184610Salfred		pq->bcdDeviceHigh = q.bcdDeviceHigh;
984184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
985184610Salfred	}
986185087Salfred	return (error);
987184610Salfred}
988184610Salfred
989184610Salfredstatic int
990185087Salfredugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
991184610Salfred    struct libusb20_quirk *pq)
992184610Salfred{
993192984Sthompsa	struct usb_gen_quirk q;
994185087Salfred	int error;
995184610Salfred
996184610Salfred	memset(&q, 0, sizeof(q));
997184610Salfred
998185087Salfred	q.index = quirk_index;
999184610Salfred
1000253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q);
1001184610Salfred
1002185087Salfred	if (error) {
1003184610Salfred		if (errno == EINVAL) {
1004184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1005184610Salfred		}
1006184610Salfred	} else {
1007184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1008184610Salfred	}
1009185087Salfred	return (error);
1010184610Salfred}
1011184610Salfred
1012184610Salfredstatic int
1013184610Salfredugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
1014184610Salfred    struct libusb20_quirk *pq)
1015184610Salfred{
1016192984Sthompsa	struct usb_gen_quirk q;
1017185087Salfred	int error;
1018184610Salfred
1019184610Salfred	memset(&q, 0, sizeof(q));
1020184610Salfred
1021184610Salfred	q.vid = pq->vid;
1022184610Salfred	q.pid = pq->pid;
1023184610Salfred	q.bcdDeviceLow = pq->bcdDeviceLow;
1024184610Salfred	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1025184610Salfred	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1026184610Salfred
1027253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q);
1028185087Salfred	if (error) {
1029184610Salfred		if (errno == ENOMEM) {
1030184610Salfred			return (LIBUSB20_ERROR_NO_MEM);
1031184610Salfred		}
1032184610Salfred	}
1033185087Salfred	return (error);
1034184610Salfred}
1035184610Salfred
1036184610Salfredstatic int
1037184610Salfredugen20_root_remove_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_REMOVE), &q);
1052185087Salfred	if (error) {
1053184610Salfred		if (errno == EINVAL) {
1054184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1055184610Salfred		}
1056184610Salfred	}
1057185087Salfred	return (error);
1058184610Salfred}
1059184610Salfred
1060184610Salfredstatic int
1061188987Sthompsaugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1062188987Sthompsa{
1063253339Shselasky	return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp));
1064188987Sthompsa}
1065188987Sthompsa
1066188987Sthompsastatic int
1067188987Sthompsaugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1068188987Sthompsa{
1069253339Shselasky	return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp));
1070188987Sthompsa}
1071