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