VirtualBox

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

Last change on this file since 94321 was 93444, checked in by vboxsync, 2 years ago

VMM,Main,HostServices: Use a function table for accessing the VBoxVMM.dll/so/dylib functionality, and load it dynamically when the Console object is initialized. Also converted a few drivers in Main to use device helpers to get config values and such. bugref:10074

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

© 2023 Oracle
ContactPrivacy policyTerms of Use