VirtualBox

source: vbox/trunk/src/VBox/Main/HostUSBDeviceImpl.cpp@ 7554

Last change on this file since 7554 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: 40.1 KB
Line 
1/** @file
2 *
3 * VirtualBox IHostUSBDevice COM interface implementation
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/types.h> /* for UINT64_C */
19
20#include "HostUSBDeviceImpl.h"
21#include "MachineImpl.h"
22#include "VirtualBoxErrorInfoImpl.h"
23#include "USBProxyService.h"
24
25#include "Logging.h"
26
27#include <VBox/err.h>
28#include <iprt/cpputils.h>
29
30// constructor / destructor
31/////////////////////////////////////////////////////////////////////////////
32
33DEFINE_EMPTY_CTOR_DTOR (HostUSBDevice)
34
35HRESULT HostUSBDevice::FinalConstruct()
36{
37 mUSBProxyService = NULL;
38 mUsb = NULL;
39
40 return S_OK;
41}
42
43void HostUSBDevice::FinalRelease()
44{
45 uninit();
46}
47
48// public initializer/uninitializer for internal purposes only
49/////////////////////////////////////////////////////////////////////////////
50
51/**
52 * Initializes the USB device object.
53 *
54 * @returns COM result indicator
55 * @param aUsb Pointer to the usb device structure for which the object is to be a wrapper.
56 * This structure is now fully owned by the HostUSBDevice object and will be
57 * freed when it is destructed.
58 * @param aUSBProxyService Pointer to the USB Proxy Service object.
59 */
60HRESULT HostUSBDevice::init(PUSBDEVICE aUsb, USBProxyService *aUSBProxyService)
61{
62 ComAssertRet (aUsb, E_INVALIDARG);
63
64 /* Enclose the state transition NotReady->InInit->Ready */
65 AutoInitSpan autoInitSpan (this);
66 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
67
68 /*
69 * We need a unique ID for this VBoxSVC session.
70 * The UUID isn't stored anywhere.
71 */
72 unconst (mId).create();
73
74 /*
75 * Convert from USBDEVICESTATE to USBDeviceState.
76 *
77 * Note that not all proxy backend can detect the HELD_BY_PROXY
78 * and USED_BY_GUEST states. But that shouldn't matter much.
79 */
80 switch (aUsb->enmState)
81 {
82 default:
83 AssertMsgFailed(("aUsb->enmState=%d\n", aUsb->enmState));
84 case USBDEVICESTATE_UNSUPPORTED:
85 mState = USBDeviceState_NotSupported;
86 break;
87 case USBDEVICESTATE_USED_BY_HOST:
88 mState = USBDeviceState_Unavailable;
89 break;
90 case USBDEVICESTATE_USED_BY_HOST_CAPTURABLE:
91 mState = USBDeviceState_Busy;
92 break;
93 case USBDEVICESTATE_UNUSED:
94 mState = USBDeviceState_Available;
95 break;
96 case USBDEVICESTATE_HELD_BY_PROXY:
97 mState = USBDeviceState_Held;
98 break;
99 case USBDEVICESTATE_USED_BY_GUEST:
100 /* @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used
101 * anywhere in the proxy code; it's quite logical because the
102 * proxy doesn't know anything about guest VMs. */
103 AssertFailedReturn (E_FAIL);
104 break;
105 }
106
107 mPendingState = mState;
108 mPendingStateEx = kNothingPending;
109
110 /* Other data members */
111 mIsStatePending = false;
112 mUSBProxyService = aUSBProxyService;
113 mUsb = aUsb;
114
115 /* Confirm the successful initialization */
116 autoInitSpan.setSucceeded();
117
118 return S_OK;
119}
120
121/**
122 * Uninitializes the instance and sets the ready flag to FALSE.
123 * Called either from FinalRelease() or by the parent when it gets destroyed.
124 */
125void HostUSBDevice::uninit()
126{
127 /* Enclose the state transition Ready->InUninit->NotReady */
128 AutoUninitSpan autoUninitSpan (this);
129 if (autoUninitSpan.uninitDone())
130 return;
131
132 if (mUsb != NULL)
133 {
134 USBProxyService::freeDevice (mUsb);
135 mUsb = NULL;
136 }
137
138 mUSBProxyService = NULL;
139}
140
141// IUSBDevice properties
142/////////////////////////////////////////////////////////////////////////////
143
144STDMETHODIMP HostUSBDevice::COMGETTER(Id)(GUIDPARAMOUT aId)
145{
146 if (!aId)
147 return E_INVALIDARG;
148
149 AutoCaller autoCaller (this);
150 CheckComRCReturnRC (autoCaller.rc());
151
152 /* mId is constant during life time, no need to lock */
153 mId.cloneTo (aId);
154
155 return S_OK;
156}
157
158STDMETHODIMP HostUSBDevice::COMGETTER(VendorId)(USHORT *aVendorId)
159{
160 if (!aVendorId)
161 return E_INVALIDARG;
162
163 AutoCaller autoCaller (this);
164 CheckComRCReturnRC (autoCaller.rc());
165
166 AutoReaderLock alock (this);
167
168 *aVendorId = mUsb->idVendor;
169
170 return S_OK;
171}
172
173STDMETHODIMP HostUSBDevice::COMGETTER(ProductId)(USHORT *aProductId)
174{
175 if (!aProductId)
176 return E_INVALIDARG;
177
178 AutoCaller autoCaller (this);
179 CheckComRCReturnRC (autoCaller.rc());
180
181 AutoReaderLock alock (this);
182
183 *aProductId = mUsb->idProduct;
184
185 return S_OK;
186}
187
188STDMETHODIMP HostUSBDevice::COMGETTER(Revision)(USHORT *aRevision)
189{
190 if (!aRevision)
191 return E_INVALIDARG;
192
193 AutoCaller autoCaller (this);
194 CheckComRCReturnRC (autoCaller.rc());
195
196 AutoReaderLock alock (this);
197
198 *aRevision = mUsb->bcdDevice;
199
200 return S_OK;
201}
202
203STDMETHODIMP HostUSBDevice::COMGETTER(Manufacturer)(BSTR *aManufacturer)
204{
205 if (!aManufacturer)
206 return E_INVALIDARG;
207
208 AutoCaller autoCaller (this);
209 CheckComRCReturnRC (autoCaller.rc());
210
211 AutoReaderLock alock (this);
212
213 Bstr (mUsb->pszManufacturer).cloneTo (aManufacturer);
214
215 return S_OK;
216}
217
218STDMETHODIMP HostUSBDevice::COMGETTER(Product)(BSTR *aProduct)
219{
220 if (!aProduct)
221 return E_INVALIDARG;
222
223 AutoCaller autoCaller (this);
224 CheckComRCReturnRC (autoCaller.rc());
225
226 AutoReaderLock alock (this);
227
228 Bstr (mUsb->pszProduct).cloneTo (aProduct);
229
230 return S_OK;
231}
232
233STDMETHODIMP HostUSBDevice::COMGETTER(SerialNumber)(BSTR *aSerialNumber)
234{
235 if (!aSerialNumber)
236 return E_INVALIDARG;
237
238 AutoCaller autoCaller (this);
239 CheckComRCReturnRC (autoCaller.rc());
240
241 AutoReaderLock alock (this);
242
243 Bstr (mUsb->pszSerialNumber).cloneTo (aSerialNumber);
244
245 return S_OK;
246}
247
248STDMETHODIMP HostUSBDevice::COMGETTER(Address)(BSTR *aAddress)
249{
250 if (!aAddress)
251 return E_INVALIDARG;
252
253 AutoCaller autoCaller (this);
254 CheckComRCReturnRC (autoCaller.rc());
255
256 AutoReaderLock alock (this);
257
258 Bstr (mUsb->pszAddress).cloneTo (aAddress);
259
260 return S_OK;
261}
262
263STDMETHODIMP HostUSBDevice::COMGETTER(Port)(USHORT *aPort)
264{
265 if (!aPort)
266 return E_INVALIDARG;
267
268 AutoCaller autoCaller (this);
269 CheckComRCReturnRC (autoCaller.rc());
270
271 AutoReaderLock alock (this);
272
273 ///@todo implement
274 aPort = 0;
275
276 return S_OK;
277}
278
279STDMETHODIMP HostUSBDevice::COMGETTER(Version)(USHORT *aVersion)
280{
281 if (!aVersion)
282 return E_INVALIDARG;
283
284 AutoCaller autoCaller (this);
285 CheckComRCReturnRC (autoCaller.rc());
286
287 AutoReaderLock alock (this);
288
289 *aVersion = mUsb->bcdUSB >> 8;
290
291 return S_OK;
292}
293
294STDMETHODIMP HostUSBDevice::COMGETTER(PortVersion)(USHORT *aPortVersion)
295{
296 if (!aPortVersion)
297 return E_INVALIDARG;
298
299 AutoCaller autoCaller (this);
300 CheckComRCReturnRC (autoCaller.rc());
301
302 AutoReaderLock alock (this);
303
304 /** @todo implement this correctly or things just won't work right, see the vista defect. */
305 *aPortVersion = mUsb->bcdUSB >> 8;
306
307 return S_OK;
308}
309
310STDMETHODIMP HostUSBDevice::COMGETTER(Remote)(BOOL *aRemote)
311{
312 if (!aRemote)
313 return E_INVALIDARG;
314
315 AutoCaller autoCaller (this);
316 CheckComRCReturnRC (autoCaller.rc());
317
318 AutoReaderLock alock (this);
319
320 *aRemote = FALSE;
321
322 return S_OK;
323}
324
325// IHostUSBDevice properties
326/////////////////////////////////////////////////////////////////////////////
327
328STDMETHODIMP HostUSBDevice::COMGETTER(State) (USBDeviceState_T *aState)
329{
330 if (!aState)
331 return E_POINTER;
332
333 AutoCaller autoCaller (this);
334 CheckComRCReturnRC (autoCaller.rc());
335
336 AutoReaderLock alock (this);
337
338 *aState = mState;
339
340 return S_OK;
341}
342
343
344// public methods only for internal purposes
345////////////////////////////////////////////////////////////////////////////////
346
347/**
348 * @note Locks this object for reading.
349 */
350Utf8Str HostUSBDevice::name()
351{
352 Utf8Str name;
353
354 AutoCaller autoCaller (this);
355 AssertComRCReturn (autoCaller.rc(), name);
356
357 AutoReaderLock alock (this);
358
359 bool haveManufacturer = mUsb->pszManufacturer && *mUsb->pszManufacturer;
360 bool haveProduct = mUsb->pszProduct && *mUsb->pszProduct;
361 if (haveManufacturer && haveProduct)
362 name = Utf8StrFmt ("%s %s", mUsb->pszManufacturer,
363 mUsb->pszProduct);
364 else if (haveManufacturer)
365 name = Utf8StrFmt ("%s", mUsb->pszManufacturer);
366 else if (haveProduct)
367 name = Utf8StrFmt ("%s", mUsb->pszProduct);
368 else
369 name = "<unknown>";
370
371 return name;
372}
373
374/**
375 * Requests the USB proxy service to capture the device and sets the pending
376 * state to Captured.
377 *
378 * If the state change may be performed immediately (for example, Hold ->
379 * Captured), then the machine is informed before this method returns.
380 *
381 * @param aMachine Machine that will capture this device on success.
382 * @param aMaskedIfs The interfaces to hide from the guest.
383 * @return @c false if the device could be immediately captured
384 * but the VM process refused to grab it;
385 * @c true otherwise.
386 *
387 * @note Must be called from under the object write lock.
388 *
389 * @note May lock the given machine object for reading.
390 */
391bool HostUSBDevice::requestCapture (SessionMachine *aMachine, ULONG aMaskedIfs /* = 0*/)
392{
393 LogFlowThisFunc (("\n"));
394
395 AssertReturn (aMachine, false);
396
397 AssertReturn (isLockedOnCurrentThread(), false);
398
399 AssertReturn (mIsStatePending == false, false);
400
401 AssertReturn (
402 mState == USBDeviceState_Busy ||
403 mState == USBDeviceState_Available ||
404 mState == USBDeviceState_Held,
405 false);
406
407 if (mState == USBDeviceState_Held)
408 {
409 /* can perform immediate capture, inform the VM process */
410
411 ComPtr <IUSBDevice> d = this;
412
413 mIsStatePending = true;
414 mPendingSince = 0;
415
416 /* the VM process will query the object, so leave the lock */
417 AutoLock alock (this);
418 alock.leave();
419
420 LogFlowThisFunc (("Calling machine->onUSBDeviceAttach()...\n"));
421
422 HRESULT rc = aMachine->onUSBDeviceAttach (d, NULL, aMaskedIfs);
423
424 /* The VM process has a legal reason to fail (for example, incorrect
425 * usbfs permissions or out of virtual USB ports). More over, the VM
426 * process might have been accidentially crashed and not accessible
427 * any more (so that calling an uninitialized SessionMachine returns a
428 * failure). So don't assert. */
429
430 LogFlowThisFunc (("Done machine->onUSBDeviceAttach()=%08X\n", rc));
431
432 alock.enter();
433
434 mIsStatePending = false;
435 mPendingStateEx = kNothingPending;
436
437 if (SUCCEEDED (rc))
438 {
439 mState = mPendingState = USBDeviceState_Captured;
440 mMachine = aMachine;
441 return true;
442 }
443
444 return false;
445 }
446
447 mIsStatePending = true;
448 mPendingState = USBDeviceState_Captured;
449 mPendingStateEx = kNothingPending;
450 mPendingSince = RTTimeNanoTS();
451 mMachine = aMachine;
452 mMaskedIfs = aMaskedIfs;
453
454 mUSBProxyService->captureDevice (this);
455
456 return true;
457}
458
459/**
460 * Requests the USB proxy service to release the device and sets the pending
461 * state to Available.
462 *
463 * If the state change may be performed immediately (for example, the current
464 * state is Busy), this method does nothing.
465 *
466 * @note Must be called from under the object write lock.
467 */
468void HostUSBDevice::requestRelease()
469{
470 LogFlowThisFunc (("\n"));
471
472 AssertReturnVoid (isLockedOnCurrentThread());
473
474 AssertReturnVoid (mIsStatePending == false);
475
476 AssertReturnVoid (
477 mState == USBDeviceState_Busy ||
478 mState == USBDeviceState_Available ||
479 mState == USBDeviceState_Held);
480
481 if (mState != USBDeviceState_Held)
482 return;
483
484 mIsStatePending = true;
485 mPendingState = USBDeviceState_Available;
486 mPendingStateEx = kNothingPending;
487 mPendingSince = RTTimeNanoTS();
488
489 mUSBProxyService->releaseDevice (this);
490}
491
492/**
493 * Requests the USB proxy service to release the device, sets the pending
494 * state to Held and removes the machine association if any.
495 *
496 * If the state change may be performed immediately (for example, the current
497 * state is already Held), this method does nothing but removes the machine
498 * association.
499 *
500 * @note Must be called from under the object write lock.
501 */
502void HostUSBDevice::requestHold()
503{
504 LogFlowThisFunc (("\n"));
505
506 AssertReturnVoid (isLockedOnCurrentThread());
507
508 AssertReturnVoid (mIsStatePending == false);
509
510 AssertReturnVoid (
511 mState == USBDeviceState_Busy ||
512 mState == USBDeviceState_Available ||
513 mState == USBDeviceState_Held);
514
515 mMachine.setNull();
516
517 if (mState == USBDeviceState_Held)
518 return;
519
520 mIsStatePending = true;
521 mPendingState = USBDeviceState_Held;
522 mPendingStateEx = kNothingPending;
523 mPendingSince = RTTimeNanoTS();
524
525 mUSBProxyService->captureDevice (this);
526}
527
528/**
529 * Sets the device state from Captured to Held and resets the machine
530 * association (if any). Usually called before applying filters.
531 *
532 * @note Must be called from under the object write lock.
533 */
534void HostUSBDevice::setHeld()
535{
536 LogFlowThisFunc (("\n"));
537
538 AssertReturnVoid (isLockedOnCurrentThread());
539
540 AssertReturnVoid (mState == USBDeviceState_Captured);
541 AssertReturnVoid (mPendingState == USBDeviceState_Captured);
542 AssertReturnVoid (mIsStatePending == false);
543
544 mState = USBDeviceState_Held;
545 mMachine.setNull();
546}
547
548/**
549 * Resets all device data and informs the machine (if any) about the
550 * detachment. Must be called when this device is physically detached from
551 * the host.
552 *
553 * @note Must be called from under the object write lock.
554 */
555void HostUSBDevice::onDetachedPhys()
556{
557 LogFlowThisFunc (("\n"));
558
559 AssertReturnVoid (isLockedOnCurrentThread());
560
561 if (!mMachine.isNull() && mState == USBDeviceState_Captured)
562 {
563 /* the device is captured by a machine, instruct it to release */
564
565 mIsStatePending = true;
566 mPendingSince = 0;
567
568 /* the VM process will query the object, so leave the lock */
569 AutoLock alock (this);
570 alock.leave();
571
572 LogFlowThisFunc (("Calling machine->onUSBDeviceDetach()...\n"));
573
574 HRESULT rc = mMachine->onUSBDeviceDetach (mId, NULL);
575 NOREF(rc);
576
577 /* This call may expectedly fail with rc = NS_ERROR_FAILURE (on XPCOM)
578 * if the VM process requests device release right before termination
579 * and then terminates before onUSBDeviceDetach() reached
580 * it. Therefore, we don't assert here. On MS COM, there should be
581 * something similar (with the different error code). More over, the
582 * VM process might have been accidentially crashed and not accessible
583 * any more (so that calling an uninitialized SessionMachine returns a
584 * failure). So don't assert. */
585
586 LogFlowThisFunc (("Done machine->onUSBDeviceDetach()=%08X\n", rc));
587
588 alock.enter();
589
590 /* Reset all fields. The object should have been
591 * uninitialized after this method returns, so it doesn't really
592 * matter what state we put it in. */
593 mIsStatePending = false;
594 mState = mPendingState = USBDeviceState_NotSupported;
595 mPendingStateEx = kNothingPending;
596 mMachine.setNull();
597 }
598}
599
600/**
601 * Handles the finished pending state change and informs the VM process if
602 * necessary.
603 *
604 * @note Must be called from under the object write lock.
605 */
606void HostUSBDevice::handlePendingStateChange()
607{
608 LogFlowThisFunc (("\n"));
609
610 AssertReturnVoid (isLockedOnCurrentThread());
611
612 AssertReturnVoid (mIsStatePending == true);
613 AssertReturnVoid (mState != USBDeviceState_Captured || mPendingStateEx != kNothingPending);
614
615 bool wasCapture = false;
616
617 HRESULT requestRC = S_OK;
618 Bstr errorText;
619
620 switch (mPendingStateEx)
621 {
622 case kNothingPending:
623 switch (mPendingState)
624 {
625 case USBDeviceState_Captured:
626 {
627 if (mState == USBDeviceState_Held)
628 {
629 if (!mMachine.isNull())
630 wasCapture = true;
631 else
632 {
633 /* it is a canceled capture request. Give the device back
634 * to the host. */
635 mPendingState = USBDeviceState_Available;
636 mUSBProxyService->releaseDevice (this);
637 }
638 }
639 else
640 {
641 /* couldn't capture the device, will report an error */
642 wasCapture = true;
643
644 Assert (!mMachine.isNull());
645
646 /// @todo more detailed error message depending on the state?
647 // probably need some error code/string from the USB proxy itself
648
649 requestRC = E_FAIL;
650 errorText = Utf8StrFmt (
651 tr ("USB device '%s' with UUID {%Vuuid} is being accessed by the host "
652 "computer and cannot be attached to the virtual machine."
653 "Please try later"),
654 name().raw(), id().raw());
655 }
656 break;
657 }
658 case USBDeviceState_Available:
659 {
660 Assert (mMachine.isNull());
661
662 if (mState == USBDeviceState_Held)
663 {
664 /* couldn't release the device (give it back to the host).
665 * there is nobody to report an error to (the machine has
666 * already been deassociated because VMM has already detached
667 * the device before requesting a release). */
668 }
669 else
670 {
671 /* it is a canceled release request. Leave at the host */
672 /// @todo we may want to re-run all filters in this case
673 }
674 break;
675 }
676 case USBDeviceState_Held:
677 {
678 if (mState == USBDeviceState_Held)
679 {
680 /* All right, the device is now held (due to some global
681 * filter). */
682 break;
683 }
684 else
685 {
686 /* couldn't capture the device requested by the global
687 * filter, there is nobody to report an error to. */
688 }
689 break;
690 }
691 default:
692 AssertFailed();
693 }
694 break;
695
696 /*
697 * The device has reappeared, the caller (Host) will (maybe) reapply filters,
698 * since we don't quite know we set the machine to NULL.
699 */
700 case kDetachingPendingAttachFilters:
701 mMachine.setNull();
702 break;
703
704 /*
705 * The device has reappeared while the detach operation is still in
706 * progress, just clear the pending operation and leave the machine as is.
707 */
708 case kDetachingPendingAttach:
709 break;
710
711 case kDetachingPendingDetach:
712 case kDetachingPendingDetachFilters:
713 default:
714 AssertMsgFailed(("%d\n", mPendingStateEx));
715 return;
716 }
717
718 ComObjPtr <VirtualBoxErrorInfo> error;
719 if (FAILED (requestRC))
720 {
721 LogFlowThisFunc (("Request failed, requestRC=%08X, text='%ls'\n",
722 requestRC, errorText.raw()));
723
724 error.createObject();
725 error->init (requestRC, COM_IIDOF (IHostUSBDevice),
726 Bstr (HostUSBDevice::getComponentName()),
727 errorText.raw());
728 }
729
730 if (wasCapture)
731 {
732 /* inform the VM process */
733
734 ComPtr <IUSBDevice> d = this;
735
736 /* the VM process will query the object, so leave the lock */
737 AutoLock alock (this);
738 alock.leave();
739
740 LogFlowThisFunc (("Calling machine->onUSBDeviceAttach()...\n"));
741
742 HRESULT rc = mMachine->onUSBDeviceAttach (d, error, mMaskedIfs);
743
744 /* The VM process has a legal reason to fail (for example, incorrect
745 * usbfs permissions or out of virtual USB ports). More over, the VM
746 * process might have been accidentially crashed and not accessible
747 * any more (so that calling an uninitialized SessionMachine returns a
748 * failure). So don't assert. */
749
750 /// @todo we will probably want to re-run all filters on failure
751
752 LogFlowThisFunc (("Done machine->onUSBDeviceAttach()=%08X\n", rc));
753
754 alock.enter();
755
756 if (SUCCEEDED (requestRC) && SUCCEEDED (rc))
757 {
758 mIsStatePending = false;
759 mState = mPendingState = USBDeviceState_Captured;
760 mPendingStateEx = kNothingPending;
761 return;
762 }
763
764 /* on failure, either from the proxy or from the VM process,
765 * deassociate from the machine */
766 mMachine.setNull();
767 }
768
769 mIsStatePending = false;
770 mPendingState = mState;
771 mPendingStateEx = kNothingPending;
772}
773
774/**
775 * Cancels pending state change due to machine termination or timeout.
776 *
777 * @note Must be called from under the object write lock.
778 * @param aTimeout Whether this is a timeout or not.
779 */
780void HostUSBDevice::cancelPendingState(bool aTimeout /*= false*/)
781{
782 LogFlowThisFunc (("\n"));
783
784 AssertReturnVoid (isLockedOnCurrentThread());
785
786 AssertReturnVoid (mIsStatePending == true);
787 AssertReturnVoid (aTimeout || !mMachine.isNull());
788
789 switch (mPendingStateEx)
790 {
791 case kNothingPending:
792 switch (mPendingState)
793 {
794 case USBDeviceState_Captured:
795 /* reset mMachine to deassociate it from the filter and tell
796 * handlePendingStateChange() what to do */
797 mMachine.setNull();
798 if (!aTimeout)
799 break;
800 case USBDeviceState_Available:
801 case USBDeviceState_Held:
802 if (aTimeout)
803 {
804 mPendingStateEx = kNothingPending;
805 mIsStatePending = false;
806 break;
807 }
808 /* fall thru */
809 default:
810 AssertFailed();
811 }
812 break;
813
814 case kDetachingPendingDetach:
815 case kDetachingPendingDetachFilters:
816 case kDetachingPendingAttach:
817 case kDetachingPendingAttachFilters:
818 LogFlowThisFunc (("cancelling reattach in state %d\n", mPendingStateEx));
819 mMachine.setNull();
820 mPendingStateEx = kNothingPending;
821 mIsStatePending = false;
822 break;
823
824 default:
825 AssertMsgFailed(("%d\n", mPendingStateEx));
826 break;
827 }
828}
829
830/**
831 * Returns true if this device matches the given filter data.
832 *
833 * @note It is assumed, that the filter data owner is appropriately
834 * locked before calling this method.
835 *
836 * @note
837 * This method MUST correlate with
838 * USBController::hasMatchingFilter (IUSBDevice *)
839 * in the sense of the device matching logic.
840 *
841 * @note Locks this object for reading.
842 */
843bool HostUSBDevice::isMatch (const USBDeviceFilter::Data &aData)
844{
845 AutoCaller autoCaller (this);
846 AssertComRCReturn (autoCaller.rc(), false);
847
848 AutoReaderLock alock (this);
849
850 if (!aData.mActive)
851 return false;
852
853#ifndef VBOX_WITH_USBFILTER
854 if (!aData.mVendorId.isMatch (mUsb->idVendor))
855 {
856 LogFlowThisFunc (("vendor not match %04X\n",
857 mUsb->idVendor));
858 return false;
859 }
860 if (!aData.mProductId.isMatch (mUsb->idProduct))
861 {
862 LogFlowThisFunc (("product id not match %04X\n",
863 mUsb->idProduct));
864 return false;
865 }
866 if (!aData.mRevision.isMatch (mUsb->bcdDevice))
867 {
868 LogFlowThisFunc (("rev not match %04X\n",
869 mUsb->bcdDevice));
870 return false;
871 }
872
873#if !defined (RT_OS_WINDOWS)
874 // these filters are temporarily ignored on Win32
875 if (!aData.mManufacturer.isMatch (Bstr (mUsb->pszManufacturer)))
876 return false;
877 if (!aData.mProduct.isMatch (Bstr (mUsb->pszProduct)))
878 return false;
879 if (!aData.mSerialNumber.isMatch (Bstr (mUsb->pszSerialNumber)))
880 return false;
881 /// @todo (dmik) pusPort is yet absent
882// if (!aData.mPort.isMatch (Bstr (mUsb->pusPort)))
883// return false;
884#endif
885
886 // Host USB devices are local, so remote is always FALSE
887 if (!aData.mRemote.isMatch (FALSE))
888 {
889 LogFlowMember (("Host::HostUSBDevice: remote not match FALSE\n"));
890 return false;
891 }
892
893 /// @todo (dmik): bird, I assumed isMatch() is called only for devices
894 // that are suitable for holding/capturing (also assuming that when the device
895 // is just attached it first goes to our filter driver, and only after applying
896 // filters goes back to the system when appropriate). So the below
897 // doesn't look too correct; moreover, currently there is no determinable
898 // "any match" state for intervalic filters, and it will be not so easy
899 // to determine this state for an arbitrary regexp expression...
900 // For now, I just check that the string filter is empty (which doesn't
901 // actually reflect all possible "any match" filters).
902 //
903 // bird: This method was called for any device some weeks back, and it most certainly
904 // should be called for 'busy' devices still. However, we do *not* want 'busy' devices
905 // to match empty filters (because that will for instance capture all USB keyboards & mice).
906 // You assumption about a filter driver is not correct on linux. We're racing with
907 // everyone else in the system there - see your problem with usbfs access.
908 //
909 // The customer *requires* a way of matching all devices which the host isn't using,
910 // if that is now difficult or the below method opens holes in the matching, this *must*
911 // be addresses immediately.
912
913 /*
914 * If all the criteria is empty, devices which are used by the host will not match.
915 */
916 if ( mUsb->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
917 && aData.mVendorId.string().isEmpty()
918 && aData.mProductId.string().isEmpty()
919 && aData.mRevision.string().isEmpty()
920 && aData.mManufacturer.string().isEmpty()
921 && aData.mProduct.string().isEmpty()
922 && aData.mSerialNumber.string().isEmpty())
923 return false;
924
925#else /* VBOX_WITH_USBFILTER */
926 if (USBFilterMatchDevice (&aData.mUSBFilter, mUsb))
927 {
928 /* Don't match busy devices with a 100% wildcard filter - this will
929 later become a filter prop (ring-3 only). */
930 if ( mUsb->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
931 && !USBFilterHasAnySubstatialCriteria (&aData.mUSBFilter))
932 return false;
933 }
934#endif /* VBOX_WITH_USBFILTER */
935
936 LogFlowThisFunc (("returns true\n"));
937 return true;
938}
939
940
941/**
942 * Compares this device with a USBDEVICE and decides which comes first.
943 *
944 * If the device has a pending state request, a non-strict comparison is made
945 * (to properly detect a re-attached device). Otherwise, a strict comparison
946 * is performed.
947 *
948 * @param aDev2 Device 2.
949 *
950 * @return < 0 if this should come before aDev2.
951 * @return 0 if this and aDev2 are equal.
952 * @return > 0 if this should come after aDev2.
953 *
954 * @note Must be called from under the object write lock.
955 */
956int HostUSBDevice::compare (PCUSBDEVICE aDev2)
957{
958 AssertReturn (isLockedOnCurrentThread(), -1);
959
960 return compare (mUsb, aDev2, !isStatePending());
961}
962
963/**
964 * Compares two USB devices and decides which comes first.
965 *
966 * If @a aIsStrict is @c true then the comparison will indicate a difference
967 * even if the same physical device (represented by @a aDev1) has been just
968 * re-attached to the host computer (represented by @a aDev2) and got a
969 * different address from the host OS, etc.
970 *
971 * If @a aIsStrict is @c false, then such a re-attached device will be
972 * considered equal to the previous device definition and this function will
973 * retrun 0.
974 *
975 * @param aDev1 Device 1.
976 * @param aDev2 Device 2.
977 * @param aIsStrict @c true to do a strict check and @c false otherwise.
978
979 * @return < 0 if aDev1 should come before aDev2.
980 * @return 0 if aDev1 and aDev2 are equal.
981 * @return > 0 if aDev1 should come after aDev2.
982 */
983/*static*/
984int HostUSBDevice::compare (PCUSBDEVICE aDev1, PCUSBDEVICE aDev2,
985 bool aIsStrict /* = true */)
986{
987 /* The non-strict checks tries as best as it can to distiguish between
988 different physical devices of the same product. Unfortunately this
989 isn't always possible and we might end up a bit confused in rare cases... */
990
991 int iDiff = aDev1->idVendor - aDev2->idVendor;
992 if (iDiff)
993 return iDiff;
994
995 iDiff = aDev1->idProduct - aDev2->idProduct;
996 if (iDiff)
997 return iDiff;
998
999 iDiff = aDev1->bcdDevice - aDev2->bcdDevice;
1000 if (iDiff)
1001 return iDiff;
1002
1003 if (aDev1->u64SerialHash != aDev2->u64SerialHash)
1004 return aDev1->u64SerialHash < aDev2->u64SerialHash ? -1 : 1;
1005
1006 if (!aIsStrict)
1007 return 0;
1008
1009 /* The rest is considered as a strict check since it includes bits that
1010 may vary on logical reconnects (or whatever you wish to call it). */
1011 return strcmp (aDev1->pszAddress, aDev2->pszAddress);
1012}
1013
1014/**
1015 * Updates the state of the device.
1016 *
1017 * If this method returns @c true, Host::onUSBDeviceStateChanged() will be
1018 * called to process the state change (complete the state change request,
1019 * inform the VM process etc.).
1020 *
1021 * If this method returns @c false, it is assumed that the given state change
1022 * is "minor": it doesn't require any further action other than update the
1023 * mState field with the actual state value.
1024 *
1025 * Regardless of the return value, this method always takes ownership of the
1026 * new USBDEVICE structure passed in and updates the pNext and pPrev fiends in
1027 * it using the values of the old structure.
1028 *
1029 * @param aDev The current device state as seen by the proxy backend.
1030 *
1031 * @return Whether the Host object should be bothered with this state change.
1032 *
1033 * @note Locks this object for writing.
1034 */
1035bool HostUSBDevice::updateState (PCUSBDEVICE aDev)
1036{
1037 LogFlowThisFunc (("\n"));
1038
1039 AssertReturn (isLockedOnCurrentThread(), false);
1040
1041 AutoCaller autoCaller (this);
1042 AssertComRCReturn (autoCaller.rc(), false);
1043
1044 AutoLock alock (this);
1045
1046 /* Replace the existing structure by the new one */
1047 if (mUsb != aDev)
1048 {
1049 aDev->pNext = mUsb->pNext;
1050 aDev->pPrev = mUsb->pPrev;
1051 USBProxyService::freeDevice (mUsb);
1052 mUsb = aDev;
1053 }
1054
1055 bool isImportant = false;
1056
1057 /*
1058 * We have to be pretty conservative here because the proxy backend
1059 * doesn't necessarily know everything that's going on. So, rather
1060 * be overly careful than changing the state once when we shouldn't!
1061 *
1062 * In particular, we treat changing between three states Unavailable, Busy
1063 * and Available as non-important (because they all mean that the device
1064 * is owned by the host) and return false in this case. We may want to
1065 * change it later and, e.g. re-run all USB filters when the device goes from
1066 * from Busy to Available).
1067 *
1068 * 2007-07-04: State transitions from Unavailable to Busy or Available
1069 * are now considered important and will cause filters to
1070 * be rerun on the device. (See #2030 and #1870.)
1071 */
1072
1073 LogFlowThisFunc (("aDev->enmState=%d mState=%d mPendingState=%d mPendingStateEx=%d\n",
1074 aDev->enmState, mState, mPendingState, mPendingStateEx));
1075
1076 switch (aDev->enmState)
1077 {
1078 default:
1079 AssertMsgFailed (("aDev->enmState=%d\n", aDev->enmState));
1080 case USBDEVICESTATE_UNSUPPORTED:
1081 Assert (mState == USBDeviceState_NotSupported);
1082 switch (mState)
1083 {
1084 case USBDeviceState_Captured:
1085 isImportant = mIsStatePending;
1086 break;
1087 }
1088 return isImportant;
1089
1090 case USBDEVICESTATE_USED_BY_HOST:
1091 switch (mState)
1092 {
1093 case USBDeviceState_Unavailable:
1094 return false;
1095 /* the following state changes don't require any action for now */
1096 case USBDeviceState_Busy:
1097 case USBDeviceState_Available:
1098 isImportant = false;
1099 break;
1100#ifndef RT_OS_WINDOWS /* Only windows really knows whether the device is unavailable or captured. */
1101 case USBDeviceState_Captured:
1102 if (!mIsStatePending)
1103 return false;
1104 /* fall thru */
1105#endif
1106 default:
1107 isImportant = true;
1108 }
1109 LogFlowThisFunc (("%d -> %d\n",
1110 mState, USBDeviceState_Unavailable));
1111 mState = USBDeviceState_Unavailable;
1112 return isImportant;
1113
1114 case USBDEVICESTATE_USED_BY_HOST_CAPTURABLE:
1115 switch (mState)
1116 {
1117 case USBDeviceState_Busy:
1118 return false;
1119 case USBDeviceState_Available:
1120 isImportant = false;
1121 break;
1122 case USBDeviceState_Captured:
1123#ifndef RT_OS_WINDOWS /* Only Windows really knows whether the device is busy or captured. */
1124 if (!mIsStatePending)
1125 return false;
1126#endif
1127 /* Remain in the captured state if it's an async detach. */
1128 if (mPendingStateEx != kNothingPending)
1129 {
1130 LogFlowThisFunc (("USBDeviceCaptured - async detach completed (%d)\n", mPendingStateEx));
1131 return true;
1132 }
1133 /* fall thru */
1134 default:
1135 /* USBDeviceState_Unavailable: The device has become capturable, re-run filters. */
1136 /* USBDeviceState_Held: Pending request. */
1137 /* USBDeviceState_Captured: Pending request. */
1138 /* USBDeviceState_NotSupported: Something is broken. */
1139 isImportant = true;
1140 }
1141 LogFlowThisFunc (("%d -> %d\n",
1142 mState, USBDeviceState_Busy));
1143 mState = USBDeviceState_Busy;
1144 return isImportant;
1145
1146 case USBDEVICESTATE_UNUSED:
1147 switch (mState)
1148 {
1149 case USBDeviceState_Available:
1150 return false;
1151#if defined(RT_OS_LINUX) /* Hack for /proc/bus/usb/devices not necessarily putting up a driver. */ \
1152 || defined(RT_OS_DARWIN) /* We're a bit clueless as to the exact device state, just like linux. */
1153 case USBDeviceState_Captured:
1154 if ( !mIsStatePending
1155 || mPendingStateEx != kNothingPending)
1156 return false;
1157 isImportant = true;
1158 break;
1159#endif
1160 /* the following state changes don't require any action for now */
1161 case USBDeviceState_Busy:
1162 isImportant = false;
1163 break;
1164 default:
1165 /* USBDeviceState_Unavailable: The device has become available, re-run filters. */
1166 /* USBDeviceState_Held: Pending request. */
1167 /* USBDeviceState_NotSupported: Something is broken. */
1168 isImportant = true;
1169 }
1170 LogFlowThisFunc (("%d -> %d\n",
1171 mState, USBDeviceState_Available));
1172 mState = USBDeviceState_Available;
1173 return isImportant;
1174
1175 case USBDEVICESTATE_HELD_BY_PROXY:
1176 switch (mState)
1177 {
1178 case USBDeviceState_Held:
1179 return false;
1180 case USBDeviceState_Captured:
1181 if (!mIsStatePending)
1182 return false;
1183 /* no break */
1184 default:
1185 LogFlowThisFunc (("%d -> %d\n",
1186 mState, USBDeviceState_Held));
1187 mState = USBDeviceState_Held;
1188 return true;
1189 }
1190 break;
1191
1192 case USBDEVICESTATE_USED_BY_GUEST:
1193 /** @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used
1194 * anywhere in the proxy code; it's quite logical because the
1195 * proxy doesn't know anything about guest VMs. */
1196 AssertFailed();
1197#if 0
1198 switch (mState)
1199 {
1200 case USBDeviceState_Captured:
1201 /* the proxy may confuse following state(s) with captured */
1202 case USBDeviceState_Held:
1203 case USBDeviceState_Available:
1204 case USBDeviceState_Busy:
1205 return false;
1206 default:
1207 LogFlowThisFunc (("%d -> %d\n",
1208 mState, USBDeviceState_Held));
1209 mState = USBDeviceState_Held;
1210 return true;
1211 }
1212#endif
1213 break;
1214 }
1215
1216 return false;
1217}
1218
1219/**
1220 * Checks for timeout of any pending async operation.
1221 *
1222 * The caller must write lock the object prior to calling
1223 * this method.
1224 */
1225void HostUSBDevice::checkForAsyncTimeout()
1226{
1227 AssertReturnVoid (isLockedOnCurrentThread());
1228
1229#ifndef RT_OS_WINDOWS /* no timeouts on windows yet since I don't have all the details here... */
1230 if (isStatePending() && mPendingSince)
1231 {
1232 uint64_t elapsedNanoseconds = RTTimeNanoTS() - mPendingSince;
1233 if (elapsedNanoseconds > UINT64_C (60000000000) ) /* 60 seconds */
1234 {
1235 LogRel (("USB: Async operation timed out; mPendingState=%d mPendingStateEx=%d idVendor=%04x (%s) idProduct=%04x (%s) bcdDevice=%04x\n",
1236 mPendingState, mPendingStateEx, mUsb->idVendor, mUsb->pszManufacturer, mUsb->idProduct, mUsb->pszProduct, mUsb->bcdDevice));
1237
1238 cancelPendingState (true);
1239 }
1240 }
1241#endif
1242}
1243
1244/**
1245 * This method is called by the USB proxy and Host to work the
1246 * logical reconnection operation.
1247 *
1248 * @param aStage kDeatchingPendingDetach, kDeatchingPendingDetachFilters,
1249 * kDetachingPendingAttach or kDetachingPendingAttachFilters.
1250 *
1251 * @returns Success indicator.
1252 */
1253bool HostUSBDevice::setLogicalReconnect (InternalState aStage)
1254{
1255 AssertReturn (isLockedOnCurrentThread(), false);
1256
1257 switch (aStage)
1258 {
1259 case kDetachingPendingDetach:
1260 AssertReturn (!mIsStatePending, false);
1261 mPendingState = mState;
1262 mIsStatePending = true;
1263 mPendingSince = RTTimeNanoTS();
1264 LogFlowThisFunc (("pending detach\n"));
1265 break;
1266
1267 case kDetachingPendingDetachFilters:
1268 AssertReturn (mIsStatePending, false);
1269 AssertReturn (mPendingStateEx == kDetachingPendingDetach, false);
1270 LogFlowThisFunc (("pending detach+filters\n"));
1271 break;
1272
1273 case kDetachingPendingAttach:
1274 AssertReturn (mIsStatePending, false);
1275 AssertReturn (mPendingStateEx == kDetachingPendingDetach, false);
1276 LogFlowThisFunc (("pending attach\n"));
1277 break;
1278
1279 case kDetachingPendingAttachFilters:
1280 AssertReturn (mIsStatePending, false);
1281 AssertReturn ( mPendingStateEx == kDetachingPendingAttach
1282 || mPendingStateEx == kDetachingPendingDetachFilters, false);
1283 LogFlowThisFunc (("pending attach+filters\n"));
1284 break;
1285
1286 default:
1287 AssertFailedReturn (false);
1288 }
1289 mPendingStateEx = aStage;
1290 return true;
1291}
1292
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