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