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