libusb20_ugen20.c revision 185290
1/* $FreeBSD: head/lib/libusb20/libusb20_ugen20.c 185290 2008-11-25 08:04:40Z 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 void
311ugen20_tr_release(struct libusb20_device *pdev)
312{
313	struct usb2_fs_uninit fs_uninit;
314
315	if (pdev->nTransfer == 0) {
316		return;
317	}
318	/* release all pending USB transfers */
319	if (pdev->privBeData != NULL) {
320		memset(&fs_uninit, 0, sizeof(fs_uninit));
321		if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) {
322			/* ignore any errors of this kind */
323		}
324	}
325	return;
326}
327
328static int
329ugen20_tr_renew(struct libusb20_device *pdev)
330{
331	struct usb2_fs_init fs_init;
332	struct usb2_fs_endpoint *pfse;
333	int error;
334	uint32_t size;
335	uint16_t nMaxTransfer;
336
337	nMaxTransfer = pdev->nTransfer;
338	error = 0;
339
340	if (nMaxTransfer == 0) {
341		goto done;
342	}
343	size = nMaxTransfer * sizeof(*pfse);
344
345	if (pdev->privBeData == NULL) {
346		pfse = malloc(size);
347		if (pfse == NULL) {
348			error = LIBUSB20_ERROR_NO_MEM;
349			goto done;
350		}
351		pdev->privBeData = pfse;
352	}
353	/* reset endpoint data */
354	memset(pdev->privBeData, 0, size);
355
356	memset(&fs_init, 0, sizeof(fs_init));
357
358	fs_init.pEndpoints = pdev->privBeData;
359	fs_init.ep_index_max = nMaxTransfer;
360
361	if (ioctl(pdev->file, USB_FS_INIT, &fs_init)) {
362		error = LIBUSB20_ERROR_OTHER;
363		goto done;
364	}
365done:
366	return (error);
367}
368
369static int
370ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
371{
372	uint32_t plugtime;
373	char buf[64];
374	int f;
375	int g;
376	int error;
377
378	snprintf(buf, sizeof(buf), "/dev/ugen%u.%u",
379	    pdev->bus_number, pdev->device_address);
380
381	/*
382	 * We need two file handles, one for the control endpoint and one
383	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
384	 * kernel locking.
385	 */
386	g = open(buf, O_RDWR);
387	if (g < 0) {
388		return (LIBUSB20_ERROR_NO_DEVICE);
389	}
390	f = open(buf, O_RDWR);
391	if (f < 0) {
392		close(g);
393		return (LIBUSB20_ERROR_NO_DEVICE);
394	}
395	if (ioctl(f, USB_GET_PLUGTIME, &plugtime)) {
396		error = LIBUSB20_ERROR_OTHER;
397		goto done;
398	}
399	/* check that the correct device is still plugged */
400	if (pdev->session_data.plugtime != plugtime) {
401		error = LIBUSB20_ERROR_NO_DEVICE;
402		goto done;
403	}
404	/* need to set this before "tr_renew()" */
405	pdev->file = f;
406	pdev->file_ctrl = g;
407
408	/* renew all USB transfers */
409	error = ugen20_tr_renew(pdev);
410	if (error) {
411		goto done;
412	}
413	/* set methods */
414	pdev->methods = &libusb20_ugen20_device_methods;
415
416done:
417	if (error) {
418		if (pdev->privBeData) {
419			/* cleanup after "tr_renew()" */
420			free(pdev->privBeData);
421			pdev->privBeData = NULL;
422		}
423		pdev->file = -1;
424		pdev->file_ctrl = -1;
425		close(f);
426		close(g);
427	}
428	return (error);
429}
430
431static int
432ugen20_close_device(struct libusb20_device *pdev)
433{
434	struct usb2_fs_uninit fs_uninit;
435
436	if (pdev->privBeData) {
437		memset(&fs_uninit, 0, sizeof(fs_uninit));
438		if (ioctl(pdev->file, USB_FS_UNINIT, &fs_uninit)) {
439			/* ignore this error */
440		}
441		free(pdev->privBeData);
442	}
443	pdev->nTransfer = 0;
444	pdev->privBeData = NULL;
445	close(pdev->file);
446	close(pdev->file_ctrl);
447	pdev->file = -1;
448	pdev->file_ctrl = -1;
449	return (0);			/* success */
450}
451
452static void
453ugen20_exit_backend(struct libusb20_backend *pbe)
454{
455	return;				/* nothing to do */
456}
457
458static int
459ugen20_get_config_desc_full(struct libusb20_device *pdev,
460    uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
461{
462	struct usb2_gen_descriptor gen_desc;
463	struct usb2_config_descriptor cdesc;
464	uint8_t *ptr;
465	uint16_t len;
466	int error;
467
468	memset(&gen_desc, 0, sizeof(gen_desc));
469
470	gen_desc.ugd_data = &cdesc;
471	gen_desc.ugd_maxlen = sizeof(cdesc);
472	gen_desc.ugd_config_index = cfg_index;
473
474	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
475	if (error) {
476		return (LIBUSB20_ERROR_OTHER);
477	}
478	len = UGETW(cdesc.wTotalLength);
479	if (len < sizeof(cdesc)) {
480		/* corrupt descriptor */
481		return (LIBUSB20_ERROR_OTHER);
482	}
483	ptr = malloc(len);
484	if (!ptr) {
485		return (LIBUSB20_ERROR_NO_MEM);
486	}
487	gen_desc.ugd_data = ptr;
488	gen_desc.ugd_maxlen = len;
489
490	error = ioctl(pdev->file_ctrl, USB_GET_FULL_DESC, &gen_desc);
491	if (error) {
492		free(ptr);
493		return (LIBUSB20_ERROR_OTHER);
494	}
495	/* make sure that the device doesn't fool us */
496	memcpy(ptr, &cdesc, sizeof(cdesc));
497
498	*ppbuf = ptr;
499	*plen = len;
500
501	return (0);			/* success */
502}
503
504static int
505ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
506{
507	int temp;
508
509	if (ioctl(pdev->file_ctrl, USB_GET_CONFIG, &temp)) {
510		return (LIBUSB20_ERROR_OTHER);
511	}
512	*pindex = temp;
513
514	return (0);
515}
516
517static int
518ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
519{
520	int temp = cfg_index;
521
522	/* release all active USB transfers */
523	ugen20_tr_release(pdev);
524
525	if (ioctl(pdev->file_ctrl, USB_SET_CONFIG, &temp)) {
526		return (LIBUSB20_ERROR_OTHER);
527	}
528	return (ugen20_tr_renew(pdev));
529}
530
531static int
532ugen20_claim_interface(struct libusb20_device *pdev, uint8_t iface_index)
533{
534	int temp = iface_index;
535
536	if (ioctl(pdev->file_ctrl, USB_CLAIM_INTERFACE, &temp)) {
537		return (LIBUSB20_ERROR_OTHER);
538	}
539	return (0);
540}
541
542static int
543ugen20_release_interface(struct libusb20_device *pdev, uint8_t iface_index)
544{
545	int temp = iface_index;
546
547	if (ioctl(pdev->file_ctrl, USB_RELEASE_INTERFACE, &temp)) {
548		return (LIBUSB20_ERROR_OTHER);
549	}
550	return (0);
551}
552
553static int
554ugen20_set_alt_index(struct libusb20_device *pdev,
555    uint8_t iface_index, uint8_t alt_index)
556{
557	struct usb2_alt_interface alt_iface;
558
559	memset(&alt_iface, 0, sizeof(alt_iface));
560
561	alt_iface.uai_interface_index = iface_index;
562	alt_iface.uai_alt_index = alt_index;
563
564	/* release all active USB transfers */
565	ugen20_tr_release(pdev);
566
567	if (ioctl(pdev->file_ctrl, USB_SET_ALTINTERFACE, &alt_iface)) {
568		return (LIBUSB20_ERROR_OTHER);
569	}
570	return (ugen20_tr_renew(pdev));
571}
572
573static int
574ugen20_reset_device(struct libusb20_device *pdev)
575{
576	int temp = 0;
577
578	/* release all active USB transfers */
579	ugen20_tr_release(pdev);
580
581	if (ioctl(pdev->file_ctrl, USB_DEVICEENUMERATE, &temp)) {
582		return (LIBUSB20_ERROR_OTHER);
583	}
584	return (ugen20_tr_renew(pdev));
585}
586
587static int
588ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
589{
590	int temp;
591
592	switch (power_mode) {
593	case LIBUSB20_POWER_OFF:
594		temp = USB_POWER_MODE_OFF;
595		break;
596	case LIBUSB20_POWER_ON:
597		temp = USB_POWER_MODE_ON;
598		break;
599	case LIBUSB20_POWER_SAVE:
600		temp = USB_POWER_MODE_SAVE;
601		break;
602	case LIBUSB20_POWER_SUSPEND:
603		temp = USB_POWER_MODE_SUSPEND;
604		break;
605	case LIBUSB20_POWER_RESUME:
606		temp = USB_POWER_MODE_RESUME;
607		break;
608	default:
609		return (LIBUSB20_ERROR_INVALID_PARAM);
610	}
611	if (ioctl(pdev->file_ctrl, USB_SET_POWER_MODE, &temp)) {
612		return (LIBUSB20_ERROR_OTHER);
613	}
614	return (0);
615}
616
617static int
618ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
619{
620	int temp;
621
622	if (ioctl(pdev->file_ctrl, USB_GET_POWER_MODE, &temp)) {
623		return (LIBUSB20_ERROR_OTHER);
624	}
625	switch (temp) {
626	case USB_POWER_MODE_OFF:
627		temp = LIBUSB20_POWER_OFF;
628		break;
629	case USB_POWER_MODE_ON:
630		temp = LIBUSB20_POWER_ON;
631		break;
632	case USB_POWER_MODE_SAVE:
633		temp = LIBUSB20_POWER_SAVE;
634		break;
635	case USB_POWER_MODE_SUSPEND:
636		temp = LIBUSB20_POWER_SUSPEND;
637		break;
638	case USB_POWER_MODE_RESUME:
639		temp = LIBUSB20_POWER_RESUME;
640		break;
641	default:
642		temp = LIBUSB20_POWER_ON;
643		break;
644	}
645	*power_mode = temp;
646	return (0);			/* success */
647}
648
649static int
650ugen20_kernel_driver_active(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_ACTIVE, &temp)) {
656		return (LIBUSB20_ERROR_OTHER);
657	}
658	return (0);			/* kernel driver is active */
659}
660
661static int
662ugen20_detach_kernel_driver(struct libusb20_device *pdev,
663    uint8_t iface_index)
664{
665	int temp = iface_index;
666
667	if (ioctl(pdev->file_ctrl, USB_IFACE_DRIVER_DETACH, &temp)) {
668		return (LIBUSB20_ERROR_OTHER);
669	}
670	return (0);			/* kernel driver is active */
671}
672
673static int
674ugen20_do_request_sync(struct libusb20_device *pdev,
675    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
676    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
677{
678	struct usb2_ctl_request req;
679
680	memset(&req, 0, sizeof(req));
681
682	req.ucr_data = data;
683	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
684		req.ucr_flags |= USB_SHORT_XFER_OK;
685	}
686	if (libusb20_me_encode(&req.ucr_request,
687	    sizeof(req.ucr_request), setup)) {
688		/* ignore */
689	}
690	if (ioctl(pdev->file_ctrl, USB_DO_REQUEST, &req)) {
691		return (LIBUSB20_ERROR_OTHER);
692	}
693	if (pactlen) {
694		/* get actual length */
695		*pactlen = req.ucr_actlen;
696	}
697	return (0);			/* kernel driver is active */
698}
699
700static int
701ugen20_process(struct libusb20_device *pdev)
702{
703	struct usb2_fs_complete temp;
704	struct usb2_fs_endpoint *fsep;
705	struct libusb20_transfer *xfer;
706
707	while (1) {
708
709		if (ioctl(pdev->file, USB_FS_COMPLETE, &temp)) {
710			if (errno == EBUSY) {
711				break;
712			} else {
713				/* device detached */
714				return (LIBUSB20_ERROR_OTHER);
715			}
716		}
717		fsep = pdev->privBeData;
718		xfer = pdev->pTransfer;
719		fsep += temp.ep_index;
720		xfer += temp.ep_index;
721
722		/* update transfer status */
723
724		if (fsep->status == 0) {
725			xfer->aFrames = fsep->aFrames;
726			xfer->timeComplete = fsep->isoc_time_complete;
727			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
728		} else if (fsep->status == USB_ERR_CANCELLED) {
729			xfer->aFrames = 0;
730			xfer->timeComplete = 0;
731			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
732		} else if (fsep->status == USB_ERR_STALLED) {
733			xfer->aFrames = 0;
734			xfer->timeComplete = 0;
735			xfer->status = LIBUSB20_TRANSFER_STALL;
736		} else if (fsep->status == USB_ERR_TIMEOUT) {
737			xfer->aFrames = 0;
738			xfer->timeComplete = 0;
739			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
740		} else {
741			xfer->aFrames = 0;
742			xfer->timeComplete = 0;
743			xfer->status = LIBUSB20_TRANSFER_ERROR;
744		}
745		libusb20_tr_callback_wrapper(xfer);
746	}
747	return (0);			/* done */
748}
749
750static int
751ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
752    uint32_t MaxFrameCount, uint8_t ep_no)
753{
754	struct usb2_fs_open temp;
755	struct usb2_fs_endpoint *fsep;
756
757	memset(&temp, 0, sizeof(temp));
758
759	fsep = xfer->pdev->privBeData;
760	fsep += xfer->trIndex;
761
762	temp.max_bufsize = MaxBufSize;
763	temp.max_frames = MaxFrameCount;
764	temp.ep_index = xfer->trIndex;
765	temp.ep_no = ep_no;
766
767	if (ioctl(xfer->pdev->file, USB_FS_OPEN, &temp)) {
768		return (LIBUSB20_ERROR_INVALID_PARAM);
769	}
770	/* maximums might have changed - update */
771	xfer->maxFrames = temp.max_frames;
772
773	/* "max_bufsize" should be multiple of "max_packet_length" */
774	xfer->maxTotalLength = temp.max_bufsize;
775	xfer->maxPacketLen = temp.max_packet_length;
776
777	/* setup buffer and length lists */
778	fsep->ppBuffer = xfer->ppBuffer;/* zero copy */
779	fsep->pLength = xfer->pLength;	/* zero copy */
780
781	return (0);			/* success */
782}
783
784static int
785ugen20_tr_close(struct libusb20_transfer *xfer)
786{
787	struct usb2_fs_close temp;
788
789	memset(&temp, 0, sizeof(temp));
790
791	temp.ep_index = xfer->trIndex;
792
793	if (ioctl(xfer->pdev->file, USB_FS_CLOSE, &temp)) {
794		return (LIBUSB20_ERROR_INVALID_PARAM);
795	}
796	return (0);			/* success */
797}
798
799static int
800ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
801{
802	struct usb2_fs_clear_stall_sync temp;
803
804	memset(&temp, 0, sizeof(temp));
805
806	/* if the transfer is active, an error will be returned */
807
808	temp.ep_index = xfer->trIndex;
809
810	if (ioctl(xfer->pdev->file, USB_FS_CLEAR_STALL_SYNC, &temp)) {
811		return (LIBUSB20_ERROR_INVALID_PARAM);
812	}
813	return (0);			/* success */
814}
815
816static void
817ugen20_tr_submit(struct libusb20_transfer *xfer)
818{
819	struct usb2_fs_start temp;
820	struct usb2_fs_endpoint *fsep;
821
822	memset(&temp, 0, sizeof(temp));
823
824	fsep = xfer->pdev->privBeData;
825	fsep += xfer->trIndex;
826
827	fsep->nFrames = xfer->nFrames;
828	fsep->flags = 0;
829	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
830		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
831	}
832	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
833		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
834	}
835	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
836		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
837	}
838	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
839		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
840	}
841	fsep->timeout = xfer->timeout;
842
843	temp.ep_index = xfer->trIndex;
844
845	if (ioctl(xfer->pdev->file, USB_FS_START, &temp)) {
846		/* ignore any errors - should never happen */
847	}
848	return;				/* success */
849}
850
851static void
852ugen20_tr_cancel_async(struct libusb20_transfer *xfer)
853{
854	struct usb2_fs_stop temp;
855
856	memset(&temp, 0, sizeof(temp));
857
858	temp.ep_index = xfer->trIndex;
859
860	if (ioctl(xfer->pdev->file, USB_FS_STOP, &temp)) {
861		/* ignore any errors - should never happen */
862	}
863	return;
864}
865
866static int
867ugen20_be_ioctl(uint32_t cmd, void *data)
868{
869	int f;
870	int error;
871
872	f = open("/dev/usb", O_RDONLY);
873	if (f < 0)
874		return (LIBUSB20_ERROR_OTHER);
875	error = ioctl(f, cmd, data);
876	if (error == -1) {
877		if (errno == EPERM) {
878			error = LIBUSB20_ERROR_ACCESS;
879		} else {
880			error = LIBUSB20_ERROR_OTHER;
881		}
882	}
883	close(f);
884	return (error);
885}
886
887static int
888ugen20_be_do_perm(uint32_t get_cmd, uint32_t set_cmd, uint8_t bus,
889    uint8_t dev, uint8_t iface, uid_t *uid,
890    gid_t *gid, mode_t *mode)
891{
892	struct usb2_dev_perm perm;
893	int error;
894
895	memset(&perm, 0, sizeof(perm));
896
897	perm.bus_index = bus;
898	perm.dev_index = dev;
899	perm.iface_index = iface;
900
901	error = ugen20_be_ioctl(get_cmd, &perm);
902	if (error)
903		return (error);
904
905	if (set_cmd == 0) {
906		if (uid)
907			*uid = perm.user_id;
908		if (gid)
909			*gid = perm.group_id;
910		if (mode)
911			*mode = perm.mode;
912		return (0);
913	}
914	if (uid)
915		perm.user_id = *uid;
916	if (gid)
917		perm.group_id = *gid;
918	if (mode)
919		perm.mode = *mode;
920
921	return (ugen20_be_ioctl(set_cmd, &perm));
922}
923
924static int
925ugen20_bus_set_owner(struct libusb20_backend *pbe,
926    uint8_t bus, uid_t user, gid_t group)
927{
928	return (ugen20_be_do_perm(USB_GET_BUS_PERM, USB_SET_BUS_PERM,
929	    bus, 0, 0, &user, &group, NULL));
930}
931
932static int
933ugen20_bus_get_owner(struct libusb20_backend *pbe, uint8_t bus,
934    uid_t *user, gid_t *group)
935{
936	return (ugen20_be_do_perm(USB_GET_BUS_PERM, 0,
937	    bus, 0, 0, user, group, NULL));
938}
939
940static int
941ugen20_bus_set_perm(struct libusb20_backend *pbe,
942    uint8_t bus, mode_t mode)
943{
944	return (ugen20_be_do_perm(USB_GET_BUS_PERM, USB_SET_BUS_PERM,
945	    bus, 0, 0, NULL, NULL, &mode));
946}
947
948static int
949ugen20_bus_get_perm(struct libusb20_backend *pbe,
950    uint8_t bus, mode_t *mode)
951{
952	return (ugen20_be_do_perm(USB_GET_BUS_PERM, 0,
953	    bus, 0, 0, NULL, NULL, mode));
954}
955
956static int
957ugen20_dev_get_iface_owner(struct libusb20_device *pdev,
958    uint8_t iface_index, uid_t *user, gid_t *group)
959{
960	return (ugen20_be_do_perm(USB_GET_IFACE_PERM, 0,
961	    pdev->bus_number, pdev->device_address, iface_index,
962	    user, group, NULL));
963}
964
965static int
966ugen20_dev_get_iface_perm(struct libusb20_device *pdev,
967    uint8_t iface_index, mode_t *mode)
968{
969	return (ugen20_be_do_perm(USB_GET_IFACE_PERM, 0,
970	    pdev->bus_number, pdev->device_address, iface_index,
971	    NULL, NULL, mode));
972}
973
974static int
975ugen20_dev_get_owner(struct libusb20_device *pdev,
976    uid_t *user, gid_t *group)
977{
978	return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, 0,
979	    pdev->bus_number, pdev->device_address, 0,
980	    user, group, NULL));
981}
982
983static int
984ugen20_dev_get_perm(struct libusb20_device *pdev, mode_t *mode)
985{
986	return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, 0,
987	    pdev->bus_number, pdev->device_address, 0,
988	    NULL, NULL, mode));
989}
990
991static int
992ugen20_dev_set_iface_owner(struct libusb20_device *pdev,
993    uint8_t iface_index, uid_t user, gid_t group)
994{
995	return (ugen20_be_do_perm(USB_GET_IFACE_PERM, USB_SET_IFACE_PERM,
996	    pdev->bus_number, pdev->device_address, iface_index,
997	    &user, &group, NULL));
998}
999
1000static int
1001ugen20_dev_set_iface_perm(struct libusb20_device *pdev,
1002    uint8_t iface_index, mode_t mode)
1003{
1004	return (ugen20_be_do_perm(USB_GET_IFACE_PERM, USB_SET_IFACE_PERM,
1005	    pdev->bus_number, pdev->device_address, iface_index,
1006	    NULL, NULL, &mode));
1007}
1008
1009static int
1010ugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
1011    uint16_t quirk_index, struct libusb20_quirk *pq)
1012{
1013	struct usb2_gen_quirk q;
1014	int error;
1015
1016	memset(&q, 0, sizeof(q));
1017
1018	q.index = quirk_index;
1019
1020	error = ugen20_be_ioctl(USB_DEV_QUIRK_GET, &q);
1021
1022	if (error) {
1023		if (errno == EINVAL) {
1024			return (LIBUSB20_ERROR_NOT_FOUND);
1025		}
1026	} else {
1027		pq->vid = q.vid;
1028		pq->pid = q.pid;
1029		pq->bcdDeviceLow = q.bcdDeviceLow;
1030		pq->bcdDeviceHigh = q.bcdDeviceHigh;
1031		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1032	}
1033	return (error);
1034}
1035
1036static int
1037ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
1038    struct libusb20_quirk *pq)
1039{
1040	struct usb2_gen_quirk q;
1041	int error;
1042
1043	memset(&q, 0, sizeof(q));
1044
1045	q.index = quirk_index;
1046
1047	error = ugen20_be_ioctl(USB_QUIRK_NAME_GET, &q);
1048
1049	if (error) {
1050		if (errno == EINVAL) {
1051			return (LIBUSB20_ERROR_NOT_FOUND);
1052		}
1053	} else {
1054		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1055	}
1056	return (error);
1057}
1058
1059static int
1060ugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
1061    struct libusb20_quirk *pq)
1062{
1063	struct usb2_gen_quirk q;
1064	int error;
1065
1066	memset(&q, 0, sizeof(q));
1067
1068	q.vid = pq->vid;
1069	q.pid = pq->pid;
1070	q.bcdDeviceLow = pq->bcdDeviceLow;
1071	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1072	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1073
1074	error = ugen20_be_ioctl(USB_DEV_QUIRK_ADD, &q);
1075	if (error) {
1076		if (errno == ENOMEM) {
1077			return (LIBUSB20_ERROR_NO_MEM);
1078		}
1079	}
1080	return (error);
1081}
1082
1083static int
1084ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
1085    struct libusb20_quirk *pq)
1086{
1087	struct usb2_gen_quirk q;
1088	int error;
1089
1090	memset(&q, 0, sizeof(q));
1091
1092	q.vid = pq->vid;
1093	q.pid = pq->pid;
1094	q.bcdDeviceLow = pq->bcdDeviceLow;
1095	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1096	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1097
1098	error = ugen20_be_ioctl(USB_DEV_QUIRK_REMOVE, &q);
1099	if (error) {
1100		if (errno == EINVAL) {
1101			return (LIBUSB20_ERROR_NOT_FOUND);
1102		}
1103	}
1104	return (error);
1105}
1106
1107static int
1108ugen20_dev_set_owner(struct libusb20_device *pdev,
1109    uid_t user, gid_t group)
1110{
1111	return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, USB_SET_DEVICE_PERM,
1112	    pdev->bus_number, pdev->device_address, 0,
1113	    &user, &group, NULL));
1114}
1115
1116static int
1117ugen20_dev_set_perm(struct libusb20_device *pdev, mode_t mode)
1118{
1119	return (ugen20_be_do_perm(USB_GET_DEVICE_PERM, USB_SET_DEVICE_PERM,
1120	    pdev->bus_number, pdev->device_address, 0,
1121	    NULL, NULL, &mode));
1122}
1123
1124static int
1125ugen20_root_set_owner(struct libusb20_backend *pbe,
1126    uid_t user, gid_t group)
1127{
1128	return (ugen20_be_do_perm(USB_GET_ROOT_PERM, USB_SET_ROOT_PERM, 0, 0, 0,
1129	    &user, &group, NULL));
1130}
1131
1132static int
1133ugen20_root_get_owner(struct libusb20_backend *pbe, uid_t *user, gid_t *group)
1134{
1135	return (ugen20_be_do_perm(USB_GET_ROOT_PERM, 0, 0, 0, 0,
1136	    user, group, NULL));
1137}
1138
1139static int
1140ugen20_root_set_perm(struct libusb20_backend *pbe, mode_t mode)
1141{
1142	return (ugen20_be_do_perm(USB_GET_ROOT_PERM, USB_SET_ROOT_PERM, 0, 0, 0,
1143	    NULL, NULL, &mode));
1144}
1145
1146static int
1147ugen20_root_get_perm(struct libusb20_backend *pbe, mode_t *mode)
1148{
1149	return (ugen20_be_do_perm(USB_GET_ROOT_PERM, 0, 0, 0, 0,
1150	    NULL, NULL, mode));
1151}
1152