VirtualBox

source: vbox/trunk/src/VBox/Main/USBProxyService.cpp@ 7829

Last change on this file since 7829 was 7207, checked in by vboxsync, 17 years ago

Main: Reworked enums to avoid 1) weird duplication of enum name when referring to enum values in cross-platform code; 2) possible clashes on Win32 due to putting identifiers like Paused or Disabled to the global namespace (via C enums). In the new style, enums are used like this: a) USBDeviceState_T v = USBDeviceState_Busy from cross-platform non-Qt code; b) KUSBDeviceState v = KUSBDeviceState_Busy from Qt code; c) USBDeviceState v = USBDeviceState_Busy from plain Win32 and d) PRUInt32 USBDeviceState v = USBDeviceState::Busy from plain XPCOM.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.3 KB
Line 
1/** @file
2 * VirtualBox USB Proxy Service (base) class.
3 */
4
5/*
6 * Copyright (C) 2006-2007 innotek GmbH
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 */
16
17#include "USBProxyService.h"
18#include "Logging.h"
19
20#include <VBox/err.h>
21#include <iprt/asm.h>
22#include <iprt/semaphore.h>
23
24
25
26/** @todo add the required locking. */
27
28/**
29 * Initialize data members.
30 */
31USBProxyService::USBProxyService (Host *aHost)
32 : mHost (aHost), mThread (NIL_RTTHREAD), mTerminate (false), mDevices (), mLastError (VINF_SUCCESS)
33{
34 LogFlowThisFunc (("aHost=%p\n", aHost));
35}
36
37
38/**
39 * Empty destructor.
40 */
41USBProxyService::~USBProxyService()
42{
43 LogFlowThisFunc (("\n"));
44 Assert (mThread == NIL_RTTHREAD);
45 mDevices.clear();
46 mTerminate = true;
47 mHost = NULL;
48}
49
50
51bool USBProxyService::isActive (void)
52{
53 return mThread != NIL_RTTHREAD;
54}
55
56
57int USBProxyService::getLastError (void)
58{
59 return mLastError;
60}
61
62
63int USBProxyService::start (void)
64{
65 int rc = VINF_SUCCESS;
66 if (mThread == NIL_RTTHREAD)
67 {
68 /*
69 * Force update before starting the poller thread.
70 */
71 wait (0);
72 processChanges ();
73
74 /*
75 * Create the poller thread which will look for changes.
76 */
77 mTerminate = false;
78 rc = RTThreadCreate (&mThread, USBProxyService::serviceThread, this,
79 0, RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "USBPROXY");
80 AssertRC (rc);
81 if (VBOX_SUCCESS (rc))
82 LogFlowThisFunc (("started mThread=%RTthrd\n", mThread));
83 else
84 {
85 mThread = NIL_RTTHREAD;
86 mLastError = rc;
87 }
88 }
89 else
90 LogFlowThisFunc (("already running, mThread=%RTthrd\n", mThread));
91 return rc;
92}
93
94
95int USBProxyService::stop (void)
96{
97 int rc = VINF_SUCCESS;
98 if (mThread != NIL_RTTHREAD)
99 {
100 /*
101 * Mark the thread for termination and kick it.
102 */
103 ASMAtomicXchgSize (&mTerminate, true);
104 rc = interruptWait();
105 AssertRC (rc);
106
107 /*
108 * Wait for the thread to finish and then update the state.
109 */
110 rc = RTThreadWait (mThread, 60000, NULL);
111 if (rc == VERR_INVALID_HANDLE)
112 rc = VINF_SUCCESS;
113 if (VBOX_SUCCESS (rc))
114 {
115 LogFlowThisFunc (("stopped mThread=%RTthrd\n", mThread));
116 mThread = NIL_RTTHREAD;
117 mTerminate = false;
118 }
119 else
120 {
121 AssertRC (rc);
122 mLastError = rc;
123 }
124 }
125 else
126 LogFlowThisFunc (("not active\n"));
127
128 return rc;
129}
130
131
132/**
133 * Sort a list of USB devices.
134 *
135 * @returns Pointer to the head of the sorted doubly linked list.
136 * @param aDevices Head pointer (can be both singly and doubly linked list).
137 */
138static PUSBDEVICE sortDevices (PUSBDEVICE pDevices)
139{
140 PUSBDEVICE pHead = NULL;
141 PUSBDEVICE pTail = NULL;
142 while (pDevices)
143 {
144 /* unlink head */
145 PUSBDEVICE pDev = pDevices;
146 pDevices = pDev->pNext;
147 if (pDevices)
148 pDevices->pPrev = NULL;
149
150 /* find location. */
151 PUSBDEVICE pCur = pTail;
152 while ( pCur
153 && HostUSBDevice::compare (pCur, pDev) > 0)
154 pCur = pCur->pPrev;
155
156 /* insert (after pCur) */
157 pDev->pPrev = pCur;
158 if (pCur)
159 {
160 pDev->pNext = pCur->pNext;
161 pCur->pNext = pDev;
162 if (pDev->pNext)
163 pDev->pNext->pPrev = pDev;
164 else
165 pTail = pDev;
166 }
167 else
168 {
169 pDev->pNext = pHead;
170 if (pHead)
171 pHead->pPrev = pDev;
172 else
173 pTail = pDev;
174 pHead = pDev;
175 }
176 }
177
178 return pHead;
179}
180
181
182void USBProxyService::processChanges (void)
183{
184 LogFlowThisFunc (("\n"));
185
186 /*
187 * Get the sorted list of USB devices.
188 */
189 PUSBDEVICE pDevices = getDevices();
190 if (pDevices)
191 {
192 pDevices = sortDevices (pDevices);
193
194 /*
195 * We need to lock the host object for writing because
196 * a) the subsequent code may call Host methods that require a write
197 * lock
198 * b) we will lock HostUSBDevice objects below and want to make sure
199 * the lock order is always the same (Host, HostUSBDevice, as
200 * expected by Host) to avoid cross-deadlocks
201 */
202 AutoLock hostLock (mHost);
203
204 /*
205 * Compare previous list with the previous list of devices
206 * and merge in any changes while notifying Host.
207 */
208 HostUSBDeviceList::iterator It = this->mDevices.begin();
209 while ( It != mDevices.end()
210 || pDevices)
211 {
212 ComObjPtr <HostUSBDevice> DevPtr;
213
214 if (It != mDevices.end())
215 DevPtr = *It;
216
217 /*
218 * Assert that the object is still alive (we still reference it in
219 * the collection and we're the only one who calls uninit() on it
220 */
221 HostUSBDevice::AutoCaller devCaller (DevPtr.isNull() ? NULL : DevPtr);
222 AssertComRC (devCaller.rc());
223
224 /*
225 * Lock the device object since we will read/write it's
226 * properties. All Host callbacks also imply the object is locked.
227 */
228 AutoLock devLock (DevPtr.isNull() ? NULL : DevPtr);
229
230 /*
231 * Compare.
232 */
233 int iDiff;
234 if (DevPtr.isNull())
235 iDiff = 1;
236 else
237 {
238 if (!pDevices)
239 iDiff = -1;
240 else
241 iDiff = DevPtr->compare (pDevices);
242 }
243 if (!iDiff)
244 {
245 /*
246 * The device still there, update the state and move on. The PUSBDEVICE
247 * structure is eaten by updateDeviceState / HostUSBDevice::updateState().
248 */
249 PUSBDEVICE pCur = pDevices;
250 pDevices = pDevices->pNext;
251 pCur->pPrev = pCur->pNext = NULL;
252
253 if (updateDeviceState (DevPtr, pCur))
254 {
255 Log (("USBProxyService::processChanges: state change %p:{.idVendor=%#06x, .idProduct=%#06x, .pszProduct=\"%s\", .pszManufacturer=\"%s\"} state=%d%s\n",
256 (HostUSBDevice *)DevPtr, pCur->idVendor, pCur->idProduct, pCur->pszProduct, pCur->pszManufacturer, DevPtr->state(), DevPtr->isStatePending() ? " (pending async op)" : ""));
257 mHost->onUSBDeviceStateChanged (DevPtr);
258 }
259 It++;
260 }
261 else
262 {
263 if (iDiff > 0)
264 {
265 /*
266 * Head of pDevices was attached.
267 */
268 PUSBDEVICE pNew = pDevices;
269 pDevices = pDevices->pNext;
270 pNew->pPrev = pNew->pNext = NULL;
271
272 ComObjPtr <HostUSBDevice> NewObj;
273 NewObj.createObject();
274 NewObj->init (pNew, this);
275 Log (("USBProxyService::processChanges: attached %p/%p:{.idVendor=%#06x, .idProduct=%#06x, .pszProduct=\"%s\", .pszManufacturer=\"%s\"}\n",
276 (HostUSBDevice *)NewObj, pNew, pNew->idVendor, pNew->idProduct, pNew->pszProduct, pNew->pszManufacturer));
277 deviceAdded (NewObj, pNew);
278
279 /* Not really necessary to lock here, but make Assert checks happy. */
280 AutoLock newDevLock (NewObj);
281
282 mDevices.insert (It, NewObj);
283 mHost->onUSBDeviceAttached (NewObj);
284 }
285 else
286 {
287 /*
288 * DevPtr was detached, unless there is a pending async request.
289 * Check if the async request timed out before making a decision.
290 */
291 if (DevPtr->isStatePending())
292 DevPtr->checkForAsyncTimeout();
293 if (DevPtr->isStatePending())
294 {
295 if (DevPtr->pendingStateEx() == HostUSBDevice::kDetachingPendingDetach)
296 DevPtr->setLogicalReconnect (HostUSBDevice::kDetachingPendingAttach);
297 else if (DevPtr->pendingStateEx() == HostUSBDevice::kDetachingPendingDetachFilters)
298 DevPtr->setLogicalReconnect (HostUSBDevice::kDetachingPendingAttachFilters);
299 It++;
300 Log (("USBProxyService::processChanges: detached but pending %d/%d %p\n",
301 DevPtr->pendingState(), DevPtr->pendingStateEx(), (HostUSBDevice *)DevPtr));
302 }
303 else
304 {
305 It = mDevices.erase (It);
306 deviceRemoved (DevPtr);
307 mHost->onUSBDeviceDetached (DevPtr);
308 Log (("USBProxyService::processChanges: detached %p\n",
309 (HostUSBDevice *)DevPtr)); /** @todo add details .*/
310
311 /* from now on, the object is no more valid,
312 * uninitialize to avoid abuse */
313 devCaller.release();
314 DevPtr->uninit();
315 }
316 }
317 }
318 } /* while */
319 }
320 else
321 {
322 /* we need to lock the host object for writing because
323 * a) the subsequent code may call Host methods that require a write
324 * lock
325 * b) we will lock HostUSBDevice objects below and want to make sure
326 * the lock order is always the same (Host, HostUSBDevice, as
327 * expected by Host) to avoid cross-deadlocks */
328
329 AutoLock hostLock (mHost);
330
331 /* All devices were detached */
332 HostUSBDeviceList::iterator It = this->mDevices.begin();
333 while (It != mDevices.end())
334 {
335 ComObjPtr <HostUSBDevice> DevPtr = *It;
336
337 /* assert that the object is still alive (we still reference it in
338 * the collection and we're the only one who calls uninit() on it */
339 HostUSBDevice::AutoCaller devCaller (DevPtr);
340 AssertComRC (devCaller.rc());
341
342 AutoLock devLock (DevPtr);
343
344 /*
345 * DevPtr was detached.
346 */
347 It = mDevices.erase (It);
348 mHost->onUSBDeviceDetached (DevPtr);
349 Log (("USBProxyService::processChanges: detached %p\n",
350 (HostUSBDevice *)DevPtr)); /** @todo add details .*/
351
352 /* from now on, the object is no more valid,
353 * uninitialize to avoid abuse */
354 devCaller.release();
355 DevPtr->uninit();
356 }
357 }
358
359 LogFlowThisFunc (("returns void\n"));
360}
361
362
363/*static*/ DECLCALLBACK (int) USBProxyService::serviceThread (RTTHREAD Thread, void *pvUser)
364{
365 USBProxyService *pThis = (USBProxyService *)pvUser;
366 LogFlowFunc (("pThis=%p\n", pThis));
367 pThis->serviceThreadInit();
368
369 /*
370 * Processing loop.
371 */
372 for (;;)
373 {
374 pThis->wait (RT_INDEFINITE_WAIT);
375 if (pThis->mTerminate)
376 break;
377 pThis->processChanges();
378 }
379
380 pThis->serviceThreadTerm();
381 LogFlowFunc (("returns VINF_SUCCESS\n"));
382 return VINF_SUCCESS;
383}
384
385
386/*static*/ void USBProxyService::freeInterfaceMembers (PUSBINTERFACE pIf, unsigned cIfs)
387{
388 while (cIfs-- > 0)
389 {
390 RTMemFree (pIf->paEndpoints);
391 pIf->paEndpoints = NULL;
392 RTStrFree ((char *)pIf->pszDriver);
393 pIf->pszDriver = NULL;
394 RTStrFree ((char *)pIf->pszInterface);
395 pIf->pszInterface = NULL;
396
397 freeInterfaceMembers(pIf->paAlts, pIf->cAlts);
398 RTMemFree(pIf->paAlts);
399 pIf->paAlts = NULL;
400 pIf->cAlts = 0;
401
402 /* next */
403 pIf++;
404 }
405}
406
407/*static*/ void USBProxyService::freeDeviceMembers (PUSBDEVICE pDevice)
408{
409 PUSBCONFIG pCfg = pDevice->paConfigurations;
410 unsigned cCfgs = pDevice->bNumConfigurations;
411 while (cCfgs-- > 0)
412 {
413 freeInterfaceMembers (pCfg->paInterfaces, pCfg->bNumInterfaces);
414 RTMemFree (pCfg->paInterfaces);
415 pCfg->paInterfaces = NULL;
416 pCfg->bNumInterfaces = 0;
417
418 RTStrFree ((char *)pCfg->pszConfiguration);
419 pCfg->pszConfiguration = NULL;
420
421 /* next */
422 pCfg++;
423 }
424 RTMemFree (pDevice->paConfigurations);
425 pDevice->paConfigurations = NULL;
426
427 RTStrFree ((char *)pDevice->pszManufacturer);
428 pDevice->pszManufacturer = NULL;
429 RTStrFree ((char *)pDevice->pszProduct);
430 pDevice->pszProduct = NULL;
431 RTStrFree ((char *)pDevice->pszSerialNumber);
432 pDevice->pszSerialNumber = NULL;
433
434 RTStrFree ((char *)pDevice->pszAddress);
435 pDevice->pszAddress = NULL;
436}
437
438/*static*/ void USBProxyService::freeDevice (PUSBDEVICE pDevice)
439{
440 freeDeviceMembers (pDevice);
441 RTMemFree (pDevice);
442}
443
444
445/* static */ uint64_t USBProxyService::calcSerialHash (const char *aSerial)
446{
447 if (!aSerial)
448 aSerial = "";
449
450 register const uint8_t *pu8 = (const uint8_t *)aSerial;
451 register uint64_t u64 = 14695981039346656037ULL;
452 for (;;)
453 {
454 register uint8_t u8 = *pu8;
455 if (!u8)
456 break;
457 u64 = (u64 * 1099511628211ULL) ^ u8;
458 pu8++;
459 }
460
461 return u64;
462}
463
464
465#ifdef VBOX_WITH_USBFILTER
466/*static*/ void USBProxyService::initFilterFromDevice (PUSBFILTER aFilter, HostUSBDevice *aDevice)
467{
468 PCUSBDEVICE pDev = aDevice->mUsb;
469 int vrc;
470
471 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_VENDOR_ID, pDev->idVendor, true); AssertRC(vrc);
472 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_PRODUCT_ID, pDev->idProduct, true); AssertRC(vrc);
473 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_REV, pDev->bcdDevice, true); AssertRC(vrc);
474 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_CLASS, pDev->bDeviceClass, true); AssertRC(vrc);
475 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_SUB_CLASS, pDev->bDeviceSubClass, true); AssertRC(vrc);
476 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_PROTOCOL, pDev->bDeviceProtocol, true); AssertRC(vrc);
477 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_PORT, pDev->bPort, true); AssertRC(vrc);
478 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_BUS, pDev->bBus, false); AssertRC(vrc); /* not available on darwin yet... */
479 if (pDev->pszSerialNumber)
480 {
481 vrc = USBFilterSetStringExact (aFilter, USBFILTERIDX_SERIAL_NUMBER_STR, pDev->pszSerialNumber, true);
482 AssertRC (vrc);
483 }
484 if (pDev->pszProduct)
485 {
486 vrc = USBFilterSetStringExact (aFilter, USBFILTERIDX_PRODUCT_STR, pDev->pszProduct, true);
487 AssertRC (vrc);
488 }
489 if (pDev->pszManufacturer)
490 {
491 vrc = USBFilterSetStringExact (aFilter, USBFILTERIDX_MANUFACTURER_STR, pDev->pszManufacturer, true);
492 AssertRC (vrc);
493 }
494}
495#endif /* VBOX_WITH_USBFILTER */
496
497
498bool USBProxyService::updateDeviceStateFake (HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice)
499{
500 AssertReturn (aDevice, false);
501 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
502
503 if (aDevice->isStatePending())
504 {
505 switch (aDevice->pendingStateEx())
506 {
507 case HostUSBDevice::kNothingPending:
508 switch (aDevice->pendingState())
509 {
510 /* @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used anywhere in the proxy code; it's
511 * quite logical because the proxy doesn't know anything about guest VMs. We use HELD_BY_PROXY
512 * instead -- it is sufficient and is what Main expects. */
513 case USBDeviceState_Captured: aUSBDevice->enmState = USBDEVICESTATE_HELD_BY_PROXY; break;
514 case USBDeviceState_Held: aUSBDevice->enmState = USBDEVICESTATE_HELD_BY_PROXY; break;
515 case USBDeviceState_Available: aUSBDevice->enmState = USBDEVICESTATE_UNUSED; break;
516 case USBDeviceState_Unavailable: aUSBDevice->enmState = USBDEVICESTATE_USED_BY_HOST; break;
517 case USBDeviceState_Busy: aUSBDevice->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; break;
518 default:
519 AssertMsgFailed(("%d\n", aDevice->pendingState()));
520 break;
521 }
522 break;
523
524 /* don't call updateDeviceState until it's reattached. */
525 case HostUSBDevice::kDetachingPendingDetach:
526 case HostUSBDevice::kDetachingPendingDetachFilters:
527 freeDevice(aUSBDevice);
528 return false;
529
530 /* Let updateDeviceState / HostUSBDevice::updateState deal with this. */
531 case HostUSBDevice::kDetachingPendingAttach:
532 case HostUSBDevice::kDetachingPendingAttachFilters:
533 break;
534
535 default:
536 AssertMsgFailed(("%d\n", aDevice->pendingStateEx()));
537 break;
538 }
539 }
540
541 return USBProxyService::updateDeviceState (aDevice, aUSBDevice);
542}
543
544
545
546/* Stubs which the host specific classes overrides: */
547
548
549int USBProxyService::wait (unsigned aMillies)
550{
551 return RTThreadSleep (250);
552}
553
554
555int USBProxyService::interruptWait (void)
556{
557 return VERR_NOT_IMPLEMENTED;
558}
559
560
561PUSBDEVICE USBProxyService::getDevices (void)
562{
563 return NULL;
564}
565
566
567void USBProxyService::serviceThreadInit (void)
568{
569}
570
571
572void USBProxyService::serviceThreadTerm (void)
573{
574}
575
576
577/**
578 * The default implementation returns non-NULL to emulate successful insertions
579 * for those subclasses that don't reimplement this method.
580 */
581#ifndef VBOX_WITH_USBFILTER
582void *USBProxyService::insertFilter (IUSBDeviceFilter * /* aFilter */)
583#else
584void *USBProxyService::insertFilter (PCUSBFILTER /* aFilter */)
585#endif
586{
587 // return non-NULL to prevent failed assertions in Main
588 return (void *) 1;
589}
590
591
592void USBProxyService::removeFilter (void * /* aId */)
593{
594}
595
596
597int USBProxyService::captureDevice (HostUSBDevice * /* aDevice */)
598{
599 return VERR_NOT_IMPLEMENTED;
600}
601
602
603void USBProxyService::detachingDevice (HostUSBDevice * /* aDevice */)
604{
605}
606
607
608int USBProxyService::releaseDevice (HostUSBDevice * /* aDevice */)
609{
610 return VERR_NOT_IMPLEMENTED;
611}
612
613
614bool USBProxyService::updateDeviceState (HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice)
615{
616 AssertReturn (aDevice, false);
617 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
618
619 bool fRc = aDevice->updateState (aUSBDevice);
620 /* A little hack to work around state quirks wrt detach/reattach. */
621 if ( !fRc
622 && aDevice->isStatePending()
623 && ( aDevice->pendingStateEx() == HostUSBDevice::kDetachingPendingAttach
624 || aDevice->pendingStateEx() == HostUSBDevice::kDetachingPendingAttachFilters))
625 fRc = true;
626 return fRc;
627}
628
629
630void USBProxyService::deviceAdded (HostUSBDevice * /* aDevice */, PUSBDEVICE /* aUSBDevice */)
631{
632}
633
634
635void USBProxyService::deviceRemoved (HostUSBDevice * /* aDevice */)
636{
637}
638
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