VirtualBox

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

Last change on this file since 4753 was 4071, checked in by vboxsync, 17 years ago

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.1 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 as published by the Free Software Foundation,
12 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
13 * distribution. VirtualBox OSE is distributed in the hope that it will
14 * 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::freeDeviceMembers (PUSBDEVICE pDevice)
387{
388 PUSBCONFIG pCfg = pDevice->paConfigurations;
389 unsigned cCfgs = pDevice->bNumConfigurations;
390 while (cCfgs-- > 0)
391 {
392 PUSBINTERFACE pIf = pCfg->paInterfaces;
393 unsigned cIfs = pCfg->bNumInterfaces;
394 while (cIfs-- > 0)
395 {
396 RTMemFree (pIf->paEndpoints);
397 pIf->paEndpoints = NULL;
398 RTStrFree ((char *)pIf->pszDriver);
399 pIf->pszDriver = NULL;
400 RTStrFree ((char *)pIf->pszInterface);
401 pIf->pszInterface = NULL;
402 /* next */
403 pIf++;
404 }
405 RTMemFree (pCfg->paInterfaces);
406 pCfg->paInterfaces = NULL;
407 RTStrFree ((char *)pCfg->pszConfiguration);
408 pCfg->pszConfiguration = NULL;
409
410 /* next */
411 pCfg++;
412 }
413 RTMemFree (pDevice->paConfigurations);
414 pDevice->paConfigurations = NULL;
415
416 RTStrFree ((char *)pDevice->pszManufacturer);
417 pDevice->pszManufacturer = NULL;
418 RTStrFree ((char *)pDevice->pszProduct);
419 pDevice->pszProduct = NULL;
420 RTStrFree ((char *)pDevice->pszSerialNumber);
421 pDevice->pszSerialNumber = NULL;
422
423 RTStrFree ((char *)pDevice->pszAddress);
424 pDevice->pszAddress = NULL;
425}
426
427/*static*/ void USBProxyService::freeDevice (PUSBDEVICE pDevice)
428{
429 freeDeviceMembers (pDevice);
430 RTMemFree (pDevice);
431}
432
433
434/* static */ uint64_t USBProxyService::calcSerialHash (const char *aSerial)
435{
436 if (!aSerial)
437 aSerial = "";
438
439 register const uint8_t *pu8 = (const uint8_t *)aSerial;
440 register uint64_t u64 = 14695981039346656037ULL;
441 for (;;)
442 {
443 register uint8_t u8 = *pu8;
444 if (!u8)
445 break;
446 u64 = (u64 * 1099511628211ULL) ^ u8;
447 pu8++;
448 }
449
450 return u64;
451}
452
453
454#ifdef VBOX_WITH_USBFILTER
455/*static*/ void USBProxyService::initFilterFromDevice (PUSBFILTER aFilter, HostUSBDevice *aDevice)
456{
457 PCUSBDEVICE pDev = aDevice->mUsb;
458 int vrc;
459
460 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_VENDOR_ID, pDev->idVendor, true); AssertRC(vrc);
461 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_PRODUCT_ID, pDev->idProduct, true); AssertRC(vrc);
462 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_REV, pDev->bcdDevice, true); AssertRC(vrc);
463 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_CLASS, pDev->bDeviceClass, true); AssertRC(vrc);
464 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_SUB_CLASS, pDev->bDeviceSubClass, true); AssertRC(vrc);
465 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_DEVICE_PROTOCOL, pDev->bDeviceProtocol, true); AssertRC(vrc);
466 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_PORT, pDev->bPort, true); AssertRC(vrc);
467 vrc = USBFilterSetNumExact (aFilter, USBFILTERIDX_BUS, pDev->bBus, false); AssertRC(vrc); /* not available on darwin yet... */
468 if (pDev->pszSerialNumber)
469 {
470 vrc = USBFilterSetStringExact (aFilter, USBFILTERIDX_SERIAL_NUMBER_STR, pDev->pszSerialNumber, true);
471 AssertRC (vrc);
472 }
473 if (pDev->pszProduct)
474 {
475 vrc = USBFilterSetStringExact (aFilter, USBFILTERIDX_PRODUCT_STR, pDev->pszProduct, true);
476 AssertRC (vrc);
477 }
478 if (pDev->pszManufacturer)
479 {
480 vrc = USBFilterSetStringExact (aFilter, USBFILTERIDX_MANUFACTURER_STR, pDev->pszManufacturer, true);
481 AssertRC (vrc);
482 }
483}
484#endif /* VBOX_WITH_USBFILTER */
485
486
487bool USBProxyService::updateDeviceStateFake (HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice)
488{
489 AssertReturn (aDevice, false);
490 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
491
492 if (aDevice->isStatePending())
493 {
494 switch (aDevice->pendingStateEx())
495 {
496 case HostUSBDevice::kNothingPending:
497 switch (aDevice->pendingState())
498 {
499 /* @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used anywhere in the proxy code; it's
500 * quite logical because the proxy doesn't know anything about guest VMs. We use HELD_BY_PROXY
501 * instead -- it is sufficient and is what Main expects. */
502 case USBDeviceState_USBDeviceCaptured: aUSBDevice->enmState = USBDEVICESTATE_HELD_BY_PROXY; break;
503 case USBDeviceState_USBDeviceHeld: aUSBDevice->enmState = USBDEVICESTATE_HELD_BY_PROXY; break;
504 case USBDeviceState_USBDeviceAvailable: aUSBDevice->enmState = USBDEVICESTATE_UNUSED; break;
505 case USBDeviceState_USBDeviceUnavailable: aUSBDevice->enmState = USBDEVICESTATE_USED_BY_HOST; break;
506 case USBDeviceState_USBDeviceBusy: aUSBDevice->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; break;
507 default:
508 AssertMsgFailed(("%d\n", aDevice->pendingState()));
509 break;
510 }
511 break;
512
513 /* don't call updateDeviceState until it's reattached. */
514 case HostUSBDevice::kDetachingPendingDetach:
515 case HostUSBDevice::kDetachingPendingDetachFilters:
516 freeDevice(aUSBDevice);
517 return false;
518
519 /* Let updateDeviceState / HostUSBDevice::updateState deal with this. */
520 case HostUSBDevice::kDetachingPendingAttach:
521 case HostUSBDevice::kDetachingPendingAttachFilters:
522 break;
523
524 default:
525 AssertMsgFailed(("%d\n", aDevice->pendingStateEx()));
526 break;
527 }
528 }
529
530 return USBProxyService::updateDeviceState (aDevice, aUSBDevice);
531}
532
533
534
535/* Stubs which the host specific classes overrides: */
536
537
538int USBProxyService::wait (unsigned aMillies)
539{
540 return RTThreadSleep (250);
541}
542
543
544int USBProxyService::interruptWait (void)
545{
546 return VERR_NOT_IMPLEMENTED;
547}
548
549
550PUSBDEVICE USBProxyService::getDevices (void)
551{
552 return NULL;
553}
554
555
556void USBProxyService::serviceThreadInit (void)
557{
558}
559
560
561void USBProxyService::serviceThreadTerm (void)
562{
563}
564
565
566/**
567 * The default implementation returns non-NULL to emulate successful insertions
568 * for those subclasses that don't reimplement this method.
569 */
570#ifndef VBOX_WITH_USBFILTER
571void *USBProxyService::insertFilter (IUSBDeviceFilter * /* aFilter */)
572#else
573void *USBProxyService::insertFilter (PCUSBFILTER /* aFilter */)
574#endif
575{
576 // return non-NULL to prevent failed assertions in Main
577 return (void *) 1;
578}
579
580
581void USBProxyService::removeFilter (void * /* aId */)
582{
583}
584
585
586int USBProxyService::captureDevice (HostUSBDevice * /* aDevice */)
587{
588 return VERR_NOT_IMPLEMENTED;
589}
590
591
592void USBProxyService::detachingDevice (HostUSBDevice * /* aDevice */)
593{
594}
595
596
597int USBProxyService::releaseDevice (HostUSBDevice * /* aDevice */)
598{
599 return VERR_NOT_IMPLEMENTED;
600}
601
602
603bool USBProxyService::updateDeviceState (HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice)
604{
605 AssertReturn (aDevice, false);
606 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
607
608 bool fRc = aDevice->updateState (aUSBDevice);
609 /* A little hack to work around state quirks wrt detach/reattach. */
610 if ( !fRc
611 && aDevice->isStatePending()
612 && ( aDevice->pendingStateEx() == HostUSBDevice::kDetachingPendingAttach
613 || aDevice->pendingStateEx() == HostUSBDevice::kDetachingPendingAttachFilters))
614 fRc = true;
615 return fRc;
616}
617
618
619void USBProxyService::deviceAdded (HostUSBDevice * /* aDevice */, PUSBDEVICE /* aUSBDevice */)
620{
621}
622
623
624void USBProxyService::deviceRemoved (HostUSBDevice * /* aDevice */)
625{
626}
627
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