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