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