1/* $FreeBSD: stable/10/lib/libusb/libusb20_ugen20.c 356399 2020-01-06 09:22:33Z hselasky $ */
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_get_stats_t ugen20_get_stats;
83static libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
84static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
85static libusb20_do_request_sync_t ugen20_do_request_sync;
86static libusb20_process_t ugen20_process;
87
88/* USB transfer specific */
89static libusb20_tr_open_t ugen20_tr_open;
90static libusb20_tr_close_t ugen20_tr_close;
91static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
92static libusb20_tr_submit_t ugen20_tr_submit;
93static libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
94
95static const struct libusb20_device_methods libusb20_ugen20_device_methods = {
96	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
97};
98
99static const char *
100ugen20_get_backend_name(void)
101{
102	return ("FreeBSD UGEN 2.0");
103}
104
105static uint32_t
106ugen20_path_convert_one(const char **pp)
107{
108	const char *ptr;
109	uint32_t temp = 0;
110
111	ptr = *pp;
112
113	while ((*ptr >= '0') && (*ptr <= '9')) {
114		temp *= 10;
115		temp += (*ptr - '0');
116		if (temp >= 1000000) {
117			/* catch overflow early */
118			return (0xFFFFFFFF);
119		}
120		ptr++;
121	}
122
123	if (*ptr == '.') {
124		/* skip dot */
125		ptr++;
126	}
127	*pp = ptr;
128
129	return (temp);
130}
131
132static int
133ugen20_enumerate(struct libusb20_device *pdev, const char *id)
134{
135	const char *tmp = id;
136	struct usb_device_descriptor ddesc;
137	struct usb_device_info devinfo;
138	uint32_t plugtime;
139	char buf[64];
140	int f;
141	int error;
142
143	pdev->bus_number = ugen20_path_convert_one(&tmp);
144	pdev->device_address = ugen20_path_convert_one(&tmp);
145
146	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
147	    pdev->bus_number, pdev->device_address);
148
149	f = open(buf, O_RDWR);
150	if (f < 0) {
151		return (LIBUSB20_ERROR_OTHER);
152	}
153	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
154		error = LIBUSB20_ERROR_OTHER;
155		goto done;
156	}
157	/* store when the device was plugged */
158	pdev->session_data.plugtime = plugtime;
159
160	if (ioctl(f, IOUSB(USB_GET_DEVICE_DESC), &ddesc)) {
161		error = LIBUSB20_ERROR_OTHER;
162		goto done;
163	}
164	LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc));
165
166	libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc));
167
168	if (pdev->ddesc.bNumConfigurations == 0) {
169		error = LIBUSB20_ERROR_OTHER;
170		goto done;
171	} else if (pdev->ddesc.bNumConfigurations >= 8) {
172		error = LIBUSB20_ERROR_OTHER;
173		goto done;
174	}
175	if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) {
176		error = LIBUSB20_ERROR_OTHER;
177		goto done;
178	}
179	switch (devinfo.udi_mode) {
180	case USB_MODE_DEVICE:
181		pdev->usb_mode = LIBUSB20_MODE_DEVICE;
182		break;
183	default:
184		pdev->usb_mode = LIBUSB20_MODE_HOST;
185		break;
186	}
187
188	switch (devinfo.udi_speed) {
189	case USB_SPEED_LOW:
190		pdev->usb_speed = LIBUSB20_SPEED_LOW;
191		break;
192	case USB_SPEED_FULL:
193		pdev->usb_speed = LIBUSB20_SPEED_FULL;
194		break;
195	case USB_SPEED_HIGH:
196		pdev->usb_speed = LIBUSB20_SPEED_HIGH;
197		break;
198	case USB_SPEED_VARIABLE:
199		pdev->usb_speed = LIBUSB20_SPEED_VARIABLE;
200		break;
201	case USB_SPEED_SUPER:
202		pdev->usb_speed = LIBUSB20_SPEED_SUPER;
203		break;
204	default:
205		pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN;
206		break;
207	}
208
209	/* get parent HUB index and port */
210
211	pdev->parent_address = devinfo.udi_hubindex;
212	pdev->parent_port = devinfo.udi_hubport;
213
214	/* generate a nice description for printout */
215
216	snprintf(pdev->usb_desc, sizeof(pdev->usb_desc),
217	    USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number,
218	    pdev->device_address, devinfo.udi_vendor,
219	    devinfo.udi_product, pdev->bus_number);
220
221	error = 0;
222done:
223	close(f);
224	return (error);
225}
226
227struct ugen20_urd_state {
228	struct usb_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 = libusb20_pass_ptr(st->buf);
246		st->urd.urd_maxlen = sizeof(st->buf);
247		st->nparsed = 0;
248
249		if (ioctl(st->f, IOUSB(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_DEVICE_NAME, 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 usb_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, IOUSB(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 usb_fs_init fs_init;
332	struct usb_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 = libusb20_pass_ptr(pdev->privBeData);
359	fs_init.ep_index_max = nMaxTransfer;
360
361	if (ioctl(pdev->file, IOUSB(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/" USB_GENERIC_NAME "%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, IOUSB(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 usb_fs_uninit fs_uninit;
435
436	if (pdev->privBeData) {
437		memset(&fs_uninit, 0, sizeof(fs_uninit));
438		if (ioctl(pdev->file, IOUSB(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 usb_gen_descriptor gen_desc;
463	struct usb_config_descriptor cdesc;
464	uint8_t *ptr;
465	uint16_t len;
466	int error;
467
468	/* make sure memory is initialised */
469	memset(&cdesc, 0, sizeof(cdesc));
470	memset(&gen_desc, 0, sizeof(gen_desc));
471
472	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
473	gen_desc.ugd_maxlen = sizeof(cdesc);
474	gen_desc.ugd_config_index = cfg_index;
475
476	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
477	if (error) {
478		return (LIBUSB20_ERROR_OTHER);
479	}
480	len = UGETW(cdesc.wTotalLength);
481	if (len < sizeof(cdesc)) {
482		/* corrupt descriptor */
483		return (LIBUSB20_ERROR_OTHER);
484	}
485	ptr = malloc(len);
486	if (!ptr) {
487		return (LIBUSB20_ERROR_NO_MEM);
488	}
489
490	/* make sure memory is initialised */
491	memset(ptr, 0, len);
492
493	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
494	gen_desc.ugd_maxlen = len;
495
496	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
497	if (error) {
498		free(ptr);
499		return (LIBUSB20_ERROR_OTHER);
500	}
501	/* make sure that the device doesn't fool us */
502	memcpy(ptr, &cdesc, sizeof(cdesc));
503
504	*ppbuf = ptr;
505	*plen = len;
506
507	return (0);			/* success */
508}
509
510static int
511ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
512{
513	int temp;
514
515	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) {
516		return (LIBUSB20_ERROR_OTHER);
517	}
518	*pindex = temp;
519
520	return (0);
521}
522
523static int
524ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
525{
526	int temp = cfg_index;
527
528	/* release all active USB transfers */
529	ugen20_tr_release(pdev);
530
531	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) {
532		return (LIBUSB20_ERROR_OTHER);
533	}
534	return (ugen20_tr_renew(pdev));
535}
536
537static int
538ugen20_set_alt_index(struct libusb20_device *pdev,
539    uint8_t iface_index, uint8_t alt_index)
540{
541	struct usb_alt_interface alt_iface;
542
543	memset(&alt_iface, 0, sizeof(alt_iface));
544
545	alt_iface.uai_interface_index = iface_index;
546	alt_iface.uai_alt_index = alt_index;
547
548	/* release all active USB transfers */
549	ugen20_tr_release(pdev);
550
551	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) {
552		return (LIBUSB20_ERROR_OTHER);
553	}
554	return (ugen20_tr_renew(pdev));
555}
556
557static int
558ugen20_reset_device(struct libusb20_device *pdev)
559{
560	int temp = 0;
561
562	/* release all active USB transfers */
563	ugen20_tr_release(pdev);
564
565	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) {
566		return (LIBUSB20_ERROR_OTHER);
567	}
568	return (ugen20_tr_renew(pdev));
569}
570
571static int
572ugen20_check_connected(struct libusb20_device *pdev)
573{
574	uint32_t plugtime;
575	int error = 0;
576
577	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
578		error = LIBUSB20_ERROR_NO_DEVICE;
579		goto done;
580	}
581
582	if (pdev->session_data.plugtime != plugtime) {
583		error = LIBUSB20_ERROR_NO_DEVICE;
584		goto done;
585	}
586done:
587	return (error);
588}
589
590static int
591ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
592{
593	int temp;
594
595	switch (power_mode) {
596	case LIBUSB20_POWER_OFF:
597		temp = USB_POWER_MODE_OFF;
598		break;
599	case LIBUSB20_POWER_ON:
600		temp = USB_POWER_MODE_ON;
601		break;
602	case LIBUSB20_POWER_SAVE:
603		temp = USB_POWER_MODE_SAVE;
604		break;
605	case LIBUSB20_POWER_SUSPEND:
606		temp = USB_POWER_MODE_SUSPEND;
607		break;
608	case LIBUSB20_POWER_RESUME:
609		temp = USB_POWER_MODE_RESUME;
610		break;
611	default:
612		return (LIBUSB20_ERROR_INVALID_PARAM);
613	}
614	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) {
615		return (LIBUSB20_ERROR_OTHER);
616	}
617	return (0);
618}
619
620static int
621ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
622{
623	int temp;
624
625	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) {
626		return (LIBUSB20_ERROR_OTHER);
627	}
628	switch (temp) {
629	case USB_POWER_MODE_OFF:
630		temp = LIBUSB20_POWER_OFF;
631		break;
632	case USB_POWER_MODE_ON:
633		temp = LIBUSB20_POWER_ON;
634		break;
635	case USB_POWER_MODE_SAVE:
636		temp = LIBUSB20_POWER_SAVE;
637		break;
638	case USB_POWER_MODE_SUSPEND:
639		temp = LIBUSB20_POWER_SUSPEND;
640		break;
641	case USB_POWER_MODE_RESUME:
642		temp = LIBUSB20_POWER_RESUME;
643		break;
644	default:
645		temp = LIBUSB20_POWER_ON;
646		break;
647	}
648	*power_mode = temp;
649	return (0);			/* success */
650}
651
652static int
653ugen20_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
654{
655	struct usb_device_port_path udpp;
656
657	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_DEV_PORT_PATH), &udpp))
658		return (LIBUSB20_ERROR_OTHER);
659
660	if (udpp.udp_port_level > bufsize)
661		return (LIBUSB20_ERROR_OVERFLOW);
662
663	memcpy(buf, udpp.udp_port_no, udpp.udp_port_level);
664
665	return (udpp.udp_port_level);	/* success */
666}
667
668static int
669ugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
670{
671	int temp;
672
673	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) {
674		return (LIBUSB20_ERROR_OTHER);
675	}
676	*power_usage = temp;
677	return (0);			/* success */
678}
679
680static int
681ugen20_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats)
682{
683	struct usb_device_stats st;
684
685	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICESTATS), &st))
686		return (LIBUSB20_ERROR_OTHER);
687
688	memset(pstats, 0, sizeof(*pstats));
689
690	pstats->xfer_ok[0] = st.uds_requests_ok[0];
691	pstats->xfer_ok[1] = st.uds_requests_ok[1];
692	pstats->xfer_ok[2] = st.uds_requests_ok[2];
693	pstats->xfer_ok[3] = st.uds_requests_ok[3];
694
695	pstats->xfer_fail[0] = st.uds_requests_fail[0];
696	pstats->xfer_fail[1] = st.uds_requests_fail[1];
697	pstats->xfer_fail[2] = st.uds_requests_fail[2];
698	pstats->xfer_fail[3] = st.uds_requests_fail[3];
699
700	return (0);			/* success */
701}
702
703static int
704ugen20_kernel_driver_active(struct libusb20_device *pdev,
705    uint8_t iface_index)
706{
707	int temp = iface_index;
708
709	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) {
710		return (LIBUSB20_ERROR_OTHER);
711	}
712	return (0);			/* kernel driver is active */
713}
714
715static int
716ugen20_detach_kernel_driver(struct libusb20_device *pdev,
717    uint8_t iface_index)
718{
719	int temp = iface_index;
720
721	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) {
722		return (LIBUSB20_ERROR_OTHER);
723	}
724	return (0);			/* kernel driver is detached */
725}
726
727static int
728ugen20_do_request_sync(struct libusb20_device *pdev,
729    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
730    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
731{
732	struct usb_ctl_request req;
733
734	memset(&req, 0, sizeof(req));
735
736	req.ucr_data = libusb20_pass_ptr(data);
737	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
738		req.ucr_flags |= USB_SHORT_XFER_OK;
739	}
740	if (libusb20_me_encode(&req.ucr_request,
741	    sizeof(req.ucr_request), setup)) {
742		/* ignore */
743	}
744	if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) {
745		return (LIBUSB20_ERROR_OTHER);
746	}
747	if (pactlen) {
748		/* get actual length */
749		*pactlen = req.ucr_actlen;
750	}
751	return (0);			/* request was successful */
752}
753
754static int
755ugen20_process(struct libusb20_device *pdev)
756{
757	struct usb_fs_complete temp;
758	struct usb_fs_endpoint *fsep;
759	struct libusb20_transfer *xfer;
760
761	while (1) {
762
763	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
764			if (errno == EBUSY) {
765				break;
766			} else {
767				/* device detached */
768				return (LIBUSB20_ERROR_OTHER);
769			}
770		}
771		fsep = pdev->privBeData;
772		xfer = pdev->pTransfer;
773		fsep += temp.ep_index;
774		xfer += temp.ep_index;
775
776		/* update transfer status */
777
778		if (fsep->status == 0) {
779			xfer->aFrames = fsep->aFrames;
780			xfer->timeComplete = fsep->isoc_time_complete;
781			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
782		} else if (fsep->status == USB_ERR_CANCELLED) {
783			xfer->aFrames = 0;
784			xfer->timeComplete = 0;
785			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
786		} else if (fsep->status == USB_ERR_STALLED) {
787			xfer->aFrames = 0;
788			xfer->timeComplete = 0;
789			xfer->status = LIBUSB20_TRANSFER_STALL;
790		} else if (fsep->status == USB_ERR_TIMEOUT) {
791			xfer->aFrames = 0;
792			xfer->timeComplete = 0;
793			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
794		} else {
795			xfer->aFrames = 0;
796			xfer->timeComplete = 0;
797			xfer->status = LIBUSB20_TRANSFER_ERROR;
798		}
799		libusb20_tr_callback_wrapper(xfer);
800	}
801	return (0);			/* done */
802}
803
804static int
805ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
806    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
807    uint8_t pre_scale)
808{
809	union {
810		struct usb_fs_open fs_open;
811		struct usb_fs_open_stream fs_open_stream;
812	} temp;
813	struct usb_fs_endpoint *fsep;
814
815	if (pre_scale)
816		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
817
818	memset(&temp, 0, sizeof(temp));
819
820	fsep = xfer->pdev->privBeData;
821	fsep += xfer->trIndex;
822
823	temp.fs_open.max_bufsize = MaxBufSize;
824	temp.fs_open.max_frames = MaxFrameCount;
825	temp.fs_open.ep_index = xfer->trIndex;
826	temp.fs_open.ep_no = ep_no;
827
828	if (stream_id != 0) {
829		temp.fs_open_stream.stream_id = stream_id;
830
831		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream))
832			return (LIBUSB20_ERROR_INVALID_PARAM);
833	} else {
834		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open))
835			return (LIBUSB20_ERROR_INVALID_PARAM);
836	}
837	/* maximums might have changed - update */
838	xfer->maxFrames = temp.fs_open.max_frames;
839
840	/* "max_bufsize" should be multiple of "max_packet_length" */
841	xfer->maxTotalLength = temp.fs_open.max_bufsize;
842	xfer->maxPacketLen = temp.fs_open.max_packet_length;
843
844	/* setup buffer and length lists using zero copy */
845	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
846	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
847
848	return (0);			/* success */
849}
850
851static int
852ugen20_tr_close(struct libusb20_transfer *xfer)
853{
854	struct usb_fs_close temp;
855
856	memset(&temp, 0, sizeof(temp));
857
858	temp.ep_index = xfer->trIndex;
859
860	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) {
861		return (LIBUSB20_ERROR_INVALID_PARAM);
862	}
863	return (0);			/* success */
864}
865
866static int
867ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
868{
869	struct usb_fs_clear_stall_sync temp;
870
871	memset(&temp, 0, sizeof(temp));
872
873	/* if the transfer is active, an error will be returned */
874
875	temp.ep_index = xfer->trIndex;
876
877	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) {
878		return (LIBUSB20_ERROR_INVALID_PARAM);
879	}
880	return (0);			/* success */
881}
882
883static void
884ugen20_tr_submit(struct libusb20_transfer *xfer)
885{
886	struct usb_fs_start temp;
887	struct usb_fs_endpoint *fsep;
888
889	memset(&temp, 0, sizeof(temp));
890
891	fsep = xfer->pdev->privBeData;
892	fsep += xfer->trIndex;
893
894	fsep->nFrames = xfer->nFrames;
895	fsep->flags = 0;
896	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
897		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
898	}
899	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
900		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
901	}
902	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
903		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
904	}
905	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
906		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
907	}
908	/* NOTE: The "fsep->timeout" variable is 16-bit. */
909	if (xfer->timeout > 65535)
910		fsep->timeout = 65535;
911	else
912		fsep->timeout = xfer->timeout;
913
914	temp.ep_index = xfer->trIndex;
915
916	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) {
917		/* ignore any errors - should never happen */
918	}
919	return;				/* success */
920}
921
922static void
923ugen20_tr_cancel_async(struct libusb20_transfer *xfer)
924{
925	struct usb_fs_stop temp;
926
927	memset(&temp, 0, sizeof(temp));
928
929	temp.ep_index = xfer->trIndex;
930
931	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) {
932		/* ignore any errors - should never happen */
933	}
934	return;
935}
936
937static int
938ugen20_be_ioctl(uint32_t cmd, void *data)
939{
940	int f;
941	int error;
942
943	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
944	if (f < 0)
945		return (LIBUSB20_ERROR_OTHER);
946	error = ioctl(f, cmd, data);
947	if (error == -1) {
948		if (errno == EPERM) {
949			error = LIBUSB20_ERROR_ACCESS;
950		} else {
951			error = LIBUSB20_ERROR_OTHER;
952		}
953	}
954	close(f);
955	return (error);
956}
957
958static int
959ugen20_dev_get_iface_desc(struct libusb20_device *pdev,
960    uint8_t iface_index, char *buf, uint8_t len)
961{
962	struct usb_gen_descriptor ugd;
963
964	memset(&ugd, 0, sizeof(ugd));
965
966	ugd.ugd_data = libusb20_pass_ptr(buf);
967	ugd.ugd_maxlen = len;
968	ugd.ugd_iface_index = iface_index;
969
970	if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) {
971		return (LIBUSB20_ERROR_INVALID_PARAM);
972	}
973	return (0);
974}
975
976static int
977ugen20_dev_get_info(struct libusb20_device *pdev,
978    struct usb_device_info *pinfo)
979{
980	if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) {
981		return (LIBUSB20_ERROR_INVALID_PARAM);
982	}
983	return (0);
984}
985
986static int
987ugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
988    uint16_t quirk_index, struct libusb20_quirk *pq)
989{
990	struct usb_gen_quirk q;
991	int error;
992
993	memset(&q, 0, sizeof(q));
994
995	q.index = quirk_index;
996
997	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q);
998
999	if (error) {
1000		if (errno == EINVAL) {
1001			return (LIBUSB20_ERROR_NOT_FOUND);
1002		}
1003	} else {
1004		pq->vid = q.vid;
1005		pq->pid = q.pid;
1006		pq->bcdDeviceLow = q.bcdDeviceLow;
1007		pq->bcdDeviceHigh = q.bcdDeviceHigh;
1008		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1009	}
1010	return (error);
1011}
1012
1013static int
1014ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
1015    struct libusb20_quirk *pq)
1016{
1017	struct usb_gen_quirk q;
1018	int error;
1019
1020	memset(&q, 0, sizeof(q));
1021
1022	q.index = quirk_index;
1023
1024	error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q);
1025
1026	if (error) {
1027		if (errno == EINVAL) {
1028			return (LIBUSB20_ERROR_NOT_FOUND);
1029		}
1030	} else {
1031		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1032	}
1033	return (error);
1034}
1035
1036static int
1037ugen20_root_add_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_ADD), &q);
1052	if (error) {
1053		if (errno == ENOMEM) {
1054			return (LIBUSB20_ERROR_NO_MEM);
1055		}
1056	}
1057	return (error);
1058}
1059
1060static int
1061ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
1062    struct libusb20_quirk *pq)
1063{
1064	struct usb_gen_quirk q;
1065	int error;
1066
1067	memset(&q, 0, sizeof(q));
1068
1069	q.vid = pq->vid;
1070	q.pid = pq->pid;
1071	q.bcdDeviceLow = pq->bcdDeviceLow;
1072	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1073	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1074
1075	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q);
1076	if (error) {
1077		if (errno == EINVAL) {
1078			return (LIBUSB20_ERROR_NOT_FOUND);
1079		}
1080	}
1081	return (error);
1082}
1083
1084static int
1085ugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1086{
1087	return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp));
1088}
1089
1090static int
1091ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1092{
1093	return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp));
1094}
1095