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