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