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