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