VirtualBox

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

Last change on this file since 53401 was 53062, checked in by vboxsync, 10 years ago

USB: Integrate USB sniffer. Make it possible to specify a file to dump the traffic to when attaching a USB device with VBoxManage

  • 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 53062 2014-10-15 12:34:18Z 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 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, "drv:", strlen("drv:")) == 0)
240 {
241 pszSrc += strlen("drv:");
242 fDev = false;
243 }
244 else if (RTStrNICmp(pszSrc, "dev:", strlen("dev:")) == 0)
245 {
246 pszSrc += strlen("dev:");
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 WebcamsMap::iterator it = m.webcams.begin();
395 while (it != m.webcams.end())
396 {
397 EUSBWEBCAM *p = it->second;
398 m.webcams.erase(it++);
399 p->Release();
400 }
401 alock.release();
402
403 /* Enclose the state transition Ready->InUninit->NotReady */
404 AutoUninitSpan autoUninitSpan(this);
405 if (autoUninitSpan.uninitDone())
406 return;
407}
408
409HRESULT EmulatedUSB::getWebcams(std::vector<com::Utf8Str> &aWebcams)
410{
411 HRESULT hrc = S_OK;
412
413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
414
415 try
416 {
417 aWebcams.resize(m.webcams.size());
418 }
419 catch (std::bad_alloc &)
420 {
421 hrc = E_OUTOFMEMORY;
422 }
423 catch (...)
424 {
425 hrc = E_FAIL;
426 }
427
428 if (SUCCEEDED(hrc))
429 {
430 size_t i;
431 WebcamsMap::const_iterator it;
432 for (i = 0, it = m.webcams.begin(); it != m.webcams.end(); ++it)
433 aWebcams[i++] = it->first;
434 }
435
436 return hrc;
437}
438
439static const Utf8Str s_pathDefault(".0");
440
441HRESULT EmulatedUSB::webcamAttach(const com::Utf8Str &aPath,
442 const com::Utf8Str &aSettings)
443{
444 return i_webcamAttachInternal(aPath, aSettings, "HostWebcam", NULL);
445}
446
447HRESULT EmulatedUSB::i_webcamAttachInternal(const com::Utf8Str &aPath,
448 const com::Utf8Str &aSettings,
449 const char *pszDriver,
450 void *pvObject)
451{
452 HRESULT hrc = S_OK;
453
454 const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
455
456 Console::SafeVMPtr ptrVM(m.pConsole);
457 if (ptrVM.isOk())
458 {
459 EUSBWEBCAM *p = new EUSBWEBCAM();
460 if (p)
461 {
462 hrc = p->Initialize(m.pConsole, this, &path, &aSettings, pvObject);
463 if (SUCCEEDED(hrc))
464 {
465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
466 WebcamsMap::const_iterator it = m.webcams.find(path);
467 if (it == m.webcams.end())
468 {
469 p->AddRef();
470 try
471 {
472 m.webcams[path] = p;
473 }
474 catch (std::bad_alloc &)
475 {
476 hrc = E_OUTOFMEMORY;
477 }
478 catch (...)
479 {
480 hrc = E_FAIL;
481 }
482 p->enmStatus = EUSBDEVICE_ATTACHING;
483 }
484 else
485 {
486 hrc = E_FAIL;
487 }
488 }
489
490 if (SUCCEEDED(hrc))
491 {
492 hrc = p->Attach(m.pConsole, ptrVM.rawUVM(), pszDriver);
493 }
494
495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
496 if (SUCCEEDED(hrc))
497 {
498 p->enmStatus = EUSBDEVICE_ATTACHED;
499 }
500 else
501 {
502 if (p->enmStatus != EUSBDEVICE_CREATED)
503 {
504 m.webcams.erase(path);
505 }
506 }
507 alock.release();
508
509 p->Release();
510 }
511 else
512 {
513 hrc = E_OUTOFMEMORY;
514 }
515 }
516 else
517 {
518 hrc = VBOX_E_INVALID_VM_STATE;
519 }
520
521 return hrc;
522}
523
524HRESULT EmulatedUSB::webcamDetach(const com::Utf8Str &aPath)
525{
526 return i_webcamDetachInternal(aPath);
527}
528
529HRESULT EmulatedUSB::i_webcamDetachInternal(const com::Utf8Str &aPath)
530{
531 HRESULT hrc = S_OK;
532
533 const Utf8Str &path = aPath.isEmpty() || aPath == "."? s_pathDefault: aPath;
534
535 Console::SafeVMPtr ptrVM(m.pConsole);
536 if (ptrVM.isOk())
537 {
538 EUSBWEBCAM *p = NULL;
539
540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
541 WebcamsMap::iterator it = m.webcams.find(path);
542 if (it != m.webcams.end())
543 {
544 if (it->second->enmStatus == EUSBDEVICE_ATTACHED)
545 {
546 p = it->second;
547 m.webcams.erase(it);
548 }
549 }
550 alock.release();
551
552 if (p)
553 {
554 hrc = p->Detach(m.pConsole, ptrVM.rawUVM());
555 p->Release();
556 }
557 else
558 {
559 hrc = E_INVALIDARG;
560 }
561 }
562 else
563 {
564 hrc = VBOX_E_INVALID_VM_STATE;
565 }
566
567 return hrc;
568}
569
570/* static */ DECLCALLBACK(int) EmulatedUSB::eusbCallbackEMT(EmulatedUSB *pThis, char *pszId, uint32_t iEvent,
571 void *pvData, uint32_t cbData)
572{
573 LogRelFlowFunc(("id %s event %d, data %p %d\n", pszId, iEvent, pvData, cbData));
574
575 NOREF(cbData);
576
577 int rc = VINF_SUCCESS;
578 if (iEvent == 0)
579 {
580 com::Utf8Str path;
581 HRESULT hr = pThis->webcamPathFromId(&path, pszId);
582 if (SUCCEEDED(hr))
583 {
584 hr = pThis->webcamDetach(path);
585 if (FAILED(hr))
586 {
587 rc = VERR_INVALID_STATE;
588 }
589 }
590 else
591 {
592 rc = VERR_NOT_FOUND;
593 }
594 }
595 else
596 {
597 rc = VERR_INVALID_PARAMETER;
598 }
599
600 RTMemFree(pszId);
601 RTMemFree(pvData);
602
603 LogRelFlowFunc(("rc %Rrc\n", rc));
604 return rc;
605}
606
607/* static */ DECLCALLBACK(int) EmulatedUSB::i_eusbCallback(void *pv, const char *pszId, uint32_t iEvent,
608 const void *pvData, uint32_t cbData)
609{
610 /* Make a copy of parameters, forward to EMT and leave the callback to not hold any lock in the device. */
611 int rc = VINF_SUCCESS;
612
613 void *pvIdCopy = NULL;
614 void *pvDataCopy = NULL;
615 if (cbData > 0)
616 {
617 pvDataCopy = RTMemDup(pvData, cbData);
618 if (!pvDataCopy)
619 {
620 rc = VERR_NO_MEMORY;
621 }
622 }
623
624 if (RT_SUCCESS(rc))
625 {
626 pvIdCopy = RTMemDup(pszId, strlen(pszId) + 1);
627 if (!pvIdCopy)
628 {
629 rc = VERR_NO_MEMORY;
630 }
631 }
632
633 if (RT_SUCCESS(rc))
634 {
635 EmulatedUSB *pThis = (EmulatedUSB *)pv;
636 Console::SafeVMPtr ptrVM(pThis->m.pConsole);
637 if (ptrVM.isOk())
638 {
639 /* No wait. */
640 rc = VMR3ReqCallNoWaitU(ptrVM.rawUVM(), 0 /* idDstCpu */,
641 (PFNRT)EmulatedUSB::eusbCallbackEMT, 5,
642 pThis, pvIdCopy, iEvent, pvDataCopy, cbData);
643 }
644 else
645 {
646 rc = VERR_INVALID_STATE;
647 }
648 }
649
650 if (RT_FAILURE(rc))
651 {
652 RTMemFree(pvIdCopy);
653 RTMemFree(pvDataCopy);
654 }
655
656 return rc;
657}
658
659HRESULT EmulatedUSB::webcamPathFromId(com::Utf8Str *pPath, const char *pszId)
660{
661 HRESULT hr = S_OK;
662
663 Console::SafeVMPtr ptrVM(m.pConsole);
664 if (ptrVM.isOk())
665 {
666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
667 WebcamsMap::const_iterator it;
668 for (it = m.webcams.begin(); it != m.webcams.end(); ++it)
669 {
670 EUSBWEBCAM *p = it->second;
671 if (p->HasId(pszId))
672 {
673 *pPath = it->first;
674 break;
675 }
676 }
677
678 if (it == m.webcams.end())
679 {
680 hr = E_FAIL;
681 }
682 alock.release();
683 }
684 else
685 {
686 hr = VBOX_E_INVALID_VM_STATE;
687 }
688
689 return hr;
690}
691
692/* 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