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