VirtualBox

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

Last change on this file since 3668 was 3664, checked in by vboxsync, 17 years ago

ugly state mess hacking.

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