VirtualBox

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

Last change on this file since 70772 was 69500, checked in by vboxsync, 7 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 69500 2017-10-28 15:14:05Z 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->setError(VBOX_E_IPRT_ERROR,
216 "Init emulated USB webcam (%Rrc)", vrc);
217 }
218
219 return hrc;
220}
221
222HRESULT EUSBWEBCAM::settingsParse(void)
223{
224 HRESULT hr = S_OK;
225
226 /* Parse mSettings string:
227 * "[dev:|drv:]Name1=Value1;[dev:|drv:]Name2=Value2"
228 */
229 char *pszSrc = mSettings.mutableRaw();
230
231 if (pszSrc)
232 {
233 while (*pszSrc)
234 {
235 /* Does the setting belong to device of driver. Default is both. */
236 bool fDev = true;
237 bool fDrv = true;
238 if (RTStrNICmp(pszSrc, RT_STR_TUPLE("drv:")) == 0)
239 {
240 pszSrc += sizeof("drv:")-1;
241 fDev = false;
242 }
243 else if (RTStrNICmp(pszSrc, RT_STR_TUPLE("dev:")) == 0)
244 {
245 pszSrc += sizeof("dev:")-1;
246 fDrv = false;
247 }
248
249 char *pszEq = RTStrStr(pszSrc, "=");
250 if (!pszEq)
251 {
252 hr = E_INVALIDARG;
253 break;
254 }
255
256 char *pszEnd = RTStrStr(pszEq, ";");
257 if (!pszEnd)
258 {
259 pszEnd = pszEq + strlen(pszEq);
260 }
261
262 *pszEq = 0;
263 char chEnd = *pszEnd;
264 *pszEnd = 0;
265
266 /* Empty strings not allowed. */
267 if (*pszSrc != 0 && pszEq[1] != 0)
268 {
269 if (fDev)
270 {
271 mDevSettings[pszSrc] = &pszEq[1];
272 }
273 if (fDrv)
274 {
275 mDrvSettings[pszSrc] = &pszEq[1];
276 }
277 }
278
279 *pszEq = '=';
280 *pszEnd = chEnd;
281
282 pszSrc = pszEnd;
283 if (*pszSrc == ';')
284 {
285 pszSrc++;
286 }
287 }
288
289 if (SUCCEEDED(hr))
290 {
291 EUSBSettingsMap::const_iterator it;
292 for (it = mDevSettings.begin(); it != mDevSettings.end(); ++it)
293 LogRelFlowFunc(("[dev:%s] = [%s]\n", it->first.c_str(), it->second.c_str()));
294 for (it = mDrvSettings.begin(); it != mDrvSettings.end(); ++it)
295 LogRelFlowFunc(("[drv:%s] = [%s]\n", it->first.c_str(), it->second.c_str()));
296 }
297 }
298
299 return hr;
300}
301
302HRESULT EUSBWEBCAM::Attach(Console *pConsole,
303 PUVM pUVM,
304 const char *pszDriver)
305{
306 HRESULT hrc = S_OK;
307
308 int vrc = VMR3ReqCallWaitU(pUVM, 0 /* idDstCpu (saved state, see #6232) */,
309 (PFNRT)emulatedWebcamAttach, 3,
310 pUVM, this, pszDriver);
311
312 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
313 {
314 LogFlowThisFunc(("%Rrc\n", vrc));
315 hrc = pConsole->setError(VBOX_E_IPRT_ERROR,
316 "Attach emulated USB webcam (%Rrc)", vrc);
317 }
318
319 return hrc;
320}
321
322HRESULT EUSBWEBCAM::Detach(Console *pConsole,
323 PUVM pUVM)
324{
325 HRESULT hrc = S_OK;
326
327 int vrc = VMR3ReqCallWaitU(pUVM, 0 /* idDstCpu (saved state, see #6232) */,
328 (PFNRT)emulatedWebcamDetach, 2,
329 pUVM, this);
330
331 if (SUCCEEDED(hrc) && RT_FAILURE(vrc))
332 {
333 LogFlowThisFunc(("%Rrc\n", vrc));
334 hrc = pConsole->setError(VBOX_E_IPRT_ERROR,
335 "Detach emulated USB webcam (%Rrc)", vrc);
336 }
337
338 return hrc;
339}
340
341
342/*
343 * EmulatedUSB implementation.
344 */
345DEFINE_EMPTY_CTOR_DTOR(EmulatedUSB)
346
347HRESULT EmulatedUSB::FinalConstruct()
348{
349 return BaseFinalConstruct();
350}
351
352void EmulatedUSB::FinalRelease()
353{
354 uninit();
355
356 BaseFinalRelease();
357}
358
359/*
360 * Initializes the instance.
361 *
362 * @param pConsole The owner.
363 */
364HRESULT EmulatedUSB::init(ComObjPtr<Console> pConsole)
365{
366 LogFlowThisFunc(("\n"));
367
368 ComAssertRet(!pConsole.isNull(), E_INVALIDARG);
369
370 /* Enclose the state transition NotReady->InInit->Ready */
371 AutoInitSpan autoInitSpan(this);
372 AssertReturn(autoInitSpan.isOk(), E_FAIL);
373
374 m.pConsole = pConsole;
375
376 /* Confirm a successful initialization */
377 autoInitSpan.setSucceeded();
378
379 return S_OK;
380}
381
382/*
383 * Uninitializes the instance.
384 * Called either from FinalRelease() or by the parent when it gets destroyed.
385 */
386void EmulatedUSB::uninit()
387{
388 LogFlowThisFunc(("\n"));
389
390 m.pConsole.setNull();
391
392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
393 for (WebcamsMap::iterator it = m.webcams.begin(); it != m.webcams.end(); ++it)
394 {
395 EUSBWEBCAM *p = it->second;
396 if (p)
397 {
398 it->second = NULL;
399 p->Release();
400 }
401 }
402 m.webcams.clear();
403 alock.release();
404
405 /* Enclose the state transition Ready->InUninit->NotReady */
406 AutoUninitSpan autoUninitSpan(this);
407 if (autoUninitSpan.uninitDone())
408 return;
409}
410
411HRESULT EmulatedUSB::getWebcams(std::vector<com::Utf8Str> &aWebcams)
412{
413 HRESULT hrc = S_OK;
414
415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
416
417 try
418 {
419 aWebcams.resize(m.webcams.size());
420 }
421 catch (std::bad_alloc &)
422 {
423 hrc = E_OUTOFMEMORY;
424 }
425 catch (...)
426 {
427 hrc = E_FAIL;
428 }
429
430 if (SUCCEEDED(hrc))
431 {
432 size_t i;
433 WebcamsMap::const_iterator it;
434 for (i = 0, it = m.webcams.begin(); it != m.webcams.end(); ++it)
435 aWebcams[i++] = it->first;
436 }
437
438 return hrc;
439}
440
441static const Utf8Str s_pathDefault(".0");
442
443HRESULT EmulatedUSB::webcamAttach(const com::Utf8Str &aPath,
444 const com::Utf8Str &aSettings)
445{
446 return i_webcamAttachInternal(aPath, aSettings, "HostWebcam", NULL);
447}
448
449HRESULT EmulatedUSB::i_webcamAttachInternal(const com::Utf8Str &aPath,
450 const com::Utf8Str &aSettings,
451 const char *pszDriver,
452 void *pvObject)
453{
454 HRESULT hrc = S_OK;
455
456 const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
457
458 Console::SafeVMPtr ptrVM(m.pConsole);
459 if (ptrVM.isOk())
460 {
461 EUSBWEBCAM *p = new EUSBWEBCAM();
462 if (p)
463 {
464 hrc = p->Initialize(m.pConsole, this, &path, &aSettings, pvObject);
465 if (SUCCEEDED(hrc))
466 {
467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
468 WebcamsMap::const_iterator it = m.webcams.find(path);
469 if (it == m.webcams.end())
470 {
471 p->AddRef();
472 try
473 {
474 m.webcams[path] = p;
475 }
476 catch (std::bad_alloc &)
477 {
478 hrc = E_OUTOFMEMORY;
479 }
480 catch (...)
481 {
482 hrc = E_FAIL;
483 }
484 p->enmStatus = EUSBDEVICE_ATTACHING;
485 }
486 else
487 {
488 hrc = E_FAIL;
489 }
490 }
491
492 if (SUCCEEDED(hrc))
493 {
494 hrc = p->Attach(m.pConsole, ptrVM.rawUVM(), pszDriver);
495 }
496
497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
498 if (SUCCEEDED(hrc))
499 {
500 p->enmStatus = EUSBDEVICE_ATTACHED;
501 }
502 else
503 {
504 if (p->enmStatus != EUSBDEVICE_CREATED)
505 {
506 m.webcams.erase(path);
507 }
508 }
509 alock.release();
510
511 p->Release();
512 }
513 else
514 {
515 hrc = E_OUTOFMEMORY;
516 }
517 }
518 else
519 {
520 hrc = VBOX_E_INVALID_VM_STATE;
521 }
522
523 return hrc;
524}
525
526HRESULT EmulatedUSB::webcamDetach(const com::Utf8Str &aPath)
527{
528 return i_webcamDetachInternal(aPath);
529}
530
531HRESULT EmulatedUSB::i_webcamDetachInternal(const com::Utf8Str &aPath)
532{
533 HRESULT hrc = S_OK;
534
535 const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
536
537 Console::SafeVMPtr ptrVM(m.pConsole);
538 if (ptrVM.isOk())
539 {
540 EUSBWEBCAM *p = NULL;
541
542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
543 WebcamsMap::iterator it = m.webcams.find(path);
544 if (it != m.webcams.end())
545 {
546 if (it->second->enmStatus == EUSBDEVICE_ATTACHED)
547 {
548 p = it->second;
549 m.webcams.erase(it);
550 }
551 }
552 alock.release();
553
554 if (p)
555 {
556 hrc = p->Detach(m.pConsole, ptrVM.rawUVM());
557 p->Release();
558 }
559 else
560 {
561 hrc = E_INVALIDARG;
562 }
563 }
564 else
565 {
566 hrc = VBOX_E_INVALID_VM_STATE;
567 }
568
569 return hrc;
570}
571
572/* static */ DECLCALLBACK(int) EmulatedUSB::eusbCallbackEMT(EmulatedUSB *pThis, char *pszId, uint32_t iEvent,
573 void *pvData, uint32_t cbData)
574{
575 LogRelFlowFunc(("id %s event %d, data %p %d\n", pszId, iEvent, pvData, cbData));
576
577 NOREF(cbData);
578
579 int rc = VINF_SUCCESS;
580 if (iEvent == 0)
581 {
582 com::Utf8Str path;
583 HRESULT hr = pThis->webcamPathFromId(&path, pszId);
584 if (SUCCEEDED(hr))
585 {
586 hr = pThis->webcamDetach(path);
587 if (FAILED(hr))
588 {
589 rc = VERR_INVALID_STATE;
590 }
591 }
592 else
593 {
594 rc = VERR_NOT_FOUND;
595 }
596 }
597 else
598 {
599 rc = VERR_INVALID_PARAMETER;
600 }
601
602 RTMemFree(pszId);
603 RTMemFree(pvData);
604
605 LogRelFlowFunc(("rc %Rrc\n", rc));
606 return rc;
607}
608
609/* static */ DECLCALLBACK(int) EmulatedUSB::i_eusbCallback(void *pv, const char *pszId, uint32_t iEvent,
610 const void *pvData, uint32_t cbData)
611{
612 /* Make a copy of parameters, forward to EMT and leave the callback to not hold any lock in the device. */
613 int rc = VINF_SUCCESS;
614
615 void *pvIdCopy = NULL;
616 void *pvDataCopy = NULL;
617 if (cbData > 0)
618 {
619 pvDataCopy = RTMemDup(pvData, cbData);
620 if (!pvDataCopy)
621 {
622 rc = VERR_NO_MEMORY;
623 }
624 }
625
626 if (RT_SUCCESS(rc))
627 {
628 pvIdCopy = RTMemDup(pszId, strlen(pszId) + 1);
629 if (!pvIdCopy)
630 {
631 rc = VERR_NO_MEMORY;
632 }
633 }
634
635 if (RT_SUCCESS(rc))
636 {
637 EmulatedUSB *pThis = (EmulatedUSB *)pv;
638 Console::SafeVMPtr ptrVM(pThis->m.pConsole);
639 if (ptrVM.isOk())
640 {
641 /* No wait. */
642 rc = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), 0 /* idDstCpu */,
643 (PFNRT)EmulatedUSB::eusbCallbackEMT, 5,
644 pThis, pvIdCopy, iEvent, pvDataCopy, cbData);
645 }
646 else
647 {
648 rc = VERR_INVALID_STATE;
649 }
650 }
651
652 if (RT_FAILURE(rc))
653 {
654 RTMemFree(pvIdCopy);
655 RTMemFree(pvDataCopy);
656 }
657
658 return rc;
659}
660
661HRESULT EmulatedUSB::webcamPathFromId(com::Utf8Str *pPath, const char *pszId)
662{
663 HRESULT hr = S_OK;
664
665 Console::SafeVMPtr ptrVM(m.pConsole);
666 if (ptrVM.isOk())
667 {
668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
669 WebcamsMap::const_iterator it;
670 for (it = m.webcams.begin(); it != m.webcams.end(); ++it)
671 {
672 EUSBWEBCAM *p = it->second;
673 if (p->HasId(pszId))
674 {
675 *pPath = it->first;
676 break;
677 }
678 }
679
680 if (it == m.webcams.end())
681 {
682 hr = E_FAIL;
683 }
684 alock.release();
685 }
686 else
687 {
688 hr = VBOX_E_INVALID_VM_STATE;
689 }
690
691 return hr;
692}
693
694/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use