VirtualBox

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

Last change on this file since 94521 was 94368, checked in by vboxsync, 2 years ago

Main,UsbWebcam: Drop passing pointers through CFGM in favor of using VMM2USERMETHODS::pfnQueryGenericObject, bugref:10053

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

© 2023 Oracle
ContactPrivacy policyTerms of Use