VirtualBox

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

Last change on this file since 31888 was 31888, checked in by vboxsync, 15 years ago

Devices: export USB proxy device to OSE

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.5 KB
Line 
1/* $Id: USBProxyDevice-freebsd.cpp 31888 2010-08-24 07:43:59Z vboxsync $ */
2/** @file
3 * USB device proxy - the FreeBSD backend.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * Oracle Corporation confidential
10 * All rights reserved
11 */
12
13/*******************************************************************************
14* Header Files *
15*******************************************************************************/
16#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
17#ifdef VBOX
18# include <iprt/stdint.h>
19#endif
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <sys/ioctl.h>
23#include <sys/poll.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <string.h>
27#include <stdlib.h>
28#include <limits.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <dev/usb/usb.h>
33#include <dev/usb/usbdi.h>
34#include <dev/usb/usb_ioctl.h>
35
36#include <VBox/pdm.h>
37#include <VBox/err.h>
38#include <VBox/log.h>
39#include <VBox/vusb.h>
40#include <iprt/assert.h>
41#include <iprt/stream.h>
42#include <iprt/alloc.h>
43#include <iprt/thread.h>
44#include <iprt/time.h>
45#include <iprt/asm.h>
46#include <iprt/string.h>
47#include <iprt/file.h>
48#include "../USBProxyDevice.h"
49
50/** Maximum endpoints supported. */
51#define USBFBSD_MAXENDPOINTS 32
52#define USBFBSD_EPADDR_NUM_MASK 0x0F
53#define USBFBSD_EPADDR_DIR_MASK 0x80
54#define USBPROXY_FREEBSD_NO_ENTRY_FREE ((unsigned)~0)
55/** This really needs to be defined in vusb.h! */
56#ifndef VUSB_DIR_TO_DEV
57# define VUSB_DIR_TO_DEV 0x00
58#endif
59
60/*******************************************************************************
61* Structures and Typedefs *
62*******************************************************************************/
63typedef struct VUSBURBFBSD
64{
65 /** Pointer to the URB. */
66 PVUSBURB pUrb;
67 /** Buffer pointers. */
68 void *apvData[2];
69 /** Buffer lengths. */
70 uint32_t acbData[2];
71} VUSBURBFBSD, *PVUSBURBFBSD;
72
73typedef struct USBENDPOINTFBSD
74{
75 /** Flag whether it is opened. */
76 bool fOpen;
77 /** Index in the endpoint list. */
78 unsigned iEndpoint;
79 /** Associated endpoint. */
80 struct usb_fs_endpoint *pXferEndpoint;
81} USBENDPOINTFBSD, *PUSBENDPOINTFBSD;
82
83/**
84 * Data for the FreeBSD usb proxy backend.
85 */
86typedef struct USBPROXYDEVFBSD
87{
88 /** The open file. */
89 RTFILE File;
90 /** Critical section protecting the two lists. */
91 RTCRITSECT CritSect;
92 /** Pointer to the array of USB endpoints. */
93 struct usb_fs_endpoint *paXferEndpoints;
94 /** Pointer to the array of URB structures.
95 * They entries must be in sync with the above array. */
96 PVUSBURBFBSD paUrbs;
97 /** Number of entries in both arrays. */
98 unsigned cXferEndpoints;
99 /** Pointer to the Fifo containing the indexes for free Xfer
100 * endpoints. */
101 unsigned *paXferFree;
102 /** Index of the next free entry to write to. */
103 unsigned iXferFreeNextWrite;
104 /** Index of the next entry to read from. */
105 unsigned iXferFreeNextRead;
106 /** Status of opened endpoints. */
107 USBENDPOINTFBSD aEpOpened[USBFBSD_MAXENDPOINTS];
108 /** The list of landed FreeBSD URBs. Doubly linked.
109 * Only the split head will appear in this list. */
110 PVUSBURB pTaxingHead;
111 /** The tail of the landed FreeBSD URBs. */
112 PVUSBURB pTaxingTail;
113} USBPROXYDEVFBSD, *PUSBPROXYDEVFBSD;
114
115/*******************************************************************************
116* Internal Functions *
117*******************************************************************************/
118static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries);
119static void usbProxFreeBSDUrbUnplugged(PUSBPROXYDEV pProxyDev);
120static PUSBENDPOINTFBSD usbProxyFreeBSDEndpointOpen(PUSBPROXYDEV pProxyDev, int Endpoint);
121static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint);
122
123/**
124 * Wrapper for the ioctl call.
125 *
126 * This wrapper will repeate the call if we get an EINTR or EAGAIN. It can also
127 * handle ENODEV (detached device) errors.
128 *
129 * @returns whatever ioctl returns.
130 * @param pProxyDev The proxy device.
131 * @param iCmd The ioctl command / function.
132 * @param pvArg The ioctl argument / data.
133 * @param fHandleNoDev Whether to handle ENXIO.
134 * @param cTries The number of retries. Use UINT32_MAX for (kind of) indefinite retries.
135 * @internal
136 */
137static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries)
138{
139 int rc = VINF_SUCCESS;
140
141 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
142 do
143 {
144 do
145 {
146 rc = ioctl(pDevFBSD->File, iCmd, pvArg);
147 if (rc >= 0)
148 return rc;
149 } while (errno == EINTR);
150
151 if (errno == ENXIO && fHandleNoDev)
152 {
153 usbProxFreeBSDUrbUnplugged(pProxyDev);
154 Log(("usb-freebsd: ENXIO -> unplugged. pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
155 errno = ENODEV;
156 break;
157 }
158 if (errno != EAGAIN)
159 {
160 LogFlow(("usbProxyFreeBSDDoIoCtl returned %Rrc\n", RTErrConvertFromErrno(errno)));
161 break;
162 }
163 } while (cTries-- > 0);
164
165 return rc;
166}
167
168/**
169 * Setup a USB request packet.
170 */
171static void usbProxyFreeBSDSetupReq(struct usb_device_request *pSetupData, uint8_t bmRequestType, uint8_t bRequest,
172 uint16_t wValue, uint16_t wIndex, uint16_t wLength)
173{
174 LogFlow(("usbProxyFreeBSDSetupReq: pSetupData=%p bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
175 pSetupData, bmRequestType, bRequest, wValue, wIndex, wLength));
176
177 pSetupData->bmRequestType = bmRequestType;
178 pSetupData->bRequest = bRequest;
179
180 /* Handle endianess here. Currently no swapping is needed. */
181 pSetupData->wValue[0] = wValue & 0xff;
182 pSetupData->wValue[1] = (wValue >> 8) & 0xff;
183 pSetupData->wIndex[0] = wIndex & 0xff;
184 pSetupData->wIndex[1] = (wIndex >> 8) & 0xff;
185 pSetupData->wLength[0] = wLength & 0xff;
186 pSetupData->wLength[1] = (wLength >> 8) & 0xff;
187// pSetupData->wIndex = wIndex;
188// pSetupData->wLength = wLength;
189}
190
191/**
192 * The device has been unplugged.
193 * Cancel all in-flight URBs and put them up for reaping.
194 */
195static void usbProxFreeBSDUrbUnplugged(PUSBPROXYDEV pProxyDev)
196{
197 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
198
199 /*
200 * Shoot down all flying URBs.
201 */
202 RTCritSectEnter(&pDevFBSD->CritSect);
203 pProxyDev->fDetached = true;
204
205#if 0 /** @todo */
206 PUSBPROXYURBFBSD pUrbTaxing = NULL;
207 PUSBPROXYURBFBSD pUrbFBSD = pDevLnx->pInFlightHead;
208 pDevFBSD->pInFlightHead = NULL;
209 while (pUrbFBSD)
210 {
211 PUSBPROXYURBFBSD pCur = pUrbFBSD;
212 pUrbFBSD = pUrbFBSD->pNext;
213
214 ioctl(pDevFBSD->File, USBDEVFS_DISCARDURB, &pCur->KUrb);
215 if (!pCur->KUrb.status)
216 pCur->KUrb.status = -ENODEV;
217
218 /* insert into the taxing list. */
219 pCur->pPrev = NULL;
220 if ( !pCur->pSplitHead
221 || pCur == pCur->pSplitHead)
222 {
223 pCur->pNext = pUrbTaxing;
224 if (pUrbTaxing)
225 pUrbTaxing->pPrev = pCur;
226 pUrbTaxing = pCur;
227 }
228 else
229 pCur->pNext = NULL;
230 }
231
232 /* Append the URBs we shot down to the taxing queue. */
233 if (pUrbTaxing)
234 {
235 pUrbTaxing->pPrev = pDevFBSD->pTaxingTail;
236 if (pUrbTaxing->pPrev)
237 pUrbTaxing->pPrev->pNext = pUrbTaxing;
238 else
239 pDevFBSD->pTaxingTail = pDevFBSD->pTaxingHead = pUrbTaxing;
240 }
241#endif
242 RTCritSectLeave(&pDevFBSD->CritSect);
243}
244
245DECLINLINE(void) usbProxyFreeBSDSetEntryFree(PUSBPROXYDEVFBSD pProxyDev, unsigned iEntry)
246{
247 pProxyDev->paXferFree[pProxyDev->iXferFreeNextWrite] = iEntry;
248 pProxyDev->iXferFreeNextWrite++;
249 pProxyDev->iXferFreeNextWrite %= (pProxyDev->cXferEndpoints+1);
250}
251
252DECLINLINE(unsigned) usbProxyFreeBSDGetEntryFree(PUSBPROXYDEVFBSD pProxyDev)
253{
254 unsigned iEntry;
255
256 if (pProxyDev->iXferFreeNextWrite != pProxyDev->iXferFreeNextRead)
257 {
258 iEntry = pProxyDev->paXferFree[pProxyDev->iXferFreeNextRead];
259 pProxyDev->iXferFreeNextRead++;
260 pProxyDev->iXferFreeNextRead %= (pProxyDev->cXferEndpoints+1);
261 }
262 else
263 iEntry = USBPROXY_FREEBSD_NO_ENTRY_FREE;
264
265 return iEntry;
266}
267
268static PUSBENDPOINTFBSD usbProxyFreeBSDEndpointOpen(PUSBPROXYDEV pProxyDev, int Endpoint)
269{
270 LogFlow(("usbProxyFreeBSDEndpointOpen: pProxyDev=%p Endpoint=%d\n", pProxyDev, Endpoint));
271
272 int EndPtIndex = (Endpoint & USBFBSD_EPADDR_NUM_MASK) + ((Endpoint & USBFBSD_EPADDR_DIR_MASK) ? USBFBSD_MAXENDPOINTS / 2 : 0);
273 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
274 PUSBENDPOINTFBSD pEndpointFBSD = &pDevFBSD->aEpOpened[EndPtIndex];
275 struct usb_fs_endpoint *pXferEndpoint;
276
277 AssertMsg(EndPtIndex < USBFBSD_MAXENDPOINTS, ("Endpoint index exceeds limit %d\n", EndPtIndex));
278
279 if (!pEndpointFBSD->fOpen)
280 {
281 struct usb_fs_open UsbFsOpen;
282
283 pEndpointFBSD->iEndpoint = usbProxyFreeBSDGetEntryFree(pDevFBSD);
284 if (pEndpointFBSD->iEndpoint == USBPROXY_FREEBSD_NO_ENTRY_FREE)
285 return NULL;
286
287 LogFlow(("usbProxyFreeBSDEndpointOpen: ep_index=%d\n", pEndpointFBSD->iEndpoint));
288
289 UsbFsOpen.ep_index = pEndpointFBSD->iEndpoint;
290 UsbFsOpen.ep_no = Endpoint;
291 UsbFsOpen.max_bufsize = 256 * _1K; /* Hardcoded assumption about the URBs we get. */
292 UsbFsOpen.max_frames = 2;
293
294 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_OPEN, &UsbFsOpen, true, UINT32_MAX);
295 if (rc)
296 return NULL;
297
298 pEndpointFBSD->fOpen = true;
299 pEndpointFBSD->pXferEndpoint = &pDevFBSD->paXferEndpoints[pEndpointFBSD->iEndpoint];
300 }
301 else
302 {
303 AssertMsgReturn(!pDevFBSD->paUrbs[pEndpointFBSD->iEndpoint].pUrb, ("Endpoint is busy"), NULL);
304 pEndpointFBSD->pXferEndpoint = &pDevFBSD->paXferEndpoints[pEndpointFBSD->iEndpoint];
305 }
306
307 return pEndpointFBSD;
308}
309
310static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint)
311{
312 LogFlow(("usbProxyFreeBSDEndpointClose: pProxyDev=%p Endpoint=%d\n", pProxyDev, Endpoint));
313
314 AssertMsg(Endpoint < USBFBSD_MAXENDPOINTS, ("Endpoint index exceeds limit %d\n", Endpoint));
315
316 int EndPtIndex = (Endpoint & USBFBSD_EPADDR_NUM_MASK) + ((Endpoint & USBFBSD_EPADDR_DIR_MASK) ? USBFBSD_MAXENDPOINTS / 2 : 0);
317 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
318 PUSBENDPOINTFBSD pEndpointFBSD = &pDevFBSD->aEpOpened[EndPtIndex];
319
320 if (pEndpointFBSD->fOpen)
321 {
322 struct usb_fs_close UsbFsClose;
323
324 AssertMsgReturn(!pDevFBSD->paUrbs[pEndpointFBSD->iEndpoint].pUrb, ("Endpoint is busy"), NULL);
325
326 UsbFsClose.ep_index = pEndpointFBSD->iEndpoint;
327
328 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_CLOSE, &UsbFsClose, true, UINT32_MAX);
329 if (rc)
330 {
331 LogFlow(("usbProxyFreeBSDEndpointClose: failed rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
332 return RTErrConvertFromErrno(errno);
333 }
334
335 usbProxyFreeBSDSetEntryFree(pDevFBSD, pEndpointFBSD->iEndpoint);
336 pEndpointFBSD->fOpen = false;
337 }
338
339 return VINF_SUCCESS;
340}
341
342/**
343 * Opens the device file.
344 *
345 * @returns VBox status code.
346 * @param pProxyDev The device instance.
347 * @param pszAddress If we are using usbfs, this is the path to the
348 * device. If we are using sysfs, this is a string of
349 * the form "sysfs:<sysfs path>//device:<device node>".
350 * In the second case, the two paths are guaranteed
351 * not to contain the substring "//".
352 * @param pvBackend Backend specific pointer, unused for the linux backend.
353 */
354static int usbProxyFreeBSDOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend)
355{
356 LogFlow(("usbProxyFreeBSDOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));
357
358 /*
359 * Try open the device node.
360 */
361 RTFILE File;
362 int rc = RTFileOpen(&File, pszAddress, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
363 if (RT_SUCCESS(rc))
364 {
365 /*
366 * Allocate and initialize the linux backend data.
367 */
368 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)RTMemAllocZ(sizeof(USBPROXYDEVFBSD));
369 if (pDevFBSD)
370 {
371 pDevFBSD->File = File;
372 rc = RTCritSectInit(&pDevFBSD->CritSect);
373 if (RT_SUCCESS(rc))
374 {
375 unsigned cTransfersMax = 127; /* Maximum in the kernel atm. */
376
377 /* Allocate arrays for data transfers. */
378 pDevFBSD->paXferEndpoints = (struct usb_fs_endpoint *)RTMemAllocZ(cTransfersMax * sizeof(struct usb_fs_endpoint));
379 pDevFBSD->paUrbs = (PVUSBURBFBSD)RTMemAllocZ(cTransfersMax * sizeof(VUSBURBFBSD));
380 pDevFBSD->paXferFree = (unsigned *)RTMemAllocZ((cTransfersMax + 1) * sizeof(unsigned));
381 pDevFBSD->cXferEndpoints = cTransfersMax;
382
383 if (pDevFBSD->paXferEndpoints && pDevFBSD->paUrbs && pDevFBSD->paXferFree)
384 {
385 /* Initialize the kernel side. */
386 struct usb_fs_init UsbFsInit;
387
388 UsbFsInit.pEndpoints = pDevFBSD->paXferEndpoints;
389 UsbFsInit.ep_index_max = cTransfersMax;
390 rc = ioctl(File, USB_FS_INIT, &UsbFsInit);
391 if (!rc)
392 {
393 for (unsigned i = 0; i < cTransfersMax; i++)
394 usbProxyFreeBSDSetEntryFree(pDevFBSD, i);
395
396 for (unsigned i= 0; i < USBFBSD_MAXENDPOINTS; i++)
397 pDevFBSD->aEpOpened[i].fOpen = false;
398
399 pProxyDev->Backend.pv = pDevFBSD;
400
401 LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns successfully File=%d iActiveCfg=%d\n",
402 pProxyDev, pszAddress, pDevFBSD->File, pProxyDev->iActiveCfg));
403
404 return VINF_SUCCESS;
405 }
406 else
407 rc = RTErrConvertFromErrno(errno);
408 }
409 else
410 rc = VERR_NO_MEMORY;
411
412 if (pDevFBSD->paXferEndpoints)
413 RTMemFree(pDevFBSD->paXferEndpoints);
414 if (pDevFBSD->paUrbs)
415 RTMemFree(pDevFBSD->paUrbs);
416 if (pDevFBSD->paXferFree)
417 RTMemFree(pDevFBSD->paXferFree);
418 }
419
420 RTMemFree(pDevFBSD);
421 }
422 else
423 rc = VERR_NO_MEMORY;
424 RTFileClose(File);
425 }
426 else if (rc == VERR_ACCESS_DENIED)
427 rc = VERR_VUSB_USBFS_PERMISSION;
428
429 Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%Rrc!\n", pProxyDev, pszAddress, rc));
430 pProxyDev->Backend.pv = NULL;
431
432 NOREF(pvBackend);
433 return rc;
434
435 return VINF_SUCCESS;
436}
437
438
439/**
440 * Claims all the interfaces and figures out the
441 * current configuration.
442 *
443 * @returns VINF_SUCCESS.
444 * @param pProxyDev The proxy device.
445 */
446static int usbProxyFreeBSDInit(PUSBPROXYDEV pProxyDev)
447{
448 LogFlow(("usbProxyFreeBSDInit: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
449 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
450
451 /* Retrieve current active configuration. */
452 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG, &pProxyDev->iActiveCfg, true, UINT32_MAX);
453 if (RT_FAILURE(rc))
454 {
455 pProxyDev->iActiveCfg = -1;
456 return rc;
457 }
458
459 Log(("usbProxyFreeBSDInit: iActiveCfg=%d\n", pProxyDev->iActiveCfg));
460 pProxyDev->cIgnoreSetConfigs = 1;
461 pProxyDev->iActiveCfg++;
462
463 return VINF_SUCCESS;
464}
465
466
467/**
468 * Closes the proxy device.
469 */
470static void usbProxyFreeBSDClose(PUSBPROXYDEV pProxyDev)
471{
472 LogFlow(("usbProxyFreeBSDClose: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
473 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
474 Assert(pDevFBSD);
475 if (!pDevFBSD)
476 return;
477
478 RTCritSectDelete(&pDevFBSD->CritSect);
479
480 struct usb_fs_uninit UsbFsUninit;
481 UsbFsUninit.dummy = 0;
482
483 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false, 1);
484 AssertMsg(!rc, ("Freeing kernel ressources failed rc=%Rrc\n", RTErrConvertFromErrno(errno)));
485
486 if (pDevFBSD->paXferEndpoints)
487 RTMemFree(pDevFBSD->paXferEndpoints);
488 if (pDevFBSD->paUrbs)
489 RTMemFree(pDevFBSD->paUrbs);
490 if (pDevFBSD->paXferFree)
491 RTMemFree(pDevFBSD->paXferFree);
492
493 RTFileClose(pDevFBSD->File);
494 pDevFBSD->File = NIL_RTFILE;
495
496 RTMemFree(pDevFBSD);
497 pProxyDev->Backend.pv = NULL;
498
499 LogFlow(("usbProxyFreeBSDClose: returns\n"));
500}
501
502
503/**
504 * Reset a device.
505 *
506 * @returns VBox status code.
507 * @param pDev The device to reset.
508 */
509static int usbProxyFreeBSDReset(PUSBPROXYDEV pProxyDev, bool fResetOnFreeBSD)
510{
511 LogFlow(("usbProxyFreeBSDReset: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
512 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
513
514 /* Close any open endpoints. */
515 for (unsigned i = 0; i < USBFBSD_MAXENDPOINTS; i++)
516 usbProxyFreeBSDEndpointClose(pProxyDev, i);
517
518 /* We need to release kernel ressources first. */
519 struct usb_fs_uninit UsbFsUninit;
520 UsbFsUninit.dummy = 0;
521
522 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false, 1);
523 AssertMsg(!rc, ("Freeing kernel ressources failed rc=%Rrc\n", RTErrConvertFromErrno(errno)));
524
525 /* Resetting is not possible from a normal user account */
526#if 0
527 int iUnused = 0;
528 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DEVICEENUMERATE, &iUnused, true, UINT32_MAX);
529 if (rc)
530 return RTErrConvertFromErrno(errno);
531#endif
532
533 /* Allocate kernel ressources again. */
534 struct usb_fs_init UsbFsInit;
535
536 UsbFsInit.pEndpoints = pDevFBSD->paXferEndpoints;
537 UsbFsInit.ep_index_max = pDevFBSD->cXferEndpoints;
538 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_INIT, &UsbFsInit, true, UINT32_MAX);
539 if (!rc)
540 {
541 /* Retrieve current active configuration. */
542 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG, &pProxyDev->iActiveCfg, true, UINT32_MAX);
543 if (rc)
544 {
545 pProxyDev->iActiveCfg = -1;
546 rc = RTErrConvertFromErrno(errno);
547 }
548 else
549 {
550 pProxyDev->cIgnoreSetConfigs = 2;
551 pProxyDev->iActiveCfg++;
552 }
553 }
554 else
555 rc = RTErrConvertFromErrno(errno);
556
557 Log(("usbProxyFreeBSDReset: iActiveCfg=%d\n", pProxyDev->iActiveCfg));
558
559 return rc;
560}
561
562
563/**
564 * SET_CONFIGURATION.
565 *
566 * The caller makes sure that it's not called first time after open or reset
567 * with the active interface.
568 *
569 * @returns success indicator.
570 * @param pProxyDev The device instance data.
571 * @param iCfg The configuration to set.
572 */
573static int usbProxyFreeBSDSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
574{
575 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
576
577 LogFlow(("usbProxyFreeBSDSetConfig: pProxyDev=%s cfg=%#x\n",
578 pProxyDev->pUsbIns->pszName, iCfg));
579
580 /* Close any open endpoints. */
581 for (unsigned i = 0; i < USBFBSD_MAXENDPOINTS; i++)
582 usbProxyFreeBSDEndpointClose(pProxyDev, i);
583
584 /* We need to release kernel ressources first. */
585 struct usb_fs_uninit UsbFsUninit;
586 UsbFsUninit.dummy = 0;
587
588 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false, 1);
589 AssertMsg(!rc, ("Freeing kernel ressources failed rc=%Rrc\n", RTErrConvertFromErrno(errno)));
590
591 int iCfgIndex = 0;
592
593 /* Get theconfiguration index matching the value. */
594 for (iCfgIndex = 0; iCfgIndex < pProxyDev->DevDesc.bNumConfigurations; iCfgIndex++)
595 {
596 if (pProxyDev->paCfgDescs[iCfgIndex].Core.bConfigurationValue == iCfg)
597 break;
598 }
599
600 if (RT_UNLIKELY(iCfgIndex == pProxyDev->DevDesc.bNumConfigurations))
601 {
602 LogFlow(("usbProxyFreeBSDSetConfig: configuration %d not found\n", iCfg));
603 return false;
604 }
605
606 /* Set the config */
607 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iCfgIndex, true, UINT32_MAX);
608 if (RT_FAILURE(rc))
609 {
610 LogFlow(("usbProxyFreeBSDSetConfig: setting config index %d failed rc=%d errno=%Rrc\n", iCfgIndex, rc, RTErrConvertFromErrno(errno)));
611 return false;
612 }
613
614 /* Allocate kernel ressources again. */
615 struct usb_fs_init UsbFsInit;
616
617 UsbFsInit.pEndpoints = pDevFBSD->paXferEndpoints;
618 UsbFsInit.ep_index_max = pDevFBSD->cXferEndpoints;
619 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_INIT, &UsbFsInit, true, UINT32_MAX);
620
621
622 LogFlow(("usbProxyFreeBSDSetConfig: rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
623
624 if (!rc)
625 return true;
626 else
627 return false;
628}
629
630
631/**
632 * Claims an interface.
633 * @returns success indicator.
634 */
635static int usbProxyFreeBSDClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
636{
637 LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, iIf));
638
639 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_CLAIM_INTERFACE, &iIf, true, UINT32_MAX);
640 if (RT_FAILURE(rc))
641 return false;
642
643 return true;
644}
645
646
647/**
648 * Releases an interface.
649 * @returns success indicator.
650 */
651static int usbProxyFreeBSDReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
652{
653 LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s ifnum=%#x\n", pProxyDev->pUsbIns->pszName, iIf));
654
655 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_RELEASE_INTERFACE, &iIf, true, UINT32_MAX);
656 if (RT_FAILURE(rc))
657 return false;
658
659 return true;
660}
661
662
663/**
664 * SET_INTERFACE.
665 *
666 * @returns success indicator.
667 */
668static int usbProxyFreeBSDSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
669{
670 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
671
672 LogFlow(("usbProxyFreeBSDSetInterface: pProxyDev=%p iIf=%#x iAlt=%#x\n", pProxyDev, iIf, iAlt));
673
674 /* Close any open endpoints. */
675 for (unsigned i = 0; i < USBFBSD_MAXENDPOINTS; i++)
676 usbProxyFreeBSDEndpointClose(pProxyDev, i);
677
678 /* We need to release kernel ressources first. */
679 struct usb_fs_uninit UsbFsUninit;
680 UsbFsUninit.dummy = 0;
681
682 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false, 1);
683 AssertMsg(!rc, ("Freeing kernel ressources failed rc=%Rrc\n", RTErrConvertFromErrno(errno)));
684
685 struct usb_alt_interface UsbIntAlt;
686 UsbIntAlt.uai_interface_index = iIf;
687 UsbIntAlt.uai_alt_index = iAlt;
688 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_ALTINTERFACE, &UsbIntAlt, true, UINT32_MAX);
689 if (rc)
690 {
691 LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d failed rc=%d errno=%Rrc\n", iIf, iAlt, rc,RTErrConvertFromErrno(errno)));
692 return false;
693 }
694
695 /* Allocate kernel ressources again. */
696 struct usb_fs_init UsbFsInit;
697
698 UsbFsInit.pEndpoints = pDevFBSD->paXferEndpoints;
699 UsbFsInit.ep_index_max = pDevFBSD->cXferEndpoints;
700 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_INIT, &UsbFsInit, true, UINT32_MAX);
701
702 LogFlow(("usbProxyFreeBSDSetInterface: rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
703
704 if (!rc)
705 return true;
706 else
707 return false;
708}
709
710
711/**
712 * Clears the halted endpoint 'EndPt'.
713 */
714static bool usbProxyFreeBSDClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int EndPt)
715{
716 LogFlow(("usbProxyFreeBSDClearHaltedEp: pProxyDev=%s EndPt=%u\n", pProxyDev->pUsbIns->pszName, EndPt));
717
718 /*
719 * Clearing the zero control pipe doesn't make sense. Just ignore it.
720 */
721 if (EndPt == 0)
722 return true;
723
724 struct usb_ctl_request Req;
725
726 memset(&Req, 0, sizeof(struct usb_ctl_request));
727 usbProxyFreeBSDSetupReq(&Req.ucr_request, VUSB_DIR_TO_DEV | VUSB_TO_ENDPOINT, VUSB_REQ_CLEAR_FEATURE, 0, EndPt, 0);
728
729 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DO_REQUEST, &Req, true, 1);
730 if (rc)
731 {
732 LogFlow(("usbProxyFreeBSDClearHaltedEp: failed rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
733 return false;
734 }
735
736 LogFlow(("usbProxyFreeBSDClearHaltedEp: succeeded\n"));
737
738 return true;
739}
740
741
742/**
743 * @copydoc USBPROXYBACK::pfnUrbQueue
744 */
745static int usbProxyFreeBSDUrbQueue(PVUSBURB pUrb)
746{
747 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUrb->pUsbIns, PUSBPROXYDEV);
748 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
749
750 LogFlow(("usbProxyFreeBSDUrbQueue: pUrb=%p\n", pUrb));
751
752 uint8_t EndPt = pUrb->EndPt;
753 if (pUrb->EndPt)
754 EndPt = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0);
755
756 PUSBENDPOINTFBSD pEndpointFBSD = usbProxyFreeBSDEndpointOpen(pProxyDev, EndPt);
757 if (!pEndpointFBSD)
758 return false;
759
760 PVUSBURBFBSD pUrbFBSD = &pDevFBSD->paUrbs[pEndpointFBSD->iEndpoint];
761 AssertMsg(!pUrbFBSD->pUrb, ("Assigned entry is busy\n"));
762 pUrbFBSD->pUrb = pUrb;
763
764 struct usb_fs_start UsbFsStart;
765 unsigned cFrames;
766
767 if (pUrb->enmType == VUSBXFERTYPE_MSG)
768 {
769 PVUSBSETUP pSetup = (PVUSBSETUP)&pUrb->abData[0];
770
771 pUrbFBSD->apvData[0] = pSetup;
772 pUrbFBSD->acbData[0] = sizeof(VUSBSETUP);
773
774 if (pSetup->wLength)
775 {
776 pUrbFBSD->apvData[1] = &pUrb->abData[sizeof(VUSBSETUP)];
777 pUrbFBSD->acbData[1] = pSetup->wLength;
778 cFrames = 2;
779 }
780 else
781 cFrames = 1;
782 }
783 else
784 {
785 pUrbFBSD->apvData[0] = &pUrb->abData[0];
786 pUrbFBSD->acbData[0] = pUrb->cbData;
787 cFrames = 1;
788 }
789
790 struct usb_fs_endpoint *pXferEndpoint = pEndpointFBSD->pXferEndpoint;
791 pXferEndpoint->ppBuffer = &pUrbFBSD->apvData[0];
792 pXferEndpoint->pLength = &pUrbFBSD->acbData[0];
793 pXferEndpoint->nFrames = cFrames;
794 pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE; /* Timeout handling will be done during reap. */
795 pXferEndpoint->flags = pUrb->fShortNotOk ? 0 : USB_FS_FLAG_MULTI_SHORT_OK;
796
797 /* Start the transfer */
798 UsbFsStart.ep_index = pEndpointFBSD->iEndpoint;
799 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_START, &UsbFsStart, true, UINT32_MAX);
800
801 LogFlow(("usbProxyFreeBSDUrbQueue: USB_FS_START returned rc=%d errno=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
802 if (rc)
803 {
804 return false;
805 }
806
807 return true;
808}
809
810
811/**
812 * Reap URBs in-flight on a device.
813 *
814 * @returns Pointer to a completed URB.
815 * @returns NULL if no URB was completed.
816 * @param pProxyDev The device.
817 * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
818 */
819static PVUSBURB usbProxyFreeBSDUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
820{
821 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
822
823 LogFlow(("usbProxyFreeBSDUrbReap: cMillies=%u\n", cMillies));
824
825 /* We will poll for finished urbs because the ioctl doesn't take a timeout parameter. */
826 struct pollfd PollFd;
827 PVUSBURB pUrb = NULL;
828
829 PollFd.fd = (int)pDevFBSD->File;
830 PollFd.events = POLLIN | POLLRDNORM | POLLOUT;
831 PollFd.revents = 0;
832
833 struct usb_fs_complete UsbFsComplete;
834
835 UsbFsComplete.ep_index = 0;
836
837 int rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true, UINT32_MAX);
838 if (!rc)
839 {
840 struct usb_fs_endpoint *pXferEndpoint = &pDevFBSD->paXferEndpoints[UsbFsComplete.ep_index];
841 PVUSBURBFBSD pUrbFBSD = &pDevFBSD->paUrbs[UsbFsComplete.ep_index];
842
843 LogFlow(("Reaped URB %#p\n", pUrbFBSD->pUrb));
844
845 pUrb = pUrbFBSD->pUrb;
846 AssertMsg(pUrb, ("No URB handle for the completed entry\n"));
847 pUrbFBSD->pUrb = NULL;
848
849 switch (pXferEndpoint->status)
850 {
851 case USB_ERR_NORMAL_COMPLETION:
852 pUrb->enmStatus = VUSBSTATUS_OK;
853 break;
854 case USB_ERR_STALLED:
855 pUrb->enmStatus = VUSBSTATUS_STALL;
856 break;
857 default:
858 AssertMsgFailed(("Unexpected status code %d\n", pXferEndpoint->status));
859 }
860 }
861 else
862 {
863
864 rc = poll(&PollFd, 1, cMillies == RT_INDEFINITE_WAIT ? INFTIM : cMillies);
865 if (rc == 1)
866 {
867 // do
868 {
869 UsbFsComplete.ep_index = 0;
870 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true, UINT32_MAX);
871 if (!rc)
872 {
873 struct usb_fs_endpoint *pXferEndpoint = &pDevFBSD->paXferEndpoints[UsbFsComplete.ep_index];
874 PVUSBURBFBSD pUrbFBSD = &pDevFBSD->paUrbs[UsbFsComplete.ep_index];
875
876 LogFlow(("Reaped URB %#p\n", pUrbFBSD->pUrb));
877
878 pUrb = pUrbFBSD->pUrb;
879 AssertMsg(pUrb, ("No URB handle for the completed entry\n"));
880 pUrbFBSD->pUrb = NULL;
881
882 switch (pXferEndpoint->status)
883 {
884 case USB_ERR_NORMAL_COMPLETION:
885 pUrb->enmStatus = VUSBSTATUS_OK;
886 break;
887 case USB_ERR_STALLED:
888 pUrb->enmStatus = VUSBSTATUS_STALL;
889 break;
890 default:
891 AssertMsgFailed(("Unexpected status code %d\n", pXferEndpoint->status));
892 }
893
894 rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true, UINT32_MAX);
895 AssertMsg(((rc == -1) && (errno == EBUSY)), ("Expected return value rc=%d rc=%Rrc\n", rc, RTErrConvertFromErrno(errno)));
896 }
897 else
898 LogFlow(("couldn't get completed URB rc=%Rrc\n", RTErrConvertFromErrno(errno)));
899 }
900 // while (!rc);
901 }
902 else
903 LogFlow(("poll returned rc=%d rcRT=%Rrc\n", rc, rc < 0 ? RTErrConvertFromErrno(errno) : VERR_TIMEOUT));
904 }
905
906 return pUrb;
907}
908
909
910/**
911 * Cancels the URB.
912 * The URB requires reaping, so we don't change its state.
913 */
914static void usbProxyFreeBSDUrbCancel(PVUSBURB pUrb)
915{
916 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUrb->pUsbIns, PUSBPROXYDEV);
917 PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)pProxyDev->Backend.pv;
918
919
920}
921
922
923/**
924 * The FreeBSD USB Proxy Backend.
925 */
926extern const USBPROXYBACK g_USBProxyDeviceHost =
927{
928 "host",
929 usbProxyFreeBSDOpen,
930 usbProxyFreeBSDInit,
931 usbProxyFreeBSDClose,
932 usbProxyFreeBSDReset,
933 usbProxyFreeBSDSetConfig,
934 usbProxyFreeBSDClaimInterface,
935 usbProxyFreeBSDReleaseInterface,
936 usbProxyFreeBSDSetInterface,
937 usbProxyFreeBSDClearHaltedEp,
938 usbProxyFreeBSDUrbQueue,
939 usbProxyFreeBSDUrbCancel,
940 usbProxyFreeBSDUrbReap,
941 0
942};
943
944
945/*
946 * Local Variables:
947 * mode: c
948 * c-file-style: "bsd"
949 * c-basic-offset: 4
950 * tab-width: 4
951 * indent-tabs-mode: s
952 * End:
953 */
954
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