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