VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostDebugAudio.cpp@ 70013

Last change on this file since 70013 was 70013, checked in by vboxsync, 7 years ago

Audio: Unified audio debug / dumping code to also get device DMA data when enabled at runtime.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: DrvHostDebugAudio.cpp 70013 2017-12-08 11:52:00Z vboxsync $ */
2/** @file
3 * Debug audio driver.
4 *
5 * Host backend for dumping and injecting audio data from/to the device emulation.
6 */
7
8/*
9 * Copyright (C) 2016-2017 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include <iprt/alloc.h>
21#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
22
23#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
24#include <VBox/log.h>
25#include <VBox/vmm/pdmaudioifs.h>
26
27#include "DrvAudio.h"
28#include "VBoxDD.h"
29
30
31/**
32 * Structure for keeping a debug input/output stream.
33 */
34typedef struct DEBUGAUDIOSTREAM
35{
36 /** The stream's acquired configuration. */
37 PPDMAUDIOSTREAMCFG pCfg;
38 /** Audio file to dump output to or read input from. */
39 PPDMAUDIOFILE pFile;
40 union
41 {
42 struct
43 {
44 /** Timestamp of last captured samples. */
45 uint64_t tsLastCaptured;
46 } In;
47 struct
48 {
49 uint8_t *auPlayBuffer;
50 uint32_t cbPlayBuffer;
51 } Out;
52 };
53} DEBUGAUDIOSTREAM, *PDEBUGAUDIOSTREAM;
54
55/**
56 * Debug audio driver instance data.
57 * @implements PDMIAUDIOCONNECTOR
58 */
59typedef struct DRVHOSTDEBUGAUDIO
60{
61 /** Pointer to the driver instance structure. */
62 PPDMDRVINS pDrvIns;
63 /** Pointer to host audio interface. */
64 PDMIHOSTAUDIO IHostAudio;
65} DRVHOSTDEBUGAUDIO, *PDRVHOSTDEBUGAUDIO;
66
67/*******************************************PDM_AUDIO_DRIVER******************************/
68
69
70/**
71 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
72 */
73static DECLCALLBACK(int) drvHostDebugAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
74{
75 RT_NOREF(pInterface);
76 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
77
78 pBackendCfg->cbStreamOut = sizeof(DEBUGAUDIOSTREAM);
79 pBackendCfg->cbStreamIn = sizeof(DEBUGAUDIOSTREAM);
80
81 pBackendCfg->cMaxStreamsOut = 1; /* Output */
82 pBackendCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
83
84 return VINF_SUCCESS;
85}
86
87
88/**
89 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
90 */
91static DECLCALLBACK(int) drvHostDebugAudioInit(PPDMIHOSTAUDIO pInterface)
92{
93 RT_NOREF(pInterface);
94
95 LogFlowFuncLeaveRC(VINF_SUCCESS);
96 return VINF_SUCCESS;
97}
98
99
100/**
101 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
102 */
103static DECLCALLBACK(void) drvHostDebugAudioShutdown(PPDMIHOSTAUDIO pInterface)
104{
105 RT_NOREF(pInterface);
106}
107
108
109/**
110 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
111 */
112static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostDebugAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
113{
114 RT_NOREF(enmDir);
115 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
116
117 return PDMAUDIOBACKENDSTS_RUNNING;
118}
119
120
121static int debugCreateStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
122 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
123{
124 RT_NOREF(pDrv, pStreamDbg, pCfgReq);
125
126 if (pCfgAcq)
127 pCfgAcq->cFrameBufferHint = _1K;
128
129 return VINF_SUCCESS;
130}
131
132
133static int debugCreateStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg,
134 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
135{
136 RT_NOREF(pDrv);
137
138 int rc = VINF_SUCCESS;
139
140 pStreamDbg->Out.cbPlayBuffer = _1K * PDMAUDIOSTREAMCFG_F2B(pCfgReq, 1); /** @todo Make this configurable? */
141 pStreamDbg->Out.auPlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
142 if (!pStreamDbg->Out.auPlayBuffer)
143 rc = VERR_NO_MEMORY;
144
145 if (RT_SUCCESS(rc))
146 {
147 char szTemp[RTPATH_MAX];
148 rc = RTPathTemp(szTemp, sizeof(szTemp));
149 if (RT_SUCCESS(rc))
150 {
151 char szFile[RTPATH_MAX];
152 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, "DebugAudioOut",
153 pDrv->pDrvIns->iInstance, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
154 if (RT_SUCCESS(rc))
155 {
156 rc = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE, &pStreamDbg->pFile);
157 if (RT_SUCCESS(rc))
158 {
159 rc = DrvAudioHlpFileOpen(pStreamDbg->pFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
160 &pCfgReq->Props);
161 }
162
163 if (RT_FAILURE(rc))
164 LogRel(("DebugAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
165 }
166 else
167 LogRel(("DebugAudio: Unable to build file name for temp dir '%s': %Rrc\n", szTemp, rc));
168 }
169 else
170 LogRel(("DebugAudio: Unable to retrieve temp dir: %Rrc\n", rc));
171 }
172
173 if (RT_SUCCESS(rc))
174 {
175 if (pCfgAcq)
176 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer);
177 }
178
179 return rc;
180}
181
182
183/**
184 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
185 */
186static DECLCALLBACK(int) drvHostDebugAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
187 PPDMAUDIOBACKENDSTREAM pStream,
188 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
189{
190 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
191 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
192 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
193 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
194
195 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
196 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
197
198 int rc;
199 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
200 rc = debugCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
201 else
202 rc = debugCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
203
204 if (RT_SUCCESS(rc))
205 {
206 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
207 if (!pStreamDbg->pCfg)
208 rc = VERR_NO_MEMORY;
209 }
210
211 return rc;
212}
213
214
215/**
216 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
217 */
218static DECLCALLBACK(int) drvHostDebugAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
219 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
220 uint32_t *pcxWritten)
221{
222 RT_NOREF(pInterface);
223 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
224
225 uint32_t cbWrittenTotal = 0;
226
227 uint32_t cbAvail = cxBuf;
228 while (cbAvail)
229 {
230 uint32_t cbChunk = RT_MIN(cbAvail, pStreamDbg->Out.cbPlayBuffer);
231
232 memcpy(pStreamDbg->Out.auPlayBuffer, (uint8_t *)pvBuf + cbWrittenTotal, cbChunk);
233
234 int rc2 = DrvAudioHlpFileWrite(pStreamDbg->pFile, pStreamDbg->Out.auPlayBuffer, cbChunk, 0 /* fFlags */);
235 if (RT_FAILURE(rc2))
236 {
237 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
238 break;
239 }
240
241 Assert(cbAvail >= cbChunk);
242 cbAvail -= cbChunk;
243
244 cbWrittenTotal += cbChunk;
245 }
246
247 if (pcxWritten)
248 *pcxWritten = cbWrittenTotal;
249
250 return VINF_SUCCESS;
251}
252
253
254/**
255 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
256 */
257static DECLCALLBACK(int) drvHostDebugAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
258 void *pvBuf, uint32_t cxBuf, uint32_t *pcxRead)
259{
260 RT_NOREF(pInterface, pStream, pvBuf, cxBuf);
261
262 /* Never capture anything. */
263 if (pcxRead)
264 *pcxRead = 0;
265
266 return VINF_SUCCESS;
267}
268
269
270static int debugDestroyStreamIn(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
271{
272 RT_NOREF(pDrv, pStreamDbg);
273 return VINF_SUCCESS;
274}
275
276
277static int debugDestroyStreamOut(PDRVHOSTDEBUGAUDIO pDrv, PDEBUGAUDIOSTREAM pStreamDbg)
278{
279 RT_NOREF(pDrv);
280
281 if (pStreamDbg->Out.auPlayBuffer)
282 {
283 RTMemFree(pStreamDbg->Out.auPlayBuffer);
284 pStreamDbg->Out.auPlayBuffer = NULL;
285 }
286
287 DrvAudioHlpFileDestroy(pStreamDbg->pFile);
288
289 return VINF_SUCCESS;
290}
291
292
293/**
294 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
295 */
296static DECLCALLBACK(int) drvHostDebugAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
297{
298 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
299
300 PDRVHOSTDEBUGAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTDEBUGAUDIO, IHostAudio);
301 PDEBUGAUDIOSTREAM pStreamDbg = (PDEBUGAUDIOSTREAM)pStream;
302
303 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
304 return VINF_SUCCESS;
305
306 int rc;
307 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
308 rc = debugDestroyStreamIn (pDrv, pStreamDbg);
309 else
310 rc = debugDestroyStreamOut(pDrv, pStreamDbg);
311
312 if (RT_SUCCESS(rc))
313 {
314 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
315 pStreamDbg->pCfg = NULL;
316 }
317
318 return rc;
319}
320
321
322/**
323 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
324 */
325static DECLCALLBACK(int) drvHostDebugAudioStreamControl(PPDMIHOSTAUDIO pInterface,
326 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
327{
328 RT_NOREF(enmStreamCmd);
329 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
330 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
331
332 return VINF_SUCCESS;
333}
334
335
336/**
337 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
338 */
339static DECLCALLBACK(uint32_t) drvHostDebugAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
340{
341 RT_NOREF(pInterface, pStream);
342
343 return 0; /* Never capture anything. */
344}
345
346
347/**
348 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
349 */
350static DECLCALLBACK(uint32_t) drvHostDebugAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
351{
352 RT_NOREF(pInterface, pStream);
353
354 return UINT32_MAX;
355}
356
357
358/**
359 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
360 */
361static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostDebugAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
362{
363 RT_NOREF(pInterface, pStream);
364
365 return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED);
366}
367
368
369/**
370 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
371 */
372static DECLCALLBACK(int) drvHostDebugAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
373{
374 RT_NOREF(pInterface, pStream);
375 return VINF_SUCCESS;
376}
377
378
379/**
380 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
381 */
382static DECLCALLBACK(void *) drvHostDebugAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
383{
384 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
385 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
386
387 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
388 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
389 return NULL;
390}
391
392
393/**
394 * Constructs a Null audio driver instance.
395 *
396 * @copydoc FNPDMDRVCONSTRUCT
397 */
398static DECLCALLBACK(int) drvHostDebugAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
399{
400 RT_NOREF(pCfg, fFlags);
401 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
402 PDRVHOSTDEBUGAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDEBUGAUDIO);
403 LogRel(("Audio: Initializing DEBUG driver\n"));
404
405 /*
406 * Init the static parts.
407 */
408 pThis->pDrvIns = pDrvIns;
409 /* IBase */
410 pDrvIns->IBase.pfnQueryInterface = drvHostDebugAudioQueryInterface;
411 /* IHostAudio */
412 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostDebugAudio);
413
414#ifdef VBOX_AUDIO_DEBUG_DUMP_PCM_DATA
415 RTFileDelete(VBOX_AUDIO_DEBUG_DUMP_PCM_DATA_PATH "AudioDebugOutput.pcm");
416#endif
417
418 return VINF_SUCCESS;
419}
420
421/**
422 * Char driver registration record.
423 */
424const PDMDRVREG g_DrvHostDebugAudio =
425{
426 /* u32Version */
427 PDM_DRVREG_VERSION,
428 /* szName */
429 "DebugAudio",
430 /* szRCMod */
431 "",
432 /* szR0Mod */
433 "",
434 /* pszDescription */
435 "Debug audio host driver",
436 /* fFlags */
437 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
438 /* fClass. */
439 PDM_DRVREG_CLASS_AUDIO,
440 /* cMaxInstances */
441 ~0U,
442 /* cbInstance */
443 sizeof(DRVHOSTDEBUGAUDIO),
444 /* pfnConstruct */
445 drvHostDebugAudioConstruct,
446 /* pfnDestruct */
447 NULL,
448 /* pfnRelocate */
449 NULL,
450 /* pfnIOCtl */
451 NULL,
452 /* pfnPowerOn */
453 NULL,
454 /* pfnReset */
455 NULL,
456 /* pfnSuspend */
457 NULL,
458 /* pfnResume */
459 NULL,
460 /* pfnAttach */
461 NULL,
462 /* pfnDetach */
463 NULL,
464 /* pfnPowerOff */
465 NULL,
466 /* pfnSoftReset */
467 NULL,
468 /* u32EndVersion */
469 PDM_DRVREG_VERSION
470};
471
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