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