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