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