VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/freebsd/USBProxyDevice-freebsd.cpp@ 36479

Last change on this file since 36479 was 35346, checked in by vboxsync, 14 years ago

VMM reorg: Moving the public include files from include/VBox to include/VBox/vmm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.8 KB
Line 
1/* $Id: USBProxyDevice-freebsd.cpp 35346 2010-12-27 16:13:13Z vboxsync $ */
2/** @file
3 * USB device proxy - the FreeBSD backend.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 * Copyright (C) 2010 Hans Petter Selasky
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
23#ifdef VBOX
24# include <iprt/stdint.h>
25#endif
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <sys/ioctl.h>
29#include <sys/poll.h>
30#include <stdint.h>
31#include <stdio.h>
32#include <string.h>
33#include <stdlib.h>
34#include <limits.h>
35#include <unistd.h>
36#include <fcntl.h>
37#include <errno.h>
38#include <dev/usb/usb.h>
39#include <dev/usb/usbdi.h>
40#include <dev/usb/usb_ioctl.h>
41
42#include <VBox/vmm/pdm.h>
43#include <VBox/err.h>
44#include <VBox/log.h>
45#include <VBox/vusb.h>
46#include <iprt/assert.h>
47#include <iprt/stream.h>
48#include <iprt/alloc.h>
49#include <iprt/thread.h>
50#include <iprt/time.h>
51#include <iprt/asm.h>
52#include <iprt/string.h>
53#include <iprt/file.h>
54#include "../USBProxyDevice.h"
55
56/** Maximum endpoints supported. */
57#define USBFBSD_MAXENDPOINTS 127
58#define USBFBSD_MAXFRAMES 56
59
60/** This really needs to be defined in vusb.h! */
61#ifndef VUSB_DIR_TO_DEV
62# define VUSB_DIR_TO_DEV 0x00
63#endif
64
65/*******************************************************************************
66* Structures and Typedefs *
67*******************************************************************************/
68typedef struct USBENDPOINTFBSD
69{
70 /** Flag whether it is opened. */
71 bool fOpen;
72 /** Flag whether it is cancelling. */
73 bool fCancelling;
74 /** Buffer pointers. */
75 void *apvData[USBFBSD_MAXFRAMES];
76 /** Buffer lengths. */
77 uint32_t acbData[USBFBSD_MAXFRAMES];
78 /** Initial buffer length. */
79 uint32_t cbData0;
80 /** Pointer to the URB. */
81 PVUSBURB pUrb;
82 /** Copy of endpoint number. */
83 unsigned iEpNum;
84 /** Maximum transfer length. */
85 unsigned cMaxIo;
86 /** Maximum frame count. */
87 unsigned cMaxFrames;
88} USBENDPOINTFBSD, *PUSBENDPOINTFBSD;
89
90/**
91 * Data for the FreeBSD usb proxy backend.
92 */
93typedef struct USBPROXYDEVFBSD
94{
95 /** The open file. */
96 RTFILE File;
97 /** Software endpoint structures */
98 USBENDPOINTFBSD aSwEndpoint[USBFBSD_MAXENDPOINTS];
99 /** Flag whether an URB is cancelling. */
100 bool fCancelling;
101 /** Flag whether initialised or not */
102 bool fInit;
103 /** Kernel endpoint structures */
104 struct usb_fs_endpoint aHwEndpoint[USBFBSD_MAXENDPOINTS];
105} USBPROXYDEVFBSD, *PUSBPROXYDEVFBSD;
106
107/*******************************************************************************
108* Internal Functions *
109*******************************************************************************/
110static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint);
111
112/**
113 * Wrapper for the ioctl call.
114 *
115 * This wrapper will repeat the call if we get an EINTR or EAGAIN. It can also
116 * handle ENODEV (detached device) errors.
117 *
118 * @returns whatever ioctl returns.
119 * @param pProxyDev The proxy device.
120 * @param iCmd The ioctl command / function.
121 * @param pvArg The ioctl argument / data.
122 * @param fHandleNoDev Whether to handle ENXIO.
123 * @internal
124 */
125static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd,
126 void *pvArg, bool fHandleNoDev)
127{
128 int rc = VINF_SUCCESS;
129 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
130
131 LogFlow(("usbProxyFreeBSDDoIoCtl: iCmd=%#x\n", iCmd));
132
133 do
134 {
135 rc = ioctl(pDevFBSD->File, iCmd, pvArg);
136 if (rc >= 0)
137 return VINF_SUCCESS;
138 } while (errno == EINTR);
139
140 if (errno == ENXIO && fHandleNoDev)
141 {
142 Log(("usbProxyFreeBSDDoIoCtl: ENXIO -> unplugged. pProxyDev=%s\n",
143 pProxyDev->pUsbIns->pszName));
144 errno = ENODEV;
145 }
146 else if (errno != EAGAIN)
147 {
148 LogFlow(("usbProxyFreeBSDDoIoCtl: Returned %d. pProxyDev=%s\n",
149 errno, pProxyDev->pUsbIns->pszName));
150 }
151 return RTErrConvertFromErrno(errno);
152}
153
154/**
155 * Init USB subsystem.
156 */
157static int usbProxyFreeBSDFsInit(PUSBPROXYDEV pProxyDev)
158{
159 struct usb_fs_init UsbFsInit;
160 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
161 int rc;
162
163 LogFlow(("usbProxyFreeBSDFsInit: pProxyDev=%p\n", (void *)pProxyDev));
164
165 /* Sanity check */
166 AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER);
167
168 if (pDevFBSD->fInit == true)
169 return VINF_SUCCESS;
170
171 /* Zero default */
172 memset(&UsbFsInit, 0, sizeof(UsbFsInit));
173
174 UsbFsInit.pEndpoints = pDevFBSD->aHwEndpoint;
175 UsbFsInit.ep_index_max = USBFBSD_MAXENDPOINTS;
176
177 /* Init USB subsystem */
178 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_INIT, &UsbFsInit, false);
179 if (RT_SUCCESS(rc))
180 pDevFBSD->fInit = true;
181
182 return rc;
183}
184
185/**
186 * Uninit USB subsystem.
187 */
188static int usbProxyFreeBSDFsUnInit(PUSBPROXYDEV pProxyDev)
189{
190 struct usb_fs_uninit UsbFsUninit;
191 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
192 int rc;
193
194 LogFlow(("usbProxyFreeBSDFsUnInit: ProxyDev=%p\n", (void *)pProxyDev));
195
196 /* Sanity check */
197 AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER);
198
199 if (pDevFBSD->fInit != true)
200 return VINF_SUCCESS;
201
202 /* Close any open endpoints. */
203 for (unsigned n = 0; n != USBFBSD_MAXENDPOINTS; n++)
204 usbProxyFreeBSDEndpointClose(pProxyDev, n);
205
206 /* Zero default */
207 memset(&UsbFsUninit, 0, sizeof(UsbFsUninit));
208
209 /* Uninit USB subsystem */
210 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false);
211 if (RT_SUCCESS(rc))
212 pDevFBSD->fInit = false;
213
214 return rc;
215}
216
217/**
218 * Setup a USB request packet.
219 */
220static void usbProxyFreeBSDSetupReq(struct usb_device_request *pSetupData,
221 uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue,
222 uint16_t wIndex, uint16_t wLength)
223{
224 LogFlow(("usbProxyFreeBSDSetupReq: pSetupData=%p bmRequestType=%x "
225 "bRequest=%x wValue=%x wIndex=%x wLength=%x\n", (void *)pSetupData,
226 bmRequestType, bRequest, wValue, wIndex, wLength));
227
228 pSetupData->bmRequestType = bmRequestType;
229 pSetupData->bRequest = bRequest;
230
231 /* Handle endianess here. Currently no swapping is needed. */
232 pSetupData->wValue[0] = wValue & 0xff;
233 pSetupData->wValue[1] = (wValue >> 8) & 0xff;
234 pSetupData->wIndex[0] = wIndex & 0xff;
235 pSetupData->wIndex[1] = (wIndex >> 8) & 0xff;
236 pSetupData->wLength[0] = wLength & 0xff;
237 pSetupData->wLength[1] = (wLength >> 8) & 0xff;
238}
239
240static int usbProxyFreeBSDEndpointOpen(PUSBPROXYDEV pProxyDev, int Endpoint, bool fIsoc, int index)
241{
242 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
243 PUSBENDPOINTFBSD pEndpointFBSD;
244 struct usb_fs_endpoint *pXferEndpoint;
245 struct usb_fs_open UsbFsOpen;
246 int rc;
247
248 LogFlow(("usbProxyFreeBSDEndpointOpen: pProxyDev=%p Endpoint=%d\n",
249 (void *)pProxyDev, Endpoint));
250
251 for (; index < USBFBSD_MAXENDPOINTS; index++)
252 {
253 pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
254 if (pEndpointFBSD->fCancelling)
255 continue;
256 if ( pEndpointFBSD->fOpen
257 && !pEndpointFBSD->pUrb
258 && (int)pEndpointFBSD->iEpNum == Endpoint)
259 return index;
260 }
261
262 if (index == USBFBSD_MAXENDPOINTS)
263 {
264 for (index = 0; index != USBFBSD_MAXENDPOINTS; index++)
265 {
266 pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
267 if (pEndpointFBSD->fCancelling)
268 continue;
269 if (!pEndpointFBSD->fOpen)
270 break;
271 }
272 if (index == USBFBSD_MAXENDPOINTS)
273 return -1;
274 }
275 /* set ppBuffer and pLength */
276
277 pXferEndpoint = &pDevFBSD->aHwEndpoint[index];
278 pXferEndpoint->ppBuffer = &pEndpointFBSD->apvData[0];
279 pXferEndpoint->pLength = &pEndpointFBSD->acbData[0];
280
281 LogFlow(("usbProxyFreeBSDEndpointOpen: ep_index=%d ep_num=%d\n",
282 index, Endpoint));
283
284 memset(&UsbFsOpen, 0, sizeof(UsbFsOpen));
285
286 UsbFsOpen.ep_index = index;
287 UsbFsOpen.ep_no = Endpoint;
288 UsbFsOpen.max_bufsize = 256 * 1024;
289 /* Hardcoded assumption about the URBs we get. */
290
291 UsbFsOpen.max_frames = fIsoc ? USBFBSD_MAXFRAMES : 2;
292
293 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_OPEN, &UsbFsOpen, true);
294 if (RT_FAILURE(rc))
295 {
296 if (rc == VERR_RESOURCE_BUSY)
297 LogFlow(("usbProxyFreeBSDEndpointOpen: EBUSY\n"));
298
299 return -1;
300 }
301 pEndpointFBSD->fOpen = true;
302 pEndpointFBSD->pUrb = NULL;
303 pEndpointFBSD->iEpNum = Endpoint;
304 pEndpointFBSD->cMaxIo = UsbFsOpen.max_bufsize;
305 pEndpointFBSD->cMaxFrames = UsbFsOpen.max_frames;
306
307 return index;
308}
309
310/**
311 * Close an endpoint.
312 *
313 * @returns VBox status code.
314 */
315static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint)
316{
317 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
318 PUSBENDPOINTFBSD pEndpointFBSD = &pDevFBSD->aSwEndpoint[Endpoint];
319 struct usb_fs_close UsbFsClose;
320 int rc = VINF_SUCCESS;
321
322 LogFlow(("usbProxyFreeBSDEndpointClose: pProxyDev=%p Endpoint=%d\n",
323 (void *)pProxyDev, Endpoint));
324
325 /* check for cancelling */
326 if (pEndpointFBSD->pUrb != NULL)
327 {
328 pEndpointFBSD->fCancelling = true;
329 pDevFBSD->fCancelling = true;
330 }
331
332 /* check for opened */
333 if (pEndpointFBSD->fOpen)
334 {
335 pEndpointFBSD->fOpen = false;
336
337 /* Zero default */
338 memset(&UsbFsClose, 0, sizeof(UsbFsClose));
339
340 /* Set endpoint index */
341 UsbFsClose.ep_index = Endpoint;
342
343 /* Close endpoint */
344 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_CLOSE, &UsbFsClose, true);
345 }
346 return rc;
347}
348
349/**
350 * Opens the device file.
351 *
352 * @returns VBox status code.
353 * @param pProxyDev The device instance.
354 * @param pszAddress If we are using usbfs, this is the path to the
355 * device. If we are using sysfs, this is a string of
356 * the form "sysfs:<sysfs path>//device:<device node>".
357 * In the second case, the two paths are guaranteed
358 * not to contain the substring "//".
359 * @param pvBackend Backend specific pointer, unused for the linux backend.
360 */
361static int usbProxyFreeBSDOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress,
362 void *pvBackend)
363{
364 int rc;
365
366 LogFlow(("usbProxyFreeBSDOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));
367
368 /*
369 * Try open the device node.
370 */
371 RTFILE File;
372
373 rc = RTFileOpen(&File, pszAddress, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
374 if (RT_SUCCESS(rc))
375 {
376 /*
377 * Allocate and initialize the linux backend data.
378 */
379 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)
380 RTMemAllocZ(sizeof(USBPROXYDEVFBSD));
381
382 if (pDevFBSD)
383 {
384 pDevFBSD->File = File;
385 pProxyDev->Backend.pv = pDevFBSD;
386
387 rc = usbProxyFreeBSDFsInit(pProxyDev);
388 if (RT_SUCCESS(rc))
389 {
390 LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns "
391 "successfully File=%d iActiveCfg=%d\n",
392 pProxyDev, pszAddress,
393 pDevFBSD->File, pProxyDev->iActiveCfg));
394
395 return VINF_SUCCESS;
396 }
397
398 RTMemFree(pDevFBSD);
399 }
400 else
401 rc = VERR_NO_MEMORY;
402
403 RTFileClose(File);
404 }
405 else if (rc == VERR_ACCESS_DENIED)
406 rc = VERR_VUSB_USBFS_PERMISSION;
407
408 Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%d!\n",
409 pProxyDev, pszAddress, rc));
410
411 pProxyDev->Backend.pv = NULL;
412
413 NOREF(pvBackend);
414 return rc;
415}
416
417
418/**
419 * Claims all the interfaces and figures out the
420 * current configuration.
421 *
422 * @returns VINF_SUCCESS.
423 * @param pProxyDev The proxy device.
424 */
425static int usbProxyFreeBSDInit(PUSBPROXYDEV pProxyDev)
426{
427 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
428 int rc;
429
430 LogFlow(("usbProxyFreeBSDInit: pProxyDev=%s\n",
431 pProxyDev->pUsbIns->pszName));
432
433 /* Retrieve current active configuration. */
434 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG,
435 &pProxyDev->iActiveCfg, true);
436 if (RT_FAILURE(rc) || pProxyDev->iActiveCfg == 255)
437 {
438 pProxyDev->cIgnoreSetConfigs = 0;
439 pProxyDev->iActiveCfg = -1;
440 }
441 else
442 {
443 pProxyDev->cIgnoreSetConfigs = 1;
444 pProxyDev->iActiveCfg++;
445 }
446
447 Log(("usbProxyFreeBSDInit: iActiveCfg=%d\n", pProxyDev->iActiveCfg));
448
449 return rc;
450}
451
452/**
453 * Closes the proxy device.
454 */
455static void usbProxyFreeBSDClose(PUSBPROXYDEV pProxyDev)
456{
457 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
458
459 LogFlow(("usbProxyFreeBSDClose: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
460
461 /* sanity check */
462 AssertPtrReturnVoid(pDevFBSD);
463
464 usbProxyFreeBSDFsUnInit(pProxyDev);
465
466 RTFileClose(pDevFBSD->File);
467
468 pDevFBSD->File = NIL_RTFILE;
469
470 RTMemFree(pDevFBSD);
471
472 pProxyDev->Backend.pv = NULL;
473
474 LogFlow(("usbProxyFreeBSDClose: returns\n"));
475}
476
477/**
478 * Reset a device.
479 *
480 * @returns VBox status code.
481 * @param pDev The device to reset.
482 */
483static int usbProxyFreeBSDReset(PUSBPROXYDEV pProxyDev, bool fResetOnFreeBSD)
484{
485 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
486 int iParm;
487 int rc = VINF_SUCCESS;
488
489 LogFlow(("usbProxyFreeBSDReset: pProxyDev=%s\n",
490 pProxyDev->pUsbIns->pszName));
491
492 if (!fResetOnFreeBSD)
493 goto done;
494
495 /* We need to release kernel ressources first. */
496 rc = usbProxyFreeBSDFsUnInit(pProxyDev);
497 if (RT_FAILURE(rc))
498 goto done;
499
500 /* Resetting is only possible as super-user, ignore any failures: */
501 iParm = 0;
502 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DEVICEENUMERATE, &iParm, true);
503 if (RT_FAILURE(rc))
504 {
505 /* Set the config instead of bus reset */
506 iParm = 255;
507 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true);
508 if (RT_SUCCESS(rc))
509 {
510 iParm = 0;
511 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true);
512 }
513 }
514 usleep(10000); /* nice it! */
515
516 /* Allocate kernel ressources again. */
517 rc = usbProxyFreeBSDFsInit(pProxyDev);
518 if (RT_FAILURE(rc))
519 goto done;
520
521 /* Retrieve current active configuration. */
522 rc = usbProxyFreeBSDInit(pProxyDev);
523
524done:
525 pProxyDev->cIgnoreSetConfigs = 2;
526
527 return rc;
528}
529
530/**
531 * SET_CONFIGURATION.
532 *
533 * The caller makes sure that it's not called first time after open or reset
534 * with the active interface.
535 *
536 * @returns success indicator.
537 * @param pProxyDev The device instance data.
538 * @param iCfg The configuration to set.
539 */
540static int usbProxyFreeBSDSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
541{
542 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
543 int iCfgIndex;
544 int rc;
545
546 LogFlow(("usbProxyFreeBSDSetConfig: pProxyDev=%s cfg=%x\n",
547 pProxyDev->pUsbIns->pszName, iCfg));
548
549 /* We need to release kernel ressources first. */
550 rc = usbProxyFreeBSDFsUnInit(pProxyDev);
551 if (RT_FAILURE(rc))
552 {
553 LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
554 "failed failed rc=%d\n", rc));
555 return false;
556 }
557
558 if (iCfg == 0)
559 {
560 /* Unconfigure */
561 iCfgIndex = 255;
562 }
563 else
564 {
565 /* Get the configuration index matching the value. */
566 for (iCfgIndex = 0; iCfgIndex < pProxyDev->DevDesc.bNumConfigurations; iCfgIndex++)
567 {
568 if (pProxyDev->paCfgDescs[iCfgIndex].Core.bConfigurationValue == iCfg)
569 break;
570 }
571
572 if (iCfgIndex == pProxyDev->DevDesc.bNumConfigurations)
573 {
574 LogFlow(("usbProxyFreeBSDSetConfig: configuration "
575 "%d not found\n", iCfg));
576 return false;
577 }
578 }
579
580 /* Set the config */
581 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iCfgIndex, true);
582 if (RT_FAILURE(rc))
583 return false;
584
585 /* Allocate kernel ressources again. */
586 rc = usbProxyFreeBSDFsInit(pProxyDev);
587 if (RT_FAILURE(rc))
588 return false;
589
590 return true;
591}
592
593/**
594 * Claims an interface.
595 * @returns success indicator.
596 */
597static int usbProxyFreeBSDClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
598{
599 int rc;
600
601 LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s "
602 "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));
603
604 /*
605 * Try to detach kernel driver on this interface, ignore any
606 * failures
607 */
608 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_IFACE_DRIVER_DETACH, &iIf, true);
609
610 /* Try to claim interface */
611 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_CLAIM_INTERFACE, &iIf, true);
612 if (RT_FAILURE(rc))
613 return false;
614
615 return true;
616}
617
618/**
619 * Releases an interface.
620 * @returns success indicator.
621 */
622static int usbProxyFreeBSDReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
623{
624 int rc;
625
626 LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s "
627 "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));
628
629 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_RELEASE_INTERFACE, &iIf, true);
630 if (RT_FAILURE(rc))
631 return false;
632
633 return true;
634}
635
636/**
637 * SET_INTERFACE.
638 *
639 * @returns success indicator.
640 */
641static int
642usbProxyFreeBSDSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
643{
644 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
645 struct usb_alt_interface UsbIntAlt;
646 int rc;
647
648 LogFlow(("usbProxyFreeBSDSetInterface: pProxyDev=%p iIf=%x iAlt=%x\n",
649 pProxyDev, iIf, iAlt));
650
651 /* We need to release kernel ressources first. */
652 rc = usbProxyFreeBSDFsUnInit(pProxyDev);
653 if (RT_FAILURE(rc))
654 {
655 LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
656 "failed failed rc=%d\n", rc));
657 return false;
658 }
659 memset(&UsbIntAlt, 0, sizeof(UsbIntAlt));
660 UsbIntAlt.uai_interface_index = iIf;
661 UsbIntAlt.uai_alt_index = iAlt;
662
663 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_ALTINTERFACE, &UsbIntAlt, true);
664 if (RT_FAILURE(rc))
665 {
666 LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d "
667 "failed rc=%d\n", iIf, iAlt, rc));
668 return false;
669 }
670
671 rc = usbProxyFreeBSDFsInit(pProxyDev);
672 if (RT_FAILURE(rc))
673 return false;
674
675 return true;
676}
677
678/**
679 * Clears the halted endpoint 'ep_num'.
680 */
681static bool usbProxyFreeBSDClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int ep_num)
682{
683 struct usb_ctl_request Req;
684 int rc;
685
686 LogFlow(("usbProxyFreeBSDClearHaltedEp: pProxyDev=%s ep_num=%u\n",
687 pProxyDev->pUsbIns->pszName, ep_num));
688
689 /*
690 * Clearing the zero control pipe doesn't make sense.
691 * Just ignore it.
692 */
693 if ((ep_num & 0xF) == 0)
694 return true;
695
696 memset(&Req, 0, sizeof(Req));
697
698 usbProxyFreeBSDSetupReq(&Req.ucr_request,
699 VUSB_DIR_TO_DEV | VUSB_TO_ENDPOINT,
700 VUSB_REQ_CLEAR_FEATURE, 0, ep_num, 0);
701
702 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DO_REQUEST, &Req, true);
703
704 LogFlow(("usbProxyFreeBSDClearHaltedEp: rc=%Rrc\n", rc));
705
706 if (RT_FAILURE(rc))
707 return false;
708
709 return true;
710}
711
712/**
713 * @copydoc USBPROXYBACK::pfnUrbQueue
714 */
715static int usbProxyFreeBSDUrbQueue(PVUSBURB pUrb)
716{
717 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUrb->pUsbIns, PUSBPROXYDEV);
718 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
719 PUSBENDPOINTFBSD pEndpointFBSD;
720 struct usb_fs_endpoint *pXferEndpoint;
721 struct usb_fs_start UsbFsStart;
722 unsigned cFrames;
723 uint8_t *pbData;
724 int index;
725 int ep_num;
726 int rc;
727
728 LogFlow(("usbProxyFreeBSDUrbQueue: pUrb=%p EndPt=%u Dir=%u\n",
729 pUrb, (unsigned)pUrb->EndPt, (unsigned)pUrb->enmDir));
730
731 ep_num = pUrb->EndPt;
732
733 if ((pUrb->enmType != VUSBXFERTYPE_MSG) && (pUrb->enmDir == VUSBDIRECTION_IN))
734 ep_num |= 0x80;
735
736 index = 0;
737
738retry:
739
740 index = usbProxyFreeBSDEndpointOpen(pProxyDev, ep_num,
741 (pUrb->enmType == VUSBXFERTYPE_ISOC),
742 index);
743
744 if (index < 0)
745 return false;
746
747 pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
748 pXferEndpoint = &pDevFBSD->aHwEndpoint[index];
749
750 pbData = pUrb->abData;
751
752 switch (pUrb->enmType)
753 {
754 case VUSBXFERTYPE_MSG:
755 {
756 pEndpointFBSD->apvData[0] = pbData;
757 pEndpointFBSD->acbData[0] = 8;
758
759 /* check wLength */
760 if (pbData[6] || pbData[7])
761 {
762 pEndpointFBSD->apvData[1] = pbData + 8;
763 pEndpointFBSD->acbData[1] = pbData[6] | (pbData[7] << 8);
764 cFrames = 2;
765 }
766 else
767 {
768 pEndpointFBSD->apvData[1] = NULL;
769 pEndpointFBSD->acbData[1] = 0;
770 cFrames = 1;
771 }
772
773 LogFlow(("usbProxyFreeBSDUrbQueue: pUrb->cbData=%u, 0x%02x, "
774 "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
775 pUrb->cbData, pbData[0], pbData[1], pbData[2], pbData[3],
776 pbData[4], pbData[5], pbData[6], pbData[7]));
777
778 pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
779 pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
780 break;
781 }
782 case VUSBXFERTYPE_ISOC:
783 {
784 unsigned i;
785
786 for (i = 0; i < pUrb->cIsocPkts; i++)
787 {
788 if (i >= pEndpointFBSD->cMaxFrames)
789 break;
790 pEndpointFBSD->apvData[i] = pbData + pUrb->aIsocPkts[i].off;
791 pEndpointFBSD->acbData[i] = pUrb->aIsocPkts[i].cb;
792 }
793 /* Timeout handling will be done during reap. */
794 pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
795 pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
796 cFrames = i;
797 break;
798 }
799 default:
800 {
801 pEndpointFBSD->apvData[0] = pbData;
802 pEndpointFBSD->cbData0 = pUrb->cbData;
803
804 /* XXX maybe we have to loop */
805 if (pUrb->cbData > pEndpointFBSD->cMaxIo)
806 pEndpointFBSD->acbData[0] = pEndpointFBSD->cMaxIo;
807 else
808 pEndpointFBSD->acbData[0] = pUrb->cbData;
809
810 /* Timeout handling will be done during reap. */
811 pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
812 pXferEndpoint->flags = pUrb->fShortNotOk ? 0 : USB_FS_FLAG_MULTI_SHORT_OK;
813 cFrames = 1;
814 break;
815 }
816 }
817
818 /* store number of frames */
819 pXferEndpoint->nFrames = cFrames;
820
821 /* zero-default */
822 memset(&UsbFsStart, 0, sizeof(UsbFsStart));
823
824 /* Start the transfer */
825 UsbFsStart.ep_index = index;
826
827 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_START, &UsbFsStart, true);
828
829 LogFlow(("usbProxyFreeBSDUrbQueue: USB_FS_START returned rc=%d "
830 "len[0]=%u len[1]=%u cbData=%u index=%u ep_num=%u\n", rc,
831 (unsigned)pEndpointFBSD->acbData[0],
832 (unsigned)pEndpointFBSD->acbData[1],
833 (unsigned)pUrb->cbData,
834 (unsigned)index, (unsigned)ep_num));
835
836 if (RT_FAILURE(rc))
837 {
838 if (rc == VERR_RESOURCE_BUSY)
839 {
840 index++;
841 goto retry;
842 }
843 return false;
844 }
845 pUrb->Dev.pvPrivate = (void *)(long)(index + 1);
846 pEndpointFBSD->pUrb = pUrb;
847
848 return true;
849}
850
851/**
852 * Reap URBs in-flight on a device.
853 *
854 * @returns Pointer to a completed URB.
855 * @returns NULL if no URB was completed.
856 * @param pProxyDev The device.
857 * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
858 */
859static PVUSBURB usbProxyFreeBSDUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
860{
861 struct usb_fs_endpoint *pXferEndpoint;
862 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
863 PUSBENDPOINTFBSD pEndpointFBSD;
864 PVUSBURB pUrb;
865 struct usb_fs_complete UsbFsComplete;
866 struct pollfd PollFd;
867 int rc;
868
869 LogFlow(("usbProxyFreeBSDUrbReap: pProxyDev=%p, cMillies=%u\n",
870 pProxyDev, cMillies));
871
872repeat:
873
874 pUrb = NULL;
875
876 /* check for cancelled transfers */
877 if (pDevFBSD->fCancelling)
878 {
879 for (unsigned n = 0; n < USBFBSD_MAXENDPOINTS; n++)
880 {
881 pEndpointFBSD = &pDevFBSD->aSwEndpoint[n];
882 if (pEndpointFBSD->fCancelling)
883 {
884 pEndpointFBSD->fCancelling = false;
885 pUrb = pEndpointFBSD->pUrb;
886 pEndpointFBSD->pUrb = NULL;
887
888 if (pUrb != NULL)
889 break;
890 }
891 }
892
893 if (pUrb != NULL)
894 {
895 pUrb->enmStatus = VUSBSTATUS_INVALID;
896 pUrb->Dev.pvPrivate = NULL;
897
898 switch (pUrb->enmType)
899 {
900 case VUSBXFERTYPE_MSG:
901 pUrb->cbData = 0;
902 break;
903 case VUSBXFERTYPE_ISOC:
904 pUrb->cbData = 0;
905 for (int n = 0; n < (int)pUrb->cIsocPkts; n++)
906 pUrb->aIsocPkts[n].cb = 0;
907 break;
908 default:
909 pUrb->cbData = 0;
910 break;
911 }
912 return pUrb;
913 }
914 pDevFBSD->fCancelling = false;
915 }
916 /* Zero default */
917
918 memset(&UsbFsComplete, 0, sizeof(UsbFsComplete));
919
920 /* Check if any endpoints are complete */
921 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true);
922 if (RT_SUCCESS(rc))
923 {
924 pXferEndpoint = &pDevFBSD->aHwEndpoint[UsbFsComplete.ep_index];
925 pEndpointFBSD = &pDevFBSD->aSwEndpoint[UsbFsComplete.ep_index];
926
927 LogFlow(("usbProxyFreeBSDUrbReap: Reaped "
928 "URB %#p\n", pEndpointFBSD->pUrb));
929
930 if (pXferEndpoint->status == USB_ERR_CANCELLED)
931 goto repeat;
932
933 pUrb = pEndpointFBSD->pUrb;
934 pEndpointFBSD->pUrb = NULL;
935 if (pUrb == NULL)
936 goto repeat;
937
938 switch (pXferEndpoint->status)
939 {
940 case USB_ERR_NORMAL_COMPLETION:
941 pUrb->enmStatus = VUSBSTATUS_OK;
942 break;
943 case USB_ERR_STALLED:
944 pUrb->enmStatus = VUSBSTATUS_STALL;
945 break;
946 default:
947 pUrb->enmStatus = VUSBSTATUS_INVALID;
948 break;
949 }
950
951 pUrb->Dev.pvPrivate = NULL;
952
953 switch (pUrb->enmType)
954 {
955 case VUSBXFERTYPE_MSG:
956 pUrb->cbData = pEndpointFBSD->acbData[0] + pEndpointFBSD->acbData[1];
957 break;
958 case VUSBXFERTYPE_ISOC:
959 {
960 int n;
961
962 if (pUrb->enmDir == VUSBDIRECTION_OUT)
963 break;
964 pUrb->cbData = 0;
965 for (n = 0; n < (int)pUrb->cIsocPkts; n++)
966 {
967 if (n >= (int)pEndpointFBSD->cMaxFrames)
968 break;
969 pUrb->cbData += pEndpointFBSD->acbData[n];
970 pUrb->aIsocPkts[n].cb = pEndpointFBSD->acbData[n];
971 }
972 for (; n < (int)pUrb->cIsocPkts; n++)
973 pUrb->aIsocPkts[n].cb = 0;
974
975 break;
976 }
977 default:
978 pUrb->cbData = pEndpointFBSD->acbData[0];
979 break;
980 }
981
982 LogFlow(("usbProxyFreeBSDUrbReap: Status=%d epindex=%u "
983 "len[0]=%d len[1]=%d\n",
984 (int)pXferEndpoint->status,
985 (unsigned)UsbFsComplete.ep_index,
986 (unsigned)pEndpointFBSD->acbData[0],
987 (unsigned)pEndpointFBSD->acbData[1]));
988
989 }
990 else if (cMillies && rc == VERR_RESOURCE_BUSY)
991 {
992 /* Poll for finished transfers */
993 PollFd.fd = (int)pDevFBSD->File;
994 PollFd.events = POLLIN | POLLRDNORM;
995 PollFd.revents = 0;
996
997 rc = poll(&PollFd, 1, (cMillies == RT_INDEFINITE_WAIT) ? INFTIM : cMillies);
998 if (rc >= 1)
999 {
1000 goto repeat;
1001 }
1002 else
1003 {
1004 LogFlow(("usbProxyFreeBSDUrbReap: "
1005 "poll returned rc=%d\n", rc));
1006 }
1007 }
1008 return pUrb;
1009}
1010
1011/**
1012 * Cancels the URB.
1013 * The URB requires reaping, so we don't change its state.
1014 */
1015static void usbProxyFreeBSDUrbCancel(PVUSBURB pUrb)
1016{
1017 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUrb->pUsbIns, PUSBPROXYDEV);
1018 int index;
1019
1020 index = (int)(long)pUrb->Dev.pvPrivate - 1;
1021
1022 if (index < 0 || index >= USBFBSD_MAXENDPOINTS)
1023 return;
1024
1025 LogFlow(("usbProxyFreeBSDUrbCancel: epindex=%u\n", (unsigned)index));
1026
1027 usbProxyFreeBSDEndpointClose(pProxyDev, index);
1028}
1029
1030/**
1031 * The FreeBSD USB Proxy Backend.
1032 */
1033extern const USBPROXYBACK g_USBProxyDeviceHost =
1034{
1035 "host",
1036 usbProxyFreeBSDOpen,
1037 usbProxyFreeBSDInit,
1038 usbProxyFreeBSDClose,
1039 usbProxyFreeBSDReset,
1040 usbProxyFreeBSDSetConfig,
1041 usbProxyFreeBSDClaimInterface,
1042 usbProxyFreeBSDReleaseInterface,
1043 usbProxyFreeBSDSetInterface,
1044 usbProxyFreeBSDClearHaltedEp,
1045 usbProxyFreeBSDUrbQueue,
1046 usbProxyFreeBSDUrbCancel,
1047 usbProxyFreeBSDUrbReap,
1048 0
1049};
1050
1051/*
1052 * Local Variables:
1053 * mode: c
1054 * c-file-style: "bsd"
1055 * c-basic-offset: 4
1056 * tab-width: 4
1057 * indent-tabs-mode: s
1058 * End:
1059 */
1060
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette