VirtualBox

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

Last change on this file since 49120 was 49120, checked in by vboxsync, 11 years ago

Main: emulated webcam updates.

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