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