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