VirtualBox

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

Last change on this file since 67414 was 66891, checked in by vboxsync, 8 years ago

Main/EmulatedUSBImpl.cpp: WebcamsMap cleanup.

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