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