xref: /dragonfly/lib/libusb/libusb20_ugen20.c (revision 871df6aa)
19b0c1abeSSascha Wildner /* $FreeBSD: head/lib/libusb/libusb20_ugen20.c 255242 2013-09-05 12:21:11Z hselasky $ */
21d96047eSMarkus Pfeiffer /*-
31d96047eSMarkus Pfeiffer  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
41d96047eSMarkus Pfeiffer  *
51d96047eSMarkus Pfeiffer  * Redistribution and use in source and binary forms, with or without
61d96047eSMarkus Pfeiffer  * modification, are permitted provided that the following conditions
71d96047eSMarkus Pfeiffer  * are met:
81d96047eSMarkus Pfeiffer  * 1. Redistributions of source code must retain the above copyright
91d96047eSMarkus Pfeiffer  *    notice, this list of conditions and the following disclaimer.
101d96047eSMarkus Pfeiffer  * 2. Redistributions in binary form must reproduce the above copyright
111d96047eSMarkus Pfeiffer  *    notice, this list of conditions and the following disclaimer in the
121d96047eSMarkus Pfeiffer  *    documentation and/or other materials provided with the distribution.
131d96047eSMarkus Pfeiffer  *
141d96047eSMarkus Pfeiffer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151d96047eSMarkus Pfeiffer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161d96047eSMarkus Pfeiffer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171d96047eSMarkus Pfeiffer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181d96047eSMarkus Pfeiffer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191d96047eSMarkus Pfeiffer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201d96047eSMarkus Pfeiffer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211d96047eSMarkus Pfeiffer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221d96047eSMarkus Pfeiffer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231d96047eSMarkus Pfeiffer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241d96047eSMarkus Pfeiffer  * SUCH DAMAGE.
251d96047eSMarkus Pfeiffer  */
261d96047eSMarkus Pfeiffer 
279b0c1abeSSascha Wildner #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
289b0c1abeSSascha Wildner #include LIBUSB_GLOBAL_INCLUDE_FILE
299b0c1abeSSascha Wildner #else
301d96047eSMarkus Pfeiffer #include <errno.h>
311d96047eSMarkus Pfeiffer #include <fcntl.h>
321d96047eSMarkus Pfeiffer #include <stdio.h>
331d96047eSMarkus Pfeiffer #include <stdlib.h>
341d96047eSMarkus Pfeiffer #include <string.h>
351d96047eSMarkus Pfeiffer #include <unistd.h>
369b0c1abeSSascha Wildner #include <time.h>
379b0c1abeSSascha Wildner #include <sys/queue.h>
389b0c1abeSSascha Wildner #include <sys/types.h>
399b0c1abeSSascha Wildner #endif
409b0c1abeSSascha Wildner 
419b0c1abeSSascha Wildner #include <bus/u4b/usb.h>
429b0c1abeSSascha Wildner #include <bus/u4b/usbdi.h>
439b0c1abeSSascha Wildner #include <bus/u4b/usb_ioctl.h>
441d96047eSMarkus Pfeiffer 
451d96047eSMarkus Pfeiffer #include "libusb20.h"
461d96047eSMarkus Pfeiffer #include "libusb20_desc.h"
471d96047eSMarkus Pfeiffer #include "libusb20_int.h"
481d96047eSMarkus Pfeiffer 
499b0c1abeSSascha Wildner #ifndef	IOUSB
509b0c1abeSSascha Wildner #define IOUSB(a) a
519b0c1abeSSascha Wildner #endif
521d96047eSMarkus Pfeiffer 
531d96047eSMarkus Pfeiffer static libusb20_init_backend_t ugen20_init_backend;
541d96047eSMarkus Pfeiffer static libusb20_open_device_t ugen20_open_device;
551d96047eSMarkus Pfeiffer static libusb20_close_device_t ugen20_close_device;
561d96047eSMarkus Pfeiffer static libusb20_get_backend_name_t ugen20_get_backend_name;
571d96047eSMarkus Pfeiffer static libusb20_exit_backend_t ugen20_exit_backend;
581d96047eSMarkus Pfeiffer static libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc;
591d96047eSMarkus Pfeiffer static libusb20_dev_get_info_t ugen20_dev_get_info;
601d96047eSMarkus Pfeiffer static libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk;
611d96047eSMarkus Pfeiffer static libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name;
621d96047eSMarkus Pfeiffer static libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk;
631d96047eSMarkus Pfeiffer static libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk;
641d96047eSMarkus Pfeiffer static libusb20_root_set_template_t ugen20_root_set_template;
651d96047eSMarkus Pfeiffer static libusb20_root_get_template_t ugen20_root_get_template;
661d96047eSMarkus Pfeiffer 
671d96047eSMarkus Pfeiffer const struct libusb20_backend_methods libusb20_ugen20_backend = {
681d96047eSMarkus Pfeiffer 	LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20)
691d96047eSMarkus Pfeiffer };
701d96047eSMarkus Pfeiffer 
711d96047eSMarkus Pfeiffer /* USB device specific */
721d96047eSMarkus Pfeiffer static libusb20_get_config_desc_full_t ugen20_get_config_desc_full;
731d96047eSMarkus Pfeiffer static libusb20_get_config_index_t ugen20_get_config_index;
741d96047eSMarkus Pfeiffer static libusb20_set_config_index_t ugen20_set_config_index;
751d96047eSMarkus Pfeiffer static libusb20_set_alt_index_t ugen20_set_alt_index;
761d96047eSMarkus Pfeiffer static libusb20_reset_device_t ugen20_reset_device;
771d96047eSMarkus Pfeiffer static libusb20_check_connected_t ugen20_check_connected;
781d96047eSMarkus Pfeiffer static libusb20_set_power_mode_t ugen20_set_power_mode;
791d96047eSMarkus Pfeiffer static libusb20_get_power_mode_t ugen20_get_power_mode;
809b0c1abeSSascha Wildner static libusb20_get_port_path_t ugen20_get_port_path;
819b0c1abeSSascha Wildner static libusb20_get_power_usage_t ugen20_get_power_usage;
821d96047eSMarkus Pfeiffer static libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
831d96047eSMarkus Pfeiffer static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
841d96047eSMarkus Pfeiffer static libusb20_do_request_sync_t ugen20_do_request_sync;
851d96047eSMarkus Pfeiffer static libusb20_process_t ugen20_process;
861d96047eSMarkus Pfeiffer 
871d96047eSMarkus Pfeiffer /* USB transfer specific */
881d96047eSMarkus Pfeiffer static libusb20_tr_open_t ugen20_tr_open;
891d96047eSMarkus Pfeiffer static libusb20_tr_close_t ugen20_tr_close;
901d96047eSMarkus Pfeiffer static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync;
911d96047eSMarkus Pfeiffer static libusb20_tr_submit_t ugen20_tr_submit;
921d96047eSMarkus Pfeiffer static libusb20_tr_cancel_async_t ugen20_tr_cancel_async;
931d96047eSMarkus Pfeiffer 
941d96047eSMarkus Pfeiffer static const struct libusb20_device_methods libusb20_ugen20_device_methods = {
951d96047eSMarkus Pfeiffer 	LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20)
961d96047eSMarkus Pfeiffer };
971d96047eSMarkus Pfeiffer 
981d96047eSMarkus Pfeiffer static const char *
ugen20_get_backend_name(void)991d96047eSMarkus Pfeiffer ugen20_get_backend_name(void)
1001d96047eSMarkus Pfeiffer {
1011d96047eSMarkus Pfeiffer 	return ("FreeBSD UGEN 2.0");
1021d96047eSMarkus Pfeiffer }
1031d96047eSMarkus Pfeiffer 
1041d96047eSMarkus Pfeiffer static uint32_t
ugen20_path_convert_one(const char ** pp)1051d96047eSMarkus Pfeiffer ugen20_path_convert_one(const char **pp)
1061d96047eSMarkus Pfeiffer {
1071d96047eSMarkus Pfeiffer 	const char *ptr;
1081d96047eSMarkus Pfeiffer 	uint32_t temp = 0;
1091d96047eSMarkus Pfeiffer 
1101d96047eSMarkus Pfeiffer 	ptr = *pp;
1111d96047eSMarkus Pfeiffer 
1121d96047eSMarkus Pfeiffer 	while ((*ptr >= '0') && (*ptr <= '9')) {
1131d96047eSMarkus Pfeiffer 		temp *= 10;
1141d96047eSMarkus Pfeiffer 		temp += (*ptr - '0');
1151d96047eSMarkus Pfeiffer 		if (temp >= 1000000) {
1161d96047eSMarkus Pfeiffer 			/* catch overflow early */
117aa3e5c14SSascha Wildner 			return (0xFFFFFFFF);
1181d96047eSMarkus Pfeiffer 		}
1191d96047eSMarkus Pfeiffer 		ptr++;
1201d96047eSMarkus Pfeiffer 	}
1211d96047eSMarkus Pfeiffer 
1221d96047eSMarkus Pfeiffer 	if (*ptr == '.') {
1231d96047eSMarkus Pfeiffer 		/* skip dot */
1241d96047eSMarkus Pfeiffer 		ptr++;
1251d96047eSMarkus Pfeiffer 	}
1261d96047eSMarkus Pfeiffer 	*pp = ptr;
1271d96047eSMarkus Pfeiffer 
1281d96047eSMarkus Pfeiffer 	return (temp);
1291d96047eSMarkus Pfeiffer }
1301d96047eSMarkus Pfeiffer 
1311d96047eSMarkus Pfeiffer static int
ugen20_enumerate(struct libusb20_device * pdev,const char * id)1321d96047eSMarkus Pfeiffer ugen20_enumerate(struct libusb20_device *pdev, const char *id)
1331d96047eSMarkus Pfeiffer {
1341d96047eSMarkus Pfeiffer 	const char *tmp = id;
1351d96047eSMarkus Pfeiffer 	struct usb_device_descriptor ddesc;
1361d96047eSMarkus Pfeiffer 	struct usb_device_info devinfo;
1371d96047eSMarkus Pfeiffer 	uint32_t plugtime;
1381d96047eSMarkus Pfeiffer 	char buf[64];
1391d96047eSMarkus Pfeiffer 	int f;
1401d96047eSMarkus Pfeiffer 	int error;
1411d96047eSMarkus Pfeiffer 
1421d96047eSMarkus Pfeiffer 	pdev->bus_number = ugen20_path_convert_one(&tmp);
1431d96047eSMarkus Pfeiffer 	pdev->device_address = ugen20_path_convert_one(&tmp);
1441d96047eSMarkus Pfeiffer 
1451d96047eSMarkus Pfeiffer 	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
1461d96047eSMarkus Pfeiffer 	    pdev->bus_number, pdev->device_address);
1471d96047eSMarkus Pfeiffer 
1481d96047eSMarkus Pfeiffer 	f = open(buf, O_RDWR);
1491d96047eSMarkus Pfeiffer 	if (f < 0) {
1501d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
1511d96047eSMarkus Pfeiffer 	}
1529b0c1abeSSascha Wildner 	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
1531d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_OTHER;
1541d96047eSMarkus Pfeiffer 		goto done;
1551d96047eSMarkus Pfeiffer 	}
1561d96047eSMarkus Pfeiffer 	/* store when the device was plugged */
1571d96047eSMarkus Pfeiffer 	pdev->session_data.plugtime = plugtime;
1581d96047eSMarkus Pfeiffer 
1599b0c1abeSSascha Wildner 	if (ioctl(f, IOUSB(USB_GET_DEVICE_DESC), &ddesc)) {
1601d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_OTHER;
1611d96047eSMarkus Pfeiffer 		goto done;
1621d96047eSMarkus Pfeiffer 	}
1631d96047eSMarkus Pfeiffer 	LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc));
1641d96047eSMarkus Pfeiffer 
1651d96047eSMarkus Pfeiffer 	libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc));
1661d96047eSMarkus Pfeiffer 
1671d96047eSMarkus Pfeiffer 	if (pdev->ddesc.bNumConfigurations == 0) {
1681d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_OTHER;
1691d96047eSMarkus Pfeiffer 		goto done;
1701d96047eSMarkus Pfeiffer 	} else if (pdev->ddesc.bNumConfigurations >= 8) {
1711d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_OTHER;
1721d96047eSMarkus Pfeiffer 		goto done;
1731d96047eSMarkus Pfeiffer 	}
1749b0c1abeSSascha Wildner 	if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) {
1751d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_OTHER;
1761d96047eSMarkus Pfeiffer 		goto done;
1771d96047eSMarkus Pfeiffer 	}
1781d96047eSMarkus Pfeiffer 	switch (devinfo.udi_mode) {
1791d96047eSMarkus Pfeiffer 	case USB_MODE_DEVICE:
1801d96047eSMarkus Pfeiffer 		pdev->usb_mode = LIBUSB20_MODE_DEVICE;
1811d96047eSMarkus Pfeiffer 		break;
1821d96047eSMarkus Pfeiffer 	default:
1831d96047eSMarkus Pfeiffer 		pdev->usb_mode = LIBUSB20_MODE_HOST;
1841d96047eSMarkus Pfeiffer 		break;
1851d96047eSMarkus Pfeiffer 	}
1861d96047eSMarkus Pfeiffer 
1871d96047eSMarkus Pfeiffer 	switch (devinfo.udi_speed) {
1881d96047eSMarkus Pfeiffer 	case USB_SPEED_LOW:
1891d96047eSMarkus Pfeiffer 		pdev->usb_speed = LIBUSB20_SPEED_LOW;
1901d96047eSMarkus Pfeiffer 		break;
1911d96047eSMarkus Pfeiffer 	case USB_SPEED_FULL:
1921d96047eSMarkus Pfeiffer 		pdev->usb_speed = LIBUSB20_SPEED_FULL;
1931d96047eSMarkus Pfeiffer 		break;
1941d96047eSMarkus Pfeiffer 	case USB_SPEED_HIGH:
1951d96047eSMarkus Pfeiffer 		pdev->usb_speed = LIBUSB20_SPEED_HIGH;
1961d96047eSMarkus Pfeiffer 		break;
1971d96047eSMarkus Pfeiffer 	case USB_SPEED_VARIABLE:
1981d96047eSMarkus Pfeiffer 		pdev->usb_speed = LIBUSB20_SPEED_VARIABLE;
1991d96047eSMarkus Pfeiffer 		break;
2001d96047eSMarkus Pfeiffer 	case USB_SPEED_SUPER:
2011d96047eSMarkus Pfeiffer 		pdev->usb_speed = LIBUSB20_SPEED_SUPER;
2021d96047eSMarkus Pfeiffer 		break;
2031d96047eSMarkus Pfeiffer 	default:
2041d96047eSMarkus Pfeiffer 		pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN;
2051d96047eSMarkus Pfeiffer 		break;
2061d96047eSMarkus Pfeiffer 	}
2071d96047eSMarkus Pfeiffer 
2081d96047eSMarkus Pfeiffer 	/* get parent HUB index and port */
2091d96047eSMarkus Pfeiffer 
2101d96047eSMarkus Pfeiffer 	pdev->parent_address = devinfo.udi_hubindex;
2111d96047eSMarkus Pfeiffer 	pdev->parent_port = devinfo.udi_hubport;
2121d96047eSMarkus Pfeiffer 
2131d96047eSMarkus Pfeiffer 	/* generate a nice description for printout */
2141d96047eSMarkus Pfeiffer 
2151d96047eSMarkus Pfeiffer 	snprintf(pdev->usb_desc, sizeof(pdev->usb_desc),
216*871df6aaSzrj 	    USB_GENERIC_NAME "%u.%u: <%.24s %.24s> at usbus%u",
217*871df6aaSzrj 	    pdev->bus_number, pdev->device_address,
218*871df6aaSzrj 	    devinfo.udi_vendor, devinfo.udi_product, pdev->bus_number);
2191d96047eSMarkus Pfeiffer 
2201d96047eSMarkus Pfeiffer 	error = 0;
2211d96047eSMarkus Pfeiffer done:
2221d96047eSMarkus Pfeiffer 	close(f);
2231d96047eSMarkus Pfeiffer 	return (error);
2241d96047eSMarkus Pfeiffer }
2251d96047eSMarkus Pfeiffer 
2261d96047eSMarkus Pfeiffer struct ugen20_urd_state {
2271d96047eSMarkus Pfeiffer 	struct usb_read_dir urd;
2281d96047eSMarkus Pfeiffer 	uint32_t nparsed;
2291d96047eSMarkus Pfeiffer 	int	f;
2301d96047eSMarkus Pfeiffer 	uint8_t *ptr;
2311d96047eSMarkus Pfeiffer 	const char *src;
2321d96047eSMarkus Pfeiffer 	const char *dst;
2331d96047eSMarkus Pfeiffer 	uint8_t	buf[256];
2341d96047eSMarkus Pfeiffer 	uint8_t	dummy_zero[1];
2351d96047eSMarkus Pfeiffer };
2361d96047eSMarkus Pfeiffer 
2371d96047eSMarkus Pfeiffer static int
ugen20_readdir(struct ugen20_urd_state * st)2381d96047eSMarkus Pfeiffer ugen20_readdir(struct ugen20_urd_state *st)
2391d96047eSMarkus Pfeiffer {
2401d96047eSMarkus Pfeiffer 	;				/* style fix */
2411d96047eSMarkus Pfeiffer repeat:
2421d96047eSMarkus Pfeiffer 	if (st->ptr == NULL) {
2431d96047eSMarkus Pfeiffer 		st->urd.urd_startentry += st->nparsed;
2441d96047eSMarkus Pfeiffer 		st->urd.urd_data = libusb20_pass_ptr(st->buf);
2451d96047eSMarkus Pfeiffer 		st->urd.urd_maxlen = sizeof(st->buf);
2461d96047eSMarkus Pfeiffer 		st->nparsed = 0;
2471d96047eSMarkus Pfeiffer 
2489b0c1abeSSascha Wildner 		if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) {
2491d96047eSMarkus Pfeiffer 			return (EINVAL);
2501d96047eSMarkus Pfeiffer 		}
2511d96047eSMarkus Pfeiffer 		st->ptr = st->buf;
2521d96047eSMarkus Pfeiffer 	}
2531d96047eSMarkus Pfeiffer 	if (st->ptr[0] == 0) {
2541d96047eSMarkus Pfeiffer 		if (st->nparsed) {
2551d96047eSMarkus Pfeiffer 			st->ptr = NULL;
2561d96047eSMarkus Pfeiffer 			goto repeat;
2571d96047eSMarkus Pfeiffer 		} else {
2581d96047eSMarkus Pfeiffer 			return (ENXIO);
2591d96047eSMarkus Pfeiffer 		}
2601d96047eSMarkus Pfeiffer 	}
2611d96047eSMarkus Pfeiffer 	st->src = (void *)(st->ptr + 1);
2621d96047eSMarkus Pfeiffer 	st->dst = st->src + strlen(st->src) + 1;
2631d96047eSMarkus Pfeiffer 	st->ptr = st->ptr + st->ptr[0];
2641d96047eSMarkus Pfeiffer 	st->nparsed++;
2651d96047eSMarkus Pfeiffer 
2661d96047eSMarkus Pfeiffer 	if ((st->ptr < st->buf) ||
2671d96047eSMarkus Pfeiffer 	    (st->ptr > st->dummy_zero)) {
2681d96047eSMarkus Pfeiffer 		/* invalid entry */
2691d96047eSMarkus Pfeiffer 		return (EINVAL);
2701d96047eSMarkus Pfeiffer 	}
2711d96047eSMarkus Pfeiffer 	return (0);
2721d96047eSMarkus Pfeiffer }
2731d96047eSMarkus Pfeiffer 
2741d96047eSMarkus Pfeiffer static int
ugen20_init_backend(struct libusb20_backend * pbe)2751d96047eSMarkus Pfeiffer ugen20_init_backend(struct libusb20_backend *pbe)
2761d96047eSMarkus Pfeiffer {
2771d96047eSMarkus Pfeiffer 	struct ugen20_urd_state state;
2781d96047eSMarkus Pfeiffer 	struct libusb20_device *pdev;
2791d96047eSMarkus Pfeiffer 
2801d96047eSMarkus Pfeiffer 	memset(&state, 0, sizeof(state));
2811d96047eSMarkus Pfeiffer 
2821d96047eSMarkus Pfeiffer 	state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
2831d96047eSMarkus Pfeiffer 	if (state.f < 0)
2841d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
2851d96047eSMarkus Pfeiffer 
2861d96047eSMarkus Pfeiffer 	while (ugen20_readdir(&state) == 0) {
2871d96047eSMarkus Pfeiffer 
2881d96047eSMarkus Pfeiffer 		if ((state.src[0] != 'u') ||
2891d96047eSMarkus Pfeiffer 		    (state.src[1] != 'g') ||
2901d96047eSMarkus Pfeiffer 		    (state.src[2] != 'e') ||
2911d96047eSMarkus Pfeiffer 		    (state.src[3] != 'n')) {
2921d96047eSMarkus Pfeiffer 			continue;
2931d96047eSMarkus Pfeiffer 		}
2941d96047eSMarkus Pfeiffer 		pdev = libusb20_dev_alloc();
2951d96047eSMarkus Pfeiffer 		if (pdev == NULL) {
2961d96047eSMarkus Pfeiffer 			continue;
2971d96047eSMarkus Pfeiffer 		}
2981d96047eSMarkus Pfeiffer 		if (ugen20_enumerate(pdev, state.src + 4)) {
2991d96047eSMarkus Pfeiffer 			libusb20_dev_free(pdev);
3001d96047eSMarkus Pfeiffer 			continue;
3011d96047eSMarkus Pfeiffer 		}
3021d96047eSMarkus Pfeiffer 		/* put the device on the backend list */
3031d96047eSMarkus Pfeiffer 		libusb20_be_enqueue_device(pbe, pdev);
3041d96047eSMarkus Pfeiffer 	}
3051d96047eSMarkus Pfeiffer 	close(state.f);
3061d96047eSMarkus Pfeiffer 	return (0);			/* success */
3071d96047eSMarkus Pfeiffer }
3081d96047eSMarkus Pfeiffer 
3091d96047eSMarkus Pfeiffer static void
ugen20_tr_release(struct libusb20_device * pdev)3101d96047eSMarkus Pfeiffer ugen20_tr_release(struct libusb20_device *pdev)
3111d96047eSMarkus Pfeiffer {
3121d96047eSMarkus Pfeiffer 	struct usb_fs_uninit fs_uninit;
3131d96047eSMarkus Pfeiffer 
3141d96047eSMarkus Pfeiffer 	if (pdev->nTransfer == 0) {
3151d96047eSMarkus Pfeiffer 		return;
3161d96047eSMarkus Pfeiffer 	}
3171d96047eSMarkus Pfeiffer 	/* release all pending USB transfers */
3181d96047eSMarkus Pfeiffer 	if (pdev->privBeData != NULL) {
3191d96047eSMarkus Pfeiffer 		memset(&fs_uninit, 0, sizeof(fs_uninit));
3209b0c1abeSSascha Wildner 		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
3211d96047eSMarkus Pfeiffer 			/* ignore any errors of this kind */
3221d96047eSMarkus Pfeiffer 		}
3231d96047eSMarkus Pfeiffer 	}
3241d96047eSMarkus Pfeiffer 	return;
3251d96047eSMarkus Pfeiffer }
3261d96047eSMarkus Pfeiffer 
3271d96047eSMarkus Pfeiffer static int
ugen20_tr_renew(struct libusb20_device * pdev)3281d96047eSMarkus Pfeiffer ugen20_tr_renew(struct libusb20_device *pdev)
3291d96047eSMarkus Pfeiffer {
3301d96047eSMarkus Pfeiffer 	struct usb_fs_init fs_init;
3311d96047eSMarkus Pfeiffer 	struct usb_fs_endpoint *pfse;
3321d96047eSMarkus Pfeiffer 	int error;
3331d96047eSMarkus Pfeiffer 	uint32_t size;
3341d96047eSMarkus Pfeiffer 	uint16_t nMaxTransfer;
3351d96047eSMarkus Pfeiffer 
3361d96047eSMarkus Pfeiffer 	nMaxTransfer = pdev->nTransfer;
3371d96047eSMarkus Pfeiffer 	error = 0;
3381d96047eSMarkus Pfeiffer 
3391d96047eSMarkus Pfeiffer 	if (nMaxTransfer == 0) {
3401d96047eSMarkus Pfeiffer 		goto done;
3411d96047eSMarkus Pfeiffer 	}
3421d96047eSMarkus Pfeiffer 	size = nMaxTransfer * sizeof(*pfse);
3431d96047eSMarkus Pfeiffer 
3441d96047eSMarkus Pfeiffer 	if (pdev->privBeData == NULL) {
3451d96047eSMarkus Pfeiffer 		pfse = malloc(size);
3461d96047eSMarkus Pfeiffer 		if (pfse == NULL) {
3471d96047eSMarkus Pfeiffer 			error = LIBUSB20_ERROR_NO_MEM;
3481d96047eSMarkus Pfeiffer 			goto done;
3491d96047eSMarkus Pfeiffer 		}
3501d96047eSMarkus Pfeiffer 		pdev->privBeData = pfse;
3511d96047eSMarkus Pfeiffer 	}
3521d96047eSMarkus Pfeiffer 	/* reset endpoint data */
3531d96047eSMarkus Pfeiffer 	memset(pdev->privBeData, 0, size);
3541d96047eSMarkus Pfeiffer 
3551d96047eSMarkus Pfeiffer 	memset(&fs_init, 0, sizeof(fs_init));
3561d96047eSMarkus Pfeiffer 
3571d96047eSMarkus Pfeiffer 	fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData);
3581d96047eSMarkus Pfeiffer 	fs_init.ep_index_max = nMaxTransfer;
3591d96047eSMarkus Pfeiffer 
3609b0c1abeSSascha Wildner 	if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) {
3611d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_OTHER;
3621d96047eSMarkus Pfeiffer 		goto done;
3631d96047eSMarkus Pfeiffer 	}
3641d96047eSMarkus Pfeiffer done:
3651d96047eSMarkus Pfeiffer 	return (error);
3661d96047eSMarkus Pfeiffer }
3671d96047eSMarkus Pfeiffer 
3681d96047eSMarkus Pfeiffer static int
ugen20_open_device(struct libusb20_device * pdev,uint16_t nMaxTransfer)3691d96047eSMarkus Pfeiffer ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer)
3701d96047eSMarkus Pfeiffer {
3711d96047eSMarkus Pfeiffer 	uint32_t plugtime;
3721d96047eSMarkus Pfeiffer 	char buf[64];
3731d96047eSMarkus Pfeiffer 	int f;
3741d96047eSMarkus Pfeiffer 	int g;
3751d96047eSMarkus Pfeiffer 	int error;
3761d96047eSMarkus Pfeiffer 
3771d96047eSMarkus Pfeiffer 	snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u",
3781d96047eSMarkus Pfeiffer 	    pdev->bus_number, pdev->device_address);
3791d96047eSMarkus Pfeiffer 
3801d96047eSMarkus Pfeiffer 	/*
3811d96047eSMarkus Pfeiffer 	 * We need two file handles, one for the control endpoint and one
3821d96047eSMarkus Pfeiffer 	 * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised
3831d96047eSMarkus Pfeiffer 	 * kernel locking.
3841d96047eSMarkus Pfeiffer 	 */
3851d96047eSMarkus Pfeiffer 	g = open(buf, O_RDWR);
3861d96047eSMarkus Pfeiffer 	if (g < 0) {
3871d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_NO_DEVICE);
3881d96047eSMarkus Pfeiffer 	}
3891d96047eSMarkus Pfeiffer 	f = open(buf, O_RDWR);
3901d96047eSMarkus Pfeiffer 	if (f < 0) {
3911d96047eSMarkus Pfeiffer 		close(g);
3921d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_NO_DEVICE);
3931d96047eSMarkus Pfeiffer 	}
3949b0c1abeSSascha Wildner 	if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
3951d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_OTHER;
3961d96047eSMarkus Pfeiffer 		goto done;
3971d96047eSMarkus Pfeiffer 	}
3981d96047eSMarkus Pfeiffer 	/* check that the correct device is still plugged */
3991d96047eSMarkus Pfeiffer 	if (pdev->session_data.plugtime != plugtime) {
4001d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_NO_DEVICE;
4011d96047eSMarkus Pfeiffer 		goto done;
4021d96047eSMarkus Pfeiffer 	}
4031d96047eSMarkus Pfeiffer 	/* need to set this before "tr_renew()" */
4041d96047eSMarkus Pfeiffer 	pdev->file = f;
4051d96047eSMarkus Pfeiffer 	pdev->file_ctrl = g;
4061d96047eSMarkus Pfeiffer 
4071d96047eSMarkus Pfeiffer 	/* renew all USB transfers */
4081d96047eSMarkus Pfeiffer 	error = ugen20_tr_renew(pdev);
4091d96047eSMarkus Pfeiffer 	if (error) {
4101d96047eSMarkus Pfeiffer 		goto done;
4111d96047eSMarkus Pfeiffer 	}
4121d96047eSMarkus Pfeiffer 	/* set methods */
4131d96047eSMarkus Pfeiffer 	pdev->methods = &libusb20_ugen20_device_methods;
4141d96047eSMarkus Pfeiffer 
4151d96047eSMarkus Pfeiffer done:
4161d96047eSMarkus Pfeiffer 	if (error) {
4171d96047eSMarkus Pfeiffer 		if (pdev->privBeData) {
4181d96047eSMarkus Pfeiffer 			/* cleanup after "tr_renew()" */
4191d96047eSMarkus Pfeiffer 			free(pdev->privBeData);
4201d96047eSMarkus Pfeiffer 			pdev->privBeData = NULL;
4211d96047eSMarkus Pfeiffer 		}
4221d96047eSMarkus Pfeiffer 		pdev->file = -1;
4231d96047eSMarkus Pfeiffer 		pdev->file_ctrl = -1;
4241d96047eSMarkus Pfeiffer 		close(f);
4251d96047eSMarkus Pfeiffer 		close(g);
4261d96047eSMarkus Pfeiffer 	}
4271d96047eSMarkus Pfeiffer 	return (error);
4281d96047eSMarkus Pfeiffer }
4291d96047eSMarkus Pfeiffer 
4301d96047eSMarkus Pfeiffer static int
ugen20_close_device(struct libusb20_device * pdev)4311d96047eSMarkus Pfeiffer ugen20_close_device(struct libusb20_device *pdev)
4321d96047eSMarkus Pfeiffer {
4331d96047eSMarkus Pfeiffer 	struct usb_fs_uninit fs_uninit;
4341d96047eSMarkus Pfeiffer 
4351d96047eSMarkus Pfeiffer 	if (pdev->privBeData) {
4361d96047eSMarkus Pfeiffer 		memset(&fs_uninit, 0, sizeof(fs_uninit));
4379b0c1abeSSascha Wildner 		if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) {
4381d96047eSMarkus Pfeiffer 			/* ignore this error */
4391d96047eSMarkus Pfeiffer 		}
4401d96047eSMarkus Pfeiffer 		free(pdev->privBeData);
4411d96047eSMarkus Pfeiffer 	}
4421d96047eSMarkus Pfeiffer 	pdev->nTransfer = 0;
4431d96047eSMarkus Pfeiffer 	pdev->privBeData = NULL;
4441d96047eSMarkus Pfeiffer 	close(pdev->file);
4451d96047eSMarkus Pfeiffer 	close(pdev->file_ctrl);
4461d96047eSMarkus Pfeiffer 	pdev->file = -1;
4471d96047eSMarkus Pfeiffer 	pdev->file_ctrl = -1;
4481d96047eSMarkus Pfeiffer 	return (0);			/* success */
4491d96047eSMarkus Pfeiffer }
4501d96047eSMarkus Pfeiffer 
4511d96047eSMarkus Pfeiffer static void
ugen20_exit_backend(struct libusb20_backend * pbe)4521d96047eSMarkus Pfeiffer ugen20_exit_backend(struct libusb20_backend *pbe)
4531d96047eSMarkus Pfeiffer {
4541d96047eSMarkus Pfeiffer 	return;				/* nothing to do */
4551d96047eSMarkus Pfeiffer }
4561d96047eSMarkus Pfeiffer 
4571d96047eSMarkus Pfeiffer static int
ugen20_get_config_desc_full(struct libusb20_device * pdev,uint8_t ** ppbuf,uint16_t * plen,uint8_t cfg_index)4581d96047eSMarkus Pfeiffer ugen20_get_config_desc_full(struct libusb20_device *pdev,
4591d96047eSMarkus Pfeiffer     uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index)
4601d96047eSMarkus Pfeiffer {
4611d96047eSMarkus Pfeiffer 	struct usb_gen_descriptor gen_desc;
4621d96047eSMarkus Pfeiffer 	struct usb_config_descriptor cdesc;
4631d96047eSMarkus Pfeiffer 	uint8_t *ptr;
4641d96047eSMarkus Pfeiffer 	uint16_t len;
4651d96047eSMarkus Pfeiffer 	int error;
4661d96047eSMarkus Pfeiffer 
4671d96047eSMarkus Pfeiffer 	/* make sure memory is initialised */
4681d96047eSMarkus Pfeiffer 	memset(&cdesc, 0, sizeof(cdesc));
4691d96047eSMarkus Pfeiffer 	memset(&gen_desc, 0, sizeof(gen_desc));
4701d96047eSMarkus Pfeiffer 
4711d96047eSMarkus Pfeiffer 	gen_desc.ugd_data = libusb20_pass_ptr(&cdesc);
4721d96047eSMarkus Pfeiffer 	gen_desc.ugd_maxlen = sizeof(cdesc);
4731d96047eSMarkus Pfeiffer 	gen_desc.ugd_config_index = cfg_index;
4741d96047eSMarkus Pfeiffer 
4759b0c1abeSSascha Wildner 	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
4761d96047eSMarkus Pfeiffer 	if (error) {
4771d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
4781d96047eSMarkus Pfeiffer 	}
4791d96047eSMarkus Pfeiffer 	len = UGETW(cdesc.wTotalLength);
4801d96047eSMarkus Pfeiffer 	if (len < sizeof(cdesc)) {
4811d96047eSMarkus Pfeiffer 		/* corrupt descriptor */
4821d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
4831d96047eSMarkus Pfeiffer 	}
4841d96047eSMarkus Pfeiffer 	ptr = malloc(len);
4851d96047eSMarkus Pfeiffer 	if (!ptr) {
4861d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_NO_MEM);
4871d96047eSMarkus Pfeiffer 	}
4881d96047eSMarkus Pfeiffer 
4891d96047eSMarkus Pfeiffer 	/* make sure memory is initialised */
4901d96047eSMarkus Pfeiffer 	memset(ptr, 0, len);
4911d96047eSMarkus Pfeiffer 
4921d96047eSMarkus Pfeiffer 	gen_desc.ugd_data = libusb20_pass_ptr(ptr);
4931d96047eSMarkus Pfeiffer 	gen_desc.ugd_maxlen = len;
4941d96047eSMarkus Pfeiffer 
4959b0c1abeSSascha Wildner 	error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc);
4961d96047eSMarkus Pfeiffer 	if (error) {
4971d96047eSMarkus Pfeiffer 		free(ptr);
4981d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
4991d96047eSMarkus Pfeiffer 	}
5001d96047eSMarkus Pfeiffer 	/* make sure that the device doesn't fool us */
5011d96047eSMarkus Pfeiffer 	memcpy(ptr, &cdesc, sizeof(cdesc));
5021d96047eSMarkus Pfeiffer 
5031d96047eSMarkus Pfeiffer 	*ppbuf = ptr;
5041d96047eSMarkus Pfeiffer 	*plen = len;
5051d96047eSMarkus Pfeiffer 
5061d96047eSMarkus Pfeiffer 	return (0);			/* success */
5071d96047eSMarkus Pfeiffer }
5081d96047eSMarkus Pfeiffer 
5091d96047eSMarkus Pfeiffer static int
ugen20_get_config_index(struct libusb20_device * pdev,uint8_t * pindex)5101d96047eSMarkus Pfeiffer ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex)
5111d96047eSMarkus Pfeiffer {
5121d96047eSMarkus Pfeiffer 	int temp;
5131d96047eSMarkus Pfeiffer 
5149b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) {
5151d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
5161d96047eSMarkus Pfeiffer 	}
5171d96047eSMarkus Pfeiffer 	*pindex = temp;
5181d96047eSMarkus Pfeiffer 
5191d96047eSMarkus Pfeiffer 	return (0);
5201d96047eSMarkus Pfeiffer }
5211d96047eSMarkus Pfeiffer 
5221d96047eSMarkus Pfeiffer static int
ugen20_set_config_index(struct libusb20_device * pdev,uint8_t cfg_index)5231d96047eSMarkus Pfeiffer ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index)
5241d96047eSMarkus Pfeiffer {
5251d96047eSMarkus Pfeiffer 	int temp = cfg_index;
5261d96047eSMarkus Pfeiffer 
5271d96047eSMarkus Pfeiffer 	/* release all active USB transfers */
5281d96047eSMarkus Pfeiffer 	ugen20_tr_release(pdev);
5291d96047eSMarkus Pfeiffer 
5309b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) {
5311d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
5321d96047eSMarkus Pfeiffer 	}
5331d96047eSMarkus Pfeiffer 	return (ugen20_tr_renew(pdev));
5341d96047eSMarkus Pfeiffer }
5351d96047eSMarkus Pfeiffer 
5361d96047eSMarkus Pfeiffer static int
ugen20_set_alt_index(struct libusb20_device * pdev,uint8_t iface_index,uint8_t alt_index)5371d96047eSMarkus Pfeiffer ugen20_set_alt_index(struct libusb20_device *pdev,
5381d96047eSMarkus Pfeiffer     uint8_t iface_index, uint8_t alt_index)
5391d96047eSMarkus Pfeiffer {
5401d96047eSMarkus Pfeiffer 	struct usb_alt_interface alt_iface;
5411d96047eSMarkus Pfeiffer 
5421d96047eSMarkus Pfeiffer 	memset(&alt_iface, 0, sizeof(alt_iface));
5431d96047eSMarkus Pfeiffer 
5441d96047eSMarkus Pfeiffer 	alt_iface.uai_interface_index = iface_index;
5451d96047eSMarkus Pfeiffer 	alt_iface.uai_alt_index = alt_index;
5461d96047eSMarkus Pfeiffer 
5471d96047eSMarkus Pfeiffer 	/* release all active USB transfers */
5481d96047eSMarkus Pfeiffer 	ugen20_tr_release(pdev);
5491d96047eSMarkus Pfeiffer 
5509b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) {
5511d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
5521d96047eSMarkus Pfeiffer 	}
5531d96047eSMarkus Pfeiffer 	return (ugen20_tr_renew(pdev));
5541d96047eSMarkus Pfeiffer }
5551d96047eSMarkus Pfeiffer 
5561d96047eSMarkus Pfeiffer static int
ugen20_reset_device(struct libusb20_device * pdev)5571d96047eSMarkus Pfeiffer ugen20_reset_device(struct libusb20_device *pdev)
5581d96047eSMarkus Pfeiffer {
5591d96047eSMarkus Pfeiffer 	int temp = 0;
5601d96047eSMarkus Pfeiffer 
5611d96047eSMarkus Pfeiffer 	/* release all active USB transfers */
5621d96047eSMarkus Pfeiffer 	ugen20_tr_release(pdev);
5631d96047eSMarkus Pfeiffer 
5649b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) {
5651d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
5661d96047eSMarkus Pfeiffer 	}
5671d96047eSMarkus Pfeiffer 	return (ugen20_tr_renew(pdev));
5681d96047eSMarkus Pfeiffer }
5691d96047eSMarkus Pfeiffer 
5701d96047eSMarkus Pfeiffer static int
ugen20_check_connected(struct libusb20_device * pdev)5711d96047eSMarkus Pfeiffer ugen20_check_connected(struct libusb20_device *pdev)
5721d96047eSMarkus Pfeiffer {
5731d96047eSMarkus Pfeiffer 	uint32_t plugtime;
5741d96047eSMarkus Pfeiffer 	int error = 0;
5751d96047eSMarkus Pfeiffer 
5769b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) {
5771d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_NO_DEVICE;
5781d96047eSMarkus Pfeiffer 		goto done;
5791d96047eSMarkus Pfeiffer 	}
5801d96047eSMarkus Pfeiffer 
5811d96047eSMarkus Pfeiffer 	if (pdev->session_data.plugtime != plugtime) {
5821d96047eSMarkus Pfeiffer 		error = LIBUSB20_ERROR_NO_DEVICE;
5831d96047eSMarkus Pfeiffer 		goto done;
5841d96047eSMarkus Pfeiffer 	}
5851d96047eSMarkus Pfeiffer done:
5861d96047eSMarkus Pfeiffer 	return (error);
5871d96047eSMarkus Pfeiffer }
5881d96047eSMarkus Pfeiffer 
5891d96047eSMarkus Pfeiffer static int
ugen20_set_power_mode(struct libusb20_device * pdev,uint8_t power_mode)5901d96047eSMarkus Pfeiffer ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode)
5911d96047eSMarkus Pfeiffer {
5921d96047eSMarkus Pfeiffer 	int temp;
5931d96047eSMarkus Pfeiffer 
5941d96047eSMarkus Pfeiffer 	switch (power_mode) {
5951d96047eSMarkus Pfeiffer 	case LIBUSB20_POWER_OFF:
5961d96047eSMarkus Pfeiffer 		temp = USB_POWER_MODE_OFF;
5971d96047eSMarkus Pfeiffer 		break;
5981d96047eSMarkus Pfeiffer 	case LIBUSB20_POWER_ON:
5991d96047eSMarkus Pfeiffer 		temp = USB_POWER_MODE_ON;
6001d96047eSMarkus Pfeiffer 		break;
6011d96047eSMarkus Pfeiffer 	case LIBUSB20_POWER_SAVE:
6021d96047eSMarkus Pfeiffer 		temp = USB_POWER_MODE_SAVE;
6031d96047eSMarkus Pfeiffer 		break;
6041d96047eSMarkus Pfeiffer 	case LIBUSB20_POWER_SUSPEND:
6051d96047eSMarkus Pfeiffer 		temp = USB_POWER_MODE_SUSPEND;
6061d96047eSMarkus Pfeiffer 		break;
6071d96047eSMarkus Pfeiffer 	case LIBUSB20_POWER_RESUME:
6081d96047eSMarkus Pfeiffer 		temp = USB_POWER_MODE_RESUME;
6091d96047eSMarkus Pfeiffer 		break;
6101d96047eSMarkus Pfeiffer 	default:
6111d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_INVALID_PARAM);
6121d96047eSMarkus Pfeiffer 	}
6139b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) {
6141d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
6151d96047eSMarkus Pfeiffer 	}
6161d96047eSMarkus Pfeiffer 	return (0);
6171d96047eSMarkus Pfeiffer }
6181d96047eSMarkus Pfeiffer 
6191d96047eSMarkus Pfeiffer static int
ugen20_get_power_mode(struct libusb20_device * pdev,uint8_t * power_mode)6201d96047eSMarkus Pfeiffer ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
6211d96047eSMarkus Pfeiffer {
6221d96047eSMarkus Pfeiffer 	int temp;
6231d96047eSMarkus Pfeiffer 
6249b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) {
6251d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
6261d96047eSMarkus Pfeiffer 	}
6271d96047eSMarkus Pfeiffer 	switch (temp) {
6281d96047eSMarkus Pfeiffer 	case USB_POWER_MODE_OFF:
6291d96047eSMarkus Pfeiffer 		temp = LIBUSB20_POWER_OFF;
6301d96047eSMarkus Pfeiffer 		break;
6311d96047eSMarkus Pfeiffer 	case USB_POWER_MODE_ON:
6321d96047eSMarkus Pfeiffer 		temp = LIBUSB20_POWER_ON;
6331d96047eSMarkus Pfeiffer 		break;
6341d96047eSMarkus Pfeiffer 	case USB_POWER_MODE_SAVE:
6351d96047eSMarkus Pfeiffer 		temp = LIBUSB20_POWER_SAVE;
6361d96047eSMarkus Pfeiffer 		break;
6371d96047eSMarkus Pfeiffer 	case USB_POWER_MODE_SUSPEND:
6381d96047eSMarkus Pfeiffer 		temp = LIBUSB20_POWER_SUSPEND;
6391d96047eSMarkus Pfeiffer 		break;
6401d96047eSMarkus Pfeiffer 	case USB_POWER_MODE_RESUME:
6411d96047eSMarkus Pfeiffer 		temp = LIBUSB20_POWER_RESUME;
6421d96047eSMarkus Pfeiffer 		break;
6431d96047eSMarkus Pfeiffer 	default:
6441d96047eSMarkus Pfeiffer 		temp = LIBUSB20_POWER_ON;
6451d96047eSMarkus Pfeiffer 		break;
6461d96047eSMarkus Pfeiffer 	}
6471d96047eSMarkus Pfeiffer 	*power_mode = temp;
6481d96047eSMarkus Pfeiffer 	return (0);			/* success */
6491d96047eSMarkus Pfeiffer }
6501d96047eSMarkus Pfeiffer 
6511d96047eSMarkus Pfeiffer static int
ugen20_get_port_path(struct libusb20_device * pdev,uint8_t * buf,uint8_t bufsize)6529b0c1abeSSascha Wildner ugen20_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize)
6539b0c1abeSSascha Wildner {
6549b0c1abeSSascha Wildner 	struct usb_device_port_path udpp;
6559b0c1abeSSascha Wildner 
6569b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_DEV_PORT_PATH), &udpp))
6579b0c1abeSSascha Wildner 		return (LIBUSB20_ERROR_OTHER);
6589b0c1abeSSascha Wildner 
6599b0c1abeSSascha Wildner 	if (udpp.udp_port_level > bufsize)
6609b0c1abeSSascha Wildner 		return (LIBUSB20_ERROR_OVERFLOW);
6619b0c1abeSSascha Wildner 
6629b0c1abeSSascha Wildner 	memcpy(buf, udpp.udp_port_no, udpp.udp_port_level);
6639b0c1abeSSascha Wildner 
6649b0c1abeSSascha Wildner 	return (udpp.udp_port_level);	/* success */
6659b0c1abeSSascha Wildner }
6669b0c1abeSSascha Wildner 
6679b0c1abeSSascha Wildner static int
ugen20_get_power_usage(struct libusb20_device * pdev,uint16_t * power_usage)6689b0c1abeSSascha Wildner ugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
6699b0c1abeSSascha Wildner {
6709b0c1abeSSascha Wildner 	int temp;
6719b0c1abeSSascha Wildner 
6729b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) {
6739b0c1abeSSascha Wildner 		return (LIBUSB20_ERROR_OTHER);
6749b0c1abeSSascha Wildner 	}
6759b0c1abeSSascha Wildner 	*power_usage = temp;
6769b0c1abeSSascha Wildner 	return (0);			/* success */
6779b0c1abeSSascha Wildner }
6789b0c1abeSSascha Wildner 
6799b0c1abeSSascha Wildner static int
ugen20_kernel_driver_active(struct libusb20_device * pdev,uint8_t iface_index)6801d96047eSMarkus Pfeiffer ugen20_kernel_driver_active(struct libusb20_device *pdev,
6811d96047eSMarkus Pfeiffer     uint8_t iface_index)
6821d96047eSMarkus Pfeiffer {
6831d96047eSMarkus Pfeiffer 	int temp = iface_index;
6841d96047eSMarkus Pfeiffer 
6859b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) {
6861d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
6871d96047eSMarkus Pfeiffer 	}
6881d96047eSMarkus Pfeiffer 	return (0);			/* kernel driver is active */
6891d96047eSMarkus Pfeiffer }
6901d96047eSMarkus Pfeiffer 
6911d96047eSMarkus Pfeiffer static int
ugen20_detach_kernel_driver(struct libusb20_device * pdev,uint8_t iface_index)6921d96047eSMarkus Pfeiffer ugen20_detach_kernel_driver(struct libusb20_device *pdev,
6931d96047eSMarkus Pfeiffer     uint8_t iface_index)
6941d96047eSMarkus Pfeiffer {
6951d96047eSMarkus Pfeiffer 	int temp = iface_index;
6961d96047eSMarkus Pfeiffer 
6979b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) {
6981d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
6991d96047eSMarkus Pfeiffer 	}
7009b0c1abeSSascha Wildner 	return (0);			/* kernel driver is detached */
7011d96047eSMarkus Pfeiffer }
7021d96047eSMarkus Pfeiffer 
7031d96047eSMarkus Pfeiffer static int
ugen20_do_request_sync(struct libusb20_device * pdev,struct LIBUSB20_CONTROL_SETUP_DECODED * setup,void * data,uint16_t * pactlen,uint32_t timeout,uint8_t flags)7041d96047eSMarkus Pfeiffer ugen20_do_request_sync(struct libusb20_device *pdev,
7051d96047eSMarkus Pfeiffer     struct LIBUSB20_CONTROL_SETUP_DECODED *setup,
7061d96047eSMarkus Pfeiffer     void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags)
7071d96047eSMarkus Pfeiffer {
7081d96047eSMarkus Pfeiffer 	struct usb_ctl_request req;
7091d96047eSMarkus Pfeiffer 
7101d96047eSMarkus Pfeiffer 	memset(&req, 0, sizeof(req));
7111d96047eSMarkus Pfeiffer 
7121d96047eSMarkus Pfeiffer 	req.ucr_data = libusb20_pass_ptr(data);
7131d96047eSMarkus Pfeiffer 	if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
7141d96047eSMarkus Pfeiffer 		req.ucr_flags |= USB_SHORT_XFER_OK;
7151d96047eSMarkus Pfeiffer 	}
7161d96047eSMarkus Pfeiffer 	if (libusb20_me_encode(&req.ucr_request,
7171d96047eSMarkus Pfeiffer 	    sizeof(req.ucr_request), setup)) {
7181d96047eSMarkus Pfeiffer 		/* ignore */
7191d96047eSMarkus Pfeiffer 	}
7209b0c1abeSSascha Wildner 	if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) {
7211d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
7221d96047eSMarkus Pfeiffer 	}
7231d96047eSMarkus Pfeiffer 	if (pactlen) {
7241d96047eSMarkus Pfeiffer 		/* get actual length */
7251d96047eSMarkus Pfeiffer 		*pactlen = req.ucr_actlen;
7261d96047eSMarkus Pfeiffer 	}
7279b0c1abeSSascha Wildner 	return (0);			/* request was successful */
7281d96047eSMarkus Pfeiffer }
7291d96047eSMarkus Pfeiffer 
7301d96047eSMarkus Pfeiffer static int
ugen20_process(struct libusb20_device * pdev)7311d96047eSMarkus Pfeiffer ugen20_process(struct libusb20_device *pdev)
7321d96047eSMarkus Pfeiffer {
7331d96047eSMarkus Pfeiffer 	struct usb_fs_complete temp;
7341d96047eSMarkus Pfeiffer 	struct usb_fs_endpoint *fsep;
7351d96047eSMarkus Pfeiffer 	struct libusb20_transfer *xfer;
7361d96047eSMarkus Pfeiffer 
7371d96047eSMarkus Pfeiffer 	while (1) {
7381d96047eSMarkus Pfeiffer 
7399b0c1abeSSascha Wildner 	  if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) {
7401d96047eSMarkus Pfeiffer 			if (errno == EBUSY) {
7411d96047eSMarkus Pfeiffer 				break;
7421d96047eSMarkus Pfeiffer 			} else {
7431d96047eSMarkus Pfeiffer 				/* device detached */
7441d96047eSMarkus Pfeiffer 				return (LIBUSB20_ERROR_OTHER);
7451d96047eSMarkus Pfeiffer 			}
7461d96047eSMarkus Pfeiffer 		}
7471d96047eSMarkus Pfeiffer 		fsep = pdev->privBeData;
7481d96047eSMarkus Pfeiffer 		xfer = pdev->pTransfer;
7491d96047eSMarkus Pfeiffer 		fsep += temp.ep_index;
7501d96047eSMarkus Pfeiffer 		xfer += temp.ep_index;
7511d96047eSMarkus Pfeiffer 
7521d96047eSMarkus Pfeiffer 		/* update transfer status */
7531d96047eSMarkus Pfeiffer 
7541d96047eSMarkus Pfeiffer 		if (fsep->status == 0) {
7551d96047eSMarkus Pfeiffer 			xfer->aFrames = fsep->aFrames;
7561d96047eSMarkus Pfeiffer 			xfer->timeComplete = fsep->isoc_time_complete;
7571d96047eSMarkus Pfeiffer 			xfer->status = LIBUSB20_TRANSFER_COMPLETED;
7581d96047eSMarkus Pfeiffer 		} else if (fsep->status == USB_ERR_CANCELLED) {
7591d96047eSMarkus Pfeiffer 			xfer->aFrames = 0;
7601d96047eSMarkus Pfeiffer 			xfer->timeComplete = 0;
7611d96047eSMarkus Pfeiffer 			xfer->status = LIBUSB20_TRANSFER_CANCELLED;
7621d96047eSMarkus Pfeiffer 		} else if (fsep->status == USB_ERR_STALLED) {
7631d96047eSMarkus Pfeiffer 			xfer->aFrames = 0;
7641d96047eSMarkus Pfeiffer 			xfer->timeComplete = 0;
7651d96047eSMarkus Pfeiffer 			xfer->status = LIBUSB20_TRANSFER_STALL;
7661d96047eSMarkus Pfeiffer 		} else if (fsep->status == USB_ERR_TIMEOUT) {
7671d96047eSMarkus Pfeiffer 			xfer->aFrames = 0;
7681d96047eSMarkus Pfeiffer 			xfer->timeComplete = 0;
7691d96047eSMarkus Pfeiffer 			xfer->status = LIBUSB20_TRANSFER_TIMED_OUT;
7701d96047eSMarkus Pfeiffer 		} else {
7711d96047eSMarkus Pfeiffer 			xfer->aFrames = 0;
7721d96047eSMarkus Pfeiffer 			xfer->timeComplete = 0;
7731d96047eSMarkus Pfeiffer 			xfer->status = LIBUSB20_TRANSFER_ERROR;
7741d96047eSMarkus Pfeiffer 		}
7751d96047eSMarkus Pfeiffer 		libusb20_tr_callback_wrapper(xfer);
7761d96047eSMarkus Pfeiffer 	}
7771d96047eSMarkus Pfeiffer 	return (0);			/* done */
7781d96047eSMarkus Pfeiffer }
7791d96047eSMarkus Pfeiffer 
7801d96047eSMarkus Pfeiffer static int
ugen20_tr_open(struct libusb20_transfer * xfer,uint32_t MaxBufSize,uint32_t MaxFrameCount,uint8_t ep_no,uint16_t stream_id,uint8_t pre_scale)7811d96047eSMarkus Pfeiffer ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize,
7829b0c1abeSSascha Wildner     uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id,
7839b0c1abeSSascha Wildner     uint8_t pre_scale)
7841d96047eSMarkus Pfeiffer {
7859b0c1abeSSascha Wildner 	union {
7869b0c1abeSSascha Wildner 		struct usb_fs_open fs_open;
7879b0c1abeSSascha Wildner 		struct usb_fs_open_stream fs_open_stream;
7889b0c1abeSSascha Wildner 	} temp;
7891d96047eSMarkus Pfeiffer 	struct usb_fs_endpoint *fsep;
7901d96047eSMarkus Pfeiffer 
7911d96047eSMarkus Pfeiffer 	if (pre_scale)
7921d96047eSMarkus Pfeiffer 		MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE;
7931d96047eSMarkus Pfeiffer 
7941d96047eSMarkus Pfeiffer 	memset(&temp, 0, sizeof(temp));
7951d96047eSMarkus Pfeiffer 
7961d96047eSMarkus Pfeiffer 	fsep = xfer->pdev->privBeData;
7971d96047eSMarkus Pfeiffer 	fsep += xfer->trIndex;
7981d96047eSMarkus Pfeiffer 
7999b0c1abeSSascha Wildner 	temp.fs_open.max_bufsize = MaxBufSize;
8009b0c1abeSSascha Wildner 	temp.fs_open.max_frames = MaxFrameCount;
8019b0c1abeSSascha Wildner 	temp.fs_open.ep_index = xfer->trIndex;
8029b0c1abeSSascha Wildner 	temp.fs_open.ep_no = ep_no;
8031d96047eSMarkus Pfeiffer 
8049b0c1abeSSascha Wildner 	if (stream_id != 0) {
8059b0c1abeSSascha Wildner 		temp.fs_open_stream.stream_id = stream_id;
8069b0c1abeSSascha Wildner 
8079b0c1abeSSascha Wildner 		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream))
8089b0c1abeSSascha Wildner 			return (LIBUSB20_ERROR_INVALID_PARAM);
8099b0c1abeSSascha Wildner 	} else {
8109b0c1abeSSascha Wildner 		if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open))
8111d96047eSMarkus Pfeiffer 			return (LIBUSB20_ERROR_INVALID_PARAM);
8121d96047eSMarkus Pfeiffer 	}
8131d96047eSMarkus Pfeiffer 	/* maximums might have changed - update */
8149b0c1abeSSascha Wildner 	xfer->maxFrames = temp.fs_open.max_frames;
8151d96047eSMarkus Pfeiffer 
8161d96047eSMarkus Pfeiffer 	/* "max_bufsize" should be multiple of "max_packet_length" */
8179b0c1abeSSascha Wildner 	xfer->maxTotalLength = temp.fs_open.max_bufsize;
8189b0c1abeSSascha Wildner 	xfer->maxPacketLen = temp.fs_open.max_packet_length;
8191d96047eSMarkus Pfeiffer 
8201d96047eSMarkus Pfeiffer 	/* setup buffer and length lists using zero copy */
8211d96047eSMarkus Pfeiffer 	fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer);
8221d96047eSMarkus Pfeiffer 	fsep->pLength = libusb20_pass_ptr(xfer->pLength);
8231d96047eSMarkus Pfeiffer 
8241d96047eSMarkus Pfeiffer 	return (0);			/* success */
8251d96047eSMarkus Pfeiffer }
8261d96047eSMarkus Pfeiffer 
8271d96047eSMarkus Pfeiffer static int
ugen20_tr_close(struct libusb20_transfer * xfer)8281d96047eSMarkus Pfeiffer ugen20_tr_close(struct libusb20_transfer *xfer)
8291d96047eSMarkus Pfeiffer {
8301d96047eSMarkus Pfeiffer 	struct usb_fs_close temp;
8311d96047eSMarkus Pfeiffer 
8321d96047eSMarkus Pfeiffer 	memset(&temp, 0, sizeof(temp));
8331d96047eSMarkus Pfeiffer 
8341d96047eSMarkus Pfeiffer 	temp.ep_index = xfer->trIndex;
8351d96047eSMarkus Pfeiffer 
8369b0c1abeSSascha Wildner 	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) {
8371d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_INVALID_PARAM);
8381d96047eSMarkus Pfeiffer 	}
8391d96047eSMarkus Pfeiffer 	return (0);			/* success */
8401d96047eSMarkus Pfeiffer }
8411d96047eSMarkus Pfeiffer 
8421d96047eSMarkus Pfeiffer static int
ugen20_tr_clear_stall_sync(struct libusb20_transfer * xfer)8431d96047eSMarkus Pfeiffer ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer)
8441d96047eSMarkus Pfeiffer {
8451d96047eSMarkus Pfeiffer 	struct usb_fs_clear_stall_sync temp;
8461d96047eSMarkus Pfeiffer 
8471d96047eSMarkus Pfeiffer 	memset(&temp, 0, sizeof(temp));
8481d96047eSMarkus Pfeiffer 
8491d96047eSMarkus Pfeiffer 	/* if the transfer is active, an error will be returned */
8501d96047eSMarkus Pfeiffer 
8511d96047eSMarkus Pfeiffer 	temp.ep_index = xfer->trIndex;
8521d96047eSMarkus Pfeiffer 
8539b0c1abeSSascha Wildner 	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) {
8541d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_INVALID_PARAM);
8551d96047eSMarkus Pfeiffer 	}
8561d96047eSMarkus Pfeiffer 	return (0);			/* success */
8571d96047eSMarkus Pfeiffer }
8581d96047eSMarkus Pfeiffer 
8591d96047eSMarkus Pfeiffer static void
ugen20_tr_submit(struct libusb20_transfer * xfer)8601d96047eSMarkus Pfeiffer ugen20_tr_submit(struct libusb20_transfer *xfer)
8611d96047eSMarkus Pfeiffer {
8621d96047eSMarkus Pfeiffer 	struct usb_fs_start temp;
8631d96047eSMarkus Pfeiffer 	struct usb_fs_endpoint *fsep;
8641d96047eSMarkus Pfeiffer 
8651d96047eSMarkus Pfeiffer 	memset(&temp, 0, sizeof(temp));
8661d96047eSMarkus Pfeiffer 
8671d96047eSMarkus Pfeiffer 	fsep = xfer->pdev->privBeData;
8681d96047eSMarkus Pfeiffer 	fsep += xfer->trIndex;
8691d96047eSMarkus Pfeiffer 
8701d96047eSMarkus Pfeiffer 	fsep->nFrames = xfer->nFrames;
8711d96047eSMarkus Pfeiffer 	fsep->flags = 0;
8721d96047eSMarkus Pfeiffer 	if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) {
8731d96047eSMarkus Pfeiffer 		fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK;
8741d96047eSMarkus Pfeiffer 	}
8751d96047eSMarkus Pfeiffer 	if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) {
8761d96047eSMarkus Pfeiffer 		fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK;
8771d96047eSMarkus Pfeiffer 	}
8781d96047eSMarkus Pfeiffer 	if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) {
8791d96047eSMarkus Pfeiffer 		fsep->flags |= USB_FS_FLAG_FORCE_SHORT;
8801d96047eSMarkus Pfeiffer 	}
8811d96047eSMarkus Pfeiffer 	if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) {
8821d96047eSMarkus Pfeiffer 		fsep->flags |= USB_FS_FLAG_CLEAR_STALL;
8831d96047eSMarkus Pfeiffer 	}
8841d96047eSMarkus Pfeiffer 	/* NOTE: The "fsep->timeout" variable is 16-bit. */
8851d96047eSMarkus Pfeiffer 	if (xfer->timeout > 65535)
8861d96047eSMarkus Pfeiffer 		fsep->timeout = 65535;
8871d96047eSMarkus Pfeiffer 	else
8881d96047eSMarkus Pfeiffer 		fsep->timeout = xfer->timeout;
8891d96047eSMarkus Pfeiffer 
8901d96047eSMarkus Pfeiffer 	temp.ep_index = xfer->trIndex;
8911d96047eSMarkus Pfeiffer 
8929b0c1abeSSascha Wildner 	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) {
8931d96047eSMarkus Pfeiffer 		/* ignore any errors - should never happen */
8941d96047eSMarkus Pfeiffer 	}
8951d96047eSMarkus Pfeiffer 	return;				/* success */
8961d96047eSMarkus Pfeiffer }
8971d96047eSMarkus Pfeiffer 
8981d96047eSMarkus Pfeiffer static void
ugen20_tr_cancel_async(struct libusb20_transfer * xfer)8991d96047eSMarkus Pfeiffer ugen20_tr_cancel_async(struct libusb20_transfer *xfer)
9001d96047eSMarkus Pfeiffer {
9011d96047eSMarkus Pfeiffer 	struct usb_fs_stop temp;
9021d96047eSMarkus Pfeiffer 
9031d96047eSMarkus Pfeiffer 	memset(&temp, 0, sizeof(temp));
9041d96047eSMarkus Pfeiffer 
9051d96047eSMarkus Pfeiffer 	temp.ep_index = xfer->trIndex;
9061d96047eSMarkus Pfeiffer 
9079b0c1abeSSascha Wildner 	if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) {
9081d96047eSMarkus Pfeiffer 		/* ignore any errors - should never happen */
9091d96047eSMarkus Pfeiffer 	}
9101d96047eSMarkus Pfeiffer 	return;
9111d96047eSMarkus Pfeiffer }
9121d96047eSMarkus Pfeiffer 
9131d96047eSMarkus Pfeiffer static int
ugen20_be_ioctl(uint32_t cmd,void * data)9141d96047eSMarkus Pfeiffer ugen20_be_ioctl(uint32_t cmd, void *data)
9151d96047eSMarkus Pfeiffer {
9161d96047eSMarkus Pfeiffer 	int f;
9171d96047eSMarkus Pfeiffer 	int error;
9181d96047eSMarkus Pfeiffer 
9191d96047eSMarkus Pfeiffer 	f = open("/dev/" USB_DEVICE_NAME, O_RDONLY);
9201d96047eSMarkus Pfeiffer 	if (f < 0)
9211d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_OTHER);
9221d96047eSMarkus Pfeiffer 	error = ioctl(f, cmd, data);
9231d96047eSMarkus Pfeiffer 	if (error == -1) {
9241d96047eSMarkus Pfeiffer 		if (errno == EPERM) {
9251d96047eSMarkus Pfeiffer 			error = LIBUSB20_ERROR_ACCESS;
9261d96047eSMarkus Pfeiffer 		} else {
9271d96047eSMarkus Pfeiffer 			error = LIBUSB20_ERROR_OTHER;
9281d96047eSMarkus Pfeiffer 		}
9291d96047eSMarkus Pfeiffer 	}
9301d96047eSMarkus Pfeiffer 	close(f);
9311d96047eSMarkus Pfeiffer 	return (error);
9321d96047eSMarkus Pfeiffer }
9331d96047eSMarkus Pfeiffer 
9341d96047eSMarkus Pfeiffer static int
ugen20_dev_get_iface_desc(struct libusb20_device * pdev,uint8_t iface_index,char * buf,uint8_t len)9351d96047eSMarkus Pfeiffer ugen20_dev_get_iface_desc(struct libusb20_device *pdev,
9361d96047eSMarkus Pfeiffer     uint8_t iface_index, char *buf, uint8_t len)
9371d96047eSMarkus Pfeiffer {
9381d96047eSMarkus Pfeiffer 	struct usb_gen_descriptor ugd;
9391d96047eSMarkus Pfeiffer 
9401d96047eSMarkus Pfeiffer 	memset(&ugd, 0, sizeof(ugd));
9411d96047eSMarkus Pfeiffer 
9421d96047eSMarkus Pfeiffer 	ugd.ugd_data = libusb20_pass_ptr(buf);
9431d96047eSMarkus Pfeiffer 	ugd.ugd_maxlen = len;
9441d96047eSMarkus Pfeiffer 	ugd.ugd_iface_index = iface_index;
9451d96047eSMarkus Pfeiffer 
9469b0c1abeSSascha Wildner 	if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) {
9471d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_INVALID_PARAM);
9481d96047eSMarkus Pfeiffer 	}
9491d96047eSMarkus Pfeiffer 	return (0);
9501d96047eSMarkus Pfeiffer }
9511d96047eSMarkus Pfeiffer 
9521d96047eSMarkus Pfeiffer static int
ugen20_dev_get_info(struct libusb20_device * pdev,struct usb_device_info * pinfo)9531d96047eSMarkus Pfeiffer ugen20_dev_get_info(struct libusb20_device *pdev,
9541d96047eSMarkus Pfeiffer     struct usb_device_info *pinfo)
9551d96047eSMarkus Pfeiffer {
9569b0c1abeSSascha Wildner 	if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) {
9571d96047eSMarkus Pfeiffer 		return (LIBUSB20_ERROR_INVALID_PARAM);
9581d96047eSMarkus Pfeiffer 	}
9591d96047eSMarkus Pfeiffer 	return (0);
9601d96047eSMarkus Pfeiffer }
9611d96047eSMarkus Pfeiffer 
9621d96047eSMarkus Pfeiffer static int
ugen20_root_get_dev_quirk(struct libusb20_backend * pbe,uint16_t quirk_index,struct libusb20_quirk * pq)9631d96047eSMarkus Pfeiffer ugen20_root_get_dev_quirk(struct libusb20_backend *pbe,
9641d96047eSMarkus Pfeiffer     uint16_t quirk_index, struct libusb20_quirk *pq)
9651d96047eSMarkus Pfeiffer {
9661d96047eSMarkus Pfeiffer 	struct usb_gen_quirk q;
9671d96047eSMarkus Pfeiffer 	int error;
9681d96047eSMarkus Pfeiffer 
9691d96047eSMarkus Pfeiffer 	memset(&q, 0, sizeof(q));
9701d96047eSMarkus Pfeiffer 
9711d96047eSMarkus Pfeiffer 	q.index = quirk_index;
9721d96047eSMarkus Pfeiffer 
9739b0c1abeSSascha Wildner 	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q);
9741d96047eSMarkus Pfeiffer 
9751d96047eSMarkus Pfeiffer 	if (error) {
9761d96047eSMarkus Pfeiffer 		if (errno == EINVAL) {
9771d96047eSMarkus Pfeiffer 			return (LIBUSB20_ERROR_NOT_FOUND);
9781d96047eSMarkus Pfeiffer 		}
9791d96047eSMarkus Pfeiffer 	} else {
9801d96047eSMarkus Pfeiffer 		pq->vid = q.vid;
9811d96047eSMarkus Pfeiffer 		pq->pid = q.pid;
9821d96047eSMarkus Pfeiffer 		pq->bcdDeviceLow = q.bcdDeviceLow;
9831d96047eSMarkus Pfeiffer 		pq->bcdDeviceHigh = q.bcdDeviceHigh;
9841d96047eSMarkus Pfeiffer 		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
9851d96047eSMarkus Pfeiffer 	}
9861d96047eSMarkus Pfeiffer 	return (error);
9871d96047eSMarkus Pfeiffer }
9881d96047eSMarkus Pfeiffer 
9891d96047eSMarkus Pfeiffer static int
ugen20_root_get_quirk_name(struct libusb20_backend * pbe,uint16_t quirk_index,struct libusb20_quirk * pq)9901d96047eSMarkus Pfeiffer ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index,
9911d96047eSMarkus Pfeiffer     struct libusb20_quirk *pq)
9921d96047eSMarkus Pfeiffer {
9931d96047eSMarkus Pfeiffer 	struct usb_gen_quirk q;
9941d96047eSMarkus Pfeiffer 	int error;
9951d96047eSMarkus Pfeiffer 
9961d96047eSMarkus Pfeiffer 	memset(&q, 0, sizeof(q));
9971d96047eSMarkus Pfeiffer 
9981d96047eSMarkus Pfeiffer 	q.index = quirk_index;
9991d96047eSMarkus Pfeiffer 
10009b0c1abeSSascha Wildner 	error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q);
10011d96047eSMarkus Pfeiffer 
10021d96047eSMarkus Pfeiffer 	if (error) {
10031d96047eSMarkus Pfeiffer 		if (errno == EINVAL) {
10041d96047eSMarkus Pfeiffer 			return (LIBUSB20_ERROR_NOT_FOUND);
10051d96047eSMarkus Pfeiffer 		}
10061d96047eSMarkus Pfeiffer 	} else {
10071d96047eSMarkus Pfeiffer 		strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname));
10081d96047eSMarkus Pfeiffer 	}
10091d96047eSMarkus Pfeiffer 	return (error);
10101d96047eSMarkus Pfeiffer }
10111d96047eSMarkus Pfeiffer 
10121d96047eSMarkus Pfeiffer static int
ugen20_root_add_dev_quirk(struct libusb20_backend * pbe,struct libusb20_quirk * pq)10131d96047eSMarkus Pfeiffer ugen20_root_add_dev_quirk(struct libusb20_backend *pbe,
10141d96047eSMarkus Pfeiffer     struct libusb20_quirk *pq)
10151d96047eSMarkus Pfeiffer {
10161d96047eSMarkus Pfeiffer 	struct usb_gen_quirk q;
10171d96047eSMarkus Pfeiffer 	int error;
10181d96047eSMarkus Pfeiffer 
10191d96047eSMarkus Pfeiffer 	memset(&q, 0, sizeof(q));
10201d96047eSMarkus Pfeiffer 
10211d96047eSMarkus Pfeiffer 	q.vid = pq->vid;
10221d96047eSMarkus Pfeiffer 	q.pid = pq->pid;
10231d96047eSMarkus Pfeiffer 	q.bcdDeviceLow = pq->bcdDeviceLow;
10241d96047eSMarkus Pfeiffer 	q.bcdDeviceHigh = pq->bcdDeviceHigh;
10251d96047eSMarkus Pfeiffer 	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
10261d96047eSMarkus Pfeiffer 
10279b0c1abeSSascha Wildner 	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q);
10281d96047eSMarkus Pfeiffer 	if (error) {
10291d96047eSMarkus Pfeiffer 		if (errno == ENOMEM) {
10301d96047eSMarkus Pfeiffer 			return (LIBUSB20_ERROR_NO_MEM);
10311d96047eSMarkus Pfeiffer 		}
10321d96047eSMarkus Pfeiffer 	}
10331d96047eSMarkus Pfeiffer 	return (error);
10341d96047eSMarkus Pfeiffer }
10351d96047eSMarkus Pfeiffer 
10361d96047eSMarkus Pfeiffer static int
ugen20_root_remove_dev_quirk(struct libusb20_backend * pbe,struct libusb20_quirk * pq)10371d96047eSMarkus Pfeiffer ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe,
10381d96047eSMarkus Pfeiffer     struct libusb20_quirk *pq)
10391d96047eSMarkus Pfeiffer {
10401d96047eSMarkus Pfeiffer 	struct usb_gen_quirk q;
10411d96047eSMarkus Pfeiffer 	int error;
10421d96047eSMarkus Pfeiffer 
10431d96047eSMarkus Pfeiffer 	memset(&q, 0, sizeof(q));
10441d96047eSMarkus Pfeiffer 
10451d96047eSMarkus Pfeiffer 	q.vid = pq->vid;
10461d96047eSMarkus Pfeiffer 	q.pid = pq->pid;
10471d96047eSMarkus Pfeiffer 	q.bcdDeviceLow = pq->bcdDeviceLow;
10481d96047eSMarkus Pfeiffer 	q.bcdDeviceHigh = pq->bcdDeviceHigh;
10491d96047eSMarkus Pfeiffer 	strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname));
10501d96047eSMarkus Pfeiffer 
10519b0c1abeSSascha Wildner 	error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q);
10521d96047eSMarkus Pfeiffer 	if (error) {
10531d96047eSMarkus Pfeiffer 		if (errno == EINVAL) {
10541d96047eSMarkus Pfeiffer 			return (LIBUSB20_ERROR_NOT_FOUND);
10551d96047eSMarkus Pfeiffer 		}
10561d96047eSMarkus Pfeiffer 	}
10571d96047eSMarkus Pfeiffer 	return (error);
10581d96047eSMarkus Pfeiffer }
10591d96047eSMarkus Pfeiffer 
10601d96047eSMarkus Pfeiffer static int
ugen20_root_set_template(struct libusb20_backend * pbe,int temp)10611d96047eSMarkus Pfeiffer ugen20_root_set_template(struct libusb20_backend *pbe, int temp)
10621d96047eSMarkus Pfeiffer {
10639b0c1abeSSascha Wildner 	return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp));
10641d96047eSMarkus Pfeiffer }
10651d96047eSMarkus Pfeiffer 
10661d96047eSMarkus Pfeiffer static int
ugen20_root_get_template(struct libusb20_backend * pbe,int * ptemp)10671d96047eSMarkus Pfeiffer ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp)
10681d96047eSMarkus Pfeiffer {
10699b0c1abeSSascha Wildner 	return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp));
10701d96047eSMarkus Pfeiffer }
1071