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