VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/win/USBProxyDevice-win.cpp@ 73529

Last change on this file since 73529 was 69961, checked in by vboxsync, 7 years ago

VUSB/win: Report error early if further URBs cannot be queued.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.4 KB
Line 
1/* $Id: USBProxyDevice-win.cpp 69961 2017-12-06 19:29:03Z vboxsync $ */
2/** @file
3 * USBPROXY - USB proxy, Win32 backend
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
23#include <iprt/win/windows.h>
24
25#include <VBox/vmm/pdm.h>
26#include <VBox/err.h>
27#include <VBox/usb.h>
28#include <VBox/log.h>
29#include <iprt/assert.h>
30#include <iprt/alloc.h>
31#include <iprt/err.h>
32#include <iprt/string.h>
33#include <iprt/thread.h>
34#include <iprt/asm.h>
35#include "../USBProxyDevice.h"
36#include <VBox/usblib.h>
37
38
39/*********************************************************************************************************************************
40* Structures and Typedefs *
41*********************************************************************************************************************************/
42typedef struct _QUEUED_URB
43{
44 PVUSBURB urb;
45
46 USBSUP_URB urbwin;
47 OVERLAPPED overlapped;
48 DWORD cbReturned;
49 bool fCancelled;
50} QUEUED_URB, *PQUEUED_URB;
51
52typedef struct
53{
54 /* Critical section to protect this structure. */
55 RTCRITSECT CritSect;
56 HANDLE hDev;
57 uint8_t bInterfaceNumber;
58 bool fClaimed;
59 /** The allocated size of paHandles and paQueuedUrbs. */
60 unsigned cAllocatedUrbs;
61 /** The number of URBs in the array. */
62 unsigned cQueuedUrbs;
63 /** Array of pointers to the in-flight URB structures. */
64 PQUEUED_URB *paQueuedUrbs;
65 /** Array of handles, this is parallel to paQueuedUrbs. */
66 PHANDLE paHandles;
67 /* Event sempahore to wakeup the reaper thead. */
68 HANDLE hEventWakeup;
69 /** Number of queued URBs waiting to get into the handle list. */
70 unsigned cPendingUrbs;
71 /** Array of pending URBs. */
72 PQUEUED_URB aPendingUrbs[64];
73} PRIV_USBW32, *PPRIV_USBW32;
74
75/* All functions are returning 1 on success, 0 on error */
76
77
78/*********************************************************************************************************************************
79* Internal Functions *
80*********************************************************************************************************************************/
81static int usbProxyWinSetInterface(PUSBPROXYDEV p, int iIf, int setting);
82
83/**
84 * Converts the given Windows error code to VBox handling unplugged devices.
85 *
86 * @returns VBox status code.
87 * @param pProxDev The USB proxy device instance.
88 * @param dwErr Windows error code.
89 */
90static int usbProxyWinHandleUnpluggedDevice(PUSBPROXYDEV pProxyDev, DWORD dwErr)
91{
92#ifdef LOG_ENABLED
93 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
94#endif
95
96 if ( dwErr == ERROR_INVALID_HANDLE_STATE
97 || dwErr == ERROR_BAD_COMMAND)
98 {
99 Log(("usbproxy: device %x unplugged!!\n", pPriv->hDev));
100 pProxyDev->fDetached = true;
101 }
102 else
103 AssertMsgFailed(("lasterr=%d\n", dwErr));
104 return RTErrConvertFromWin32(dwErr);
105}
106
107/**
108 * Open a USB device and create a backend instance for it.
109 *
110 * @returns VBox status code.
111 */
112static DECLCALLBACK(int) usbProxyWinOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend)
113{
114 RT_NOREF(pvBackend);
115 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
116
117 int rc = VINF_SUCCESS;
118 pPriv->cAllocatedUrbs = 32;
119 pPriv->paHandles = (PHANDLE)RTMemAllocZ(sizeof(pPriv->paHandles[0]) * pPriv->cAllocatedUrbs);
120 pPriv->paQueuedUrbs = (PQUEUED_URB *)RTMemAllocZ(sizeof(pPriv->paQueuedUrbs[0]) * pPriv->cAllocatedUrbs);
121 if ( pPriv->paQueuedUrbs
122 && pPriv->paHandles)
123 {
124 /*
125 * Open the device.
126 */
127 pPriv->hDev = CreateFile(pszAddress,
128 GENERIC_READ | GENERIC_WRITE,
129 FILE_SHARE_WRITE | FILE_SHARE_READ,
130 NULL, // no SECURITY_ATTRIBUTES structure
131 OPEN_EXISTING, // No special create flags
132 FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, // overlapped IO
133 NULL); // No template file
134 if (pPriv->hDev != INVALID_HANDLE_VALUE)
135 {
136 Log(("usbProxyWinOpen: hDev=%p\n", pPriv->hDev));
137
138 /*
139 * Check the version
140 */
141 USBSUP_VERSION version = {0};
142 DWORD cbReturned = 0;
143 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_GET_VERSION, NULL, 0, &version, sizeof(version), &cbReturned, NULL))
144 {
145 if ( version.u32Major == USBDRV_MAJOR_VERSION
146#if USBDRV_MINOR_VERSION != 0
147 && version.u32Minor >= USBDRV_MINOR_VERSION
148#endif
149 )
150 {
151 USBSUP_CLAIMDEV in;
152 in.bInterfaceNumber = 0;
153
154 cbReturned = 0;
155 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_CLAIM_DEVICE, &in, sizeof(in), &in, sizeof(in), &cbReturned, NULL))
156 {
157 if (in.fClaimed)
158 {
159 pPriv->fClaimed = true;
160#if 0 /** @todo this needs to be enabled if windows chooses a default config. Test with the TrekStor GO Stick. */
161 pProxyDev->iActiveCfg = 1;
162 pProxyDev->cIgnoreSetConfigs = 1;
163#endif
164
165 rc = RTCritSectInit(&pPriv->CritSect);
166 AssertRC(rc);
167 pPriv->hEventWakeup = CreateEvent(NULL, FALSE, FALSE, NULL);
168 Assert(pPriv->hEventWakeup);
169
170 pPriv->paHandles[0] = pPriv->hEventWakeup;
171
172 return VINF_SUCCESS;
173 }
174
175 rc = VERR_GENERAL_FAILURE;
176 Log(("usbproxy: unable to claim device %x (%s)!!\n", pPriv->hDev, pszAddress));
177 }
178 }
179 else
180 {
181 rc = VERR_VERSION_MISMATCH;
182 Log(("usbproxy: Version mismatch: %d.%d != %d.%d (cur)\n",
183 version.u32Major, version.u32Minor, USBDRV_MAJOR_VERSION, USBDRV_MINOR_VERSION));
184 }
185 }
186
187 /* Convert last error if necessary */
188 if (RT_SUCCESS(rc))
189 {
190 DWORD dwErr = GetLastError();
191 Log(("usbproxy: last error %d\n", dwErr));
192 rc = RTErrConvertFromWin32(dwErr);
193 }
194
195 CloseHandle(pPriv->hDev);
196 pPriv->hDev = INVALID_HANDLE_VALUE;
197 }
198 else
199 {
200 Log(("usbproxy: FAILED to open '%s'! last error %d\n", pszAddress, GetLastError()));
201 rc = VERR_FILE_NOT_FOUND;
202 }
203 }
204 else
205 rc = VERR_NO_MEMORY;
206
207 RTMemFree(pPriv->paQueuedUrbs);
208 RTMemFree(pPriv->paHandles);
209 return rc;
210}
211
212/**
213 * Copy the device and free resources associated with the backend.
214 */
215static DECLCALLBACK(void) usbProxyWinClose(PUSBPROXYDEV pProxyDev)
216{
217 /* Here we just close the device and free up p->priv
218 * there is no need to do anything like cancel outstanding requests
219 * that will have been done already
220 */
221 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
222 Assert(pPriv);
223 if (!pPriv)
224 return;
225 Log(("usbProxyWinClose: %p\n", pPriv->hDev));
226
227 if (pPriv->hDev != INVALID_HANDLE_VALUE)
228 {
229 Assert(pPriv->fClaimed);
230
231 USBSUP_RELEASEDEV in;
232 DWORD cbReturned = 0;
233 in.bInterfaceNumber = pPriv->bInterfaceNumber;
234 if (!DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_RELEASE_DEVICE, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
235 {
236 Log(("usbproxy: usbProxyWinClose: DeviceIoControl %#x failed with %#x!!\n", pPriv->hDev, GetLastError()));
237 }
238 if (!CloseHandle(pPriv->hDev))
239 AssertLogRelMsgFailed(("usbproxy: usbProxyWinClose: CloseHandle %#x failed with %#x!!\n", pPriv->hDev, GetLastError()));
240 pPriv->hDev = INVALID_HANDLE_VALUE;
241 }
242
243 CloseHandle(pPriv->hEventWakeup);
244 RTCritSectDelete(&pPriv->CritSect);
245
246 RTMemFree(pPriv->paQueuedUrbs);
247 RTMemFree(pPriv->paHandles);
248}
249
250
251static DECLCALLBACK(int) usbProxyWinReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux)
252{
253 RT_NOREF(fResetOnLinux);
254 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
255 DWORD cbReturned;
256 int rc;
257
258 Assert(pPriv);
259
260 Log(("usbproxy: Reset %x\n", pPriv->hDev));
261
262 /* Here we just need to assert reset signalling on the USB device */
263 cbReturned = 0;
264 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_RESET, NULL, 0, NULL, 0, &cbReturned, NULL))
265 {
266#if 0 /** @todo this needs to be enabled if windows chooses a default config. Test with the TrekStor GO Stick. */
267 pProxyDev->iActiveCfg = 1;
268 pProxyDev->cIgnoreSetConfigs = 2;
269#else
270 pProxyDev->iActiveCfg = -1;
271 pProxyDev->cIgnoreSetConfigs = 0;
272#endif
273 return VINF_SUCCESS;
274 }
275
276 rc = GetLastError();
277 if (rc == ERROR_DEVICE_REMOVED)
278 {
279 Log(("usbproxy: device %p unplugged!!\n", pPriv->hDev));
280 pProxyDev->fDetached = true;
281 }
282 return RTErrConvertFromWin32(rc);
283}
284
285static DECLCALLBACK(int) usbProxyWinSetConfig(PUSBPROXYDEV pProxyDev, int cfg)
286{
287 /* Send a SET_CONFIGURATION command to the device. We don't do this
288 * as a normal control message, because the OS might not want to
289 * be left out of the loop on such a thing.
290 *
291 * It would be OK to send a SET_CONFIGURATION control URB at this
292 * point but it has to be synchronous.
293 */
294 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
295 USBSUP_SET_CONFIG in;
296 DWORD cbReturned;
297
298 Assert(pPriv);
299
300 Log(("usbproxy: Set config of %p to %d\n", pPriv->hDev, cfg));
301 in.bConfigurationValue = cfg;
302
303 /* Here we just need to assert reset signalling on the USB device */
304 cbReturned = 0;
305 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_SET_CONFIG, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
306 return VINF_SUCCESS;
307
308 return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError());
309}
310
311static DECLCALLBACK(int) usbProxyWinClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
312{
313 /* Called just before we use an interface. Needed on Linux to claim
314 * the interface from the OS, since even when proxying the host OS
315 * might want to allow other programs to use the unused interfaces.
316 * Not relevant for Windows.
317 */
318 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
319
320 pPriv->bInterfaceNumber = iIf;
321
322 Assert(pPriv);
323 return VINF_SUCCESS;
324}
325
326static DECLCALLBACK(int) usbProxyWinReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
327{
328 RT_NOREF(pProxyDev, iIf);
329 /* The opposite of claim_interface. */
330 return VINF_SUCCESS;
331}
332
333static DECLCALLBACK(int) usbProxyWinSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int setting)
334{
335 /* Select an alternate setting for an interface, the same applies
336 * here as for set_config, you may convert this in to a control
337 * message if you want but it must be synchronous
338 */
339 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
340 USBSUP_SELECT_INTERFACE in;
341 DWORD cbReturned;
342
343 Assert(pPriv);
344
345 Log(("usbproxy: Select interface of %x to %d/%d\n", pPriv->hDev, iIf, setting));
346 in.bInterfaceNumber = iIf;
347 in.bAlternateSetting = setting;
348
349 /* Here we just need to assert reset signalling on the USB device */
350 cbReturned = 0;
351 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_SELECT_INTERFACE, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
352 return VINF_SUCCESS;
353
354 return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError());
355}
356
357/**
358 * Clears the halted endpoint 'ep'.
359 */
360static DECLCALLBACK(int) usbProxyWinClearHaltedEndPt(PUSBPROXYDEV pProxyDev, unsigned int ep)
361{
362 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
363 USBSUP_CLEAR_ENDPOINT in;
364 DWORD cbReturned;
365
366 Assert(pPriv);
367
368 Log(("usbproxy: Clear endpoint %d of %x\n", ep, pPriv->hDev));
369 in.bEndpoint = ep;
370
371 cbReturned = 0;
372 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_CLEAR_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
373 return VINF_SUCCESS;
374
375 return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError());
376}
377
378/**
379 * Aborts a pipe/endpoint (cancels all outstanding URBs on the endpoint).
380 */
381static int usbProxyWinAbortEndPt(PUSBPROXYDEV pProxyDev, unsigned int ep)
382{
383 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
384 USBSUP_CLEAR_ENDPOINT in;
385 DWORD cbReturned;
386
387 Assert(pPriv);
388
389 Log(("usbproxy: Abort endpoint %d of %x\n", ep, pPriv->hDev));
390 in.bEndpoint = ep;
391
392 cbReturned = 0;
393 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
394 return VINF_SUCCESS;
395
396 return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError());
397}
398
399/**
400 * @interface_method_impl{USBPROXYBACK,pfnUrbQueue}
401 */
402static DECLCALLBACK(int) usbProxyWinUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
403{
404 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
405 Assert(pPriv);
406
407 /* Don't even bother if we can't wait for that many objects. */
408 if (pPriv->cPendingUrbs + pPriv->cQueuedUrbs >= (MAXIMUM_WAIT_OBJECTS - 1))
409 return VERR_OUT_OF_RESOURCES;
410 if (pPriv->cPendingUrbs >= RT_ELEMENTS(pPriv->aPendingUrbs))
411 return VERR_OUT_OF_RESOURCES;
412
413 /*
414 * Allocate and initialize a URB queue structure.
415 */
416 /** @todo pool these */
417 PQUEUED_URB pQUrbWin = (PQUEUED_URB)RTMemAllocZ(sizeof(QUEUED_URB));
418 if (!pQUrbWin)
419 return VERR_NO_MEMORY;
420
421 switch (pUrb->enmType)
422 {
423 case VUSBXFERTYPE_CTRL: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_CTRL; break; /* you won't ever see these */
424 case VUSBXFERTYPE_ISOC: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_ISOC;
425 pQUrbWin->urbwin.numIsoPkts = pUrb->cIsocPkts;
426 for (unsigned i = 0; i < pUrb->cIsocPkts; ++i)
427 {
428 pQUrbWin->urbwin.aIsoPkts[i].cb = pUrb->aIsocPkts[i].cb;
429 pQUrbWin->urbwin.aIsoPkts[i].off = pUrb->aIsocPkts[i].off;
430 pQUrbWin->urbwin.aIsoPkts[i].stat = USBSUP_XFER_OK;
431 }
432 break;
433 case VUSBXFERTYPE_BULK: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_BULK; break;
434 case VUSBXFERTYPE_INTR: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_INTR; break;
435 case VUSBXFERTYPE_MSG: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_MSG; break;
436 default:
437 AssertMsgFailed(("Invalid type %d\n", pUrb->enmType));
438 return VERR_INVALID_PARAMETER;
439 }
440
441 switch (pUrb->enmDir)
442 {
443 case VUSBDIRECTION_SETUP:
444 AssertFailed();
445 pQUrbWin->urbwin.dir = USBSUP_DIRECTION_SETUP;
446 break;
447 case VUSBDIRECTION_IN:
448 pQUrbWin->urbwin.dir = USBSUP_DIRECTION_IN;
449 break;
450 case VUSBDIRECTION_OUT:
451 pQUrbWin->urbwin.dir = USBSUP_DIRECTION_OUT;
452 break;
453 default:
454 AssertMsgFailed(("Invalid direction %d\n", pUrb->enmDir));
455 return VERR_INVALID_PARAMETER;
456 }
457
458 Log(("usbproxy: Queue URB %p ep=%d cbData=%d abData=%p cIsocPkts=%d\n", pUrb, pUrb->EndPt, pUrb->cbData, pUrb->abData, pUrb->cIsocPkts));
459
460 pQUrbWin->urb = pUrb;
461 pQUrbWin->urbwin.ep = pUrb->EndPt;
462 pQUrbWin->urbwin.len = pUrb->cbData;
463 pQUrbWin->urbwin.buf = pUrb->abData;
464 pQUrbWin->urbwin.error = USBSUP_XFER_OK;
465 pQUrbWin->urbwin.flags = USBSUP_FLAG_NONE;
466 if (pUrb->enmDir == VUSBDIRECTION_IN && !pUrb->fShortNotOk)
467 pQUrbWin->urbwin.flags = USBSUP_FLAG_SHORT_OK;
468
469 int rc = VINF_SUCCESS;
470 pQUrbWin->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
471 if (pQUrbWin->overlapped.hEvent != INVALID_HANDLE_VALUE)
472 {
473 pUrb->Dev.pvPrivate = pQUrbWin;
474
475 if ( DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_SEND_URB,
476 &pQUrbWin->urbwin, sizeof(pQUrbWin->urbwin),
477 &pQUrbWin->urbwin, sizeof(pQUrbWin->urbwin),
478 &pQUrbWin->cbReturned, &pQUrbWin->overlapped)
479 || GetLastError() == ERROR_IO_PENDING)
480 {
481 /* insert into the queue */
482 RTCritSectEnter(&pPriv->CritSect);
483 unsigned j = pPriv->cPendingUrbs;
484 Assert(j < RT_ELEMENTS(pPriv->aPendingUrbs));
485 pPriv->aPendingUrbs[j] = pQUrbWin;
486 pPriv->cPendingUrbs++;
487 RTCritSectLeave(&pPriv->CritSect);
488 SetEvent(pPriv->hEventWakeup);
489 return VINF_SUCCESS;
490 }
491 else
492 {
493 DWORD dwErr = GetLastError();
494 if ( dwErr == ERROR_INVALID_HANDLE_STATE
495 || dwErr == ERROR_BAD_COMMAND)
496 {
497 Log(("usbproxy: device %p unplugged!!\n", pPriv->hDev));
498 pProxyDev->fDetached = true;
499 }
500 else
501 AssertMsgFailed(("dwErr=%X urbwin.error=%d (submit urb)\n", dwErr, pQUrbWin->urbwin.error));
502 rc = RTErrConvertFromWin32(dwErr);
503 CloseHandle(pQUrbWin->overlapped.hEvent);
504 pQUrbWin->overlapped.hEvent = INVALID_HANDLE_VALUE;
505 }
506 }
507#ifdef DEBUG_misha
508 else
509 {
510 AssertMsgFailed(("FAILED!!, hEvent(0x%p)\n", pQUrbWin->overlapped.hEvent));
511 rc = VERR_NO_MEMORY;
512 }
513#endif
514
515 Assert(pQUrbWin->overlapped.hEvent == INVALID_HANDLE_VALUE);
516 RTMemFree(pQUrbWin);
517 return rc;
518}
519
520/**
521 * Convert Windows proxy URB status to VUSB status.
522 *
523 * @returns VUSB status constant.
524 * @param win_status Windows USB proxy status constant.
525 */
526static VUSBSTATUS usbProxyWinStatusToVUsbStatus(USBSUP_ERROR win_status)
527{
528 VUSBSTATUS vusb_status;
529
530 switch (win_status)
531 {
532 case USBSUP_XFER_OK: vusb_status = VUSBSTATUS_OK; break;
533 case USBSUP_XFER_STALL: vusb_status = VUSBSTATUS_STALL; break;
534 case USBSUP_XFER_DNR: vusb_status = VUSBSTATUS_DNR; break;
535 case USBSUP_XFER_CRC: vusb_status = VUSBSTATUS_CRC; break;
536 case USBSUP_XFER_NAC: vusb_status = VUSBSTATUS_NOT_ACCESSED; break;
537 case USBSUP_XFER_UNDERRUN: vusb_status = VUSBSTATUS_DATA_UNDERRUN; break;
538 case USBSUP_XFER_OVERRUN: vusb_status = VUSBSTATUS_DATA_OVERRUN; break;
539 default:
540 AssertMsgFailed(("USB: Invalid error %d\n", win_status));
541 vusb_status = VUSBSTATUS_DNR;
542 break;
543 }
544 return vusb_status;
545}
546
547/**
548 * Reap URBs in-flight on a device.
549 *
550 * @returns Pointer to a completed URB.
551 * @returns NULL if no URB was completed.
552 * @param pProxyDev The device.
553 * @param cMillies Number of milliseconds to wait. Use 0 to not
554 * wait at all.
555 */
556static DECLCALLBACK(PVUSBURB) usbProxyWinUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
557{
558 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
559 AssertReturn(pPriv, NULL);
560
561 /*
562 * There are some unnecessary calls, just return immediately or
563 * WaitForMultipleObjects will fail.
564 */
565 if ( pPriv->cQueuedUrbs <= 0
566 && pPriv->cPendingUrbs == 0)
567 {
568 if ( cMillies != 0
569 && pPriv->cPendingUrbs == 0)
570 {
571 /* Wait for the wakeup call. */
572 DWORD cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
573 DWORD dwRc = WaitForMultipleObjects(1, &pPriv->hEventWakeup, FALSE, cMilliesWait);
574 NOREF(dwRc);
575 }
576
577 return NULL;
578 }
579
580again:
581 /* Check for pending URBs. */
582 if (pPriv->cPendingUrbs)
583 {
584 RTCritSectEnter(&pPriv->CritSect);
585
586 /* Ensure we've got sufficient space in the arrays. */
587 if (pPriv->cQueuedUrbs + pPriv->cPendingUrbs + 1 > pPriv->cAllocatedUrbs)
588 {
589 unsigned cNewMax = pPriv->cAllocatedUrbs + pPriv->cPendingUrbs + 1;
590 void *pv = RTMemRealloc(pPriv->paHandles, sizeof(pPriv->paHandles[0]) * (cNewMax + 1)); /* One extra for the wakeup event. */
591 if (!pv)
592 {
593 AssertMsgFailed(("RTMemRealloc failed for paHandles[%d]", cNewMax));
594 //break;
595 }
596 pPriv->paHandles = (PHANDLE)pv;
597
598 pv = RTMemRealloc(pPriv->paQueuedUrbs, sizeof(pPriv->paQueuedUrbs[0]) * cNewMax);
599 if (!pv)
600 {
601 AssertMsgFailed(("RTMemRealloc failed for paQueuedUrbs[%d]", cNewMax));
602 //break;
603 }
604 pPriv->paQueuedUrbs = (PQUEUED_URB *)pv;
605 pPriv->cAllocatedUrbs = cNewMax;
606 }
607
608 /* Copy the pending URBs over. */
609 for (unsigned i = 0; i < pPriv->cPendingUrbs; i++)
610 {
611 pPriv->paHandles[pPriv->cQueuedUrbs + i] = pPriv->aPendingUrbs[i]->overlapped.hEvent;
612 pPriv->paQueuedUrbs[pPriv->cQueuedUrbs + i] = pPriv->aPendingUrbs[i];
613 }
614 pPriv->cQueuedUrbs += pPriv->cPendingUrbs;
615 pPriv->cPendingUrbs = 0;
616 pPriv->paHandles[pPriv->cQueuedUrbs] = pPriv->hEventWakeup;
617 pPriv->paHandles[pPriv->cQueuedUrbs + 1] = INVALID_HANDLE_VALUE;
618
619 RTCritSectLeave(&pPriv->CritSect);
620 }
621
622 /*
623 * Wait/poll.
624 *
625 * ASSUMPTIONS:
626 * 1. The usbProxyWinUrbReap can not be run concurrently with each other
627 * so racing the cQueuedUrbs access/modification can not occur.
628 * 2. The usbProxyWinUrbReap can not be run concurrently with
629 * usbProxyWinUrbQueue so they can not race the pPriv->paHandles
630 * access/realloc.
631 */
632 unsigned cQueuedUrbs = ASMAtomicReadU32((volatile uint32_t *)&pPriv->cQueuedUrbs);
633 DWORD cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
634 PVUSBURB pUrb = NULL;
635 DWORD rc = WaitForMultipleObjects(cQueuedUrbs + 1, pPriv->paHandles, FALSE, cMilliesWait);
636
637 /* If the wakeup event fired return immediately. */
638 if (rc == WAIT_OBJECT_0 + cQueuedUrbs)
639 {
640 if (pPriv->cPendingUrbs)
641 goto again;
642 return NULL;
643 }
644
645 AssertCompile(WAIT_OBJECT_0 == 0);
646 if (/*rc >= WAIT_OBJECT_0 && */ rc < WAIT_OBJECT_0 + cQueuedUrbs)
647 {
648 RTCritSectEnter(&pPriv->CritSect);
649 unsigned iUrb = rc - WAIT_OBJECT_0;
650 PQUEUED_URB pQUrbWin = pPriv->paQueuedUrbs[iUrb];
651 pUrb = pQUrbWin->urb;
652
653 /*
654 * Remove it from the arrays.
655 */
656 cQueuedUrbs = --pPriv->cQueuedUrbs;
657 if (cQueuedUrbs != iUrb)
658 {
659 /* Move the array forward */
660 for (unsigned i=iUrb;i<cQueuedUrbs;i++)
661 {
662 pPriv->paHandles[i] = pPriv->paHandles[i+1];
663 pPriv->paQueuedUrbs[i] = pPriv->paQueuedUrbs[i+1];
664 }
665 }
666 pPriv->paHandles[cQueuedUrbs] = pPriv->hEventWakeup;
667 pPriv->paHandles[cQueuedUrbs + 1] = INVALID_HANDLE_VALUE;
668 pPriv->paQueuedUrbs[cQueuedUrbs] = NULL;
669 RTCritSectLeave(&pPriv->CritSect);
670 Assert(cQueuedUrbs == pPriv->cQueuedUrbs);
671
672 /*
673 * Update the urb.
674 */
675 pUrb->enmStatus = usbProxyWinStatusToVUsbStatus(pQUrbWin->urbwin.error);
676 pUrb->cbData = (uint32_t)pQUrbWin->urbwin.len;
677 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
678 {
679 for (unsigned i = 0; i < pUrb->cIsocPkts; ++i)
680 {
681 /* NB: Windows won't change the packet offsets, but the packets may
682 * be only partially filled or completely empty.
683 */
684 pUrb->aIsocPkts[i].enmStatus = usbProxyWinStatusToVUsbStatus(pQUrbWin->urbwin.aIsoPkts[i].stat);
685 pUrb->aIsocPkts[i].cb = pQUrbWin->urbwin.aIsoPkts[i].cb;
686 }
687 }
688 Log(("usbproxy: pUrb=%p (#%d) ep=%d cbData=%d status=%d cIsocPkts=%d ready\n",
689 pUrb, rc - WAIT_OBJECT_0, pQUrbWin->urb->EndPt, pQUrbWin->urb->cbData, pUrb->enmStatus, pUrb->cIsocPkts));
690
691 /* free the urb queuing structure */
692 if (pQUrbWin->overlapped.hEvent != INVALID_HANDLE_VALUE)
693 {
694 CloseHandle(pQUrbWin->overlapped.hEvent);
695 pQUrbWin->overlapped.hEvent = INVALID_HANDLE_VALUE;
696 }
697 RTMemFree(pQUrbWin);
698 }
699 else if ( rc == WAIT_FAILED
700 || (rc >= WAIT_ABANDONED_0 && rc < WAIT_ABANDONED_0 + cQueuedUrbs))
701 AssertMsgFailed(("USB: WaitForMultipleObjects %d objects failed with rc=%d and last error %d\n", cQueuedUrbs, rc, GetLastError()));
702
703 return pUrb;
704}
705
706
707/**
708 * Cancels an in-flight URB.
709 *
710 * The URB requires reaping, so we don't change its state.
711 *
712 * @remark There isn't a way to cancel a specific URB on Windows.
713 * on darwin. The interface only supports the aborting of
714 * all URBs pending on an endpoint. Luckily that is usually
715 * exactly what the guest wants to do.
716 */
717static DECLCALLBACK(int) usbProxyWinUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
718{
719 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
720 PQUEUED_URB pQUrbWin = (PQUEUED_URB)pUrb->Dev.pvPrivate;
721 USBSUP_CLEAR_ENDPOINT in;
722 DWORD cbReturned;
723
724 AssertPtrReturn(pQUrbWin, VERR_INVALID_PARAMETER);
725
726 in.bEndpoint = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0);
727 Log(("usbproxy: Cancel urb %p, endpoint %x\n", pUrb, in.bEndpoint));
728
729 cbReturned = 0;
730 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
731 return VINF_SUCCESS;
732
733 DWORD dwErr = GetLastError();
734 if ( dwErr == ERROR_INVALID_HANDLE_STATE
735 || dwErr == ERROR_BAD_COMMAND)
736 {
737 Log(("usbproxy: device %x unplugged!!\n", pPriv->hDev));
738 pProxyDev->fDetached = true;
739 return VINF_SUCCESS; /* Fake success and deal with the unplugged device elsewhere. */
740 }
741
742 AssertMsgFailed(("lastErr=%ld\n", dwErr));
743 return RTErrConvertFromWin32(dwErr);
744}
745
746static DECLCALLBACK(int) usbProxyWinWakeup(PUSBPROXYDEV pProxyDev)
747{
748 PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32);
749
750 SetEvent(pPriv->hEventWakeup);
751 return VINF_SUCCESS;
752}
753
754/**
755 * The Win32 USB Proxy Backend.
756 */
757extern const USBPROXYBACK g_USBProxyDeviceHost =
758{
759 /* pszName */
760 "host",
761 /* cbBackend */
762 sizeof(PRIV_USBW32),
763 usbProxyWinOpen,
764 NULL,
765 usbProxyWinClose,
766 usbProxyWinReset,
767 usbProxyWinSetConfig,
768 usbProxyWinClaimInterface,
769 usbProxyWinReleaseInterface,
770 usbProxyWinSetInterface,
771 usbProxyWinClearHaltedEndPt,
772 usbProxyWinUrbQueue,
773 usbProxyWinUrbCancel,
774 usbProxyWinUrbReap,
775 usbProxyWinWakeup,
776 0
777};
778
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