VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 17 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use