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