VirtualBox

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

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

More logging.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 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 /* we need to lock the host object for writing because
199 * a) the subsequent code may call Host methods that require a write
200 * lock
201 * b) we will lock HostUSBDevice objects below and want to make sure
202 * the lock order is always the same (Host, HostUSBDevice, as
203 * expected by Host) to avoid cross-deadlocks */
204
205 AutoLock hostLock (mHost);
206
207 /*
208 * Compare previous list with the previous list of devices
209 * and merge in any changes while notifying Host.
210 */
211 HostUSBDeviceList::iterator It = this->mDevices.begin();
212 while ( It != mDevices.end()
213 || pDevices)
214 {
215 ComObjPtr <HostUSBDevice> DevPtr;
216
217 if (It != mDevices.end())
218 DevPtr = *It;
219
220 /// @todo we want to AddCaller here to make sure the device hasn't
221 // been uninitialized (needs support for the no-op AddCaller when
222 // its argument is NULL)
223
224 /* Lock the device object since we will read/write it's
225 * properties. All Host callbacks also imply the object is
226 * locked. */
227 AutoLock devLock (DevPtr.isNull() ? NULL : DevPtr);
228
229 /*
230 * Compare.
231 */
232 int iDiff;
233 if (DevPtr.isNull())
234 iDiff = 1;
235 else
236 {
237 if (!pDevices)
238 iDiff = -1;
239 else
240 iDiff = DevPtr->compare (pDevices);
241 }
242 if (!iDiff)
243 {
244 /*
245 * Device still there, update the state and move on.
246 */
247 if (updateDeviceState (DevPtr, pDevices))
248 {
249 Log (("USBProxyService::processChanges: state change %p:{.idVendor=%#06x, .idProduct=%#06x, .pszProduct=\"%s\", .pszManufacturer=\"%s\"} state=%d%s\n",
250 (HostUSBDevice *)DevPtr, pDevices->idVendor, pDevices->idProduct, pDevices->pszProduct, pDevices->pszManufacturer, DevPtr->state(), DevPtr->isStatePending() ? " (pending async op)" : ""));
251 mHost->onUSBDeviceStateChanged (DevPtr);
252 }
253 It++;
254 PUSBDEVICE pFree = pDevices;
255 pDevices = pDevices->pNext; /* treated as singly linked */
256 freeDevice (pFree);
257 }
258 else
259 {
260 if (iDiff > 0)
261 {
262 /*
263 * Head of pDevices was attached.
264 */
265 PUSBDEVICE pNew = pDevices;
266 pDevices = pDevices->pNext;
267 pNew->pPrev = pNew->pNext = NULL;
268
269 ComObjPtr <HostUSBDevice> NewObj;
270 NewObj.createObject();
271 NewObj->init (pNew, this);
272 Log (("USBProxyService::processChanges: attached %p/%p:{.idVendor=%#06x, .idProduct=%#06x, .pszProduct=\"%s\", .pszManufacturer=\"%s\"}\n",
273 (HostUSBDevice *)NewObj, pNew, pNew->idVendor, pNew->idProduct, pNew->pszProduct, pNew->pszManufacturer));
274
275 /* not really necessary to lock here, but make Assert
276 * checks happy */
277 AutoLock newDevLock (NewObj);
278
279 mDevices.insert (It, NewObj);
280 mHost->onUSBDeviceAttached (NewObj);
281 }
282 else
283 {
284 /*
285 * DevPtr was detached, unless there is a pending async request.
286 */
287 /** @todo add a timeout here. */
288 if (!DevPtr->isStatePending())
289 {
290 It = mDevices.erase (It);
291 mHost->onUSBDeviceDetached (DevPtr);
292 Log (("USBProxyService::processChanges: detached %p\n", (HostUSBDevice *)DevPtr)); /** @todo add details .*/
293 }
294 else
295 {
296 /* a state change (re-cycle) request is pending, go
297 * to the next device */
298 It++;
299 Log (("USBProxyService::processChanges: detached but pending %d %p\n",
300 DevPtr->pendingState(), (HostUSBDevice *)DevPtr));
301 }
302 }
303 }
304 } /* while */
305 }
306 else
307 {
308 /* we need to lock the host object for writing because
309 * a) the subsequent code may call Host methods that require a write
310 * lock
311 * b) we will lock HostUSBDevice objects below and want to make sure
312 * the lock order is always the same (Host, HostUSBDevice, as
313 * expected by Host) to avoid cross-deadlocks */
314
315 AutoLock hostLock (mHost);
316
317 /* All devices were detached */
318 HostUSBDeviceList::iterator It = this->mDevices.begin();
319 while (It != mDevices.end())
320 {
321 ComObjPtr <HostUSBDevice> DevPtr = *It;
322
323 AutoLock devLock (DevPtr);
324
325 /*
326 * DevPtr was detached.
327 */
328 It = mDevices.erase (It);
329 mHost->onUSBDeviceDetached (DevPtr);
330 Log (("USBProxyService::processChanges: detached %p\n", (HostUSBDevice *)DevPtr)); /** @todo add details .*/
331 }
332 }
333
334 LogFlowThisFunc (("returns void\n"));
335}
336
337
338/*static*/ DECLCALLBACK (int) USBProxyService::serviceThread (RTTHREAD Thread, void *pvUser)
339{
340 USBProxyService *pThis = (USBProxyService *)pvUser;
341 LogFlowFunc (("pThis=%p\n", pThis));
342 pThis->serviceThreadInit();
343
344 /*
345 * Processing loop.
346 */
347 for (;;)
348 {
349 pThis->wait (RT_INDEFINITE_WAIT);
350 if (pThis->mTerminate)
351 break;
352 pThis->processChanges();
353 }
354
355 pThis->serviceThreadTerm();
356 LogFlowFunc (("returns VINF_SUCCESS\n"));
357 return VINF_SUCCESS;
358}
359
360
361/*static*/ void USBProxyService::freeDevice (PUSBDEVICE pDevice)
362{
363 PUSBCONFIG pCfg = pDevice->paConfigurations;
364 unsigned cCfgs = pDevice->bNumConfigurations;
365 while (cCfgs-- > 0)
366 {
367 PUSBINTERFACE pIf = pCfg->paInterfaces;
368 unsigned cIfs = pCfg->bNumInterfaces;
369 while (cIfs-- > 0)
370 {
371 RTMemFree (pIf->paEndpoints);
372 pIf->paEndpoints = NULL;
373 RTStrFree ((char *)pIf->pszDriver);
374 pIf->pszDriver = NULL;
375 RTStrFree ((char *)pIf->pszInterface);
376 pIf->pszInterface = NULL;
377 /* next */
378 pIf++;
379 }
380 RTMemFree (pCfg->paInterfaces);
381 pCfg->paInterfaces = NULL;
382 RTStrFree ((char *)pCfg->pszConfiguration);
383 pCfg->pszConfiguration = NULL;
384
385 /* next */
386 pCfg++;
387 }
388 RTMemFree (pDevice->paConfigurations);
389 pDevice->paConfigurations = NULL;
390
391 RTStrFree ((char *)pDevice->pszManufacturer);
392 pDevice->pszManufacturer = NULL;
393 RTStrFree ((char *)pDevice->pszProduct);
394 pDevice->pszProduct = NULL;
395 RTStrFree ((char *)pDevice->pszSerialNumber);
396 pDevice->pszSerialNumber = NULL;
397
398 RTStrFree ((char *)pDevice->pszAddress);
399 pDevice->pszAddress = NULL;
400
401 RTMemFree (pDevice);
402
403}
404
405
406/* static */ uint64_t USBProxyService::calcSerialHash (const char *aSerial)
407{
408 if (!aSerial)
409 aSerial = "";
410
411 register const uint8_t *pu8 = (const uint8_t *)aSerial;
412 register uint64_t u64 = 14695981039346656037ULL;
413 for (;;)
414 {
415 register uint8_t u8 = *pu8;
416 if (!u8)
417 break;
418 u64 = (u64 * 1099511628211ULL) ^ u8;
419 pu8++;
420 }
421
422 return u64;
423}
424
425
426bool USBProxyService::updateDeviceStateFake (HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice)
427{
428 AssertReturn (aDevice, false);
429 AssertReturn (aDevice->isLockedOnCurrentThread(), false);
430
431 if (aDevice->isStatePending())
432 {
433 switch (aDevice->pendingState())
434 {
435 /* @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used anywhere in the proxy code; it's
436 * quite logical because the proxy doesn't know anything about guest VMs. We use HELD_BY_PROXY
437 * instead -- it is sufficient and is what Main expects. */
438 case USBDeviceState_USBDeviceCaptured: aUSBDevice->enmState = USBDEVICESTATE_HELD_BY_PROXY; break;
439 case USBDeviceState_USBDeviceHeld: aUSBDevice->enmState = USBDEVICESTATE_HELD_BY_PROXY; break;
440 case USBDeviceState_USBDeviceAvailable: aUSBDevice->enmState = USBDEVICESTATE_UNUSED; break;
441 case USBDeviceState_USBDeviceUnavailable: aUSBDevice->enmState = USBDEVICESTATE_USED_BY_HOST; break;
442 case USBDeviceState_USBDeviceBusy: aUSBDevice->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; break;
443 default:
444 AssertMsgFailed(("%d\n", aDevice->pendingState()));
445 break;
446 }
447 }
448
449 return USBProxyService::updateDeviceState (aDevice, aUSBDevice);
450}
451
452
453
454/* Stubs which the host specific classes overrides: */
455
456
457int USBProxyService::wait (unsigned aMillies)
458{
459 return RTThreadSleep (250);
460}
461
462
463int USBProxyService::interruptWait (void)
464{
465 return VERR_NOT_IMPLEMENTED;
466}
467
468
469PUSBDEVICE USBProxyService::getDevices (void)
470{
471 return NULL;
472}
473
474
475void USBProxyService::serviceThreadInit (void)
476{
477}
478
479
480void USBProxyService::serviceThreadTerm (void)
481{
482}
483
484
485/**
486 * The default implementation returns non-NULL to emulate successful insertions
487 * for those subclasses that don't reimplement this method.
488 */
489void *USBProxyService::insertFilter (IUSBDeviceFilter * /* aFilter */)
490{
491 // return non-NULL to prevent failed assertions in Main
492 return (void *) 1;
493}
494
495
496void USBProxyService::removeFilter (void * /* aID */)
497{
498}
499
500
501int USBProxyService::captureDevice (HostUSBDevice *pDevice)
502{
503 return VERR_NOT_IMPLEMENTED;
504}
505
506
507int USBProxyService::holdDevice (HostUSBDevice *pDevice)
508{
509 return VERR_NOT_IMPLEMENTED;
510}
511
512
513int USBProxyService::releaseDevice (HostUSBDevice *pDevice)
514{
515 return VERR_NOT_IMPLEMENTED;
516}
517
518
519int USBProxyService::resetDevice (HostUSBDevice *pDevice)
520{
521 return VERR_NOT_IMPLEMENTED;
522}
523
524
525bool USBProxyService::updateDeviceState (HostUSBDevice *pDevice, PUSBDEVICE pUSBDevice)
526{
527 AssertReturn (pDevice, false);
528 AssertReturn (pDevice->isLockedOnCurrentThread(), false);
529
530 return pDevice->updateState (pUSBDevice);
531}
532
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