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 | *******************************************************************************/
|
---|
63 | typedef 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 |
|
---|
73 | typedef 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 | */
|
---|
86 | typedef 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 | *******************************************************************************/
|
---|
118 | static int usbProxyFreeBSDDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries);
|
---|
119 | static void usbProxFreeBSDUrbUnplugged(PUSBPROXYDEV pProxyDev);
|
---|
120 | static PUSBENDPOINTFBSD usbProxyFreeBSDEndpointOpen(PUSBPROXYDEV pProxyDev, int Endpoint);
|
---|
121 | static 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 | */
|
---|
137 | static 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 | */
|
---|
171 | static 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 | */
|
---|
195 | static 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 |
|
---|
245 | DECLINLINE(void) usbProxyFreeBSDSetEntryFree(PUSBPROXYDEVFBSD pProxyDev, unsigned iEntry)
|
---|
246 | {
|
---|
247 | pProxyDev->paXferFree[pProxyDev->iXferFreeNextWrite] = iEntry;
|
---|
248 | pProxyDev->iXferFreeNextWrite++;
|
---|
249 | pProxyDev->iXferFreeNextWrite %= (pProxyDev->cXferEndpoints+1);
|
---|
250 | }
|
---|
251 |
|
---|
252 | DECLINLINE(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 |
|
---|
268 | static 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 |
|
---|
310 | static 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 | */
|
---|
354 | static 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 | */
|
---|
446 | static 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 | */
|
---|
470 | static 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 | */
|
---|
509 | static 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 | */
|
---|
573 | static 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 | */
|
---|
635 | static 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 | */
|
---|
651 | static 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 | */
|
---|
668 | static 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 | */
|
---|
714 | static 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 | */
|
---|
745 | static 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 | */
|
---|
819 | static 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 | */
|
---|
914 | static 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 | */
|
---|
926 | extern 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 |
|
---|