VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostValidationKit.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: 15.0 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 70013 2017-12-08 11:52:00Z vboxsync $ */
2/** @file
3 * ValidationKit audio driver - host backend for dumping and injecting audio data
4 * from/to the device emulation.
5 */
6
7/*
8 * Copyright (C) 2016-2017 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#include <iprt/alloc.h>
20#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
21
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <VBox/log.h>
24#include <VBox/vmm/pdmaudioifs.h>
25
26#include "DrvAudio.h"
27#include "VBoxDD.h"
28
29
30/**
31 * Structure for keeping a VAKIT input/output stream.
32 */
33typedef struct VAKITAUDIOSTREAM
34{
35 /** The stream's acquired configuration. */
36 PPDMAUDIOSTREAMCFG pCfg;
37 /** Audio file to dump output to or read input from. */
38 PDMAUDIOFILE File;
39 /** Text file to store timing of audio buffers submittions**/
40 RTFILE hFileTiming;
41 /** Timestamp of the first play or record request**/
42 uint64_t tsStarted;
43 /** Total number of samples played or recorded so far**/
44 uint32_t uSamplesSinceStarted;
45 union
46 {
47 struct
48 {
49 /** Timestamp of last captured samples. */
50 uint64_t tsLastCaptured;
51 } In;
52 struct
53 {
54 /** Timestamp of last played samples. */
55 uint64_t tsLastPlayed;
56 uint8_t *pu8PlayBuffer;
57 uint32_t cbPlayBuffer;
58 } Out;
59 };
60} VAKITAUDIOSTREAM, *PVAKITAUDIOSTREAM;
61
62/**
63 * VAKIT audio driver instance data.
64 * @implements PDMIAUDIOCONNECTOR
65 */
66typedef struct DRVHOSTVAKITAUDIO
67{
68 /** Pointer to the driver instance structure. */
69 PPDMDRVINS pDrvIns;
70 /** Pointer to host audio interface. */
71 PDMIHOSTAUDIO IHostAudio;
72} DRVHOSTVAKITAUDIO, *PDRVHOSTVAKITAUDIO;
73
74/*******************************************PDM_AUDIO_DRIVER******************************/
75
76
77/**
78 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
79 */
80static DECLCALLBACK(int) drvHostVaKitAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
81{
82 RT_NOREF(pInterface);
83 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
84
85 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
86 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
87
88 pBackendCfg->cMaxStreamsOut = 1; /* Output */
89 pBackendCfg->cMaxStreamsIn = 2; /* Line input + microphone input. */
90
91 return VINF_SUCCESS;
92}
93
94
95/**
96 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
97 */
98static DECLCALLBACK(int) drvHostVaKitAudioInit(PPDMIHOSTAUDIO pInterface)
99{
100 RT_NOREF(pInterface);
101
102 LogFlowFuncLeaveRC(VINF_SUCCESS);
103 return VINF_SUCCESS;
104}
105
106
107/**
108 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
109 */
110static DECLCALLBACK(void) drvHostVaKitAudioShutdown(PPDMIHOSTAUDIO pInterface)
111{
112 RT_NOREF(pInterface);
113}
114
115
116/**
117 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
118 */
119static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostVaKitAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
120{
121 RT_NOREF(enmDir);
122 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
123
124 return PDMAUDIOBACKENDSTS_RUNNING;
125}
126
127
128static int debugCreateStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
129 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
130{
131 RT_NOREF(pDrv, pStreamDbg, pCfgReq);
132
133 if (pCfgAcq)
134 pCfgAcq->cFrameBufferHint = _1K;
135
136 return VINF_SUCCESS;
137}
138
139
140static int debugCreateStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
141 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
142{
143 RT_NOREF(pDrv);
144
145 int rc = VINF_SUCCESS;
146
147 pStreamDbg->tsStarted = 0;
148 pStreamDbg->uSamplesSinceStarted = 0;
149 pStreamDbg->Out.tsLastPlayed = 0;
150 pStreamDbg->Out.cbPlayBuffer = 16 * _1K * PDMAUDIOSTREAMCFG_F2B(pCfgReq, 1); /** @todo Make this configurable? */
151 pStreamDbg->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
152 if (!pStreamDbg->Out.pu8PlayBuffer)
153 rc = VERR_NO_MEMORY;
154
155 if (RT_SUCCESS(rc))
156 {
157 char szTemp[RTPATH_MAX];
158 rc = RTPathTemp(szTemp, sizeof(szTemp));
159
160 RTPathAppend(szTemp, sizeof(szTemp), "VBoxTestTmp\\VBoxAudioValKit");
161
162 if (RT_SUCCESS(rc))
163 {
164 char szFile[RTPATH_MAX];
165 rc = DrvAudioHlpGetFileName(szFile, RT_ELEMENTS(szFile), szTemp, NULL, PDMAUDIOFILETYPE_WAV);
166 if (RT_SUCCESS(rc))
167 {
168 LogFlowFunc(("%s\n", szFile));
169 rc = DrvAudioHlpFileOpen(&pStreamDbg->File, PDMAUDIOFILETYPE_WAV, szFile,
170 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
171 &pCfgReq->Props, PDMAUDIOFILE_FLAG_NONE);
172 if (RT_FAILURE(rc))
173 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
174
175 RTStrCat(szFile, sizeof(szFile), ".timing");
176 rc = RTFileOpen(&pStreamDbg->hFileTiming, szFile, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE);
177
178 if (RT_FAILURE(rc))
179 {
180 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
181 }
182 else
183 {
184 size_t cch;
185 char szTimingInfo[128];
186 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "# %dHz %dch %dbps\n",
187 pCfgReq->Props.uHz, pCfgReq->Props.cChannels, pCfgReq->Props.cBits);
188
189 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
190 }
191 }
192 else
193 LogRel(("VaKitAudio: Unable to build file name for temp dir '%s': %Rrc\n", szTemp, rc));
194 }
195 else
196 LogRel(("VaKitAudio: Unable to retrieve temp dir: %Rrc\n", rc));
197 }
198
199 if (RT_SUCCESS(rc))
200 {
201 if (pCfgAcq)
202 pCfgAcq->cFrameBufferHint = PDMAUDIOSTREAMCFG_B2F(pCfgAcq, pStreamDbg->Out.cbPlayBuffer);
203 }
204
205 return rc;
206}
207
208
209/**
210 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
211 */
212static DECLCALLBACK(int) drvHostVaKitAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
213 PPDMAUDIOBACKENDSTREAM pStream,
214 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
215{
216 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
217 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
218 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
219 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
220
221 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
222 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
223
224 int rc;
225 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
226 rc = debugCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
227 else
228 rc = debugCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
229
230 if (RT_SUCCESS(rc))
231 {
232 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
233 if (!pStreamDbg->pCfg)
234 rc = VERR_NO_MEMORY;
235 }
236
237 return rc;
238}
239
240
241/**
242 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
243 */
244static DECLCALLBACK(int) drvHostVaKitAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
245 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
246 uint32_t *pcxWritten)
247{
248 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
249 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
250 RT_NOREF(pDrv);
251
252 uint64_t tsSinceStart;
253 size_t cch;
254 char szTimingInfo[128];
255
256 if (pStreamDbg->tsStarted == 0)
257 {
258 pStreamDbg->tsStarted = RTTimeNanoTS();
259 tsSinceStart = 0;
260 }
261 else
262 {
263 tsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
264 }
265
266 // Microseconds are used everythere below
267 uint32_t sBuf = cxBuf >> pStreamDbg->pCfg->Props.cShift;
268 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
269 (uint32_t)(tsSinceStart / 1000), // Host time elapsed since Guest submitted the first buffer for playback
270 (uint32_t)(pStreamDbg->uSamplesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long all the samples submitted previously were played
271 (uint32_t)(sBuf * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long a new uSamplesReady samples should\will be played
272 sBuf);
273 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
274 pStreamDbg->uSamplesSinceStarted += sBuf;
275
276 /* Remember when samples were consumed. */
277 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);;
278
279 int rc2 = DrvAudioHlpFileWrite(&pStreamDbg->File, pvBuf, cxBuf, 0 /* fFlags */);
280 if (RT_FAILURE(rc2))
281 LogRel(("DebugAudio: Writing output failed with %Rrc\n", rc2));
282
283 *pcxWritten = cxBuf;
284
285 return VINF_SUCCESS;
286}
287
288
289/**
290 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
291 */
292static DECLCALLBACK(int) drvHostVaKitAudioStreamCapture(PPDMIHOSTAUDIO pInterface,
293 PPDMAUDIOBACKENDSTREAM pStream, void *pvBuf, uint32_t cxBuf,
294 uint32_t *pcxRead)
295{
296 RT_NOREF(pInterface, pStream, pvBuf, cxBuf);
297
298 /* Never capture anything. */
299 if (pcxRead)
300 *pcxRead = 0;
301
302 return VINF_SUCCESS;
303}
304
305
306static int debugDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
307{
308 RT_NOREF(pDrv, pStreamDbg);
309 return VINF_SUCCESS;
310}
311
312
313static int debugDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
314{
315 RT_NOREF(pDrv);
316
317 if (pStreamDbg->Out.pu8PlayBuffer)
318 {
319 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
320 pStreamDbg->Out.pu8PlayBuffer = NULL;
321 }
322
323 size_t cbDataSize = DrvAudioHlpFileGetDataSize(&pStreamDbg->File);
324
325 int rc = DrvAudioHlpFileClose(&pStreamDbg->File);
326 RTFileClose(pStreamDbg->hFileTiming);
327
328 if (RT_SUCCESS(rc))
329 {
330 /* Delete the file again if nothing but the header was written to it. */
331 bool fDeleteEmptyFiles = true; /** @todo Make deletion configurable? */
332
333 if ( !cbDataSize
334 && fDeleteEmptyFiles)
335 {
336 char szFile[RTPATH_MAX];
337
338 RTStrCopy(szFile, sizeof(szFile), pStreamDbg->File.szName);
339 RTFileDelete(szFile);
340
341 RTStrCat(szFile, sizeof(szFile), ".timing");
342 RTFileDelete(szFile);
343 }
344 else
345 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->File.szName, cbDataSize));
346 }
347
348 return rc;
349}
350
351
352static DECLCALLBACK(int) drvHostVaKitAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
353{
354 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
355
356 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
357 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
358
359 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
360 return VINF_SUCCESS;
361
362 int rc;
363 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
364 rc = debugDestroyStreamIn (pDrv, pStreamDbg);
365 else
366 rc = debugDestroyStreamOut(pDrv, pStreamDbg);
367
368 if (RT_SUCCESS(rc))
369 {
370 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
371 pStreamDbg->pCfg = NULL;
372 }
373
374 return rc;
375}
376
377static DECLCALLBACK(int) drvHostVaKitAudioStreamControl(PPDMIHOSTAUDIO pInterface,
378 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
379{
380 RT_NOREF(enmStreamCmd);
381 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
382 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
383
384 return VINF_SUCCESS;
385}
386
387/**
388 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
389 */
390static DECLCALLBACK(uint32_t) drvHostVaKitAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
391{
392 RT_NOREF(pInterface, pStream);
393
394 return UINT32_MAX;
395}
396
397
398/**
399 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
400 */
401static DECLCALLBACK(uint32_t) drvHostVaKitAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
402{
403 RT_NOREF(pInterface, pStream);
404
405 return UINT32_MAX;
406}
407
408static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostVaKitAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
409{
410 RT_NOREF(pInterface, pStream);
411
412 return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED);
413}
414
415static DECLCALLBACK(int) drvHostVaKitAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
416{
417 RT_NOREF(pInterface, pStream);
418 return VINF_SUCCESS;
419}
420
421
422/**
423 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
424 */
425static DECLCALLBACK(void *) drvHostVaKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
426{
427 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
428 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
429
430 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
431 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
432 return NULL;
433}
434
435
436/**
437 * Constructs a VaKit audio driver instance.
438 *
439 * @copydoc FNPDMDRVCONSTRUCT
440 */
441static DECLCALLBACK(int) drvHostVaKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
442{
443 RT_NOREF(pCfg, fFlags);
444 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
445 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
446 LogRel(("Audio: Initializing VAKIT driver\n"));
447
448 /*
449 * Init the static parts.
450 */
451 pThis->pDrvIns = pDrvIns;
452 /* IBase */
453 pDrvIns->IBase.pfnQueryInterface = drvHostVaKitAudioQueryInterface;
454 /* IHostAudio */
455 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostVaKitAudio);
456
457 return VINF_SUCCESS;
458}
459
460/**
461 * Char driver registration record.
462 */
463const PDMDRVREG g_DrvHostValidationKitAudio =
464{
465 /* u32Version */
466 PDM_DRVREG_VERSION,
467 /* szName */
468 "ValidationKitAudio",
469 /* szRCMod */
470 "",
471 /* szR0Mod */
472 "",
473 /* pszDescription */
474 "ValidationKitAudio audio host driver",
475 /* fFlags */
476 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
477 /* fClass. */
478 PDM_DRVREG_CLASS_AUDIO,
479 /* cMaxInstances */
480 ~0U,
481 /* cbInstance */
482 sizeof(DRVHOSTVAKITAUDIO),
483 /* pfnConstruct */
484 drvHostVaKitAudioConstruct,
485 /* pfnDestruct */
486 NULL,
487 /* pfnRelocate */
488 NULL,
489 /* pfnIOCtl */
490 NULL,
491 /* pfnPowerOn */
492 NULL,
493 /* pfnReset */
494 NULL,
495 /* pfnSuspend */
496 NULL,
497 /* pfnResume */
498 NULL,
499 /* pfnAttach */
500 NULL,
501 /* pfnDetach */
502 NULL,
503 /* pfnPowerOff */
504 NULL,
505 /* pfnSoftReset */
506 NULL,
507 /* u32EndVersion */
508 PDM_DRVREG_VERSION
509};
510
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