VirtualBox

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

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

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

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