1184610Salfred/* $FreeBSD: stable/11/lib/libusb/libusb20_ugen20.c 356398 2020-01-06 09:21:15Z 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;
80246789Shselaskystatic libusb20_get_power_usage_t ugen20_get_power_usage;
81356398Shselaskystatic libusb20_get_stats_t ugen20_get_stats;
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;
137331771Shselasky	struct usb_device_port_path udpp;
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,
218310276Strasz	    pdev->device_address, devinfo.udi_vendor,
219310276Strasz	    devinfo.udi_product, pdev->bus_number);
220184610Salfred
221331771Shselasky	/* get device port path, if any */
222331771Shselasky	if (ioctl(f, IOUSB(USB_GET_DEV_PORT_PATH), &udpp) == 0 &&
223331771Shselasky	    udpp.udp_port_level < LIBUSB20_DEVICE_PORT_PATH_MAX) {
224331771Shselasky		memcpy(pdev->port_path, udpp.udp_port_no, udpp.udp_port_level);
225331771Shselasky		pdev->port_level = udpp.udp_port_level;
226331771Shselasky	}
227331771Shselasky
228184610Salfred	error = 0;
229184610Salfreddone:
230184610Salfred	close(f);
231184610Salfred	return (error);
232184610Salfred}
233184610Salfred
234184610Salfredstruct ugen20_urd_state {
235192984Sthompsa	struct usb_read_dir urd;
236184610Salfred	uint32_t nparsed;
237184610Salfred	int	f;
238184610Salfred	uint8_t *ptr;
239184610Salfred	const char *src;
240184610Salfred	const char *dst;
241184610Salfred	uint8_t	buf[256];
242184610Salfred	uint8_t	dummy_zero[1];
243184610Salfred};
244184610Salfred
245184610Salfredstatic int
246184610Salfredugen20_readdir(struct ugen20_urd_state *st)
247184610Salfred{
248184610Salfred	;				/* style fix */
249184610Salfredrepeat:
250184610Salfred	if (st->ptr == NULL) {
251184610Salfred		st->urd.urd_startentry += st->nparsed;
252213852Shselasky		st->urd.urd_data = libusb20_pass_ptr(st->buf);
253184610Salfred		st->urd.urd_maxlen = sizeof(st->buf);
254184610Salfred		st->nparsed = 0;
255184610Salfred
256253339Shselasky		if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) {
257184610Salfred			return (EINVAL);
258184610Salfred		}
259184610Salfred		st->ptr = st->buf;
260184610Salfred	}
261184610Salfred	if (st->ptr[0] == 0) {
262184610Salfred		if (st->nparsed) {
263184610Salfred			st->ptr = NULL;
264184610Salfred			goto repeat;
265184610Salfred		} else {
266184610Salfred			return (ENXIO);
267184610Salfred		}
268184610Salfred	}
269184610Salfred	st->src = (void *)(st->ptr + 1);
270184610Salfred	st->dst = st->src + strlen(st->src) + 1;
271184610Salfred	st->ptr = st->ptr + st->ptr[0];
272184610Salfred	st->nparsed++;
273184610Salfred
274184610Salfred	if ((st->ptr < st->buf) ||
275184610Salfred	    (st->ptr > st->dummy_zero)) {
276184610Salfred		/* invalid entry */
277184610Salfred		return (EINVAL);
278184610Salfred	}
279184610Salfred	return (0);
280184610Salfred}
281184610Salfred
282184610Salfredstatic int
283184610Salfredugen20_init_backend(struct libusb20_backend *pbe)
284184610Salfred{
285184610Salfred	struct ugen20_urd_state state;
286184610Salfred	struct libusb20_device *pdev;
287184610Salfred
288184610Salfred	memset(&state, 0, sizeof(state));
289184610Salfred
290189110Sthompsa	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
291184610Salfred	if (state.f < 0)
292184610Salfred		return (LIBUSB20_ERROR_OTHER);
293184610Salfred
294184610Salfred	while (ugen20_readdir(&state) == 0) {
295184610Salfred
296184610Salfred		if ((state.src[0] != 'u') ||
297184610Salfred		    (state.src[1] != 'g') ||
298184610Salfred		    (state.src[2] != 'e') ||
299184610Salfred		    (state.src[3] != 'n')) {
300184610Salfred			continue;
301184610Salfred		}
302184610Salfred		pdev = libusb20_dev_alloc();
303184610Salfred		if (pdev == NULL) {
304184610Salfred			continue;
305184610Salfred		}
306184610Salfred		if (ugen20_enumerate(pdev, state.src + 4)) {
307184610Salfred			libusb20_dev_free(pdev);
308184610Salfred			continue;
309184610Salfred		}
310184610Salfred		/* put the device on the backend list */
311184610Salfred		libusb20_be_enqueue_device(pbe, pdev);
312184610Salfred	}
313184610Salfred	close(state.f);
314184610Salfred	return (0);			/* success */
315184610Salfred}
316184610Salfred
317185290Salfredstatic void
318185290Salfredugen20_tr_release(struct libusb20_device *pdev)
319185290Salfred{
320192984Sthompsa	struct usb_fs_uninit fs_uninit;
321185290Salfred
322185290Salfred	if (pdev->nTransfer == 0) {
323185290Salfred		return;
324185290Salfred	}
325185290Salfred	/* release all pending USB transfers */
326185290Salfred	if (pdev->privBeData != NULL) {
327185290Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
328253339Shselasky		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
329185290Salfred			/* ignore any errors of this kind */
330185290Salfred		}
331185290Salfred	}
332185290Salfred	return;
333185290Salfred}
334185290Salfred
335184610Salfredstatic int
336185087Salfredugen20_tr_renew(struct libusb20_device *pdev)
337185087Salfred{
338192984Sthompsa	struct usb_fs_init fs_init;
339192984Sthompsa	struct usb_fs_endpoint *pfse;
340185087Salfred	int error;
341185087Salfred	uint32_t size;
342185087Salfred	uint16_t nMaxTransfer;
343185087Salfred
344185087Salfred	nMaxTransfer = pdev->nTransfer;
345185087Salfred	error = 0;
346185087Salfred
347185087Salfred	if (nMaxTransfer == 0) {
348185087Salfred		goto done;
349185087Salfred	}
350185087Salfred	size = nMaxTransfer * sizeof(*pfse);
351185087Salfred
352185290Salfred	if (pdev->privBeData == NULL) {
353185087Salfred		pfse = malloc(size);
354185087Salfred		if (pfse == NULL) {
355185087Salfred			error = LIBUSB20_ERROR_NO_MEM;
356185087Salfred			goto done;
357185087Salfred		}
358185087Salfred		pdev->privBeData = pfse;
359185087Salfred	}
360185087Salfred	/* reset endpoint data */
361185087Salfred	memset(pdev->privBeData, 0, size);
362185087Salfred
363185087Salfred	memset(&fs_init, 0, sizeof(fs_init));
364185087Salfred
365213852Shselasky	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
366185087Salfred	fs_init.ep_index_max = nMaxTransfer;
367185087Salfred
368253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) {
369185087Salfred		error = LIBUSB20_ERROR_OTHER;
370185087Salfred		goto done;
371185087Salfred	}
372185087Salfreddone:
373185087Salfred	return (error);
374185087Salfred}
375185087Salfred
376185087Salfredstatic int
377184610Salfredugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
378184610Salfred{
379184610Salfred	uint32_t plugtime;
380184610Salfred	char buf[64];
381184610Salfred	int f;
382184610Salfred	int g;
383184610Salfred	int error;
384184610Salfred
385189110Sthompsa	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
386184610Salfred	    pdev->bus_number, pdev->device_address);
387184610Salfred
388184610Salfred	/*
389184610Salfred	 * We need two file handles, one for the control endpoint and one
390184610Salfred	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
391184610Salfred	 * kernel locking.
392184610Salfred	 */
393184610Salfred	g = open(buf, O_RDWR);
394184610Salfred	if (g < 0) {
395184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
396184610Salfred	}
397184610Salfred	f = open(buf, O_RDWR);
398184610Salfred	if (f < 0) {
399184610Salfred		close(g);
400184610Salfred		return (LIBUSB20_ERROR_NO_DEVICE);
401184610Salfred	}
402253339Shselasky	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
403184610Salfred		error = LIBUSB20_ERROR_OTHER;
404184610Salfred		goto done;
405184610Salfred	}
406184610Salfred	/* check that the correct device is still plugged */
407184610Salfred	if (pdev->session_data.plugtime != plugtime) {
408184610Salfred		error = LIBUSB20_ERROR_NO_DEVICE;
409184610Salfred		goto done;
410184610Salfred	}
411185087Salfred	/* need to set this before "tr_renew()" */
412185087Salfred	pdev->file = f;
413185087Salfred	pdev->file_ctrl = g;
414184610Salfred
415185087Salfred	/* renew all USB transfers */
416185087Salfred	error = ugen20_tr_renew(pdev);
417185087Salfred	if (error) {
418185087Salfred		goto done;
419184610Salfred	}
420184610Salfred	/* set methods */
421184610Salfred	pdev->methods = &libusb20_ugen20_device_methods;
422185087Salfred
423184610Salfreddone:
424184610Salfred	if (error) {
425185087Salfred		if (pdev->privBeData) {
426185087Salfred			/* cleanup after "tr_renew()" */
427185087Salfred			free(pdev->privBeData);
428185087Salfred			pdev->privBeData = NULL;
429184610Salfred		}
430185087Salfred		pdev->file = -1;
431185087Salfred		pdev->file_ctrl = -1;
432184610Salfred		close(f);
433184610Salfred		close(g);
434184610Salfred	}
435184610Salfred	return (error);
436184610Salfred}
437184610Salfred
438184610Salfredstatic int
439184610Salfredugen20_close_device(struct libusb20_device *pdev)
440184610Salfred{
441192984Sthompsa	struct usb_fs_uninit fs_uninit;
442184610Salfred
443184610Salfred	if (pdev->privBeData) {
444185087Salfred		memset(&fs_uninit, 0, sizeof(fs_uninit));
445253339Shselasky		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
446185290Salfred			/* ignore this error */
447184610Salfred		}
448184610Salfred		free(pdev->privBeData);
449184610Salfred	}
450184610Salfred	pdev->nTransfer = 0;
451184610Salfred	pdev->privBeData = NULL;
452184610Salfred	close(pdev->file);
453184610Salfred	close(pdev->file_ctrl);
454184610Salfred	pdev->file = -1;
455184610Salfred	pdev->file_ctrl = -1;
456185290Salfred	return (0);			/* success */
457184610Salfred}
458184610Salfred
459184610Salfredstatic void
460184610Salfredugen20_exit_backend(struct libusb20_backend *pbe)
461184610Salfred{
462184610Salfred	return;				/* nothing to do */
463184610Salfred}
464184610Salfred
465184610Salfredstatic int
466184610Salfredugen20_get_config_desc_full(struct libusb20_device *pdev,
467185087Salfred    uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
468184610Salfred{
469192984Sthompsa	struct usb_gen_descriptor gen_desc;
470192984Sthompsa	struct usb_config_descriptor cdesc;
471184610Salfred	uint8_t *ptr;
472184610Salfred	uint16_t len;
473184610Salfred	int error;
474184610Salfred
475199055Sthompsa	/* make sure memory is initialised */
476199055Sthompsa	memset(&cdesc, 0, sizeof(cdesc));
477185087Salfred	memset(&gen_desc, 0, sizeof(gen_desc));
478185087Salfred
479213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
480184610Salfred	gen_desc.ugd_maxlen = sizeof(cdesc);
481185087Salfred	gen_desc.ugd_config_index = cfg_index;
482184610Salfred
483253339Shselasky	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
484184610Salfred	if (error) {
485184610Salfred		return (LIBUSB20_ERROR_OTHER);
486184610Salfred	}
487184610Salfred	len = UGETW(cdesc.wTotalLength);
488184610Salfred	if (len < sizeof(cdesc)) {
489184610Salfred		/* corrupt descriptor */
490184610Salfred		return (LIBUSB20_ERROR_OTHER);
491184610Salfred	}
492184610Salfred	ptr = malloc(len);
493184610Salfred	if (!ptr) {
494184610Salfred		return (LIBUSB20_ERROR_NO_MEM);
495184610Salfred	}
496199055Sthompsa
497199055Sthompsa	/* make sure memory is initialised */
498199055Sthompsa	memset(ptr, 0, len);
499199055Sthompsa
500213852Shselasky	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
501184610Salfred	gen_desc.ugd_maxlen = len;
502184610Salfred
503253339Shselasky	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
504184610Salfred	if (error) {
505184610Salfred		free(ptr);
506184610Salfred		return (LIBUSB20_ERROR_OTHER);
507184610Salfred	}
508184610Salfred	/* make sure that the device doesn't fool us */
509184610Salfred	memcpy(ptr, &cdesc, sizeof(cdesc));
510184610Salfred
511184610Salfred	*ppbuf = ptr;
512184610Salfred	*plen = len;
513184610Salfred
514184610Salfred	return (0);			/* success */
515184610Salfred}
516184610Salfred
517184610Salfredstatic int
518184610Salfredugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
519184610Salfred{
520184610Salfred	int temp;
521184610Salfred
522253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) {
523184610Salfred		return (LIBUSB20_ERROR_OTHER);
524184610Salfred	}
525184610Salfred	*pindex = temp;
526184610Salfred
527184610Salfred	return (0);
528184610Salfred}
529184610Salfred
530184610Salfredstatic int
531185087Salfredugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
532184610Salfred{
533185087Salfred	int temp = cfg_index;
534184610Salfred
535185290Salfred	/* release all active USB transfers */
536185290Salfred	ugen20_tr_release(pdev);
537185290Salfred
538253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) {
539184610Salfred		return (LIBUSB20_ERROR_OTHER);
540184610Salfred	}
541185087Salfred	return (ugen20_tr_renew(pdev));
542184610Salfred}
543184610Salfred
544184610Salfredstatic int
545184610Salfredugen20_set_alt_index(struct libusb20_device *pdev,
546184610Salfred    uint8_t iface_index, uint8_t alt_index)
547184610Salfred{
548192984Sthompsa	struct usb_alt_interface alt_iface;
549184610Salfred
550185087Salfred	memset(&alt_iface, 0, sizeof(alt_iface));
551185087Salfred
552184610Salfred	alt_iface.uai_interface_index = iface_index;
553184610Salfred	alt_iface.uai_alt_index = alt_index;
554184610Salfred
555185290Salfred	/* release all active USB transfers */
556185290Salfred	ugen20_tr_release(pdev);
557185290Salfred
558253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) {
559184610Salfred		return (LIBUSB20_ERROR_OTHER);
560184610Salfred	}
561185087Salfred	return (ugen20_tr_renew(pdev));
562184610Salfred}
563184610Salfred
564184610Salfredstatic int
565184610Salfredugen20_reset_device(struct libusb20_device *pdev)
566184610Salfred{
567184610Salfred	int temp = 0;
568184610Salfred
569185290Salfred	/* release all active USB transfers */
570185290Salfred	ugen20_tr_release(pdev);
571185290Salfred
572253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) {
573184610Salfred		return (LIBUSB20_ERROR_OTHER);
574184610Salfred	}
575185087Salfred	return (ugen20_tr_renew(pdev));
576184610Salfred}
577184610Salfred
578184610Salfredstatic int
579203147Sthompsaugen20_check_connected(struct libusb20_device *pdev)
580203147Sthompsa{
581203147Sthompsa	uint32_t plugtime;
582203147Sthompsa	int error = 0;
583203147Sthompsa
584253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
585203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
586203147Sthompsa		goto done;
587203147Sthompsa	}
588203147Sthompsa
589203147Sthompsa	if (pdev->session_data.plugtime != plugtime) {
590203147Sthompsa		error = LIBUSB20_ERROR_NO_DEVICE;
591203147Sthompsa		goto done;
592203147Sthompsa	}
593203147Sthompsadone:
594203147Sthompsa	return (error);
595203147Sthompsa}
596203147Sthompsa
597203147Sthompsastatic int
598184610Salfredugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
599184610Salfred{
600184610Salfred	int temp;
601184610Salfred
602184610Salfred	switch (power_mode) {
603184610Salfred	case LIBUSB20_POWER_OFF:
604184610Salfred		temp = USB_POWER_MODE_OFF;
605184610Salfred		break;
606184610Salfred	case LIBUSB20_POWER_ON:
607184610Salfred		temp = USB_POWER_MODE_ON;
608184610Salfred		break;
609184610Salfred	case LIBUSB20_POWER_SAVE:
610184610Salfred		temp = USB_POWER_MODE_SAVE;
611184610Salfred		break;
612184610Salfred	case LIBUSB20_POWER_SUSPEND:
613184610Salfred		temp = USB_POWER_MODE_SUSPEND;
614184610Salfred		break;
615184610Salfred	case LIBUSB20_POWER_RESUME:
616184610Salfred		temp = USB_POWER_MODE_RESUME;
617184610Salfred		break;
618184610Salfred	default:
619184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
620184610Salfred	}
621253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) {
622184610Salfred		return (LIBUSB20_ERROR_OTHER);
623184610Salfred	}
624184610Salfred	return (0);
625184610Salfred}
626184610Salfred
627184610Salfredstatic int
628184610Salfredugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
629184610Salfred{
630184610Salfred	int temp;
631184610Salfred
632253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) {
633184610Salfred		return (LIBUSB20_ERROR_OTHER);
634184610Salfred	}
635184610Salfred	switch (temp) {
636184610Salfred	case USB_POWER_MODE_OFF:
637184610Salfred		temp = LIBUSB20_POWER_OFF;
638184610Salfred		break;
639184610Salfred	case USB_POWER_MODE_ON:
640184610Salfred		temp = LIBUSB20_POWER_ON;
641184610Salfred		break;
642184610Salfred	case USB_POWER_MODE_SAVE:
643184610Salfred		temp = LIBUSB20_POWER_SAVE;
644184610Salfred		break;
645184610Salfred	case USB_POWER_MODE_SUSPEND:
646184610Salfred		temp = LIBUSB20_POWER_SUSPEND;
647184610Salfred		break;
648184610Salfred	case USB_POWER_MODE_RESUME:
649184610Salfred		temp = LIBUSB20_POWER_RESUME;
650184610Salfred		break;
651184610Salfred	default:
652184610Salfred		temp = LIBUSB20_POWER_ON;
653184610Salfred		break;
654184610Salfred	}
655184610Salfred	*power_mode = temp;
656184610Salfred	return (0);			/* success */
657184610Salfred}
658184610Salfred
659184610Salfredstatic int
660246789Shselaskyugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
661246789Shselasky{
662246789Shselasky	int temp;
663246789Shselasky
664253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) {
665246789Shselasky		return (LIBUSB20_ERROR_OTHER);
666246789Shselasky	}
667246789Shselasky	*power_usage = temp;
668246789Shselasky	return (0);			/* success */
669246789Shselasky}
670246789Shselasky
671246789Shselaskystatic int
672356398Shselaskyugen20_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats)
673356398Shselasky{
674356398Shselasky	struct usb_device_stats st;
675356398Shselasky
676356398Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICESTATS), &st))
677356398Shselasky		return (LIBUSB20_ERROR_OTHER);
678356398Shselasky
679356398Shselasky	memset(pstats, 0, sizeof(*pstats));
680356398Shselasky
681356398Shselasky	pstats->xfer_ok[0] = st.uds_requests_ok[0];
682356398Shselasky	pstats->xfer_ok[1] = st.uds_requests_ok[1];
683356398Shselasky	pstats->xfer_ok[2] = st.uds_requests_ok[2];
684356398Shselasky	pstats->xfer_ok[3] = st.uds_requests_ok[3];
685356398Shselasky
686356398Shselasky	pstats->xfer_fail[0] = st.uds_requests_fail[0];
687356398Shselasky	pstats->xfer_fail[1] = st.uds_requests_fail[1];
688356398Shselasky	pstats->xfer_fail[2] = st.uds_requests_fail[2];
689356398Shselasky	pstats->xfer_fail[3] = st.uds_requests_fail[3];
690356398Shselasky
691356398Shselasky	return (0);			/* success */
692356398Shselasky}
693356398Shselasky
694356398Shselaskystatic int
695184610Salfredugen20_kernel_driver_active(struct libusb20_device *pdev,
696184610Salfred    uint8_t iface_index)
697184610Salfred{
698184610Salfred	int temp = iface_index;
699184610Salfred
700253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) {
701184610Salfred		return (LIBUSB20_ERROR_OTHER);
702184610Salfred	}
703184610Salfred	return (0);			/* kernel driver is active */
704184610Salfred}
705184610Salfred
706184610Salfredstatic int
707184610Salfredugen20_detach_kernel_driver(struct libusb20_device *pdev,
708184610Salfred    uint8_t iface_index)
709184610Salfred{
710184610Salfred	int temp = iface_index;
711184610Salfred
712253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) {
713184610Salfred		return (LIBUSB20_ERROR_OTHER);
714184610Salfred	}
715255242Shselasky	return (0);			/* kernel driver is detached */
716184610Salfred}
717184610Salfred
718184610Salfredstatic int
719184610Salfredugen20_do_request_sync(struct libusb20_device *pdev,
720184610Salfred    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
721184610Salfred    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
722184610Salfred{
723192984Sthompsa	struct usb_ctl_request req;
724184610Salfred
725185087Salfred	memset(&req, 0, sizeof(req));
726185087Salfred
727213852Shselasky	req.ucr_data = libusb20_pass_ptr(data);
728184610Salfred	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
729184610Salfred		req.ucr_flags |= USB_SHORT_XFER_OK;
730184610Salfred	}
731184610Salfred	if (libusb20_me_encode(&req.ucr_request,
732184610Salfred	    sizeof(req.ucr_request), setup)) {
733184610Salfred		/* ignore */
734184610Salfred	}
735253339Shselasky	if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) {
736184610Salfred		return (LIBUSB20_ERROR_OTHER);
737184610Salfred	}
738184610Salfred	if (pactlen) {
739184610Salfred		/* get actual length */
740184610Salfred		*pactlen = req.ucr_actlen;
741184610Salfred	}
742255242Shselasky	return (0);			/* request was successful */
743184610Salfred}
744184610Salfred
745184610Salfredstatic int
746184610Salfredugen20_process(struct libusb20_device *pdev)
747184610Salfred{
748192984Sthompsa	struct usb_fs_complete temp;
749192984Sthompsa	struct usb_fs_endpoint *fsep;
750184610Salfred	struct libusb20_transfer *xfer;
751184610Salfred
752184610Salfred	while (1) {
753184610Salfred
754253339Shselasky	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
755184610Salfred			if (errno == EBUSY) {
756184610Salfred				break;
757184610Salfred			} else {
758184610Salfred				/* device detached */
759184610Salfred				return (LIBUSB20_ERROR_OTHER);
760184610Salfred			}
761184610Salfred		}
762184610Salfred		fsep = pdev->privBeData;
763184610Salfred		xfer = pdev->pTransfer;
764184610Salfred		fsep += temp.ep_index;
765184610Salfred		xfer += temp.ep_index;
766184610Salfred
767184610Salfred		/* update transfer status */
768184610Salfred
769184610Salfred		if (fsep->status == 0) {
770184610Salfred			xfer->aFrames = fsep->aFrames;
771184610Salfred			xfer->timeComplete = fsep->isoc_time_complete;
772184610Salfred			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
773184610Salfred		} else if (fsep->status == USB_ERR_CANCELLED) {
774184610Salfred			xfer->aFrames = 0;
775184610Salfred			xfer->timeComplete = 0;
776184610Salfred			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
777184610Salfred		} else if (fsep->status == USB_ERR_STALLED) {
778184610Salfred			xfer->aFrames = 0;
779184610Salfred			xfer->timeComplete = 0;
780184610Salfred			xfer->status = LIBUSB20_TRANSFER_STALL;
781184610Salfred		} else if (fsep->status == USB_ERR_TIMEOUT) {
782184610Salfred			xfer->aFrames = 0;
783184610Salfred			xfer->timeComplete = 0;
784184610Salfred			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
785184610Salfred		} else {
786184610Salfred			xfer->aFrames = 0;
787184610Salfred			xfer->timeComplete = 0;
788184610Salfred			xfer->status = LIBUSB20_TRANSFER_ERROR;
789184610Salfred		}
790184610Salfred		libusb20_tr_callback_wrapper(xfer);
791184610Salfred	}
792184610Salfred	return (0);			/* done */
793184610Salfred}
794184610Salfred
795184610Salfredstatic int
796184610Salfredugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
797239239Shselasky    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
798239239Shselasky    uint8_t pre_scale)
799184610Salfred{
800239239Shselasky	union {
801239239Shselasky		struct usb_fs_open fs_open;
802239239Shselasky		struct usb_fs_open_stream fs_open_stream;
803239239Shselasky	} temp;
804192984Sthompsa	struct usb_fs_endpoint *fsep;
805184610Salfred
806219100Shselasky	if (pre_scale)
807219100Shselasky		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
808219100Shselasky
809185087Salfred	memset(&temp, 0, sizeof(temp));
810185087Salfred
811184610Salfred	fsep = xfer->pdev->privBeData;
812184610Salfred	fsep += xfer->trIndex;
813184610Salfred
814239239Shselasky	temp.fs_open.max_bufsize = MaxBufSize;
815239239Shselasky	temp.fs_open.max_frames = MaxFrameCount;
816239239Shselasky	temp.fs_open.ep_index = xfer->trIndex;
817239239Shselasky	temp.fs_open.ep_no = ep_no;
818184610Salfred
819239239Shselasky	if (stream_id != 0) {
820239239Shselasky		temp.fs_open_stream.stream_id = stream_id;
821239239Shselasky
822253339Shselasky		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream))
823239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
824239239Shselasky	} else {
825253339Shselasky		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open))
826239239Shselasky			return (LIBUSB20_ERROR_INVALID_PARAM);
827184610Salfred	}
828184610Salfred	/* maximums might have changed - update */
829239239Shselasky	xfer->maxFrames = temp.fs_open.max_frames;
830184610Salfred
831184610Salfred	/* "max_bufsize" should be multiple of "max_packet_length" */
832239239Shselasky	xfer->maxTotalLength = temp.fs_open.max_bufsize;
833239239Shselasky	xfer->maxPacketLen = temp.fs_open.max_packet_length;
834184610Salfred
835213852Shselasky	/* setup buffer and length lists using zero copy */
836213852Shselasky	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
837213852Shselasky	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
838184610Salfred
839184610Salfred	return (0);			/* success */
840184610Salfred}
841184610Salfred
842184610Salfredstatic int
843184610Salfredugen20_tr_close(struct libusb20_transfer *xfer)
844184610Salfred{
845192984Sthompsa	struct usb_fs_close temp;
846184610Salfred
847185087Salfred	memset(&temp, 0, sizeof(temp));
848185087Salfred
849184610Salfred	temp.ep_index = xfer->trIndex;
850184610Salfred
851253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) {
852184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
853184610Salfred	}
854184610Salfred	return (0);			/* success */
855184610Salfred}
856184610Salfred
857184610Salfredstatic int
858184610Salfredugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
859184610Salfred{
860192984Sthompsa	struct usb_fs_clear_stall_sync temp;
861184610Salfred
862185087Salfred	memset(&temp, 0, sizeof(temp));
863185087Salfred
864184610Salfred	/* if the transfer is active, an error will be returned */
865184610Salfred
866184610Salfred	temp.ep_index = xfer->trIndex;
867184610Salfred
868253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) {
869184610Salfred		return (LIBUSB20_ERROR_INVALID_PARAM);
870184610Salfred	}
871184610Salfred	return (0);			/* success */
872184610Salfred}
873184610Salfred
874184610Salfredstatic void
875184610Salfredugen20_tr_submit(struct libusb20_transfer *xfer)
876184610Salfred{
877192984Sthompsa	struct usb_fs_start temp;
878192984Sthompsa	struct usb_fs_endpoint *fsep;
879184610Salfred
880185087Salfred	memset(&temp, 0, sizeof(temp));
881185087Salfred
882184610Salfred	fsep = xfer->pdev->privBeData;
883184610Salfred	fsep += xfer->trIndex;
884184610Salfred
885184610Salfred	fsep->nFrames = xfer->nFrames;
886184610Salfred	fsep->flags = 0;
887184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
888184610Salfred		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
889184610Salfred	}
890184610Salfred	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
891184610Salfred		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
892184610Salfred	}
893184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
894184610Salfred		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
895184610Salfred	}
896184610Salfred	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
897184610Salfred		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
898184610Salfred	}
899198376Sthompsa	/* NOTE: The "fsep->timeout" variable is 16-bit. */
900198376Sthompsa	if (xfer->timeout > 65535)
901198376Sthompsa		fsep->timeout = 65535;
902198376Sthompsa	else
903198376Sthompsa		fsep->timeout = xfer->timeout;
904184610Salfred
905184610Salfred	temp.ep_index = xfer->trIndex;
906184610Salfred
907253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) {
908184610Salfred		/* ignore any errors - should never happen */
909184610Salfred	}
910184610Salfred	return;				/* success */
911184610Salfred}
912184610Salfred
913184610Salfredstatic void
914184610Salfredugen20_tr_cancel_async(struct libusb20_transfer *xfer)
915184610Salfred{
916192984Sthompsa	struct usb_fs_stop temp;
917184610Salfred
918185087Salfred	memset(&temp, 0, sizeof(temp));
919185087Salfred
920184610Salfred	temp.ep_index = xfer->trIndex;
921184610Salfred
922253339Shselasky	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) {
923184610Salfred		/* ignore any errors - should never happen */
924184610Salfred	}
925184610Salfred	return;
926184610Salfred}
927184610Salfred
928184610Salfredstatic int
929184610Salfredugen20_be_ioctl(uint32_t cmd, void *data)
930184610Salfred{
931184610Salfred	int f;
932185087Salfred	int error;
933184610Salfred
934189110Sthompsa	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
935184610Salfred	if (f < 0)
936184610Salfred		return (LIBUSB20_ERROR_OTHER);
937185087Salfred	error = ioctl(f, cmd, data);
938185087Salfred	if (error == -1) {
939184610Salfred		if (errno == EPERM) {
940185087Salfred			error = LIBUSB20_ERROR_ACCESS;
941184610Salfred		} else {
942185087Salfred			error = LIBUSB20_ERROR_OTHER;
943184610Salfred		}
944184610Salfred	}
945184610Salfred	close(f);
946185087Salfred	return (error);
947184610Salfred}
948184610Salfred
949184610Salfredstatic int
950188622Sthompsaugen20_dev_get_iface_desc(struct libusb20_device *pdev,
951188622Sthompsa    uint8_t iface_index, char *buf, uint8_t len)
952188622Sthompsa{
953192984Sthompsa	struct usb_gen_descriptor ugd;
954188622Sthompsa
955188622Sthompsa	memset(&ugd, 0, sizeof(ugd));
956188622Sthompsa
957213852Shselasky	ugd.ugd_data = libusb20_pass_ptr(buf);
958188622Sthompsa	ugd.ugd_maxlen = len;
959188622Sthompsa	ugd.ugd_iface_index = iface_index;
960188622Sthompsa
961253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) {
962188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
963188622Sthompsa	}
964188622Sthompsa	return (0);
965188622Sthompsa}
966188622Sthompsa
967188622Sthompsastatic int
968188622Sthompsaugen20_dev_get_info(struct libusb20_device *pdev,
969192984Sthompsa    struct usb_device_info *pinfo)
970188622Sthompsa{
971253339Shselasky	if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) {
972188622Sthompsa		return (LIBUSB20_ERROR_INVALID_PARAM);
973188622Sthompsa	}
974188622Sthompsa	return (0);
975188622Sthompsa}
976188622Sthompsa
977188622Sthompsastatic int
978184610Salfredugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
979185087Salfred    uint16_t quirk_index, struct libusb20_quirk *pq)
980184610Salfred{
981192984Sthompsa	struct usb_gen_quirk q;
982185087Salfred	int error;
983184610Salfred
984184610Salfred	memset(&q, 0, sizeof(q));
985184610Salfred
986185087Salfred	q.index = quirk_index;
987184610Salfred
988253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q);
989184610Salfred
990185087Salfred	if (error) {
991184610Salfred		if (errno == EINVAL) {
992184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
993184610Salfred		}
994184610Salfred	} else {
995184610Salfred		pq->vid = q.vid;
996184610Salfred		pq->pid = q.pid;
997184610Salfred		pq->bcdDeviceLow = q.bcdDeviceLow;
998184610Salfred		pq->bcdDeviceHigh = q.bcdDeviceHigh;
999184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1000184610Salfred	}
1001185087Salfred	return (error);
1002184610Salfred}
1003184610Salfred
1004184610Salfredstatic int
1005185087Salfredugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
1006184610Salfred    struct libusb20_quirk *pq)
1007184610Salfred{
1008192984Sthompsa	struct usb_gen_quirk q;
1009185087Salfred	int error;
1010184610Salfred
1011184610Salfred	memset(&q, 0, sizeof(q));
1012184610Salfred
1013185087Salfred	q.index = quirk_index;
1014184610Salfred
1015253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q);
1016184610Salfred
1017185087Salfred	if (error) {
1018184610Salfred		if (errno == EINVAL) {
1019184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1020184610Salfred		}
1021184610Salfred	} else {
1022184610Salfred		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1023184610Salfred	}
1024185087Salfred	return (error);
1025184610Salfred}
1026184610Salfred
1027184610Salfredstatic int
1028184610Salfredugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
1029184610Salfred    struct libusb20_quirk *pq)
1030184610Salfred{
1031192984Sthompsa	struct usb_gen_quirk q;
1032185087Salfred	int error;
1033184610Salfred
1034184610Salfred	memset(&q, 0, sizeof(q));
1035184610Salfred
1036184610Salfred	q.vid = pq->vid;
1037184610Salfred	q.pid = pq->pid;
1038184610Salfred	q.bcdDeviceLow = pq->bcdDeviceLow;
1039184610Salfred	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1040184610Salfred	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1041184610Salfred
1042253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q);
1043185087Salfred	if (error) {
1044184610Salfred		if (errno == ENOMEM) {
1045184610Salfred			return (LIBUSB20_ERROR_NO_MEM);
1046184610Salfred		}
1047184610Salfred	}
1048185087Salfred	return (error);
1049184610Salfred}
1050184610Salfred
1051184610Salfredstatic int
1052184610Salfredugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
1053184610Salfred    struct libusb20_quirk *pq)
1054184610Salfred{
1055192984Sthompsa	struct usb_gen_quirk q;
1056185087Salfred	int error;
1057184610Salfred
1058184610Salfred	memset(&q, 0, sizeof(q));
1059184610Salfred
1060184610Salfred	q.vid = pq->vid;
1061184610Salfred	q.pid = pq->pid;
1062184610Salfred	q.bcdDeviceLow = pq->bcdDeviceLow;
1063184610Salfred	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1064184610Salfred	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1065184610Salfred
1066253339Shselasky	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q);
1067185087Salfred	if (error) {
1068184610Salfred		if (errno == EINVAL) {
1069184610Salfred			return (LIBUSB20_ERROR_NOT_FOUND);
1070184610Salfred		}
1071184610Salfred	}
1072185087Salfred	return (error);
1073184610Salfred}
1074184610Salfred
1075184610Salfredstatic int
1076188987Sthompsaugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1077188987Sthompsa{
1078253339Shselasky	return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp));
1079188987Sthompsa}
1080188987Sthompsa
1081188987Sthompsastatic int
1082188987Sthompsaugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1083188987Sthompsa{
1084253339Shselasky	return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp));
1085188987Sthompsa}
1086