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