VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/EmulatedUSBImpl.cpp@ 79849

Last change on this file since 79849 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1/* $Id: EmulatedUSBImpl.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * Emulated USB manager implementation.
4 */
5
6/*
7 * Copyright (C) 2013-2019 Oracle Corporation
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#define LOG_GROUP LOG_GROUP_MAIN_EMULATEDUSB
19#include "LoggingNew.h"
20
21#include "EmulatedUSBImpl.h"
22#include "ConsoleImpl.h"
23
24#include <VBox/vmm/pdmusb.h>
25
26
27/*
28 * Emulated USB webcam device instance.
29 */
30typedef std::map <Utf8Str, Utf8Str> EUSBSettingsMap;
31
32typedef enum EUSBDEVICESTATUS
33{
34 EUSBDEVICE_CREATED,
35 EUSBDEVICE_ATTACHING,
36 EUSBDEVICE_ATTACHED
37} EUSBDEVICESTATUS;
38
39class EUSBWEBCAM /* : public EUSBDEVICE */
40{
41 private:
42 int32_t volatile mcRefs;
43
44 EmulatedUSB *mpEmulatedUSB;
45
46 RTUUID mUuid;
47 char mszUuid[RTUUID_STR_LENGTH];
48
49 Utf8Str mPath;
50 Utf8Str mSettings;
51
52 EUSBSettingsMap mDevSettings;
53 EUSBSettingsMap mDrvSettings;
54
55 void *mpvObject;
56
57 static DECLCALLBACK(int) emulatedWebcamAttach(PUVM pUVM, EUSBWEBCAM *pThis, const char *pszDriver);
58 static DECLCALLBACK(int) emulatedWebcamDetach(PUVM pUVM, EUSBWEBCAM *pThis);
59
60 HRESULT settingsParse(void);
61
62 ~EUSBWEBCAM()
63 {
64 }
65
66 public:
67 EUSBWEBCAM()
68 :
69 mcRefs(1),
70 mpEmulatedUSB(NULL),
71 mpvObject(NULL),
72 enmStatus(EUSBDEVICE_CREATED)
73 {
74 RT_ZERO(mUuid);
75 RT_ZERO(mszUuid);
76 }
77
78 int32_t AddRef(void)
79 {
80 return ASMAtomicIncS32(&mcRefs);
81 }
82
83 void Release(void)
84 {
85 int32_t c = ASMAtomicDecS32(&mcRefs);
86 if (c == 0)
87 {
88 delete this;
89 }
90 }
91
92 HRESULT Initialize(Console *pConsole,
93 EmulatedUSB *pEmulatedUSB,
94 const com::Utf8Str *aPath,
95 const com::Utf8Str *aSettings,
96 void *pvObject);
97 HRESULT Attach(Console *pConsole,
98 PUVM pUVM,
99 const char *pszDriver);
100 HRESULT Detach(Console *pConsole,
101 PUVM pUVM);
102
103 bool HasId(const char *pszId) { return RTStrCmp(pszId, mszUuid) == 0;}
104
105 EUSBDEVICESTATUS enmStatus;
106};
107
108static int emulatedWebcamInsertSettings(PCFGMNODE pConfig, EUSBSettingsMap *pSettings)
109{
110 int rc = VINF_SUCCESS;
111
112 EUSBSettingsMap::const_iterator it;
113 for (it = pSettings->begin(); it != pSettings->end(); ++it)
114 {
115 /* Convert some well known settings for backward compatibility. */
116 if ( RTStrCmp(it->first.c_str(), "MaxPayloadTransferSize") == 0
117 || RTStrCmp(it->first.c_str(), "MaxFramerate") == 0)
118 {
119 uint32_t u32 = 0;
120 rc = RTStrToUInt32Full(it->second.c_str(), 10, &u32);
121 if (rc == VINF_SUCCESS)
122 {
123 rc = CFGMR3InsertInteger(pConfig, it->first.c_str(), u32);
124 }
125 else
126 {
127 if (RT_SUCCESS(rc)) /* VWRN_* */
128 {
129 rc = VERR_INVALID_PARAMETER;
130 }
131 }
132 }
133 else
134 {
135 rc = CFGMR3InsertString(pConfig, it->first.c_str(), it->second.c_str());
136 }
137
138 if (RT_FAILURE(rc))
139 {
140 break;
141 }
142 }
143
144 return rc;
145}
146
147/* static */ DECLCALLBACK(int) EUSBWEBCAM::emulatedWebcamAttach(PUVM pUVM, EUSBWEBCAM *pThis, const char *pszDriver)
148{
149 PCFGMNODE pInstance = CFGMR3CreateTree(pUVM);
150 PCFGMNODE pConfig;
151 CFGMR3InsertNode(pInstance, "Config", &pConfig);
152 int rc = emulatedWebcamInsertSettings(pConfig, &pThis->mDevSettings);
153 if (RT_FAILURE(rc))
154 return rc;
155 PCFGMNODE pEUSB;
156 CFGMR3InsertNode(pConfig, "EmulatedUSB", &pEUSB);
157 CFGMR3InsertString(pEUSB, "Id", pThis->mszUuid);
158 CFGMR3InsertInteger(pEUSB, "pfnCallback", (uintptr_t)EmulatedUSB::i_eusbCallback);
159 CFGMR3InsertInteger(pEUSB, "pvCallback", (uintptr_t)pThis->mpEmulatedUSB);
160
161 PCFGMNODE pLunL0;
162 CFGMR3InsertNode(pInstance, "LUN#0", &pLunL0);
163 CFGMR3InsertString(pLunL0, "Driver", pszDriver);
164 CFGMR3InsertNode(pLunL0, "Config", &pConfig);
165 CFGMR3InsertString(pConfig, "DevicePath", pThis->mPath.c_str());
166 CFGMR3InsertInteger(pConfig, "Object", (uintptr_t)pThis->mpvObject);
167 rc = emulatedWebcamInsertSettings(pConfig, &pThis->mDrvSettings);
168 if (RT_FAILURE(rc))
169 return rc;
170
171 /* pInstance will be used by PDM and deallocated on error. */
172 rc = PDMR3UsbCreateEmulatedDevice(pUVM, "Webcam", pInstance, &pThis->mUuid, NULL);
173 LogRelFlowFunc(("PDMR3UsbCreateEmulatedDevice %Rrc\n", rc));
174 return rc;
175}
176
177/* static */ DECLCALLBACK(int) EUSBWEBCAM::emulatedWebcamDetach(PUVM pUVM, EUSBWEBCAM *pThis)
178{
179 return PDMR3UsbDetachDevice(pUVM, &pThis->mUuid);
180}
181
182HRESULT EUSBWEBCAM::Initialize(Console *pConsole,
183 EmulatedUSB *pEmulatedUSB,
184 const com::Utf8Str *aPath,
185 const com::Utf8Str *aSettings,
186 void *pvObject)
187{
188 HRESULT hrc = S_OK;
189
190 int vrc = RTUuidCreate(&mUuid);
191 if (RT_SUCCESS(vrc))
192 {
193 RTStrPrintf(mszUuid, sizeof(mszUuid), "%RTuuid", &mUuid);
194 hrc = mPath.assignEx(*aPath);
195 if (SUCCEEDED(hrc))
196 {
197 hrc = mSettings.assignEx(*aSettings);
198 }
199
200 if (SUCCEEDED(hrc))
201 {
202 hrc = settingsParse();
203
204 if (SUCCEEDED(hrc))
205 {
206 mpEmulatedUSB = pEmulatedUSB;
207 mpvObject = pvObject;
208 }
209 }
210 }
211
212 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
213 {
214 LogFlowThisFunc(("%Rrc\n", vrc));
215 hrc = pConsole->setErrorBoth(VBOX_E_IPRT_ERROR, vrc, "Init emulated USB webcam (%Rrc)", vrc);
216 }
217
218 return hrc;
219}
220
221HRESULT EUSBWEBCAM::settingsParse(void)
222{
223 HRESULT hr = S_OK;
224
225 /* Parse mSettings string:
226 * "[dev:|drv:]Name1=Value1;[dev:|drv:]Name2=Value2"
227 */
228 char *pszSrc = mSettings.mutableRaw();
229
230 if (pszSrc)
231 {
232 while (*pszSrc)
233 {
234 /* Does the setting belong to device of driver. Default is both. */
235 bool fDev = true;
236 bool fDrv = true;
237 if (RTStrNICmp(pszSrc, RT_STR_TUPLE("drv:")) == 0)
238 {
239 pszSrc += sizeof("drv:")-1;
240 fDev = false;
241 }
242 else if (RTStrNICmp(pszSrc, RT_STR_TUPLE("dev:")) == 0)
243 {
244 pszSrc += sizeof("dev:")-1;
245 fDrv = false;
246 }
247
248 char *pszEq = RTStrStr(pszSrc, "=");
249 if (!pszEq)
250 {
251 hr = E_INVALIDARG;
252 break;
253 }
254
255 char *pszEnd = RTStrStr(pszEq, ";");
256 if (!pszEnd)
257 {
258 pszEnd = pszEq + strlen(pszEq);
259 }
260
261 *pszEq = 0;
262 char chEnd = *pszEnd;
263 *pszEnd = 0;
264
265 /* Empty strings not allowed. */
266 if (*pszSrc != 0 && pszEq[1] != 0)
267 {
268 if (fDev)
269 {
270 mDevSettings[pszSrc] = &pszEq[1];
271 }
272 if (fDrv)
273 {
274 mDrvSettings[pszSrc] = &pszEq[1];
275 }
276 }
277
278 *pszEq = '=';
279 *pszEnd = chEnd;
280
281 pszSrc = pszEnd;
282 if (*pszSrc == ';')
283 {
284 pszSrc++;
285 }
286 }
287
288 if (SUCCEEDED(hr))
289 {
290 EUSBSettingsMap::const_iterator it;
291 for (it = mDevSettings.begin(); it != mDevSettings.end(); ++it)
292 LogRelFlowFunc(("[dev:%s] = [%s]\n", it->first.c_str(), it->second.c_str()));
293 for (it = mDrvSettings.begin(); it != mDrvSettings.end(); ++it)
294 LogRelFlowFunc(("[drv:%s] = [%s]\n", it->first.c_str(), it->second.c_str()));
295 }
296 }
297
298 return hr;
299}
300
301HRESULT EUSBWEBCAM::Attach(Console *pConsole,
302 PUVM pUVM,
303 const char *pszDriver)
304{
305 HRESULT hrc = S_OK;
306
307 int vrc = VMR3ReqCallWaitU(pUVM, 0 /* idDstCpu (saved state, see #6232) */,
308 (PFNRT)emulatedWebcamAttach, 3,
309 pUVM, this, pszDriver);
310
311 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
312 {
313 LogFlowThisFunc(("%Rrc\n", vrc));
314 hrc = pConsole->setErrorBoth(VBOX_E_VM_ERROR, vrc, "Attach emulated USB webcam (%Rrc)", vrc);
315 }
316
317 return hrc;
318}
319
320HRESULT EUSBWEBCAM::Detach(Console *pConsole,
321 PUVM pUVM)
322{
323 HRESULT hrc = S_OK;
324
325 int vrc = VMR3ReqCallWaitU(pUVM, 0 /* idDstCpu (saved state, see #6232) */,
326 (PFNRT)emulatedWebcamDetach, 2,
327 pUVM, this);
328
329 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
330 {
331 LogFlowThisFunc(("%Rrc\n", vrc));
332 hrc = pConsole->setErrorBoth(VBOX_E_VM_ERROR, vrc, "Detach emulated USB webcam (%Rrc)", vrc);
333 }
334
335 return hrc;
336}
337
338
339/*
340 * EmulatedUSB implementation.
341 */
342DEFINE_EMPTY_CTOR_DTOR(EmulatedUSB)
343
344HRESULT EmulatedUSB::FinalConstruct()
345{
346 return BaseFinalConstruct();
347}
348
349void EmulatedUSB::FinalRelease()
350{
351 uninit();
352
353 BaseFinalRelease();
354}
355
356/*
357 * Initializes the instance.
358 *
359 * @param pConsole The owner.
360 */
361HRESULT EmulatedUSB::init(ComObjPtr<Console> pConsole)
362{
363 LogFlowThisFunc(("\n"));
364
365 ComAssertRet(!pConsole.isNull(), E_INVALIDARG);
366
367 /* Enclose the state transition NotReady->InInit->Ready */
368 AutoInitSpan autoInitSpan(this);
369 AssertReturn(autoInitSpan.isOk(), E_FAIL);
370
371 m.pConsole = pConsole;
372
373 /* Confirm a successful initialization */
374 autoInitSpan.setSucceeded();
375
376 return S_OK;
377}
378
379/*
380 * Uninitializes the instance.
381 * Called either from FinalRelease() or by the parent when it gets destroyed.
382 */
383void EmulatedUSB::uninit()
384{
385 LogFlowThisFunc(("\n"));
386
387 m.pConsole.setNull();
388
389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
390 for (WebcamsMap::iterator it = m.webcams.begin(); it != m.webcams.end(); ++it)
391 {
392 EUSBWEBCAM *p = it->second;
393 if (p)
394 {
395 it->second = NULL;
396 p->Release();
397 }
398 }
399 m.webcams.clear();
400 alock.release();
401
402 /* Enclose the state transition Ready->InUninit->NotReady */
403 AutoUninitSpan autoUninitSpan(this);
404 if (autoUninitSpan.uninitDone())
405 return;
406}
407
408HRESULT EmulatedUSB::getWebcams(std::vector<com::Utf8Str> &aWebcams)
409{
410 HRESULT hrc = S_OK;
411
412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
413
414 try
415 {
416 aWebcams.resize(m.webcams.size());
417 }
418 catch (std::bad_alloc &)
419 {
420 hrc = E_OUTOFMEMORY;
421 }
422 catch (...)
423 {
424 hrc = E_FAIL;
425 }
426
427 if (SUCCEEDED(hrc))
428 {
429 size_t i;
430 WebcamsMap::const_iterator it;
431 for (i = 0, it = m.webcams.begin(); it != m.webcams.end(); ++it)
432 aWebcams[i++] = it->first;
433 }
434
435 return hrc;
436}
437
438static const Utf8Str s_pathDefault(".0");
439
440HRESULT EmulatedUSB::webcamAttach(const com::Utf8Str &aPath,
441 const com::Utf8Str &aSettings)
442{
443 return i_webcamAttachInternal(aPath, aSettings, "HostWebcam", NULL);
444}
445
446HRESULT EmulatedUSB::i_webcamAttachInternal(const com::Utf8Str &aPath,
447 const com::Utf8Str &aSettings,
448 const char *pszDriver,
449 void *pvObject)
450{
451 HRESULT hrc = S_OK;
452
453 const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
454
455 Console::SafeVMPtr ptrVM(m.pConsole);
456 if (ptrVM.isOk())
457 {
458 EUSBWEBCAM *p = new EUSBWEBCAM();
459 if (p)
460 {
461 hrc = p->Initialize(m.pConsole, this, &path, &aSettings, pvObject);
462 if (SUCCEEDED(hrc))
463 {
464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
465 WebcamsMap::const_iterator it = m.webcams.find(path);
466 if (it == m.webcams.end())
467 {
468 p->AddRef();
469 try
470 {
471 m.webcams[path] = p;
472 }
473 catch (std::bad_alloc &)
474 {
475 hrc = E_OUTOFMEMORY;
476 }
477 catch (...)
478 {
479 hrc = E_FAIL;
480 }
481 p->enmStatus = EUSBDEVICE_ATTACHING;
482 }
483 else
484 {
485 hrc = E_FAIL;
486 }
487 }
488
489 if (SUCCEEDED(hrc))
490 {
491 hrc = p->Attach(m.pConsole, ptrVM.rawUVM(), pszDriver);
492 }
493
494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
495 if (SUCCEEDED(hrc))
496 {
497 p->enmStatus = EUSBDEVICE_ATTACHED;
498 }
499 else
500 {
501 if (p->enmStatus != EUSBDEVICE_CREATED)
502 {
503 m.webcams.erase(path);
504 }
505 }
506 alock.release();
507
508 p->Release();
509 }
510 else
511 {
512 hrc = E_OUTOFMEMORY;
513 }
514 }
515 else
516 {
517 hrc = VBOX_E_INVALID_VM_STATE;
518 }
519
520 return hrc;
521}
522
523HRESULT EmulatedUSB::webcamDetach(const com::Utf8Str &aPath)
524{
525 return i_webcamDetachInternal(aPath);
526}
527
528HRESULT EmulatedUSB::i_webcamDetachInternal(const com::Utf8Str &aPath)
529{
530 HRESULT hrc = S_OK;
531
532 const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
533
534 Console::SafeVMPtr ptrVM(m.pConsole);
535 if (ptrVM.isOk())
536 {
537 EUSBWEBCAM *p = NULL;
538
539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
540 WebcamsMap::iterator it = m.webcams.find(path);
541 if (it != m.webcams.end())
542 {
543 if (it->second->enmStatus == EUSBDEVICE_ATTACHED)
544 {
545 p = it->second;
546 m.webcams.erase(it);
547 }
548 }
549 alock.release();
550
551 if (p)
552 {
553 hrc = p->Detach(m.pConsole, ptrVM.rawUVM());
554 p->Release();
555 }
556 else
557 {
558 hrc = E_INVALIDARG;
559 }
560 }
561 else
562 {
563 hrc = VBOX_E_INVALID_VM_STATE;
564 }
565
566 return hrc;
567}
568
569/* static */ DECLCALLBACK(int) EmulatedUSB::eusbCallbackEMT(EmulatedUSB *pThis, char *pszId, uint32_t iEvent,
570 void *pvData, uint32_t cbData)
571{
572 LogRelFlowFunc(("id %s event %d, data %p %d\n", pszId, iEvent, pvData, cbData));
573
574 NOREF(cbData);
575
576 int rc = VINF_SUCCESS;
577 if (iEvent == 0)
578 {
579 com::Utf8Str path;
580 HRESULT hr = pThis->webcamPathFromId(&path, pszId);
581 if (SUCCEEDED(hr))
582 {
583 hr = pThis->webcamDetach(path);
584 if (FAILED(hr))
585 {
586 rc = VERR_INVALID_STATE;
587 }
588 }
589 else
590 {
591 rc = VERR_NOT_FOUND;
592 }
593 }
594 else
595 {
596 rc = VERR_INVALID_PARAMETER;
597 }
598
599 RTMemFree(pszId);
600 RTMemFree(pvData);
601
602 LogRelFlowFunc(("rc %Rrc\n", rc));
603 return rc;
604}
605
606/* static */ DECLCALLBACK(int) EmulatedUSB::i_eusbCallback(void *pv, const char *pszId, uint32_t iEvent,
607 const void *pvData, uint32_t cbData)
608{
609 /* Make a copy of parameters, forward to EMT and leave the callback to not hold any lock in the device. */
610 int rc = VINF_SUCCESS;
611
612 void *pvIdCopy = NULL;
613 void *pvDataCopy = NULL;
614 if (cbData > 0)
615 {
616 pvDataCopy = RTMemDup(pvData, cbData);
617 if (!pvDataCopy)
618 {
619 rc = VERR_NO_MEMORY;
620 }
621 }
622
623 if (RT_SUCCESS(rc))
624 {
625 pvIdCopy = RTMemDup(pszId, strlen(pszId) + 1);
626 if (!pvIdCopy)
627 {
628 rc = VERR_NO_MEMORY;
629 }
630 }
631
632 if (RT_SUCCESS(rc))
633 {
634 EmulatedUSB *pThis = (EmulatedUSB *)pv;
635 Console::SafeVMPtr ptrVM(pThis->m.pConsole);
636 if (ptrVM.isOk())
637 {
638 /* No wait. */
639 rc = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), 0 /* idDstCpu */,
640 (PFNRT)EmulatedUSB::eusbCallbackEMT, 5,
641 pThis, pvIdCopy, iEvent, pvDataCopy, cbData);
642 }
643 else
644 {
645 rc = VERR_INVALID_STATE;
646 }
647 }
648
649 if (RT_FAILURE(rc))
650 {
651 RTMemFree(pvIdCopy);
652 RTMemFree(pvDataCopy);
653 }
654
655 return rc;
656}
657
658HRESULT EmulatedUSB::webcamPathFromId(com::Utf8Str *pPath, const char *pszId)
659{
660 HRESULT hr = S_OK;
661
662 Console::SafeVMPtr ptrVM(m.pConsole);
663 if (ptrVM.isOk())
664 {
665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
666 WebcamsMap::const_iterator it;
667 for (it = m.webcams.begin(); it != m.webcams.end(); ++it)
668 {
669 EUSBWEBCAM *p = it->second;
670 if (p->HasId(pszId))
671 {
672 *pPath = it->first;
673 break;
674 }
675 }
676
677 if (it == m.webcams.end())
678 {
679 hr = E_FAIL;
680 }
681 alock.release();
682 }
683 else
684 {
685 hr = VBOX_E_INVALID_VM_STATE;
686 }
687
688 return hr;
689}
690
691/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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