VirtualBox

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

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

Added PortVersion and Version attributes for obtaining the major USB version of the port and the device respectively.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.6 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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * 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_USBDeviceNotSupported;
86 break;
87 case USBDEVICESTATE_USED_BY_HOST:
88 mState = USBDeviceState_USBDeviceUnavailable;
89 break;
90 case USBDEVICESTATE_USED_BY_HOST_CAPTURABLE:
91 mState = USBDeviceState_USBDeviceBusy;
92 break;
93 case USBDEVICESTATE_UNUSED:
94 mState = USBDeviceState_USBDeviceAvailable;
95 break;
96 case USBDEVICESTATE_HELD_BY_PROXY:
97 mState = USBDeviceState_USBDeviceHeld;
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 * @return @c false if the device could be immediately captured
383 * but the VM process refused to grab it;
384 * @c true otherwise.
385 *
386 * @note Must be called from under the object write lock.
387 *
388 * @note May lock the given machine object for reading.
389 */
390bool HostUSBDevice::requestCapture (SessionMachine *aMachine)
391{
392 LogFlowThisFunc (("\n"));
393
394 AssertReturn (aMachine, false);
395
396 AssertReturn (isLockedOnCurrentThread(), false);
397
398 AssertReturn (mIsStatePending == false, false);
399
400 AssertReturn (
401 mState == USBDeviceState_USBDeviceBusy ||
402 mState == USBDeviceState_USBDeviceAvailable ||
403 mState == USBDeviceState_USBDeviceHeld,
404 false);
405
406 if (mState == USBDeviceState_USBDeviceHeld)
407 {
408 /* can perform immediate capture, inform the VM process */
409
410 ComPtr <IUSBDevice> d = this;
411
412 mIsStatePending = true;
413 mPendingSince = 0;
414
415 /* the VM process will query the object, so leave the lock */
416 AutoLock alock (this);
417 alock.leave();
418
419 LogFlowThisFunc (("Calling machine->onUSBDeviceAttach()...\n"));
420
421 HRESULT rc = aMachine->onUSBDeviceAttach (d, NULL);
422
423 /* The VM process has a legal reason to fail (for example, incorrect
424 * usbfs permissions or out of virtual USB ports). More over, the VM
425 * process might have been accidentially crashed and not accessible
426 * any more (so that calling an uninitialized SessionMachine returns a
427 * failure). So don't assert. */
428
429 LogFlowThisFunc (("Done machine->onUSBDeviceAttach()=%08X\n", rc));
430
431 alock.enter();
432
433 mIsStatePending = false;
434 mPendingStateEx = kNothingPending;
435
436 if (SUCCEEDED (rc))
437 {
438 mState = mPendingState = USBDeviceState_USBDeviceCaptured;
439 mMachine = aMachine;
440 return true;
441 }
442
443 return false;
444 }
445
446 mIsStatePending = true;
447 mPendingState = USBDeviceState_USBDeviceCaptured;
448 mPendingStateEx = kNothingPending;
449 mPendingSince = RTTimeNanoTS();
450 mMachine = aMachine;
451
452 mUSBProxyService->captureDevice (this);
453
454 return true;
455}
456
457/**
458 * Requests the USB proxy service to release the device and sets the pending
459 * state to Available.
460 *
461 * If the state change may be performed immediately (for example, the current
462 * state is Busy), this method does nothing.
463 *
464 * @note Must be called from under the object write lock.
465 */
466void HostUSBDevice::requestRelease()
467{
468 LogFlowThisFunc (("\n"));
469
470 AssertReturnVoid (isLockedOnCurrentThread());
471
472 AssertReturnVoid (mIsStatePending == false);
473
474 AssertReturnVoid (
475 mState == USBDeviceState_USBDeviceBusy ||
476 mState == USBDeviceState_USBDeviceAvailable ||
477 mState == USBDeviceState_USBDeviceHeld);
478
479 if (mState != USBDeviceState_USBDeviceHeld)
480 return;
481
482 mIsStatePending = true;
483 mPendingState = USBDeviceState_USBDeviceAvailable;
484 mPendingStateEx = kNothingPending;
485 mPendingSince = RTTimeNanoTS();
486
487 mUSBProxyService->releaseDevice (this);
488}
489
490/**
491 * Requests the USB proxy service to release the device, sets the pending
492 * state to Held and removes the machine association if any.
493 *
494 * If the state change may be performed immediately (for example, the current
495 * state is already Held), this method does nothing but removes the machine
496 * association.
497 *
498 * @note Must be called from under the object write lock.
499 */
500void HostUSBDevice::requestHold()
501{
502 LogFlowThisFunc (("\n"));
503
504 AssertReturnVoid (isLockedOnCurrentThread());
505
506 AssertReturnVoid (mIsStatePending == false);
507
508 AssertReturnVoid (
509 mState == USBDeviceState_USBDeviceBusy ||
510 mState == USBDeviceState_USBDeviceAvailable ||
511 mState == USBDeviceState_USBDeviceHeld);
512
513 mMachine.setNull();
514
515 if (mState == USBDeviceState_USBDeviceHeld)
516 return;
517
518 mIsStatePending = true;
519 mPendingState = USBDeviceState_USBDeviceHeld;
520 mPendingStateEx = kNothingPending;
521 mPendingSince = RTTimeNanoTS();
522
523 mUSBProxyService->captureDevice (this);
524}
525
526/**
527 * Sets the device state from Captured to Held and resets the machine
528 * association (if any). Usually called before applying filters.
529 *
530 * @note Must be called from under the object write lock.
531 */
532void HostUSBDevice::setHeld()
533{
534 LogFlowThisFunc (("\n"));
535
536 AssertReturnVoid (isLockedOnCurrentThread());
537
538 AssertReturnVoid (mState == USBDeviceState_USBDeviceCaptured);
539 AssertReturnVoid (mPendingState == USBDeviceState_USBDeviceCaptured);
540 AssertReturnVoid (mIsStatePending == false);
541
542 mState = USBDeviceState_USBDeviceHeld;
543 mMachine.setNull();
544}
545
546/**
547 * Resets all device data and informs the machine (if any) about the
548 * detachment. Must be called when this device is physically detached from
549 * the host.
550 *
551 * @note Must be called from under the object write lock.
552 */
553void HostUSBDevice::onDetachedPhys()
554{
555 LogFlowThisFunc (("\n"));
556
557 AssertReturnVoid (isLockedOnCurrentThread());
558
559 if (!mMachine.isNull() && mState == USBDeviceState_USBDeviceCaptured)
560 {
561 /* the device is captured by a machine, instruct it to release */
562
563 mIsStatePending = true;
564 mPendingSince = 0;
565
566 /* the VM process will query the object, so leave the lock */
567 AutoLock alock (this);
568 alock.leave();
569
570 LogFlowThisFunc (("Calling machine->onUSBDeviceDetach()...\n"));
571
572 HRESULT rc = mMachine->onUSBDeviceDetach (mId, NULL);
573 NOREF(rc);
574
575 /* This call may expectedly fail with rc = NS_ERROR_FAILURE (on XPCOM)
576 * if the VM process requests device release right before termination
577 * and then terminates before onUSBDeviceDetach() reached
578 * it. Therefore, we don't assert here. On MS COM, there should be
579 * something similar (with the different error code). More over, the
580 * VM process might have been accidentially crashed and not accessible
581 * any more (so that calling an uninitialized SessionMachine returns a
582 * failure). So don't assert. */
583
584 LogFlowThisFunc (("Done machine->onUSBDeviceDetach()=%08X\n", rc));
585
586 alock.enter();
587
588 /* Reset all fields. The object should have been
589 * uninitialized after this method returns, so it doesn't really
590 * matter what state we put it in. */
591 mIsStatePending = false;
592 mState = mPendingState = USBDeviceState_USBDeviceNotSupported;
593 mPendingStateEx = kNothingPending;
594 mMachine.setNull();
595 }
596}
597
598/**
599 * Handles the finished pending state change and informs the VM process if
600 * necessary.
601 *
602 * @note Must be called from under the object write lock.
603 */
604void HostUSBDevice::handlePendingStateChange()
605{
606 LogFlowThisFunc (("\n"));
607
608 AssertReturnVoid (isLockedOnCurrentThread());
609
610 AssertReturnVoid (mIsStatePending == true);
611 AssertReturnVoid (mState != USBDeviceState_USBDeviceCaptured || mPendingStateEx != kNothingPending);
612
613 bool wasCapture = false;
614
615 HRESULT requestRC = S_OK;
616 Bstr errorText;
617
618 switch (mPendingStateEx)
619 {
620 case kNothingPending:
621 switch (mPendingState)
622 {
623 case USBDeviceState_USBDeviceCaptured:
624 {
625 if (mState == USBDeviceState_USBDeviceHeld)
626 {
627 if (!mMachine.isNull())
628 wasCapture = true;
629 else
630 {
631 /* it is a canceled capture request. Give the device back
632 * to the host. */
633 mPendingState = USBDeviceState_USBDeviceAvailable;
634 mUSBProxyService->releaseDevice (this);
635 }
636 }
637 else
638 {
639 /* couldn't capture the device, will report an error */
640 wasCapture = true;
641
642 Assert (!mMachine.isNull());
643
644 /// @todo more detailed error message depending on the state?
645 // probably need some error code/string from the USB proxy itself
646
647 requestRC = E_FAIL;
648 errorText = Utf8StrFmt (
649 tr ("USB device '%s' with UUID {%Vuuid} is being accessed by the host "
650 "computer and cannot be attached to the virtual machine."
651 "Please try later"),
652 name().raw(), id().raw());
653 }
654 break;
655 }
656 case USBDeviceState_USBDeviceAvailable:
657 {
658 Assert (mMachine.isNull());
659
660 if (mState == USBDeviceState_USBDeviceHeld)
661 {
662 /* couldn't release the device (give it back to the host).
663 * there is nobody to report an error to (the machine has
664 * already been deassociated because VMM has already detached
665 * the device before requesting a release). */
666 }
667 else
668 {
669 /* it is a canceled release request. Leave at the host */
670 /// @todo we may want to re-run all filters in this case
671 }
672 break;
673 }
674 case USBDeviceState_USBDeviceHeld:
675 {
676 if (mState == USBDeviceState_USBDeviceHeld)
677 {
678 /* All right, the device is now held (due to some global
679 * filter). */
680 break;
681 }
682 else
683 {
684 /* couldn't capture the device requested by the global
685 * filter, there is nobody to report an error to. */
686 }
687 break;
688 }
689 default:
690 AssertFailed();
691 }
692 break;
693
694 /*
695 * The device has reappeared, the caller (Host) will (maybe) reapply filters,
696 * since we don't quite know we set the machine to NULL.
697 */
698 case kDetachingPendingAttachFilters:
699 mMachine.setNull();
700 break;
701
702 /*
703 * The device has reappeared while the detach operation is still in
704 * progress, just clear the pending operation and leave the machine as is.
705 */
706 case kDetachingPendingAttach:
707 break;
708
709 case kDetachingPendingDetach:
710 case kDetachingPendingDetachFilters:
711 default:
712 AssertMsgFailed(("%d\n", mPendingStateEx));
713 return;
714 }
715
716 ComObjPtr <VirtualBoxErrorInfo> error;
717 if (FAILED (requestRC))
718 {
719 LogFlowThisFunc (("Request failed, requestRC=%08X, text='%ls'\n",
720 requestRC, errorText.raw()));
721
722 error.createObject();
723 error->init (requestRC, COM_IIDOF (IHostUSBDevice),
724 Bstr (HostUSBDevice::getComponentName()),
725 errorText.raw());
726 }
727
728 if (wasCapture)
729 {
730 /* inform the VM process */
731
732 ComPtr <IUSBDevice> d = this;
733
734 /* the VM process will query the object, so leave the lock */
735 AutoLock alock (this);
736 alock.leave();
737
738 LogFlowThisFunc (("Calling machine->onUSBDeviceAttach()...\n"));
739
740 HRESULT rc = mMachine->onUSBDeviceAttach (d, error);
741
742 /* The VM process has a legal reason to fail (for example, incorrect
743 * usbfs permissions or out of virtual USB ports). More over, the VM
744 * process might have been accidentially crashed and not accessible
745 * any more (so that calling an uninitialized SessionMachine returns a
746 * failure). So don't assert. */
747
748 /// @todo we will probably want to re-run all filters on failure
749
750 LogFlowThisFunc (("Done machine->onUSBDeviceAttach()=%08X\n", rc));
751
752 alock.enter();
753
754 if (SUCCEEDED (requestRC) && SUCCEEDED (rc))
755 {
756 mIsStatePending = false;
757 mState = mPendingState = USBDeviceState_USBDeviceCaptured;
758 mPendingStateEx = kNothingPending;
759 return;
760 }
761
762 /* on failure, either from the proxy or from the VM process,
763 * deassociate from the machine */
764 mMachine.setNull();
765 }
766
767 mIsStatePending = false;
768 mPendingState = mState;
769 mPendingStateEx = kNothingPending;
770}
771
772/**
773 * Cancels pending state change due to machine termination or timeout.
774 *
775 * @note Must be called from under the object write lock.
776 * @param aTimeout Whether this is a timeout or not.
777 */
778void HostUSBDevice::cancelPendingState(bool aTimeout /*= false*/)
779{
780 LogFlowThisFunc (("\n"));
781
782 AssertReturnVoid (isLockedOnCurrentThread());
783
784 AssertReturnVoid (mIsStatePending == true);
785 AssertReturnVoid (aTimeout || !mMachine.isNull());
786
787 switch (mPendingStateEx)
788 {
789 case kNothingPending:
790 switch (mPendingState)
791 {
792 case USBDeviceState_USBDeviceCaptured:
793 /* reset mMachine to deassociate it from the filter and tell
794 * handlePendingStateChange() what to do */
795 mMachine.setNull();
796 if (!aTimeout)
797 break;
798 case USBDeviceState_USBDeviceAvailable:
799 case USBDeviceState_USBDeviceHeld:
800 if (aTimeout)
801 {
802 mPendingStateEx = kNothingPending;
803 mIsStatePending = false;
804 break;
805 }
806 /* fall thru */
807 default:
808 AssertFailed();
809 }
810 break;
811
812 case kDetachingPendingDetach:
813 case kDetachingPendingDetachFilters:
814 case kDetachingPendingAttach:
815 case kDetachingPendingAttachFilters:
816 LogFlowThisFunc (("cancelling reattach in state %d\n", mPendingStateEx));
817 mMachine.setNull();
818 mPendingStateEx = kNothingPending;
819 mIsStatePending = false;
820 break;
821
822 default:
823 AssertMsgFailed(("%d\n", mPendingStateEx));
824 break;
825 }
826}
827
828/**
829 * Returns true if this device matches the given filter data.
830 *
831 * @note It is assumed, that the filter data owner is appropriately
832 * locked before calling this method.
833 *
834 * @note
835 * This method MUST correlate with
836 * USBController::hasMatchingFilter (IUSBDevice *)
837 * in the sense of the device matching logic.
838 *
839 * @note Locks this object for reading.
840 */
841bool HostUSBDevice::isMatch (const USBDeviceFilter::Data &aData)
842{
843 AutoCaller autoCaller (this);
844 AssertComRCReturn (autoCaller.rc(), false);
845
846 AutoReaderLock alock (this);
847
848 if (!aData.mActive)
849 return false;
850
851#ifndef VBOX_WITH_USBFILTER
852 if (!aData.mVendorId.isMatch (mUsb->idVendor))
853 {
854 LogFlowThisFunc (("vendor not match %04X\n",
855 mUsb->idVendor));
856 return false;
857 }
858 if (!aData.mProductId.isMatch (mUsb->idProduct))
859 {
860 LogFlowThisFunc (("product id not match %04X\n",
861 mUsb->idProduct));
862 return false;
863 }
864 if (!aData.mRevision.isMatch (mUsb->bcdDevice))
865 {
866 LogFlowThisFunc (("rev not match %04X\n",
867 mUsb->bcdDevice));
868 return false;
869 }
870
871#if !defined (RT_OS_WINDOWS)
872 // these filters are temporarily ignored on Win32
873 if (!aData.mManufacturer.isMatch (Bstr (mUsb->pszManufacturer)))
874 return false;
875 if (!aData.mProduct.isMatch (Bstr (mUsb->pszProduct)))
876 return false;
877 if (!aData.mSerialNumber.isMatch (Bstr (mUsb->pszSerialNumber)))
878 return false;
879 /// @todo (dmik) pusPort is yet absent
880// if (!aData.mPort.isMatch (Bstr (mUsb->pusPort)))
881// return false;
882#endif
883
884 // Host USB devices are local, so remote is always FALSE
885 if (!aData.mRemote.isMatch (FALSE))
886 {
887 LogFlowMember (("Host::HostUSBDevice: remote not match FALSE\n"));
888 return false;
889 }
890
891 /// @todo (dmik): bird, I assumed isMatch() is called only for devices
892 // that are suitable for holding/capturing (also assuming that when the device
893 // is just attached it first goes to our filter driver, and only after applying
894 // filters goes back to the system when appropriate). So the below
895 // doesn't look too correct; moreover, currently there is no determinable
896 // "any match" state for intervalic filters, and it will be not so easy
897 // to determine this state for an arbitrary regexp expression...
898 // For now, I just check that the string filter is empty (which doesn't
899 // actually reflect all possible "any match" filters).
900 //
901 // bird: This method was called for any device some weeks back, and it most certainly
902 // should be called for 'busy' devices still. However, we do *not* want 'busy' devices
903 // to match empty filters (because that will for instance capture all USB keyboards & mice).
904 // You assumption about a filter driver is not correct on linux. We're racing with
905 // everyone else in the system there - see your problem with usbfs access.
906 //
907 // The customer *requires* a way of matching all devices which the host isn't using,
908 // if that is now difficult or the below method opens holes in the matching, this *must*
909 // be addresses immediately.
910
911 /*
912 * If all the criteria is empty, devices which are used by the host will not match.
913 */
914 if ( mUsb->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
915 && aData.mVendorId.string().isEmpty()
916 && aData.mProductId.string().isEmpty()
917 && aData.mRevision.string().isEmpty()
918 && aData.mManufacturer.string().isEmpty()
919 && aData.mProduct.string().isEmpty()
920 && aData.mSerialNumber.string().isEmpty())
921 return false;
922
923#else /* VBOX_WITH_USBFILTER */
924 if (USBFilterMatchDevice (&aData.mUSBFilter, mUsb))
925 {
926 /* Don't match busy devices with a 100% wildcard filter - this will
927 later become a filter prop (ring-3 only). */
928 if ( mUsb->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
929 && !USBFilterHasAnySubstatialCriteria (&aData.mUSBFilter))
930 return false;
931 }
932#endif /* VBOX_WITH_USBFILTER */
933
934 LogFlowThisFunc (("returns true\n"));
935 return true;
936}
937
938
939/**
940 * Compares this device with a USBDEVICE and decides which comes first.
941 *
942 * If the device has a pending state request, a non-strict comparison is made
943 * (to properly detect a re-attached device). Otherwise, a strict comparison
944 * is performed.
945 *
946 * @param aDev2 Device 2.
947 *
948 * @return < 0 if this should come before aDev2.
949 * @return 0 if this and aDev2 are equal.
950 * @return > 0 if this should come after aDev2.
951 *
952 * @note Must be called from under the object write lock.
953 */
954int HostUSBDevice::compare (PCUSBDEVICE aDev2)
955{
956 AssertReturn (isLockedOnCurrentThread(), -1);
957
958 return compare (mUsb, aDev2, !isStatePending());
959}
960
961/**
962 * Compares two USB devices and decides which comes first.
963 *
964 * If @a aIsStrict is @c true then the comparison will indicate a difference
965 * even if the same physical device (represented by @a aDev1) has been just
966 * re-attached to the host computer (represented by @a aDev2) and got a
967 * different address from the host OS, etc.
968 *
969 * If @a aIsStrict is @c false, then such a re-attached device will be
970 * considered equal to the previous device definition and this function will
971 * retrun 0.
972 *
973 * @param aDev1 Device 1.
974 * @param aDev2 Device 2.
975 * @param aIsStrict @c true to do a strict check and @c false otherwise.
976
977 * @return < 0 if aDev1 should come before aDev2.
978 * @return 0 if aDev1 and aDev2 are equal.
979 * @return > 0 if aDev1 should come after aDev2.
980 */
981/*static*/
982int HostUSBDevice::compare (PCUSBDEVICE aDev1, PCUSBDEVICE aDev2,
983 bool aIsStrict /* = true */)
984{
985 /* The non-strict checks tries as best as it can to distiguish between
986 different physical devices of the same product. Unfortunately this
987 isn't always possible and we might end up a bit confused in rare cases... */
988
989 int iDiff = aDev1->idVendor - aDev2->idVendor;
990 if (iDiff)
991 return iDiff;
992
993 iDiff = aDev1->idProduct - aDev2->idProduct;
994 if (iDiff)
995 return iDiff;
996
997 iDiff = aDev1->bcdDevice - aDev2->bcdDevice;
998 if (iDiff)
999 return iDiff;
1000
1001 if (aDev1->u64SerialHash != aDev2->u64SerialHash)
1002 return aDev1->u64SerialHash < aDev2->u64SerialHash ? -1 : 1;
1003
1004 if (!aIsStrict)
1005 return 0;
1006
1007 /* The rest is considered as a strict check since it includes bits that
1008 may vary on logical reconnects (or whatever you wish to call it). */
1009 return strcmp (aDev1->pszAddress, aDev2->pszAddress);
1010}
1011
1012/**
1013 * Updates the state of the device.
1014 *
1015 * If this method returns @c true, Host::onUSBDeviceStateChanged() will be
1016 * called to process the state change (complete the state change request,
1017 * inform the VM process etc.).
1018 *
1019 * If this method returns @c false, it is assumed that the given state change
1020 * is "minor": it doesn't require any further action other than update the
1021 * mState field with the actual state value.
1022 *
1023 * Regardless of the return value, this method always takes ownership of the
1024 * new USBDEVICE structure passed in and updates the pNext and pPrev fiends in
1025 * it using the values of the old structure.
1026 *
1027 * @param aDev The current device state as seen by the proxy backend.
1028 *
1029 * @return Whether the Host object should be bothered with this state change.
1030 *
1031 * @note Locks this object for writing.
1032 */
1033bool HostUSBDevice::updateState (PCUSBDEVICE aDev)
1034{
1035 LogFlowThisFunc (("\n"));
1036
1037 AssertReturn (isLockedOnCurrentThread(), false);
1038
1039 AutoCaller autoCaller (this);
1040 AssertComRCReturn (autoCaller.rc(), false);
1041
1042 AutoLock alock (this);
1043
1044 /* Replace the existing structure by the new one */
1045 if (mUsb != aDev)
1046 {
1047 aDev->pNext = mUsb->pNext;
1048 aDev->pPrev = mUsb->pPrev;
1049 USBProxyService::freeDevice (mUsb);
1050 mUsb = aDev;
1051 }
1052
1053 bool isImportant = false;
1054
1055 /*
1056 * We have to be pretty conservative here because the proxy backend
1057 * doesn't necessarily know everything that's going on. So, rather
1058 * be overly careful than changing the state once when we shouldn't!
1059 *
1060 * In particular, we treat changing between three states Unavailable, Busy
1061 * and Available as non-important (because they all mean that the device
1062 * is owned by the host) and return false in this case. We may want to
1063 * change it later and, e.g. re-run all USB filters when the device goes from
1064 * from Busy to Available).
1065 *
1066 * 2007-07-04: State transitions from Unavailable to Busy or Available
1067 * are now considered important and will cause filters to
1068 * be rerun on the device. (See #2030 and #1870.)
1069 */
1070
1071 LogFlowThisFunc (("aDev->enmState=%d mState=%d mPendingState=%d mPendingStateEx=%d\n",
1072 aDev->enmState, mState, mPendingState, mPendingStateEx));
1073
1074 switch (aDev->enmState)
1075 {
1076 default:
1077 AssertMsgFailed (("aDev->enmState=%d\n", aDev->enmState));
1078 case USBDEVICESTATE_UNSUPPORTED:
1079 Assert (mState == USBDeviceState_USBDeviceNotSupported);
1080 switch (mState)
1081 {
1082 case USBDeviceState_USBDeviceCaptured:
1083 isImportant = mIsStatePending;
1084 break;
1085 }
1086 return isImportant;
1087
1088 case USBDEVICESTATE_USED_BY_HOST:
1089 switch (mState)
1090 {
1091 case USBDeviceState_USBDeviceUnavailable:
1092 return false;
1093 /* the following state changes don't require any action for now */
1094 case USBDeviceState_USBDeviceBusy:
1095 case USBDeviceState_USBDeviceAvailable:
1096 isImportant = false;
1097 break;
1098#ifndef RT_OS_WINDOWS /* Only windows really knows whether the device is unavailable or captured. */
1099 case USBDeviceState_USBDeviceCaptured:
1100 if (!mIsStatePending)
1101 return false;
1102 /* fall thru */
1103#endif
1104 default:
1105 isImportant = true;
1106 }
1107 LogFlowThisFunc (("%d -> %d\n",
1108 mState, USBDeviceState_USBDeviceUnavailable));
1109 mState = USBDeviceState_USBDeviceUnavailable;
1110 return isImportant;
1111
1112 case USBDEVICESTATE_USED_BY_HOST_CAPTURABLE:
1113 switch (mState)
1114 {
1115 case USBDeviceState_USBDeviceBusy:
1116 return false;
1117 case USBDeviceState_USBDeviceAvailable:
1118 isImportant = false;
1119 break;
1120 case USBDeviceState_USBDeviceCaptured:
1121#ifndef RT_OS_WINDOWS /* Only Windows really knows whether the device is busy or captured. */
1122 if (!mIsStatePending)
1123 return false;
1124#endif
1125 /* Remain in the captured state if it's an async detach. */
1126 if (mPendingStateEx != kNothingPending)
1127 {
1128 LogFlowThisFunc (("USBDeviceCaptured - async detach completed (%d)\n", mPendingStateEx));
1129 return true;
1130 }
1131 /* fall thru */
1132 default:
1133 /* USBDeviceState_USBDeviceUnavailable: The device has become capturable, re-run filters. */
1134 /* USBDeviceState_USBDeviceHeld: Pending request. */
1135 /* USBDeviceState_USBDeviceCaptured: Pending request. */
1136 /* USBDeviceState_USBDeviceNotSupported: Something is broken. */
1137 isImportant = true;
1138 }
1139 LogFlowThisFunc (("%d -> %d\n",
1140 mState, USBDeviceState_USBDeviceBusy));
1141 mState = USBDeviceState_USBDeviceBusy;
1142 return isImportant;
1143
1144 case USBDEVICESTATE_UNUSED:
1145 switch (mState)
1146 {
1147 case USBDeviceState_USBDeviceAvailable:
1148 return false;
1149#if defined(RT_OS_LINUX) /* Hack for /proc/bus/usb/devices not necessarily putting up a driver. */ \
1150 || defined(RT_OS_DARWIN) /* We're a bit clueless as to the exact device state, just like linux. */
1151 case USBDeviceState_USBDeviceCaptured:
1152 if ( !mIsStatePending
1153 || mPendingStateEx != kNothingPending)
1154 return false;
1155 isImportant = true;
1156 break;
1157#endif
1158 /* the following state changes don't require any action for now */
1159 case USBDeviceState_USBDeviceBusy:
1160 isImportant = false;
1161 break;
1162 default:
1163 /* USBDeviceState_USBDeviceUnavailable: The device has become available, re-run filters. */
1164 /* USBDeviceState_USBDeviceHeld: Pending request. */
1165 /* USBDeviceState_USBDeviceNotSupported: Something is broken. */
1166 isImportant = true;
1167 }
1168 LogFlowThisFunc (("%d -> %d\n",
1169 mState, USBDeviceState_USBDeviceAvailable));
1170 mState = USBDeviceState_USBDeviceAvailable;
1171 return isImportant;
1172
1173 case USBDEVICESTATE_HELD_BY_PROXY:
1174 switch (mState)
1175 {
1176 case USBDeviceState_USBDeviceHeld:
1177 return false;
1178 case USBDeviceState_USBDeviceCaptured:
1179 if (!mIsStatePending)
1180 return false;
1181 /* no break */
1182 default:
1183 LogFlowThisFunc (("%d -> %d\n",
1184 mState, USBDeviceState_USBDeviceHeld));
1185 mState = USBDeviceState_USBDeviceHeld;
1186 return true;
1187 }
1188 break;
1189
1190 case USBDEVICESTATE_USED_BY_GUEST:
1191 /** @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used
1192 * anywhere in the proxy code; it's quite logical because the
1193 * proxy doesn't know anything about guest VMs. */
1194 AssertFailed();
1195#if 0
1196 switch (mState)
1197 {
1198 case USBDeviceState_USBDeviceCaptured:
1199 /* the proxy may confuse following state(s) with captured */
1200 case USBDeviceState_USBDeviceHeld:
1201 case USBDeviceState_USBDeviceAvailable:
1202 case USBDeviceState_USBDeviceBusy:
1203 return false;
1204 default:
1205 LogFlowThisFunc (("%d -> %d\n",
1206 mState, USBDeviceState_USBDeviceHeld));
1207 mState = USBDeviceState_USBDeviceHeld;
1208 return true;
1209 }
1210#endif
1211 break;
1212 }
1213
1214 return false;
1215}
1216
1217/**
1218 * Checks for timeout of any pending async operation.
1219 *
1220 * The caller must write lock the object prior to calling
1221 * this method.
1222 */
1223void HostUSBDevice::checkForAsyncTimeout()
1224{
1225 AssertReturnVoid (isLockedOnCurrentThread());
1226
1227#ifndef RT_OS_WINDOWS /* no timeouts on windows yet since I don't have all the details here... */
1228 if (isStatePending() && mPendingSince)
1229 {
1230 uint64_t elapsedNanoseconds = RTTimeNanoTS() - mPendingSince;
1231 if (elapsedNanoseconds > UINT64_C (60000000000) ) /* 60 seconds */
1232 {
1233 LogRel (("USB: Async operation timed out; mPendingState=%d mPendingStateEx=%d idVendor=%04x (%s) idProduct=%04x (%s) bcdDevice=%04x\n",
1234 mPendingState, mPendingStateEx, mUsb->idVendor, mUsb->pszManufacturer, mUsb->idProduct, mUsb->pszProduct, mUsb->bcdDevice));
1235
1236 cancelPendingState (true);
1237 }
1238 }
1239#endif
1240}
1241
1242/**
1243 * This method is called by the USB proxy and Host to work the
1244 * logical reconnection operation.
1245 *
1246 * @param aStage kDeatchingPendingDetach, kDeatchingPendingDetachFilters,
1247 * kDetachingPendingAttach or kDetachingPendingAttachFilters.
1248 *
1249 * @returns Success indicator.
1250 */
1251bool HostUSBDevice::setLogicalReconnect (InternalState aStage)
1252{
1253 AssertReturn (isLockedOnCurrentThread(), false);
1254
1255 switch (aStage)
1256 {
1257 case kDetachingPendingDetach:
1258 AssertReturn (!mIsStatePending, false);
1259 mPendingState = mState;
1260 mIsStatePending = true;
1261 mPendingSince = RTTimeNanoTS();
1262 LogFlowThisFunc (("pending detach\n"));
1263 break;
1264
1265 case kDetachingPendingDetachFilters:
1266 AssertReturn (mIsStatePending, false);
1267 AssertReturn (mPendingStateEx == kDetachingPendingDetach, false);
1268 LogFlowThisFunc (("pending detach+filters\n"));
1269 break;
1270
1271 case kDetachingPendingAttach:
1272 AssertReturn (mIsStatePending, false);
1273 AssertReturn (mPendingStateEx == kDetachingPendingDetach, false);
1274 LogFlowThisFunc (("pending attach\n"));
1275 break;
1276
1277 case kDetachingPendingAttachFilters:
1278 AssertReturn (mIsStatePending, false);
1279 AssertReturn ( mPendingStateEx == kDetachingPendingAttach
1280 || mPendingStateEx == kDetachingPendingDetachFilters, false);
1281 LogFlowThisFunc (("pending attach+filters\n"));
1282 break;
1283
1284 default:
1285 AssertFailedReturn (false);
1286 }
1287 mPendingStateEx = aStage;
1288 return true;
1289}
1290
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