libusb20_ugen20.c revision 198376
1/* $FreeBSD: head/lib/libusb/libusb20_ugen20.c 198376 2009-10-22 21:01:41Z thompsa $ */
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/usb/usb.h>
43#include <dev/usb/usbdi.h>
44#include <dev/usb/usb_ioctl.h>
45
46static libusb20_init_backend_t ugen20_init_backend;
47static libusb20_open_device_t ugen20_open_device;
48static libusb20_close_device_t ugen20_close_device;
49static libusb20_get_backend_name_t ugen20_get_backend_name;
50static libusb20_exit_backend_t ugen20_exit_backend;
51static libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc;
52static libusb20_dev_get_info_t ugen20_dev_get_info;
53static libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk;
54static libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name;
55static libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk;
56static libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk;
57static libusb20_root_set_template_t ugen20_root_set_template;
58static libusb20_root_get_template_t ugen20_root_get_template;
59
60const struct libusb20_backend_methods libusb20_ugen20_backend = {
61	LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20)
62};
63
64/* USB device specific */
65static libusb20_get_config_desc_full_t ugen20_get_config_desc_full;
66static libusb20_get_config_index_t ugen20_get_config_index;
67static libusb20_set_config_index_t ugen20_set_config_index;
68static libusb20_set_alt_index_t ugen20_set_alt_index;
69static libusb20_reset_device_t ugen20_reset_device;
70static libusb20_set_power_mode_t ugen20_set_power_mode;
71static libusb20_get_power_mode_t ugen20_get_power_mode;
72static libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
73static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
74static libusb20_do_request_sync_t ugen20_do_request_sync;
75static libusb20_process_t ugen20_process;
76
77/* USB transfer specific */
78static libusb20_tr_open_t ugen20_tr_open;
79static libusb20_tr_close_t ugen20_tr_close;
80static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
81static libusb20_tr_submit_t ugen20_tr_submit;
82static libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
83
84static const struct libusb20_device_methods libusb20_ugen20_device_methods = {
85	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
86};
87
88static const char *
89ugen20_get_backend_name(void)
90{
91	return ("FreeBSD UGEN 2.0");
92}
93
94static uint32_t
95ugen20_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
121static int
122ugen20_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;
206done:
207	close(f);
208	return (error);
209}
210
211struct 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
222static int
223ugen20_readdir(struct ugen20_urd_state *st)
224{
225	;				/* style fix */
226repeat:
227	if (st->ptr == NULL) {
228		st->urd.urd_startentry += st->nparsed;
229		st->urd.urd_data = 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
259static int
260ugen20_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
294static void
295ugen20_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
312static int
313ugen20_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 = 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	}
349done:
350	return (error);
351}
352
353static int
354ugen20_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
400done:
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
415static int
416ugen20_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
436static void
437ugen20_exit_backend(struct libusb20_backend *pbe)
438{
439	return;				/* nothing to do */
440}
441
442static int
443ugen20_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	memset(&gen_desc, 0, sizeof(gen_desc));
453
454	gen_desc.ugd_data = &cdesc;
455	gen_desc.ugd_maxlen = sizeof(cdesc);
456	gen_desc.ugd_config_index = cfg_index;
457
458	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
459	if (error) {
460		return (LIBUSB20_ERROR_OTHER);
461	}
462	len = UGETW(cdesc.wTotalLength);
463	if (len < sizeof(cdesc)) {
464		/* corrupt descriptor */
465		return (LIBUSB20_ERROR_OTHER);
466	}
467	ptr = malloc(len);
468	if (!ptr) {
469		return (LIBUSB20_ERROR_NO_MEM);
470	}
471	gen_desc.ugd_data = ptr;
472	gen_desc.ugd_maxlen = len;
473
474	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
475	if (error) {
476		free(ptr);
477		return (LIBUSB20_ERROR_OTHER);
478	}
479	/* make sure that the device doesn't fool us */
480	memcpy(ptr, &cdesc, sizeof(cdesc));
481
482	*ppbuf = ptr;
483	*plen = len;
484
485	return (0);			/* success */
486}
487
488static int
489ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
490{
491	int temp;
492
493	if (ioctl(pdev->file_ctrl, USB_GET_CONFIG, &temp)) {
494		return (LIBUSB20_ERROR_OTHER);
495	}
496	*pindex = temp;
497
498	return (0);
499}
500
501static int
502ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
503{
504	int temp = cfg_index;
505
506	/* release all active USB transfers */
507	ugen20_tr_release(pdev);
508
509	if (ioctl(pdev->file_ctrl, USB_SET_CONFIG, &temp)) {
510		return (LIBUSB20_ERROR_OTHER);
511	}
512	return (ugen20_tr_renew(pdev));
513}
514
515static int
516ugen20_set_alt_index(struct libusb20_device *pdev,
517    uint8_t iface_index, uint8_t alt_index)
518{
519	struct usb_alt_interface alt_iface;
520
521	memset(&alt_iface, 0, sizeof(alt_iface));
522
523	alt_iface.uai_interface_index = iface_index;
524	alt_iface.uai_alt_index = alt_index;
525
526	/* release all active USB transfers */
527	ugen20_tr_release(pdev);
528
529	if (ioctl(pdev->file_ctrl, USB_SET_ALTINTERFACE, &alt_iface)) {
530		return (LIBUSB20_ERROR_OTHER);
531	}
532	return (ugen20_tr_renew(pdev));
533}
534
535static int
536ugen20_reset_device(struct libusb20_device *pdev)
537{
538	int temp = 0;
539
540	/* release all active USB transfers */
541	ugen20_tr_release(pdev);
542
543	if (ioctl(pdev->file_ctrl, USB_DEVICEENUMERATE, &temp)) {
544		return (LIBUSB20_ERROR_OTHER);
545	}
546	return (ugen20_tr_renew(pdev));
547}
548
549static int
550ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
551{
552	int temp;
553
554	switch (power_mode) {
555	case LIBUSB20_POWER_OFF:
556		temp = USB_POWER_MODE_OFF;
557		break;
558	case LIBUSB20_POWER_ON:
559		temp = USB_POWER_MODE_ON;
560		break;
561	case LIBUSB20_POWER_SAVE:
562		temp = USB_POWER_MODE_SAVE;
563		break;
564	case LIBUSB20_POWER_SUSPEND:
565		temp = USB_POWER_MODE_SUSPEND;
566		break;
567	case LIBUSB20_POWER_RESUME:
568		temp = USB_POWER_MODE_RESUME;
569		break;
570	default:
571		return (LIBUSB20_ERROR_INVALID_PARAM);
572	}
573	if (ioctl(pdev->file_ctrl, USB_SET_POWER_MODE, &temp)) {
574		return (LIBUSB20_ERROR_OTHER);
575	}
576	return (0);
577}
578
579static int
580ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
581{
582	int temp;
583
584	if (ioctl(pdev->file_ctrl, USB_GET_POWER_MODE, &temp)) {
585		return (LIBUSB20_ERROR_OTHER);
586	}
587	switch (temp) {
588	case USB_POWER_MODE_OFF:
589		temp = LIBUSB20_POWER_OFF;
590		break;
591	case USB_POWER_MODE_ON:
592		temp = LIBUSB20_POWER_ON;
593		break;
594	case USB_POWER_MODE_SAVE:
595		temp = LIBUSB20_POWER_SAVE;
596		break;
597	case USB_POWER_MODE_SUSPEND:
598		temp = LIBUSB20_POWER_SUSPEND;
599		break;
600	case USB_POWER_MODE_RESUME:
601		temp = LIBUSB20_POWER_RESUME;
602		break;
603	default:
604		temp = LIBUSB20_POWER_ON;
605		break;
606	}
607	*power_mode = temp;
608	return (0);			/* success */
609}
610
611static int
612ugen20_kernel_driver_active(struct libusb20_device *pdev,
613    uint8_t iface_index)
614{
615	int temp = iface_index;
616
617	if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_ACTIVE, &temp)) {
618		return (LIBUSB20_ERROR_OTHER);
619	}
620	return (0);			/* kernel driver is active */
621}
622
623static int
624ugen20_detach_kernel_driver(struct libusb20_device *pdev,
625    uint8_t iface_index)
626{
627	int temp = iface_index;
628
629	if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_DETACH, &temp)) {
630		return (LIBUSB20_ERROR_OTHER);
631	}
632	return (0);			/* kernel driver is active */
633}
634
635static int
636ugen20_do_request_sync(struct libusb20_device *pdev,
637    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
638    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
639{
640	struct usb_ctl_request req;
641
642	memset(&req, 0, sizeof(req));
643
644	req.ucr_data = data;
645	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
646		req.ucr_flags |= USB_SHORT_XFER_OK;
647	}
648	if (libusb20_me_encode(&req.ucr_request,
649	    sizeof(req.ucr_request), setup)) {
650		/* ignore */
651	}
652	if (ioctl(pdev->file_ctrl, USB_DO_REQUEST, &req)) {
653		return (LIBUSB20_ERROR_OTHER);
654	}
655	if (pactlen) {
656		/* get actual length */
657		*pactlen = req.ucr_actlen;
658	}
659	return (0);			/* kernel driver is active */
660}
661
662static int
663ugen20_process(struct libusb20_device *pdev)
664{
665	struct usb_fs_complete temp;
666	struct usb_fs_endpoint *fsep;
667	struct libusb20_transfer *xfer;
668
669	while (1) {
670
671		if (ioctl(pdev->file, USB_FS_COMPLETE, &temp)) {
672			if (errno == EBUSY) {
673				break;
674			} else {
675				/* device detached */
676				return (LIBUSB20_ERROR_OTHER);
677			}
678		}
679		fsep = pdev->privBeData;
680		xfer = pdev->pTransfer;
681		fsep += temp.ep_index;
682		xfer += temp.ep_index;
683
684		/* update transfer status */
685
686		if (fsep->status == 0) {
687			xfer->aFrames = fsep->aFrames;
688			xfer->timeComplete = fsep->isoc_time_complete;
689			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
690		} else if (fsep->status == USB_ERR_CANCELLED) {
691			xfer->aFrames = 0;
692			xfer->timeComplete = 0;
693			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
694		} else if (fsep->status == USB_ERR_STALLED) {
695			xfer->aFrames = 0;
696			xfer->timeComplete = 0;
697			xfer->status = LIBUSB20_TRANSFER_STALL;
698		} else if (fsep->status == USB_ERR_TIMEOUT) {
699			xfer->aFrames = 0;
700			xfer->timeComplete = 0;
701			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
702		} else {
703			xfer->aFrames = 0;
704			xfer->timeComplete = 0;
705			xfer->status = LIBUSB20_TRANSFER_ERROR;
706		}
707		libusb20_tr_callback_wrapper(xfer);
708	}
709	return (0);			/* done */
710}
711
712static int
713ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
714    uint32_t MaxFrameCount, uint8_t ep_no)
715{
716	struct usb_fs_open temp;
717	struct usb_fs_endpoint *fsep;
718
719	memset(&temp, 0, sizeof(temp));
720
721	fsep = xfer->pdev->privBeData;
722	fsep += xfer->trIndex;
723
724	temp.max_bufsize = MaxBufSize;
725	temp.max_frames = MaxFrameCount;
726	temp.ep_index = xfer->trIndex;
727	temp.ep_no = ep_no;
728
729	if (ioctl(xfer->pdev->file, USB_FS_OPEN, &temp)) {
730		return (LIBUSB20_ERROR_INVALID_PARAM);
731	}
732	/* maximums might have changed - update */
733	xfer->maxFrames = temp.max_frames;
734
735	/* "max_bufsize" should be multiple of "max_packet_length" */
736	xfer->maxTotalLength = temp.max_bufsize;
737	xfer->maxPacketLen = temp.max_packet_length;
738
739	/* setup buffer and length lists */
740	fsep->ppBuffer = xfer->ppBuffer;/* zero copy */
741	fsep->pLength = xfer->pLength;	/* zero copy */
742
743	return (0);			/* success */
744}
745
746static int
747ugen20_tr_close(struct libusb20_transfer *xfer)
748{
749	struct usb_fs_close temp;
750
751	memset(&temp, 0, sizeof(temp));
752
753	temp.ep_index = xfer->trIndex;
754
755	if (ioctl(xfer->pdev->file, USB_FS_CLOSE, &temp)) {
756		return (LIBUSB20_ERROR_INVALID_PARAM);
757	}
758	return (0);			/* success */
759}
760
761static int
762ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
763{
764	struct usb_fs_clear_stall_sync temp;
765
766	memset(&temp, 0, sizeof(temp));
767
768	/* if the transfer is active, an error will be returned */
769
770	temp.ep_index = xfer->trIndex;
771
772	if (ioctl(xfer->pdev->file, USB_FS_CLEAR_STALL_SYNC, &temp)) {
773		return (LIBUSB20_ERROR_INVALID_PARAM);
774	}
775	return (0);			/* success */
776}
777
778static void
779ugen20_tr_submit(struct libusb20_transfer *xfer)
780{
781	struct usb_fs_start temp;
782	struct usb_fs_endpoint *fsep;
783
784	memset(&temp, 0, sizeof(temp));
785
786	fsep = xfer->pdev->privBeData;
787	fsep += xfer->trIndex;
788
789	fsep->nFrames = xfer->nFrames;
790	fsep->flags = 0;
791	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
792		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
793	}
794	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
795		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
796	}
797	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
798		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
799	}
800	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
801		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
802	}
803	/* NOTE: The "fsep->timeout" variable is 16-bit. */
804	if (xfer->timeout > 65535)
805		fsep->timeout = 65535;
806	else
807		fsep->timeout = xfer->timeout;
808
809	temp.ep_index = xfer->trIndex;
810
811	if (ioctl(xfer->pdev->file, USB_FS_START, &temp)) {
812		/* ignore any errors - should never happen */
813	}
814	return;				/* success */
815}
816
817static void
818ugen20_tr_cancel_async(struct libusb20_transfer *xfer)
819{
820	struct usb_fs_stop temp;
821
822	memset(&temp, 0, sizeof(temp));
823
824	temp.ep_index = xfer->trIndex;
825
826	if (ioctl(xfer->pdev->file, USB_FS_STOP, &temp)) {
827		/* ignore any errors - should never happen */
828	}
829	return;
830}
831
832static int
833ugen20_be_ioctl(uint32_t cmd, void *data)
834{
835	int f;
836	int error;
837
838	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
839	if (f < 0)
840		return (LIBUSB20_ERROR_OTHER);
841	error = ioctl(f, cmd, data);
842	if (error == -1) {
843		if (errno == EPERM) {
844			error = LIBUSB20_ERROR_ACCESS;
845		} else {
846			error = LIBUSB20_ERROR_OTHER;
847		}
848	}
849	close(f);
850	return (error);
851}
852
853static int
854ugen20_dev_get_iface_desc(struct libusb20_device *pdev,
855    uint8_t iface_index, char *buf, uint8_t len)
856{
857	struct usb_gen_descriptor ugd;
858
859	memset(&ugd, 0, sizeof(ugd));
860
861	ugd.ugd_data = buf;
862	ugd.ugd_maxlen = len;
863	ugd.ugd_iface_index = iface_index;
864
865	if (ioctl(pdev->file, USB_GET_IFACE_DRIVER, &ugd)) {
866		return (LIBUSB20_ERROR_INVALID_PARAM);
867	}
868	return (0);
869}
870
871static int
872ugen20_dev_get_info(struct libusb20_device *pdev,
873    struct usb_device_info *pinfo)
874{
875	if (ioctl(pdev->file, USB_GET_DEVICEINFO, pinfo)) {
876		return (LIBUSB20_ERROR_INVALID_PARAM);
877	}
878	return (0);
879}
880
881static int
882ugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
883    uint16_t quirk_index, struct libusb20_quirk *pq)
884{
885	struct usb_gen_quirk q;
886	int error;
887
888	memset(&q, 0, sizeof(q));
889
890	q.index = quirk_index;
891
892	error = ugen20_be_ioctl(USB_DEV_QUIRK_GET, &q);
893
894	if (error) {
895		if (errno == EINVAL) {
896			return (LIBUSB20_ERROR_NOT_FOUND);
897		}
898	} else {
899		pq->vid = q.vid;
900		pq->pid = q.pid;
901		pq->bcdDeviceLow = q.bcdDeviceLow;
902		pq->bcdDeviceHigh = q.bcdDeviceHigh;
903		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
904	}
905	return (error);
906}
907
908static int
909ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
910    struct libusb20_quirk *pq)
911{
912	struct usb_gen_quirk q;
913	int error;
914
915	memset(&q, 0, sizeof(q));
916
917	q.index = quirk_index;
918
919	error = ugen20_be_ioctl(USB_QUIRK_NAME_GET, &q);
920
921	if (error) {
922		if (errno == EINVAL) {
923			return (LIBUSB20_ERROR_NOT_FOUND);
924		}
925	} else {
926		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
927	}
928	return (error);
929}
930
931static int
932ugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
933    struct libusb20_quirk *pq)
934{
935	struct usb_gen_quirk q;
936	int error;
937
938	memset(&q, 0, sizeof(q));
939
940	q.vid = pq->vid;
941	q.pid = pq->pid;
942	q.bcdDeviceLow = pq->bcdDeviceLow;
943	q.bcdDeviceHigh = pq->bcdDeviceHigh;
944	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
945
946	error = ugen20_be_ioctl(USB_DEV_QUIRK_ADD, &q);
947	if (error) {
948		if (errno == ENOMEM) {
949			return (LIBUSB20_ERROR_NO_MEM);
950		}
951	}
952	return (error);
953}
954
955static int
956ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
957    struct libusb20_quirk *pq)
958{
959	struct usb_gen_quirk q;
960	int error;
961
962	memset(&q, 0, sizeof(q));
963
964	q.vid = pq->vid;
965	q.pid = pq->pid;
966	q.bcdDeviceLow = pq->bcdDeviceLow;
967	q.bcdDeviceHigh = pq->bcdDeviceHigh;
968	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
969
970	error = ugen20_be_ioctl(USB_DEV_QUIRK_REMOVE, &q);
971	if (error) {
972		if (errno == EINVAL) {
973			return (LIBUSB20_ERROR_NOT_FOUND);
974		}
975	}
976	return (error);
977}
978
979static int
980ugen20_root_set_template(struct libusb20_backend *pbe, int temp)
981{
982	return (ugen20_be_ioctl(USB_SET_TEMPLATE, &temp));
983}
984
985static int
986ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
987{
988	return (ugen20_be_ioctl(USB_GET_TEMPLATE, ptemp));
989}
990