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