VirtualBox

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

Last change on this file since 3566 was 3566, checked in by vboxsync, 18 years ago

Async USB detach (for darwin) with async operation timeout (not enabled on windows). Drop the USBProxyService::reset method. (Hope I didn't break anything, it's only tested on Darwin...)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.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.215389.xyz. 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 return aDevice->updateState (aUSBDevice);
582}
583
584
585void USBProxyService::deviceAdded (HostUSBDevice * /* aDevice */, PUSBDEVICE /* aUSBDevice */)
586{
587}
588
589
590void USBProxyService::deviceRemoved (HostUSBDevice * /* aDevice */)
591{
592}
593
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette