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