VirtualBox

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

Last change on this file since 73768 was 73003, checked in by vboxsync, 6 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

  • 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 73003 2018-07-09 11:09:32Z vboxsync $ */
2/** @file
3 * Emulated USB manager implementation.
4 */
5
6/*
7 * Copyright (C) 2013-2017 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.

© 2023 Oracle
ContactPrivacy policyTerms of Use