VirtualBox

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

Last change on this file since 48749 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.8 KB
Line 
1/* $Id: USBProxyDevice-win.cpp 44528 2013-02-04 14:27:54Z 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-2011 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 <iprt/asm.h>
39#include "../USBProxyDevice.h"
40#include <VBox/usblib.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=%u\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", rc));
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 * Allocate and initialize a URB queue structure.
445 */
446 /** @todo pool these */
447 PQUEUED_URB pQUrbWin = (PQUEUED_URB)RTMemAllocZ(sizeof(QUEUED_URB));
448 if (!pQUrbWin)
449 return false;
450
451 switch (pUrb->enmType)
452 {
453 case VUSBXFERTYPE_CTRL: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_CTRL; break; /* you won't ever see these */
454 case VUSBXFERTYPE_ISOC: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_ISOC;
455 pQUrbWin->urbwin.numIsoPkts = pUrb->cIsocPkts;
456 for (unsigned i = 0; i < pUrb->cIsocPkts; ++i)
457 {
458 pQUrbWin->urbwin.aIsoPkts[i].cb = pUrb->aIsocPkts[i].cb;
459 pQUrbWin->urbwin.aIsoPkts[i].off = pUrb->aIsocPkts[i].off;
460 pQUrbWin->urbwin.aIsoPkts[i].stat = USBSUP_XFER_OK;
461 }
462 break;
463 case VUSBXFERTYPE_BULK: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_BULK; break;
464 case VUSBXFERTYPE_INTR: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_INTR; break;
465 case VUSBXFERTYPE_MSG: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_MSG; break;
466 default:
467 AssertMsgFailed(("Invalid type %d\n", pUrb->enmType));
468 return false;
469 }
470
471 switch (pUrb->enmDir)
472 {
473 case VUSBDIRECTION_SETUP:
474 AssertFailed();
475 pQUrbWin->urbwin.dir = USBSUP_DIRECTION_SETUP;
476 break;
477 case VUSBDIRECTION_IN:
478 pQUrbWin->urbwin.dir = USBSUP_DIRECTION_IN;
479 break;
480 case VUSBDIRECTION_OUT:
481 pQUrbWin->urbwin.dir = USBSUP_DIRECTION_OUT;
482 break;
483 default:
484 AssertMsgFailed(("Invalid direction %d\n", pUrb->enmDir));
485 return false;
486 }
487
488 Log(("usbproxy: Queue URB %p ep=%d cbData=%d abData=%p cIsocPkts=%d\n", pUrb, pUrb->EndPt, pUrb->cbData, pUrb->abData, pUrb->cIsocPkts));
489
490 pQUrbWin->urb = pUrb;
491 pQUrbWin->urbwin.ep = pUrb->EndPt;
492 pQUrbWin->urbwin.len = pUrb->cbData;
493 pQUrbWin->urbwin.buf = pUrb->abData;
494 pQUrbWin->urbwin.error = USBSUP_XFER_OK;
495 pQUrbWin->urbwin.flags = USBSUP_FLAG_NONE;
496 if (pUrb->enmDir == VUSBDIRECTION_IN && !pUrb->fShortNotOk)
497 pQUrbWin->urbwin.flags = USBSUP_FLAG_SHORT_OK;
498
499 pQUrbWin->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
500 if ( pQUrbWin->overlapped.hEvent != INVALID_HANDLE_VALUE
501 && pPriv->cPendingUrbs < RT_ELEMENTS(pPriv->aPendingUrbs))
502 {
503 while (!pPriv->fThreadAsyncIoActive)
504 RTThreadSleep(1);
505
506 RTCritSectEnter(&pPriv->CritSect);
507 do
508 {
509 /* Ensure we've got sufficient space in the arrays.
510 * Do it inside the lock to ensure we do not concur
511 * with the usbProxyWinAsyncIoThread */
512 if (pPriv->cQueuedUrbs + 1 > pPriv->cAllocatedUrbs)
513 {
514 unsigned cNewMax = pPriv->cAllocatedUrbs + 32;
515 void *pv = RTMemRealloc(pPriv->paHandles, sizeof(pPriv->paHandles[0]) * cNewMax);
516 if (!pv)
517 {
518 AssertMsgFailed(("RTMemRealloc failed for paHandles[%d]", cNewMax));
519 break;
520 }
521 pPriv->paHandles = (PHANDLE)pv;
522
523 pv = RTMemRealloc(pPriv->paQueuedUrbs, sizeof(pPriv->paQueuedUrbs[0]) * cNewMax);
524 if (!pv)
525 {
526 AssertMsgFailed(("RTMemRealloc failed for paQueuedUrbs[%d]", cNewMax));
527 break;
528 }
529 pPriv->paQueuedUrbs = (PQUEUED_URB *)pv;
530 pPriv->cAllocatedUrbs = cNewMax;
531 }
532
533 pUrb->Dev.pvPrivate = pQUrbWin;
534 pPriv->aPendingUrbs[pPriv->cPendingUrbs] = pQUrbWin;
535 pPriv->cPendingUrbs++;
536 RTCritSectLeave(&pPriv->CritSect);
537 SetEvent(pPriv->hEventAsyncIo);
538 return true;
539 } while (0);
540
541 RTCritSectLeave(&pPriv->CritSect);
542 }
543#ifdef DEBUG_misha
544 else
545 {
546 AssertMsgFailed(("FAILED!!, hEvent(0x%p), cPendingUrbs(%d)\n", pQUrbWin->overlapped.hEvent, pPriv->cPendingUrbs));
547 }
548#endif
549
550 Assert(pQUrbWin->overlapped.hEvent == INVALID_HANDLE_VALUE);
551 RTMemFree(pQUrbWin);
552 return false;
553}
554
555/**
556 * Convert Windows proxy URB status to VUSB status.
557 *
558 * @returns VUSB status constant.
559 * @param win_status Windows USB proxy status constant.
560 */
561static VUSBSTATUS usbProxyWinStatusToVUsbStatus(USBSUP_ERROR win_status)
562{
563 VUSBSTATUS vusb_status;
564
565 switch (win_status)
566 {
567 case USBSUP_XFER_OK: vusb_status = VUSBSTATUS_OK; break;
568 case USBSUP_XFER_STALL: vusb_status = VUSBSTATUS_STALL; break;
569 case USBSUP_XFER_DNR: vusb_status = VUSBSTATUS_DNR; break;
570 case USBSUP_XFER_CRC: vusb_status = VUSBSTATUS_CRC; break;
571 case USBSUP_XFER_NAC: vusb_status = VUSBSTATUS_NOT_ACCESSED; break;
572 case USBSUP_XFER_UNDERRUN: vusb_status = VUSBSTATUS_DATA_UNDERRUN; break;
573 case USBSUP_XFER_OVERRUN: vusb_status = VUSBSTATUS_DATA_OVERRUN; break;
574 default:
575 AssertMsgFailed(("USB: Invalid error %d\n", win_status));
576 vusb_status = VUSBSTATUS_DNR;
577 break;
578 }
579 return vusb_status;
580}
581
582/**
583 * Reap URBs in-flight on a device.
584 *
585 * @returns Pointer to a completed URB.
586 * @returns NULL if no URB was completed.
587 * @param pProxyDev The device.
588 * @param cMillies Number of milliseconds to wait. Use 0 to not
589 * wait at all.
590 */
591static PVUSBURB usbProxyWinUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
592{
593 PPRIV_USBW32 pPriv = (PPRIV_USBW32)pProxyDev->Backend.pv;
594 AssertReturn(pPriv, NULL);
595
596 /*
597 * There are some unnecessary calls, just return immediately or
598 * WaitForMultipleObjects will fail.
599 */
600 if (pPriv->cQueuedUrbs <= 0)
601 {
602 if (cMillies != 0)
603 {
604 /* Wait for the URBs to be queued if there are some pending */
605 while (pPriv->cPendingUrbs)
606 RTThreadSleep(1);
607 if (pPriv->cQueuedUrbs <= 0)
608 return NULL;
609 }
610 else
611 return NULL;
612 }
613
614 /*
615 * Wait/poll.
616 *
617 * ASSUMPTIONS:
618 * 1. The usbProxyWinUrbReap can not be run concurrently with each other
619 * so racing the cQueuedUrbs access/modification can not occur.
620 * 2. The usbProxyWinUrbReap can not be run concurrently with
621 * usbProxyWinUrbQueue so they can not race the pPriv->paHandles
622 * access/realloc.
623 */
624 unsigned cQueuedUrbs = ASMAtomicReadU32((volatile uint32_t *)&pPriv->cQueuedUrbs);
625 PVUSBURB pUrb = NULL;
626 DWORD rc = WaitForMultipleObjects(cQueuedUrbs, pPriv->paHandles, FALSE, cMillies);
627 if (rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0 + cQueuedUrbs)
628 {
629 RTCritSectEnter(&pPriv->CritSect);
630 unsigned iUrb = rc - WAIT_OBJECT_0;
631 PQUEUED_URB pQUrbWin = pPriv->paQueuedUrbs[iUrb];
632 pUrb = pQUrbWin->urb;
633
634 /*
635 * Remove it from the arrays.
636 */
637 cQueuedUrbs = --pPriv->cQueuedUrbs;
638 if (cQueuedUrbs != iUrb)
639 {
640 /* Move the array forward */
641 for (unsigned i=iUrb;i<cQueuedUrbs;i++)
642 {
643 pPriv->paHandles[i] = pPriv->paHandles[i+1];
644 pPriv->paQueuedUrbs[i] = pPriv->paQueuedUrbs[i+1];
645 }
646 }
647 pPriv->paHandles[cQueuedUrbs] = INVALID_HANDLE_VALUE;
648 pPriv->paQueuedUrbs[cQueuedUrbs] = NULL;
649 RTCritSectLeave(&pPriv->CritSect);
650 Assert(cQueuedUrbs == pPriv->cQueuedUrbs);
651
652 /*
653 * Update the urb.
654 */
655 pUrb->enmStatus = usbProxyWinStatusToVUsbStatus(pQUrbWin->urbwin.error);
656 pUrb->cbData = (uint32_t)pQUrbWin->urbwin.len;
657 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
658 {
659 for (unsigned i = 0; i < pUrb->cIsocPkts; ++i)
660 {
661 /* NB: Windows won't change the packet offsets, but the packets may
662 * be only partially filled or completely empty.
663 */
664 pUrb->aIsocPkts[i].enmStatus = usbProxyWinStatusToVUsbStatus(pQUrbWin->urbwin.aIsoPkts[i].stat);
665 pUrb->aIsocPkts[i].cb = pQUrbWin->urbwin.aIsoPkts[i].cb;
666 }
667 }
668 Log(("usbproxy: pUrb=%p (#%d) ep=%d cbData=%d status=%d cIsocPkts=%d ready\n",
669 pUrb, rc - WAIT_OBJECT_0, pQUrbWin->urb->EndPt, pQUrbWin->urb->cbData, pUrb->enmStatus, pUrb->cIsocPkts));
670
671 /* free the urb queuing structure */
672 if (pQUrbWin->overlapped.hEvent != INVALID_HANDLE_VALUE)
673 {
674 CloseHandle(pQUrbWin->overlapped.hEvent);
675 pQUrbWin->overlapped.hEvent = INVALID_HANDLE_VALUE;
676 }
677 RTMemFree(pQUrbWin);
678 }
679 else if ( rc == WAIT_FAILED
680 || (rc >= WAIT_ABANDONED_0 && rc < WAIT_ABANDONED_0 + cQueuedUrbs))
681 AssertMsgFailed(("USB: WaitForMultipleObjects %d objects failed with rc=%d and last error %d\n", cQueuedUrbs, rc, GetLastError()));
682
683 return pUrb;
684}
685
686/* Thread to handle async URB queueing. */
687static DECLCALLBACK(int) usbProxyWinAsyncIoThread(RTTHREAD ThreadSelf, void *lpParameter)
688{
689 PPRIV_USBW32 pPriv = (PPRIV_USBW32)lpParameter;
690 HANDLE hEventWait[2];
691
692 hEventWait[0] = pPriv->hEventAsyncIo;
693 hEventWait[1] = pPriv->hEventAsyncTerm;
694
695 Log(("usbProxyWinAsyncIoThread: start\n"));
696 pPriv->fThreadAsyncIoActive = true;
697
698 while (true)
699 {
700 DWORD ret = WaitForMultipleObjects(2, hEventWait, FALSE /* wait for any */, INFINITE);
701
702 if (ret == WAIT_OBJECT_0)
703 {
704 /*
705 * Submit pending URBs.
706 */
707 RTCritSectEnter(&pPriv->CritSect);
708
709 for (unsigned i = 0; i < pPriv->cPendingUrbs; i++)
710 {
711 PQUEUED_URB pQUrbWin = pPriv->aPendingUrbs[i];
712
713 Assert(pQUrbWin);
714 if (pQUrbWin)
715 {
716 if ( DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_SEND_URB,
717 &pQUrbWin->urbwin, sizeof(pQUrbWin->urbwin),
718 &pQUrbWin->urbwin, sizeof(pQUrbWin->urbwin),
719 &pQUrbWin->cbReturned, &pQUrbWin->overlapped)
720 || GetLastError() == ERROR_IO_PENDING)
721 {
722 /* insert into the queue */
723 unsigned j = pPriv->cQueuedUrbs;
724 pPriv->paQueuedUrbs[j] = pQUrbWin;
725 pPriv->paHandles[j] = pQUrbWin->overlapped.hEvent;
726 /* do an atomic increment to allow usbProxyWinUrbReap thread get it outside a lock,
727 * being sure that pPriv->paHandles contains cQueuedUrbs valid handles */
728 ASMAtomicIncU32((uint32_t volatile *)&pPriv->cQueuedUrbs);
729 }
730 else
731 {
732 DWORD dwErr = GetLastError();
733 if ( dwErr == ERROR_INVALID_HANDLE_STATE
734 || dwErr == ERROR_BAD_COMMAND)
735 {
736 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pQUrbWin->urb->pUsbIns, PUSBPROXYDEV);
737 Log(("usbproxy: device %p unplugged!!\n", pPriv->hDev));
738 pProxyDev->fDetached = true;
739 }
740 else
741 AssertMsgFailed(("dwErr=%X urbwin.error=%d (submit urb)\n", dwErr, pQUrbWin->urbwin.error));
742 CloseHandle(pQUrbWin->overlapped.hEvent);
743 pQUrbWin->overlapped.hEvent = INVALID_HANDLE_VALUE;
744 }
745 }
746 pPriv->aPendingUrbs[i] = 0;
747 }
748 pPriv->cPendingUrbs = 0;
749 RTCritSectLeave(&pPriv->CritSect);
750 }
751 else
752 if (ret == WAIT_OBJECT_0 + 1)
753 {
754 Log(("usbProxyWinAsyncIoThread: terminating\n"));
755 CloseHandle(hEventWait[1]);
756 break;
757 }
758 else
759 {
760 Log(("usbProxyWinAsyncIoThread: unexpected return code %x\n", ret));
761 break;
762 }
763 }
764 return 0;
765}
766
767/**
768 * Cancels an in-flight URB.
769 *
770 * The URB requires reaping, so we don't change its state.
771 *
772 * @remark There isn't a way to cancel a specific URB on Windows.
773 * on darwin. The interface only supports the aborting of
774 * all URBs pending on an endpoint. Luckily that is usually
775 * exactly what the guest wants to do.
776 */
777static void usbProxyWinUrbCancel(PVUSBURB pUrb)
778{
779 PUSBPROXYDEV pProxyDev = PDMINS_2_DATA(pUrb->pUsbIns, PUSBPROXYDEV);
780 PPRIV_USBW32 pPriv = (PPRIV_USBW32)pProxyDev->Backend.pv;
781 PQUEUED_URB pQUrbWin = (PQUEUED_URB)pUrb->Dev.pvPrivate;
782 int rc;
783 USBSUP_CLEAR_ENDPOINT in;
784 DWORD cbReturned;
785 Assert(pQUrbWin);
786
787 in.bEndpoint = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0);
788 Log(("Cancel urb %p, endpoint %x\n", pUrb, in.bEndpoint));
789
790 cbReturned = 0;
791 if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
792 return;
793
794 rc = GetLastError();
795 if ( rc == ERROR_INVALID_HANDLE_STATE
796 || rc == ERROR_BAD_COMMAND)
797 {
798 Log(("usbproxy: device %x unplugged!!\n", pPriv->hDev));
799 pProxyDev->fDetached = true;
800 }
801 else
802 AssertMsgFailed(("lasterr=%d\n", rc));
803}
804
805
806/**
807 * The Win32 USB Proxy Backend.
808 */
809extern const USBPROXYBACK g_USBProxyDeviceHost =
810{
811 "host",
812 usbProxyWinOpen,
813 NULL,
814 usbProxyWinClose,
815 usbProxyWinReset,
816 usbProxyWinSetConfig,
817 usbProxyWinClaimInterface,
818 usbProxyWinReleaseInterface,
819 usbProxyWinSetInterface,
820 usbProxyWinClearHaltedEndPt,
821 usbProxyWinUrbQueue,
822 usbProxyWinUrbCancel,
823 usbProxyWinUrbReap,
824 0
825};
826
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