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