xref: /freebsd/lib/libusb/libusb20_ugen20.c (revision 69e6d7b75e96c406d072cb83ffc9b26fbf1a86fb)
1 /* $FreeBSD$ */
2 /*-
3  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
28 #include LIBUSB_GLOBAL_INCLUDE_FILE
29 #else
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <time.h>
37 #include <sys/queue.h>
38 #include <sys/types.h>
39 #endif
40 
41 #include <dev/usb/usb.h>
42 #include <dev/usb/usbdi.h>
43 #include <dev/usb/usb_ioctl.h>
44 
45 #include "libusb20.h"
46 #include "libusb20_desc.h"
47 #include "libusb20_int.h"
48 
49 static libusb20_init_backend_t ugen20_init_backend;
50 static libusb20_open_device_t ugen20_open_device;
51 static libusb20_close_device_t ugen20_close_device;
52 static libusb20_get_backend_name_t ugen20_get_backend_name;
53 static libusb20_exit_backend_t ugen20_exit_backend;
54 static libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc;
55 static libusb20_dev_get_info_t ugen20_dev_get_info;
56 static libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk;
57 static libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name;
58 static libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk;
59 static libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk;
60 static libusb20_root_set_template_t ugen20_root_set_template;
61 static libusb20_root_get_template_t ugen20_root_get_template;
62 
63 const struct libusb20_backend_methods libusb20_ugen20_backend = {
64 	LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20)
65 };
66 
67 /* USB device specific */
68 static libusb20_get_config_desc_full_t ugen20_get_config_desc_full;
69 static libusb20_get_config_index_t ugen20_get_config_index;
70 static libusb20_set_config_index_t ugen20_set_config_index;
71 static libusb20_set_alt_index_t ugen20_set_alt_index;
72 static libusb20_reset_device_t ugen20_reset_device;
73 static libusb20_check_connected_t ugen20_check_connected;
74 static libusb20_set_power_mode_t ugen20_set_power_mode;
75 static libusb20_get_power_mode_t ugen20_get_power_mode;
76 static libusb20_get_power_usage_t ugen20_get_power_usage;
77 static libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
78 static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
79 static libusb20_do_request_sync_t ugen20_do_request_sync;
80 static libusb20_process_t ugen20_process;
81 
82 /* USB transfer specific */
83 static libusb20_tr_open_t ugen20_tr_open;
84 static libusb20_tr_close_t ugen20_tr_close;
85 static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
86 static libusb20_tr_submit_t ugen20_tr_submit;
87 static libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
88 
89 static const struct libusb20_device_methods libusb20_ugen20_device_methods = {
90 	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
91 };
92 
93 static const char *
94 ugen20_get_backend_name(void)
95 {
96 	return ("FreeBSD UGEN 2.0");
97 }
98 
99 static uint32_t
100 ugen20_path_convert_one(const char **pp)
101 {
102 	const char *ptr;
103 	uint32_t temp = 0;
104 
105 	ptr = *pp;
106 
107 	while ((*ptr >= '0') && (*ptr <= '9')) {
108 		temp *= 10;
109 		temp += (*ptr - '0');
110 		if (temp >= 1000000) {
111 			/* catch overflow early */
112 			return (0xFFFFFFFF);
113 		}
114 		ptr++;
115 	}
116 
117 	if (*ptr == '.') {
118 		/* skip dot */
119 		ptr++;
120 	}
121 	*pp = ptr;
122 
123 	return (temp);
124 }
125 
126 static int
127 ugen20_enumerate(struct libusb20_device *pdev, const char *id)
128 {
129 	const char *tmp = id;
130 	struct usb_device_descriptor ddesc;
131 	struct usb_device_info devinfo;
132 	uint32_t plugtime;
133 	char buf[64];
134 	int f;
135 	int error;
136 
137 	pdev->bus_number = ugen20_path_convert_one(&tmp);
138 	pdev->device_address = ugen20_path_convert_one(&tmp);
139 
140 	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
141 	    pdev->bus_number, pdev->device_address);
142 
143 	f = open(buf, O_RDWR);
144 	if (f < 0) {
145 		return (LIBUSB20_ERROR_OTHER);
146 	}
147 	if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) {
148 		error = LIBUSB20_ERROR_OTHER;
149 		goto done;
150 	}
151 	/* store when the device was plugged */
152 	pdev->session_data.plugtime = plugtime;
153 
154 	if (ioctl(f, USB_GET_DEVICE_DESC, &ddesc)) {
155 		error = LIBUSB20_ERROR_OTHER;
156 		goto done;
157 	}
158 	LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc));
159 
160 	libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc));
161 
162 	if (pdev->ddesc.bNumConfigurations == 0) {
163 		error = LIBUSB20_ERROR_OTHER;
164 		goto done;
165 	} else if (pdev->ddesc.bNumConfigurations >= 8) {
166 		error = LIBUSB20_ERROR_OTHER;
167 		goto done;
168 	}
169 	if (ioctl(f, USB_GET_DEVICEINFO, &devinfo)) {
170 		error = LIBUSB20_ERROR_OTHER;
171 		goto done;
172 	}
173 	switch (devinfo.udi_mode) {
174 	case USB_MODE_DEVICE:
175 		pdev->usb_mode = LIBUSB20_MODE_DEVICE;
176 		break;
177 	default:
178 		pdev->usb_mode = LIBUSB20_MODE_HOST;
179 		break;
180 	}
181 
182 	switch (devinfo.udi_speed) {
183 	case USB_SPEED_LOW:
184 		pdev->usb_speed = LIBUSB20_SPEED_LOW;
185 		break;
186 	case USB_SPEED_FULL:
187 		pdev->usb_speed = LIBUSB20_SPEED_FULL;
188 		break;
189 	case USB_SPEED_HIGH:
190 		pdev->usb_speed = LIBUSB20_SPEED_HIGH;
191 		break;
192 	case USB_SPEED_VARIABLE:
193 		pdev->usb_speed = LIBUSB20_SPEED_VARIABLE;
194 		break;
195 	case USB_SPEED_SUPER:
196 		pdev->usb_speed = LIBUSB20_SPEED_SUPER;
197 		break;
198 	default:
199 		pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN;
200 		break;
201 	}
202 
203 	/* get parent HUB index and port */
204 
205 	pdev->parent_address = devinfo.udi_hubindex;
206 	pdev->parent_port = devinfo.udi_hubport;
207 
208 	/* generate a nice description for printout */
209 
210 	snprintf(pdev->usb_desc, sizeof(pdev->usb_desc),
211 	    USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number,
212 	    pdev->device_address, devinfo.udi_product,
213 	    devinfo.udi_vendor, pdev->bus_number);
214 
215 	error = 0;
216 done:
217 	close(f);
218 	return (error);
219 }
220 
221 struct ugen20_urd_state {
222 	struct usb_read_dir urd;
223 	uint32_t nparsed;
224 	int	f;
225 	uint8_t *ptr;
226 	const char *src;
227 	const char *dst;
228 	uint8_t	buf[256];
229 	uint8_t	dummy_zero[1];
230 };
231 
232 static int
233 ugen20_readdir(struct ugen20_urd_state *st)
234 {
235 	;				/* style fix */
236 repeat:
237 	if (st->ptr == NULL) {
238 		st->urd.urd_startentry += st->nparsed;
239 		st->urd.urd_data = libusb20_pass_ptr(st->buf);
240 		st->urd.urd_maxlen = sizeof(st->buf);
241 		st->nparsed = 0;
242 
243 		if (ioctl(st->f, USB_READ_DIR, &st->urd)) {
244 			return (EINVAL);
245 		}
246 		st->ptr = st->buf;
247 	}
248 	if (st->ptr[0] == 0) {
249 		if (st->nparsed) {
250 			st->ptr = NULL;
251 			goto repeat;
252 		} else {
253 			return (ENXIO);
254 		}
255 	}
256 	st->src = (void *)(st->ptr + 1);
257 	st->dst = st->src + strlen(st->src) + 1;
258 	st->ptr = st->ptr + st->ptr[0];
259 	st->nparsed++;
260 
261 	if ((st->ptr < st->buf) ||
262 	    (st->ptr > st->dummy_zero)) {
263 		/* invalid entry */
264 		return (EINVAL);
265 	}
266 	return (0);
267 }
268 
269 static int
270 ugen20_init_backend(struct libusb20_backend *pbe)
271 {
272 	struct ugen20_urd_state state;
273 	struct libusb20_device *pdev;
274 
275 	memset(&state, 0, sizeof(state));
276 
277 	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
278 	if (state.f < 0)
279 		return (LIBUSB20_ERROR_OTHER);
280 
281 	while (ugen20_readdir(&state) == 0) {
282 
283 		if ((state.src[0] != 'u') ||
284 		    (state.src[1] != 'g') ||
285 		    (state.src[2] != 'e') ||
286 		    (state.src[3] != 'n')) {
287 			continue;
288 		}
289 		pdev = libusb20_dev_alloc();
290 		if (pdev == NULL) {
291 			continue;
292 		}
293 		if (ugen20_enumerate(pdev, state.src + 4)) {
294 			libusb20_dev_free(pdev);
295 			continue;
296 		}
297 		/* put the device on the backend list */
298 		libusb20_be_enqueue_device(pbe, pdev);
299 	}
300 	close(state.f);
301 	return (0);			/* success */
302 }
303 
304 static void
305 ugen20_tr_release(struct libusb20_device *pdev)
306 {
307 	struct usb_fs_uninit fs_uninit;
308 
309 	if (pdev->nTransfer == 0) {
310 		return;
311 	}
312 	/* release all pending USB transfers */
313 	if (pdev->privBeData != NULL) {
314 		memset(&fs_uninit, 0, sizeof(fs_uninit));
315 		if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) {
316 			/* ignore any errors of this kind */
317 		}
318 	}
319 	return;
320 }
321 
322 static int
323 ugen20_tr_renew(struct libusb20_device *pdev)
324 {
325 	struct usb_fs_init fs_init;
326 	struct usb_fs_endpoint *pfse;
327 	int error;
328 	uint32_t size;
329 	uint16_t nMaxTransfer;
330 
331 	nMaxTransfer = pdev->nTransfer;
332 	error = 0;
333 
334 	if (nMaxTransfer == 0) {
335 		goto done;
336 	}
337 	size = nMaxTransfer * sizeof(*pfse);
338 
339 	if (pdev->privBeData == NULL) {
340 		pfse = malloc(size);
341 		if (pfse == NULL) {
342 			error = LIBUSB20_ERROR_NO_MEM;
343 			goto done;
344 		}
345 		pdev->privBeData = pfse;
346 	}
347 	/* reset endpoint data */
348 	memset(pdev->privBeData, 0, size);
349 
350 	memset(&fs_init, 0, sizeof(fs_init));
351 
352 	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
353 	fs_init.ep_index_max = nMaxTransfer;
354 
355 	if (ioctl(pdev->file, USB_FS_INIT, &fs_init)) {
356 		error = LIBUSB20_ERROR_OTHER;
357 		goto done;
358 	}
359 done:
360 	return (error);
361 }
362 
363 static int
364 ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
365 {
366 	uint32_t plugtime;
367 	char buf[64];
368 	int f;
369 	int g;
370 	int error;
371 
372 	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
373 	    pdev->bus_number, pdev->device_address);
374 
375 	/*
376 	 * We need two file handles, one for the control endpoint and one
377 	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
378 	 * kernel locking.
379 	 */
380 	g = open(buf, O_RDWR);
381 	if (g < 0) {
382 		return (LIBUSB20_ERROR_NO_DEVICE);
383 	}
384 	f = open(buf, O_RDWR);
385 	if (f < 0) {
386 		close(g);
387 		return (LIBUSB20_ERROR_NO_DEVICE);
388 	}
389 	if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) {
390 		error = LIBUSB20_ERROR_OTHER;
391 		goto done;
392 	}
393 	/* check that the correct device is still plugged */
394 	if (pdev->session_data.plugtime != plugtime) {
395 		error = LIBUSB20_ERROR_NO_DEVICE;
396 		goto done;
397 	}
398 	/* need to set this before "tr_renew()" */
399 	pdev->file = f;
400 	pdev->file_ctrl = g;
401 
402 	/* renew all USB transfers */
403 	error = ugen20_tr_renew(pdev);
404 	if (error) {
405 		goto done;
406 	}
407 	/* set methods */
408 	pdev->methods = &libusb20_ugen20_device_methods;
409 
410 done:
411 	if (error) {
412 		if (pdev->privBeData) {
413 			/* cleanup after "tr_renew()" */
414 			free(pdev->privBeData);
415 			pdev->privBeData = NULL;
416 		}
417 		pdev->file = -1;
418 		pdev->file_ctrl = -1;
419 		close(f);
420 		close(g);
421 	}
422 	return (error);
423 }
424 
425 static int
426 ugen20_close_device(struct libusb20_device *pdev)
427 {
428 	struct usb_fs_uninit fs_uninit;
429 
430 	if (pdev->privBeData) {
431 		memset(&fs_uninit, 0, sizeof(fs_uninit));
432 		if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) {
433 			/* ignore this error */
434 		}
435 		free(pdev->privBeData);
436 	}
437 	pdev->nTransfer = 0;
438 	pdev->privBeData = NULL;
439 	close(pdev->file);
440 	close(pdev->file_ctrl);
441 	pdev->file = -1;
442 	pdev->file_ctrl = -1;
443 	return (0);			/* success */
444 }
445 
446 static void
447 ugen20_exit_backend(struct libusb20_backend *pbe)
448 {
449 	return;				/* nothing to do */
450 }
451 
452 static int
453 ugen20_get_config_desc_full(struct libusb20_device *pdev,
454     uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
455 {
456 	struct usb_gen_descriptor gen_desc;
457 	struct usb_config_descriptor cdesc;
458 	uint8_t *ptr;
459 	uint16_t len;
460 	int error;
461 
462 	/* make sure memory is initialised */
463 	memset(&cdesc, 0, sizeof(cdesc));
464 	memset(&gen_desc, 0, sizeof(gen_desc));
465 
466 	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
467 	gen_desc.ugd_maxlen = sizeof(cdesc);
468 	gen_desc.ugd_config_index = cfg_index;
469 
470 	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
471 	if (error) {
472 		return (LIBUSB20_ERROR_OTHER);
473 	}
474 	len = UGETW(cdesc.wTotalLength);
475 	if (len < sizeof(cdesc)) {
476 		/* corrupt descriptor */
477 		return (LIBUSB20_ERROR_OTHER);
478 	}
479 	ptr = malloc(len);
480 	if (!ptr) {
481 		return (LIBUSB20_ERROR_NO_MEM);
482 	}
483 
484 	/* make sure memory is initialised */
485 	memset(ptr, 0, len);
486 
487 	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
488 	gen_desc.ugd_maxlen = len;
489 
490 	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
491 	if (error) {
492 		free(ptr);
493 		return (LIBUSB20_ERROR_OTHER);
494 	}
495 	/* make sure that the device doesn't fool us */
496 	memcpy(ptr, &cdesc, sizeof(cdesc));
497 
498 	*ppbuf = ptr;
499 	*plen = len;
500 
501 	return (0);			/* success */
502 }
503 
504 static int
505 ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
506 {
507 	int temp;
508 
509 	if (ioctl(pdev->file_ctrl, USB_GET_CONFIG, &temp)) {
510 		return (LIBUSB20_ERROR_OTHER);
511 	}
512 	*pindex = temp;
513 
514 	return (0);
515 }
516 
517 static int
518 ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
519 {
520 	int temp = cfg_index;
521 
522 	/* release all active USB transfers */
523 	ugen20_tr_release(pdev);
524 
525 	if (ioctl(pdev->file_ctrl, USB_SET_CONFIG, &temp)) {
526 		return (LIBUSB20_ERROR_OTHER);
527 	}
528 	return (ugen20_tr_renew(pdev));
529 }
530 
531 static int
532 ugen20_set_alt_index(struct libusb20_device *pdev,
533     uint8_t iface_index, uint8_t alt_index)
534 {
535 	struct usb_alt_interface alt_iface;
536 
537 	memset(&alt_iface, 0, sizeof(alt_iface));
538 
539 	alt_iface.uai_interface_index = iface_index;
540 	alt_iface.uai_alt_index = alt_index;
541 
542 	/* release all active USB transfers */
543 	ugen20_tr_release(pdev);
544 
545 	if (ioctl(pdev->file_ctrl, USB_SET_ALTINTERFACE, &alt_iface)) {
546 		return (LIBUSB20_ERROR_OTHER);
547 	}
548 	return (ugen20_tr_renew(pdev));
549 }
550 
551 static int
552 ugen20_reset_device(struct libusb20_device *pdev)
553 {
554 	int temp = 0;
555 
556 	/* release all active USB transfers */
557 	ugen20_tr_release(pdev);
558 
559 	if (ioctl(pdev->file_ctrl, USB_DEVICEENUMERATE, &temp)) {
560 		return (LIBUSB20_ERROR_OTHER);
561 	}
562 	return (ugen20_tr_renew(pdev));
563 }
564 
565 static int
566 ugen20_check_connected(struct libusb20_device *pdev)
567 {
568 	uint32_t plugtime;
569 	int error = 0;
570 
571 	if (ioctl(pdev->file_ctrl, USB_GET_PLUGTIME, &plugtime)) {
572 		error = LIBUSB20_ERROR_NO_DEVICE;
573 		goto done;
574 	}
575 
576 	if (pdev->session_data.plugtime != plugtime) {
577 		error = LIBUSB20_ERROR_NO_DEVICE;
578 		goto done;
579 	}
580 done:
581 	return (error);
582 }
583 
584 static int
585 ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
586 {
587 	int temp;
588 
589 	switch (power_mode) {
590 	case LIBUSB20_POWER_OFF:
591 		temp = USB_POWER_MODE_OFF;
592 		break;
593 	case LIBUSB20_POWER_ON:
594 		temp = USB_POWER_MODE_ON;
595 		break;
596 	case LIBUSB20_POWER_SAVE:
597 		temp = USB_POWER_MODE_SAVE;
598 		break;
599 	case LIBUSB20_POWER_SUSPEND:
600 		temp = USB_POWER_MODE_SUSPEND;
601 		break;
602 	case LIBUSB20_POWER_RESUME:
603 		temp = USB_POWER_MODE_RESUME;
604 		break;
605 	default:
606 		return (LIBUSB20_ERROR_INVALID_PARAM);
607 	}
608 	if (ioctl(pdev->file_ctrl, USB_SET_POWER_MODE, &temp)) {
609 		return (LIBUSB20_ERROR_OTHER);
610 	}
611 	return (0);
612 }
613 
614 static int
615 ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
616 {
617 	int temp;
618 
619 	if (ioctl(pdev->file_ctrl, USB_GET_POWER_MODE, &temp)) {
620 		return (LIBUSB20_ERROR_OTHER);
621 	}
622 	switch (temp) {
623 	case USB_POWER_MODE_OFF:
624 		temp = LIBUSB20_POWER_OFF;
625 		break;
626 	case USB_POWER_MODE_ON:
627 		temp = LIBUSB20_POWER_ON;
628 		break;
629 	case USB_POWER_MODE_SAVE:
630 		temp = LIBUSB20_POWER_SAVE;
631 		break;
632 	case USB_POWER_MODE_SUSPEND:
633 		temp = LIBUSB20_POWER_SUSPEND;
634 		break;
635 	case USB_POWER_MODE_RESUME:
636 		temp = LIBUSB20_POWER_RESUME;
637 		break;
638 	default:
639 		temp = LIBUSB20_POWER_ON;
640 		break;
641 	}
642 	*power_mode = temp;
643 	return (0);			/* success */
644 }
645 
646 static int
647 ugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
648 {
649 	int temp;
650 
651 	if (ioctl(pdev->file_ctrl, USB_GET_POWER_USAGE, &temp)) {
652 		return (LIBUSB20_ERROR_OTHER);
653 	}
654 	*power_usage = temp;
655 	return (0);			/* success */
656 }
657 
658 static int
659 ugen20_kernel_driver_active(struct libusb20_device *pdev,
660     uint8_t iface_index)
661 {
662 	int temp = iface_index;
663 
664 	if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_ACTIVE, &temp)) {
665 		return (LIBUSB20_ERROR_OTHER);
666 	}
667 	return (0);			/* kernel driver is active */
668 }
669 
670 static int
671 ugen20_detach_kernel_driver(struct libusb20_device *pdev,
672     uint8_t iface_index)
673 {
674 	int temp = iface_index;
675 
676 	if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_DETACH, &temp)) {
677 		return (LIBUSB20_ERROR_OTHER);
678 	}
679 	return (0);			/* kernel driver is active */
680 }
681 
682 static int
683 ugen20_do_request_sync(struct libusb20_device *pdev,
684     struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
685     void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
686 {
687 	struct usb_ctl_request req;
688 
689 	memset(&req, 0, sizeof(req));
690 
691 	req.ucr_data = libusb20_pass_ptr(data);
692 	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
693 		req.ucr_flags |= USB_SHORT_XFER_OK;
694 	}
695 	if (libusb20_me_encode(&req.ucr_request,
696 	    sizeof(req.ucr_request), setup)) {
697 		/* ignore */
698 	}
699 	if (ioctl(pdev->file_ctrl, USB_DO_REQUEST, &req)) {
700 		return (LIBUSB20_ERROR_OTHER);
701 	}
702 	if (pactlen) {
703 		/* get actual length */
704 		*pactlen = req.ucr_actlen;
705 	}
706 	return (0);			/* kernel driver is active */
707 }
708 
709 static int
710 ugen20_process(struct libusb20_device *pdev)
711 {
712 	struct usb_fs_complete temp;
713 	struct usb_fs_endpoint *fsep;
714 	struct libusb20_transfer *xfer;
715 
716 	while (1) {
717 
718 		if (ioctl(pdev->file, USB_FS_COMPLETE, &temp)) {
719 			if (errno == EBUSY) {
720 				break;
721 			} else {
722 				/* device detached */
723 				return (LIBUSB20_ERROR_OTHER);
724 			}
725 		}
726 		fsep = pdev->privBeData;
727 		xfer = pdev->pTransfer;
728 		fsep += temp.ep_index;
729 		xfer += temp.ep_index;
730 
731 		/* update transfer status */
732 
733 		if (fsep->status == 0) {
734 			xfer->aFrames = fsep->aFrames;
735 			xfer->timeComplete = fsep->isoc_time_complete;
736 			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
737 		} else if (fsep->status == USB_ERR_CANCELLED) {
738 			xfer->aFrames = 0;
739 			xfer->timeComplete = 0;
740 			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
741 		} else if (fsep->status == USB_ERR_STALLED) {
742 			xfer->aFrames = 0;
743 			xfer->timeComplete = 0;
744 			xfer->status = LIBUSB20_TRANSFER_STALL;
745 		} else if (fsep->status == USB_ERR_TIMEOUT) {
746 			xfer->aFrames = 0;
747 			xfer->timeComplete = 0;
748 			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
749 		} else {
750 			xfer->aFrames = 0;
751 			xfer->timeComplete = 0;
752 			xfer->status = LIBUSB20_TRANSFER_ERROR;
753 		}
754 		libusb20_tr_callback_wrapper(xfer);
755 	}
756 	return (0);			/* done */
757 }
758 
759 static int
760 ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
761     uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
762     uint8_t pre_scale)
763 {
764 	union {
765 		struct usb_fs_open fs_open;
766 		struct usb_fs_open_stream fs_open_stream;
767 	} temp;
768 	struct usb_fs_endpoint *fsep;
769 
770 	if (pre_scale)
771 		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
772 
773 	memset(&temp, 0, sizeof(temp));
774 
775 	fsep = xfer->pdev->privBeData;
776 	fsep += xfer->trIndex;
777 
778 	temp.fs_open.max_bufsize = MaxBufSize;
779 	temp.fs_open.max_frames = MaxFrameCount;
780 	temp.fs_open.ep_index = xfer->trIndex;
781 	temp.fs_open.ep_no = ep_no;
782 
783 	if (stream_id != 0) {
784 		temp.fs_open_stream.stream_id = stream_id;
785 
786 		if (ioctl(xfer->pdev->file, USB_FS_OPEN_STREAM, &temp.fs_open_stream))
787 			return (LIBUSB20_ERROR_INVALID_PARAM);
788 	} else {
789 		if (ioctl(xfer->pdev->file, USB_FS_OPEN, &temp.fs_open))
790 			return (LIBUSB20_ERROR_INVALID_PARAM);
791 	}
792 	/* maximums might have changed - update */
793 	xfer->maxFrames = temp.fs_open.max_frames;
794 
795 	/* "max_bufsize" should be multiple of "max_packet_length" */
796 	xfer->maxTotalLength = temp.fs_open.max_bufsize;
797 	xfer->maxPacketLen = temp.fs_open.max_packet_length;
798 
799 	/* setup buffer and length lists using zero copy */
800 	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
801 	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
802 
803 	return (0);			/* success */
804 }
805 
806 static int
807 ugen20_tr_close(struct libusb20_transfer *xfer)
808 {
809 	struct usb_fs_close temp;
810 
811 	memset(&temp, 0, sizeof(temp));
812 
813 	temp.ep_index = xfer->trIndex;
814 
815 	if (ioctl(xfer->pdev->file, USB_FS_CLOSE, &temp)) {
816 		return (LIBUSB20_ERROR_INVALID_PARAM);
817 	}
818 	return (0);			/* success */
819 }
820 
821 static int
822 ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
823 {
824 	struct usb_fs_clear_stall_sync temp;
825 
826 	memset(&temp, 0, sizeof(temp));
827 
828 	/* if the transfer is active, an error will be returned */
829 
830 	temp.ep_index = xfer->trIndex;
831 
832 	if (ioctl(xfer->pdev->file, USB_FS_CLEAR_STALL_SYNC, &temp)) {
833 		return (LIBUSB20_ERROR_INVALID_PARAM);
834 	}
835 	return (0);			/* success */
836 }
837 
838 static void
839 ugen20_tr_submit(struct libusb20_transfer *xfer)
840 {
841 	struct usb_fs_start temp;
842 	struct usb_fs_endpoint *fsep;
843 
844 	memset(&temp, 0, sizeof(temp));
845 
846 	fsep = xfer->pdev->privBeData;
847 	fsep += xfer->trIndex;
848 
849 	fsep->nFrames = xfer->nFrames;
850 	fsep->flags = 0;
851 	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
852 		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
853 	}
854 	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
855 		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
856 	}
857 	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
858 		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
859 	}
860 	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
861 		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
862 	}
863 	/* NOTE: The "fsep->timeout" variable is 16-bit. */
864 	if (xfer->timeout > 65535)
865 		fsep->timeout = 65535;
866 	else
867 		fsep->timeout = xfer->timeout;
868 
869 	temp.ep_index = xfer->trIndex;
870 
871 	if (ioctl(xfer->pdev->file, USB_FS_START, &temp)) {
872 		/* ignore any errors - should never happen */
873 	}
874 	return;				/* success */
875 }
876 
877 static void
878 ugen20_tr_cancel_async(struct libusb20_transfer *xfer)
879 {
880 	struct usb_fs_stop temp;
881 
882 	memset(&temp, 0, sizeof(temp));
883 
884 	temp.ep_index = xfer->trIndex;
885 
886 	if (ioctl(xfer->pdev->file, USB_FS_STOP, &temp)) {
887 		/* ignore any errors - should never happen */
888 	}
889 	return;
890 }
891 
892 static int
893 ugen20_be_ioctl(uint32_t cmd, void *data)
894 {
895 	int f;
896 	int error;
897 
898 	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
899 	if (f < 0)
900 		return (LIBUSB20_ERROR_OTHER);
901 	error = ioctl(f, cmd, data);
902 	if (error == -1) {
903 		if (errno == EPERM) {
904 			error = LIBUSB20_ERROR_ACCESS;
905 		} else {
906 			error = LIBUSB20_ERROR_OTHER;
907 		}
908 	}
909 	close(f);
910 	return (error);
911 }
912 
913 static int
914 ugen20_dev_get_iface_desc(struct libusb20_device *pdev,
915     uint8_t iface_index, char *buf, uint8_t len)
916 {
917 	struct usb_gen_descriptor ugd;
918 
919 	memset(&ugd, 0, sizeof(ugd));
920 
921 	ugd.ugd_data = libusb20_pass_ptr(buf);
922 	ugd.ugd_maxlen = len;
923 	ugd.ugd_iface_index = iface_index;
924 
925 	if (ioctl(pdev->file, USB_GET_IFACE_DRIVER, &ugd)) {
926 		return (LIBUSB20_ERROR_INVALID_PARAM);
927 	}
928 	return (0);
929 }
930 
931 static int
932 ugen20_dev_get_info(struct libusb20_device *pdev,
933     struct usb_device_info *pinfo)
934 {
935 	if (ioctl(pdev->file, USB_GET_DEVICEINFO, pinfo)) {
936 		return (LIBUSB20_ERROR_INVALID_PARAM);
937 	}
938 	return (0);
939 }
940 
941 static int
942 ugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
943     uint16_t quirk_index, struct libusb20_quirk *pq)
944 {
945 	struct usb_gen_quirk q;
946 	int error;
947 
948 	memset(&q, 0, sizeof(q));
949 
950 	q.index = quirk_index;
951 
952 	error = ugen20_be_ioctl(USB_DEV_QUIRK_GET, &q);
953 
954 	if (error) {
955 		if (errno == EINVAL) {
956 			return (LIBUSB20_ERROR_NOT_FOUND);
957 		}
958 	} else {
959 		pq->vid = q.vid;
960 		pq->pid = q.pid;
961 		pq->bcdDeviceLow = q.bcdDeviceLow;
962 		pq->bcdDeviceHigh = q.bcdDeviceHigh;
963 		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
964 	}
965 	return (error);
966 }
967 
968 static int
969 ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
970     struct libusb20_quirk *pq)
971 {
972 	struct usb_gen_quirk q;
973 	int error;
974 
975 	memset(&q, 0, sizeof(q));
976 
977 	q.index = quirk_index;
978 
979 	error = ugen20_be_ioctl(USB_QUIRK_NAME_GET, &q);
980 
981 	if (error) {
982 		if (errno == EINVAL) {
983 			return (LIBUSB20_ERROR_NOT_FOUND);
984 		}
985 	} else {
986 		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
987 	}
988 	return (error);
989 }
990 
991 static int
992 ugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
993     struct libusb20_quirk *pq)
994 {
995 	struct usb_gen_quirk q;
996 	int error;
997 
998 	memset(&q, 0, sizeof(q));
999 
1000 	q.vid = pq->vid;
1001 	q.pid = pq->pid;
1002 	q.bcdDeviceLow = pq->bcdDeviceLow;
1003 	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1004 	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1005 
1006 	error = ugen20_be_ioctl(USB_DEV_QUIRK_ADD, &q);
1007 	if (error) {
1008 		if (errno == ENOMEM) {
1009 			return (LIBUSB20_ERROR_NO_MEM);
1010 		}
1011 	}
1012 	return (error);
1013 }
1014 
1015 static int
1016 ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
1017     struct libusb20_quirk *pq)
1018 {
1019 	struct usb_gen_quirk q;
1020 	int error;
1021 
1022 	memset(&q, 0, sizeof(q));
1023 
1024 	q.vid = pq->vid;
1025 	q.pid = pq->pid;
1026 	q.bcdDeviceLow = pq->bcdDeviceLow;
1027 	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1028 	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1029 
1030 	error = ugen20_be_ioctl(USB_DEV_QUIRK_REMOVE, &q);
1031 	if (error) {
1032 		if (errno == EINVAL) {
1033 			return (LIBUSB20_ERROR_NOT_FOUND);
1034 		}
1035 	}
1036 	return (error);
1037 }
1038 
1039 static int
1040 ugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1041 {
1042 	return (ugen20_be_ioctl(USB_SET_TEMPLATE, &temp));
1043 }
1044 
1045 static int
1046 ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1047 {
1048 	return (ugen20_be_ioctl(USB_GET_TEMPLATE, ptemp));
1049 }
1050