1/* $FreeBSD: stable/11/lib/libusb/libusb20_ugen20.c 356398 2020-01-06 09:21:15Z 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_power_usage_t ugen20_get_power_usage;
81static libusb20_get_stats_t ugen20_get_stats;
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	struct usb_device_port_path udpp;
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	/* get device port path, if any */
222	if (ioctl(f, IOUSB(USB_GET_DEV_PORT_PATH), &udpp) == 0 &&
223	    udpp.udp_port_level < LIBUSB20_DEVICE_PORT_PATH_MAX) {
224		memcpy(pdev->port_path, udpp.udp_port_no, udpp.udp_port_level);
225		pdev->port_level = udpp.udp_port_level;
226	}
227
228	error = 0;
229done:
230	close(f);
231	return (error);
232}
233
234struct ugen20_urd_state {
235	struct usb_read_dir urd;
236	uint32_t nparsed;
237	int	f;
238	uint8_t *ptr;
239	const char *src;
240	const char *dst;
241	uint8_t	buf[256];
242	uint8_t	dummy_zero[1];
243};
244
245static int
246ugen20_readdir(struct ugen20_urd_state *st)
247{
248	;				/* style fix */
249repeat:
250	if (st->ptr == NULL) {
251		st->urd.urd_startentry += st->nparsed;
252		st->urd.urd_data = libusb20_pass_ptr(st->buf);
253		st->urd.urd_maxlen = sizeof(st->buf);
254		st->nparsed = 0;
255
256		if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) {
257			return (EINVAL);
258		}
259		st->ptr = st->buf;
260	}
261	if (st->ptr[0] == 0) {
262		if (st->nparsed) {
263			st->ptr = NULL;
264			goto repeat;
265		} else {
266			return (ENXIO);
267		}
268	}
269	st->src = (void *)(st->ptr + 1);
270	st->dst = st->src + strlen(st->src) + 1;
271	st->ptr = st->ptr + st->ptr[0];
272	st->nparsed++;
273
274	if ((st->ptr < st->buf) ||
275	    (st->ptr > st->dummy_zero)) {
276		/* invalid entry */
277		return (EINVAL);
278	}
279	return (0);
280}
281
282static int
283ugen20_init_backend(struct libusb20_backend *pbe)
284{
285	struct ugen20_urd_state state;
286	struct libusb20_device *pdev;
287
288	memset(&state, 0, sizeof(state));
289
290	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
291	if (state.f < 0)
292		return (LIBUSB20_ERROR_OTHER);
293
294	while (ugen20_readdir(&state) == 0) {
295
296		if ((state.src[0] != 'u') ||
297		    (state.src[1] != 'g') ||
298		    (state.src[2] != 'e') ||
299		    (state.src[3] != 'n')) {
300			continue;
301		}
302		pdev = libusb20_dev_alloc();
303		if (pdev == NULL) {
304			continue;
305		}
306		if (ugen20_enumerate(pdev, state.src + 4)) {
307			libusb20_dev_free(pdev);
308			continue;
309		}
310		/* put the device on the backend list */
311		libusb20_be_enqueue_device(pbe, pdev);
312	}
313	close(state.f);
314	return (0);			/* success */
315}
316
317static void
318ugen20_tr_release(struct libusb20_device *pdev)
319{
320	struct usb_fs_uninit fs_uninit;
321
322	if (pdev->nTransfer == 0) {
323		return;
324	}
325	/* release all pending USB transfers */
326	if (pdev->privBeData != NULL) {
327		memset(&fs_uninit, 0, sizeof(fs_uninit));
328		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
329			/* ignore any errors of this kind */
330		}
331	}
332	return;
333}
334
335static int
336ugen20_tr_renew(struct libusb20_device *pdev)
337{
338	struct usb_fs_init fs_init;
339	struct usb_fs_endpoint *pfse;
340	int error;
341	uint32_t size;
342	uint16_t nMaxTransfer;
343
344	nMaxTransfer = pdev->nTransfer;
345	error = 0;
346
347	if (nMaxTransfer == 0) {
348		goto done;
349	}
350	size = nMaxTransfer * sizeof(*pfse);
351
352	if (pdev->privBeData == NULL) {
353		pfse = malloc(size);
354		if (pfse == NULL) {
355			error = LIBUSB20_ERROR_NO_MEM;
356			goto done;
357		}
358		pdev->privBeData = pfse;
359	}
360	/* reset endpoint data */
361	memset(pdev->privBeData, 0, size);
362
363	memset(&fs_init, 0, sizeof(fs_init));
364
365	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
366	fs_init.ep_index_max = nMaxTransfer;
367
368	if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) {
369		error = LIBUSB20_ERROR_OTHER;
370		goto done;
371	}
372done:
373	return (error);
374}
375
376static int
377ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
378{
379	uint32_t plugtime;
380	char buf[64];
381	int f;
382	int g;
383	int error;
384
385	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
386	    pdev->bus_number, pdev->device_address);
387
388	/*
389	 * We need two file handles, one for the control endpoint and one
390	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
391	 * kernel locking.
392	 */
393	g = open(buf, O_RDWR);
394	if (g < 0) {
395		return (LIBUSB20_ERROR_NO_DEVICE);
396	}
397	f = open(buf, O_RDWR);
398	if (f < 0) {
399		close(g);
400		return (LIBUSB20_ERROR_NO_DEVICE);
401	}
402	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
403		error = LIBUSB20_ERROR_OTHER;
404		goto done;
405	}
406	/* check that the correct device is still plugged */
407	if (pdev->session_data.plugtime != plugtime) {
408		error = LIBUSB20_ERROR_NO_DEVICE;
409		goto done;
410	}
411	/* need to set this before "tr_renew()" */
412	pdev->file = f;
413	pdev->file_ctrl = g;
414
415	/* renew all USB transfers */
416	error = ugen20_tr_renew(pdev);
417	if (error) {
418		goto done;
419	}
420	/* set methods */
421	pdev->methods = &libusb20_ugen20_device_methods;
422
423done:
424	if (error) {
425		if (pdev->privBeData) {
426			/* cleanup after "tr_renew()" */
427			free(pdev->privBeData);
428			pdev->privBeData = NULL;
429		}
430		pdev->file = -1;
431		pdev->file_ctrl = -1;
432		close(f);
433		close(g);
434	}
435	return (error);
436}
437
438static int
439ugen20_close_device(struct libusb20_device *pdev)
440{
441	struct usb_fs_uninit fs_uninit;
442
443	if (pdev->privBeData) {
444		memset(&fs_uninit, 0, sizeof(fs_uninit));
445		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
446			/* ignore this error */
447		}
448		free(pdev->privBeData);
449	}
450	pdev->nTransfer = 0;
451	pdev->privBeData = NULL;
452	close(pdev->file);
453	close(pdev->file_ctrl);
454	pdev->file = -1;
455	pdev->file_ctrl = -1;
456	return (0);			/* success */
457}
458
459static void
460ugen20_exit_backend(struct libusb20_backend *pbe)
461{
462	return;				/* nothing to do */
463}
464
465static int
466ugen20_get_config_desc_full(struct libusb20_device *pdev,
467    uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
468{
469	struct usb_gen_descriptor gen_desc;
470	struct usb_config_descriptor cdesc;
471	uint8_t *ptr;
472	uint16_t len;
473	int error;
474
475	/* make sure memory is initialised */
476	memset(&cdesc, 0, sizeof(cdesc));
477	memset(&gen_desc, 0, sizeof(gen_desc));
478
479	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
480	gen_desc.ugd_maxlen = sizeof(cdesc);
481	gen_desc.ugd_config_index = cfg_index;
482
483	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
484	if (error) {
485		return (LIBUSB20_ERROR_OTHER);
486	}
487	len = UGETW(cdesc.wTotalLength);
488	if (len < sizeof(cdesc)) {
489		/* corrupt descriptor */
490		return (LIBUSB20_ERROR_OTHER);
491	}
492	ptr = malloc(len);
493	if (!ptr) {
494		return (LIBUSB20_ERROR_NO_MEM);
495	}
496
497	/* make sure memory is initialised */
498	memset(ptr, 0, len);
499
500	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
501	gen_desc.ugd_maxlen = len;
502
503	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
504	if (error) {
505		free(ptr);
506		return (LIBUSB20_ERROR_OTHER);
507	}
508	/* make sure that the device doesn't fool us */
509	memcpy(ptr, &cdesc, sizeof(cdesc));
510
511	*ppbuf = ptr;
512	*plen = len;
513
514	return (0);			/* success */
515}
516
517static int
518ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
519{
520	int temp;
521
522	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) {
523		return (LIBUSB20_ERROR_OTHER);
524	}
525	*pindex = temp;
526
527	return (0);
528}
529
530static int
531ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
532{
533	int temp = cfg_index;
534
535	/* release all active USB transfers */
536	ugen20_tr_release(pdev);
537
538	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) {
539		return (LIBUSB20_ERROR_OTHER);
540	}
541	return (ugen20_tr_renew(pdev));
542}
543
544static int
545ugen20_set_alt_index(struct libusb20_device *pdev,
546    uint8_t iface_index, uint8_t alt_index)
547{
548	struct usb_alt_interface alt_iface;
549
550	memset(&alt_iface, 0, sizeof(alt_iface));
551
552	alt_iface.uai_interface_index = iface_index;
553	alt_iface.uai_alt_index = alt_index;
554
555	/* release all active USB transfers */
556	ugen20_tr_release(pdev);
557
558	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) {
559		return (LIBUSB20_ERROR_OTHER);
560	}
561	return (ugen20_tr_renew(pdev));
562}
563
564static int
565ugen20_reset_device(struct libusb20_device *pdev)
566{
567	int temp = 0;
568
569	/* release all active USB transfers */
570	ugen20_tr_release(pdev);
571
572	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) {
573		return (LIBUSB20_ERROR_OTHER);
574	}
575	return (ugen20_tr_renew(pdev));
576}
577
578static int
579ugen20_check_connected(struct libusb20_device *pdev)
580{
581	uint32_t plugtime;
582	int error = 0;
583
584	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
585		error = LIBUSB20_ERROR_NO_DEVICE;
586		goto done;
587	}
588
589	if (pdev->session_data.plugtime != plugtime) {
590		error = LIBUSB20_ERROR_NO_DEVICE;
591		goto done;
592	}
593done:
594	return (error);
595}
596
597static int
598ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
599{
600	int temp;
601
602	switch (power_mode) {
603	case LIBUSB20_POWER_OFF:
604		temp = USB_POWER_MODE_OFF;
605		break;
606	case LIBUSB20_POWER_ON:
607		temp = USB_POWER_MODE_ON;
608		break;
609	case LIBUSB20_POWER_SAVE:
610		temp = USB_POWER_MODE_SAVE;
611		break;
612	case LIBUSB20_POWER_SUSPEND:
613		temp = USB_POWER_MODE_SUSPEND;
614		break;
615	case LIBUSB20_POWER_RESUME:
616		temp = USB_POWER_MODE_RESUME;
617		break;
618	default:
619		return (LIBUSB20_ERROR_INVALID_PARAM);
620	}
621	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) {
622		return (LIBUSB20_ERROR_OTHER);
623	}
624	return (0);
625}
626
627static int
628ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
629{
630	int temp;
631
632	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) {
633		return (LIBUSB20_ERROR_OTHER);
634	}
635	switch (temp) {
636	case USB_POWER_MODE_OFF:
637		temp = LIBUSB20_POWER_OFF;
638		break;
639	case USB_POWER_MODE_ON:
640		temp = LIBUSB20_POWER_ON;
641		break;
642	case USB_POWER_MODE_SAVE:
643		temp = LIBUSB20_POWER_SAVE;
644		break;
645	case USB_POWER_MODE_SUSPEND:
646		temp = LIBUSB20_POWER_SUSPEND;
647		break;
648	case USB_POWER_MODE_RESUME:
649		temp = LIBUSB20_POWER_RESUME;
650		break;
651	default:
652		temp = LIBUSB20_POWER_ON;
653		break;
654	}
655	*power_mode = temp;
656	return (0);			/* success */
657}
658
659static int
660ugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
661{
662	int temp;
663
664	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) {
665		return (LIBUSB20_ERROR_OTHER);
666	}
667	*power_usage = temp;
668	return (0);			/* success */
669}
670
671static int
672ugen20_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats)
673{
674	struct usb_device_stats st;
675
676	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICESTATS), &st))
677		return (LIBUSB20_ERROR_OTHER);
678
679	memset(pstats, 0, sizeof(*pstats));
680
681	pstats->xfer_ok[0] = st.uds_requests_ok[0];
682	pstats->xfer_ok[1] = st.uds_requests_ok[1];
683	pstats->xfer_ok[2] = st.uds_requests_ok[2];
684	pstats->xfer_ok[3] = st.uds_requests_ok[3];
685
686	pstats->xfer_fail[0] = st.uds_requests_fail[0];
687	pstats->xfer_fail[1] = st.uds_requests_fail[1];
688	pstats->xfer_fail[2] = st.uds_requests_fail[2];
689	pstats->xfer_fail[3] = st.uds_requests_fail[3];
690
691	return (0);			/* success */
692}
693
694static int
695ugen20_kernel_driver_active(struct libusb20_device *pdev,
696    uint8_t iface_index)
697{
698	int temp = iface_index;
699
700	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) {
701		return (LIBUSB20_ERROR_OTHER);
702	}
703	return (0);			/* kernel driver is active */
704}
705
706static int
707ugen20_detach_kernel_driver(struct libusb20_device *pdev,
708    uint8_t iface_index)
709{
710	int temp = iface_index;
711
712	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) {
713		return (LIBUSB20_ERROR_OTHER);
714	}
715	return (0);			/* kernel driver is detached */
716}
717
718static int
719ugen20_do_request_sync(struct libusb20_device *pdev,
720    struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
721    void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
722{
723	struct usb_ctl_request req;
724
725	memset(&req, 0, sizeof(req));
726
727	req.ucr_data = libusb20_pass_ptr(data);
728	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
729		req.ucr_flags |= USB_SHORT_XFER_OK;
730	}
731	if (libusb20_me_encode(&req.ucr_request,
732	    sizeof(req.ucr_request), setup)) {
733		/* ignore */
734	}
735	if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) {
736		return (LIBUSB20_ERROR_OTHER);
737	}
738	if (pactlen) {
739		/* get actual length */
740		*pactlen = req.ucr_actlen;
741	}
742	return (0);			/* request was successful */
743}
744
745static int
746ugen20_process(struct libusb20_device *pdev)
747{
748	struct usb_fs_complete temp;
749	struct usb_fs_endpoint *fsep;
750	struct libusb20_transfer *xfer;
751
752	while (1) {
753
754	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
755			if (errno == EBUSY) {
756				break;
757			} else {
758				/* device detached */
759				return (LIBUSB20_ERROR_OTHER);
760			}
761		}
762		fsep = pdev->privBeData;
763		xfer = pdev->pTransfer;
764		fsep += temp.ep_index;
765		xfer += temp.ep_index;
766
767		/* update transfer status */
768
769		if (fsep->status == 0) {
770			xfer->aFrames = fsep->aFrames;
771			xfer->timeComplete = fsep->isoc_time_complete;
772			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
773		} else if (fsep->status == USB_ERR_CANCELLED) {
774			xfer->aFrames = 0;
775			xfer->timeComplete = 0;
776			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
777		} else if (fsep->status == USB_ERR_STALLED) {
778			xfer->aFrames = 0;
779			xfer->timeComplete = 0;
780			xfer->status = LIBUSB20_TRANSFER_STALL;
781		} else if (fsep->status == USB_ERR_TIMEOUT) {
782			xfer->aFrames = 0;
783			xfer->timeComplete = 0;
784			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
785		} else {
786			xfer->aFrames = 0;
787			xfer->timeComplete = 0;
788			xfer->status = LIBUSB20_TRANSFER_ERROR;
789		}
790		libusb20_tr_callback_wrapper(xfer);
791	}
792	return (0);			/* done */
793}
794
795static int
796ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
797    uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
798    uint8_t pre_scale)
799{
800	union {
801		struct usb_fs_open fs_open;
802		struct usb_fs_open_stream fs_open_stream;
803	} temp;
804	struct usb_fs_endpoint *fsep;
805
806	if (pre_scale)
807		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
808
809	memset(&temp, 0, sizeof(temp));
810
811	fsep = xfer->pdev->privBeData;
812	fsep += xfer->trIndex;
813
814	temp.fs_open.max_bufsize = MaxBufSize;
815	temp.fs_open.max_frames = MaxFrameCount;
816	temp.fs_open.ep_index = xfer->trIndex;
817	temp.fs_open.ep_no = ep_no;
818
819	if (stream_id != 0) {
820		temp.fs_open_stream.stream_id = stream_id;
821
822		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream))
823			return (LIBUSB20_ERROR_INVALID_PARAM);
824	} else {
825		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open))
826			return (LIBUSB20_ERROR_INVALID_PARAM);
827	}
828	/* maximums might have changed - update */
829	xfer->maxFrames = temp.fs_open.max_frames;
830
831	/* "max_bufsize" should be multiple of "max_packet_length" */
832	xfer->maxTotalLength = temp.fs_open.max_bufsize;
833	xfer->maxPacketLen = temp.fs_open.max_packet_length;
834
835	/* setup buffer and length lists using zero copy */
836	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
837	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
838
839	return (0);			/* success */
840}
841
842static int
843ugen20_tr_close(struct libusb20_transfer *xfer)
844{
845	struct usb_fs_close temp;
846
847	memset(&temp, 0, sizeof(temp));
848
849	temp.ep_index = xfer->trIndex;
850
851	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) {
852		return (LIBUSB20_ERROR_INVALID_PARAM);
853	}
854	return (0);			/* success */
855}
856
857static int
858ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
859{
860	struct usb_fs_clear_stall_sync temp;
861
862	memset(&temp, 0, sizeof(temp));
863
864	/* if the transfer is active, an error will be returned */
865
866	temp.ep_index = xfer->trIndex;
867
868	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) {
869		return (LIBUSB20_ERROR_INVALID_PARAM);
870	}
871	return (0);			/* success */
872}
873
874static void
875ugen20_tr_submit(struct libusb20_transfer *xfer)
876{
877	struct usb_fs_start temp;
878	struct usb_fs_endpoint *fsep;
879
880	memset(&temp, 0, sizeof(temp));
881
882	fsep = xfer->pdev->privBeData;
883	fsep += xfer->trIndex;
884
885	fsep->nFrames = xfer->nFrames;
886	fsep->flags = 0;
887	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
888		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
889	}
890	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
891		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
892	}
893	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
894		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
895	}
896	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
897		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
898	}
899	/* NOTE: The "fsep->timeout" variable is 16-bit. */
900	if (xfer->timeout > 65535)
901		fsep->timeout = 65535;
902	else
903		fsep->timeout = xfer->timeout;
904
905	temp.ep_index = xfer->trIndex;
906
907	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) {
908		/* ignore any errors - should never happen */
909	}
910	return;				/* success */
911}
912
913static void
914ugen20_tr_cancel_async(struct libusb20_transfer *xfer)
915{
916	struct usb_fs_stop temp;
917
918	memset(&temp, 0, sizeof(temp));
919
920	temp.ep_index = xfer->trIndex;
921
922	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) {
923		/* ignore any errors - should never happen */
924	}
925	return;
926}
927
928static int
929ugen20_be_ioctl(uint32_t cmd, void *data)
930{
931	int f;
932	int error;
933
934	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
935	if (f < 0)
936		return (LIBUSB20_ERROR_OTHER);
937	error = ioctl(f, cmd, data);
938	if (error == -1) {
939		if (errno == EPERM) {
940			error = LIBUSB20_ERROR_ACCESS;
941		} else {
942			error = LIBUSB20_ERROR_OTHER;
943		}
944	}
945	close(f);
946	return (error);
947}
948
949static int
950ugen20_dev_get_iface_desc(struct libusb20_device *pdev,
951    uint8_t iface_index, char *buf, uint8_t len)
952{
953	struct usb_gen_descriptor ugd;
954
955	memset(&ugd, 0, sizeof(ugd));
956
957	ugd.ugd_data = libusb20_pass_ptr(buf);
958	ugd.ugd_maxlen = len;
959	ugd.ugd_iface_index = iface_index;
960
961	if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) {
962		return (LIBUSB20_ERROR_INVALID_PARAM);
963	}
964	return (0);
965}
966
967static int
968ugen20_dev_get_info(struct libusb20_device *pdev,
969    struct usb_device_info *pinfo)
970{
971	if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) {
972		return (LIBUSB20_ERROR_INVALID_PARAM);
973	}
974	return (0);
975}
976
977static int
978ugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
979    uint16_t quirk_index, struct libusb20_quirk *pq)
980{
981	struct usb_gen_quirk q;
982	int error;
983
984	memset(&q, 0, sizeof(q));
985
986	q.index = quirk_index;
987
988	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q);
989
990	if (error) {
991		if (errno == EINVAL) {
992			return (LIBUSB20_ERROR_NOT_FOUND);
993		}
994	} else {
995		pq->vid = q.vid;
996		pq->pid = q.pid;
997		pq->bcdDeviceLow = q.bcdDeviceLow;
998		pq->bcdDeviceHigh = q.bcdDeviceHigh;
999		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1000	}
1001	return (error);
1002}
1003
1004static int
1005ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
1006    struct libusb20_quirk *pq)
1007{
1008	struct usb_gen_quirk q;
1009	int error;
1010
1011	memset(&q, 0, sizeof(q));
1012
1013	q.index = quirk_index;
1014
1015	error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q);
1016
1017	if (error) {
1018		if (errno == EINVAL) {
1019			return (LIBUSB20_ERROR_NOT_FOUND);
1020		}
1021	} else {
1022		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
1023	}
1024	return (error);
1025}
1026
1027static int
1028ugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
1029    struct libusb20_quirk *pq)
1030{
1031	struct usb_gen_quirk q;
1032	int error;
1033
1034	memset(&q, 0, sizeof(q));
1035
1036	q.vid = pq->vid;
1037	q.pid = pq->pid;
1038	q.bcdDeviceLow = pq->bcdDeviceLow;
1039	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1040	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1041
1042	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q);
1043	if (error) {
1044		if (errno == ENOMEM) {
1045			return (LIBUSB20_ERROR_NO_MEM);
1046		}
1047	}
1048	return (error);
1049}
1050
1051static int
1052ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
1053    struct libusb20_quirk *pq)
1054{
1055	struct usb_gen_quirk q;
1056	int error;
1057
1058	memset(&q, 0, sizeof(q));
1059
1060	q.vid = pq->vid;
1061	q.pid = pq->pid;
1062	q.bcdDeviceLow = pq->bcdDeviceLow;
1063	q.bcdDeviceHigh = pq->bcdDeviceHigh;
1064	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
1065
1066	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q);
1067	if (error) {
1068		if (errno == EINVAL) {
1069			return (LIBUSB20_ERROR_NOT_FOUND);
1070		}
1071	}
1072	return (error);
1073}
1074
1075static int
1076ugen20_root_set_template(struct libusb20_backend *pbe, int temp)
1077{
1078	return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp));
1079}
1080
1081static int
1082ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
1083{
1084	return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp));
1085}
1086