VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostValidationKit.cpp@ 77436

Last change on this file since 77436 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 KB
Line 
1/* $Id: DrvHostValidationKit.cpp 76553 2019-01-01 01:45:53Z 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-2019 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 PPDMAUDIOFILE pFile;
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 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit audio driver");
86
87 pBackendCfg->cbStreamOut = sizeof(VAKITAUDIOSTREAM);
88 pBackendCfg->cbStreamIn = sizeof(VAKITAUDIOSTREAM);
89
90 pBackendCfg->cMaxStreamsOut = 1; /* Output */
91 pBackendCfg->cMaxStreamsIn = 0; /* No input supported yet. */
92
93 return VINF_SUCCESS;
94}
95
96
97/**
98 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
99 */
100static DECLCALLBACK(int) drvHostVaKitAudioInit(PPDMIHOSTAUDIO pInterface)
101{
102 RT_NOREF(pInterface);
103
104 LogFlowFuncLeaveRC(VINF_SUCCESS);
105 return VINF_SUCCESS;
106}
107
108
109/**
110 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
111 */
112static DECLCALLBACK(void) drvHostVaKitAudioShutdown(PPDMIHOSTAUDIO pInterface)
113{
114 RT_NOREF(pInterface);
115}
116
117
118/**
119 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
120 */
121static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostVaKitAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
122{
123 RT_NOREF(enmDir);
124 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
125
126 return PDMAUDIOBACKENDSTS_RUNNING;
127}
128
129
130static int vakitCreateStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
131 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
132{
133 RT_NOREF(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
134
135 return VINF_SUCCESS;
136}
137
138
139static int vakitCreateStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg,
140 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
141{
142 RT_NOREF(pDrv, pCfgAcq);
143
144 int rc = VINF_SUCCESS;
145
146 pStreamDbg->tsStarted = 0;
147 pStreamDbg->uSamplesSinceStarted = 0;
148 pStreamDbg->Out.tsLastPlayed = 0;
149 pStreamDbg->Out.cbPlayBuffer = DrvAudioHlpFramesToBytes(pCfgReq->Backend.cfBufferSize, &pCfgReq->Props);
150 pStreamDbg->Out.pu8PlayBuffer = (uint8_t *)RTMemAlloc(pStreamDbg->Out.cbPlayBuffer);
151 if (!pStreamDbg->Out.pu8PlayBuffer)
152 rc = VERR_NO_MEMORY;
153
154 if (RT_SUCCESS(rc))
155 {
156 char szTemp[RTPATH_MAX];
157 rc = RTPathTemp(szTemp, sizeof(szTemp));
158
159 RTPathAppend(szTemp, sizeof(szTemp), "VBoxTestTmp\\VBoxAudioValKit");
160
161 if (RT_SUCCESS(rc))
162 {
163 char szFile[RTPATH_MAX + 1];
164
165 rc = DrvAudioHlpFileNameGet(szFile, RT_ELEMENTS(szFile), szTemp, "VaKit",
166 0 /* Instance */, PDMAUDIOFILETYPE_WAV, PDMAUDIOFILENAME_FLAG_NONE);
167 if (RT_SUCCESS(rc))
168 {
169 rc = DrvAudioHlpFileCreate(PDMAUDIOFILETYPE_WAV, szFile, PDMAUDIOFILE_FLAG_NONE, &pStreamDbg->pFile);
170 if (RT_SUCCESS(rc))
171 rc = DrvAudioHlpFileOpen(pStreamDbg->pFile, PDMAUDIOFILE_DEFAULT_OPEN_FLAGS, &pCfgReq->Props);
172 }
173
174 if (RT_FAILURE(rc))
175 {
176 LogRel(("VaKitAudio: Creating output file '%s' failed with %Rrc\n", szFile, rc));
177 }
178 else
179 {
180 size_t cch;
181 char szTimingInfo[128];
182 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "# %dHz %dch %dbit\n",
183 pCfgReq->Props.uHz, pCfgReq->Props.cChannels, pCfgReq->Props.cBytes * 8);
184
185 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
186 }
187 }
188 else
189 LogRel(("VaKitAudio: Unable to retrieve temp dir: %Rrc\n", rc));
190 }
191
192 return rc;
193}
194
195
196/**
197 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
198 */
199static DECLCALLBACK(int) drvHostVaKitAudioStreamCreate(PPDMIHOSTAUDIO pInterface,
200 PPDMAUDIOBACKENDSTREAM pStream,
201 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
202{
203 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
204 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
205 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
206 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
207
208 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
209 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
210
211 int rc;
212 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
213 rc = vakitCreateStreamIn( pDrv, pStreamDbg, pCfgReq, pCfgAcq);
214 else
215 rc = vakitCreateStreamOut(pDrv, pStreamDbg, pCfgReq, pCfgAcq);
216
217 if (RT_SUCCESS(rc))
218 {
219 pStreamDbg->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
220 if (!pStreamDbg->pCfg)
221 rc = VERR_NO_MEMORY;
222 }
223
224 return rc;
225}
226
227
228/**
229 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
230 */
231static DECLCALLBACK(int) drvHostVaKitAudioStreamPlay(PPDMIHOSTAUDIO pInterface,
232 PPDMAUDIOBACKENDSTREAM pStream, const void *pvBuf, uint32_t cxBuf,
233 uint32_t *pcxWritten)
234{
235 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
236 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
237 RT_NOREF(pDrv);
238
239 uint64_t tsSinceStart;
240 size_t cch;
241 char szTimingInfo[128];
242
243 if (pStreamDbg->tsStarted == 0)
244 {
245 pStreamDbg->tsStarted = RTTimeNanoTS();
246 tsSinceStart = 0;
247 }
248 else
249 {
250 tsSinceStart = RTTimeNanoTS() - pStreamDbg->tsStarted;
251 }
252
253 // Microseconds are used everythere below
254 uint32_t sBuf = cxBuf >> pStreamDbg->pCfg->Props.cShift;
255 cch = RTStrPrintf(szTimingInfo, sizeof(szTimingInfo), "%d %d %d %d\n",
256 (uint32_t)(tsSinceStart / 1000), // Host time elapsed since Guest submitted the first buffer for playback
257 (uint32_t)(pStreamDbg->uSamplesSinceStarted * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long all the samples submitted previously were played
258 (uint32_t)(sBuf * 1.0E6 / pStreamDbg->pCfg->Props.uHz), // how long a new uSamplesReady samples should\will be played
259 sBuf);
260 RTFileWrite(pStreamDbg->hFileTiming, szTimingInfo, cch, NULL);
261 pStreamDbg->uSamplesSinceStarted += sBuf;
262
263 /* Remember when samples were consumed. */
264 // pStreamDbg->Out.tsLastPlayed = PDMDrvHlpTMGetVirtualTime(pDrv->pDrvIns);;
265
266 int rc2 = DrvAudioHlpFileWrite(pStreamDbg->pFile, pvBuf, cxBuf, 0 /* fFlags */);
267 if (RT_FAILURE(rc2))
268 LogRel(("VaKitAudio: Writing output failed with %Rrc\n", rc2));
269
270 *pcxWritten = cxBuf;
271
272 return VINF_SUCCESS;
273}
274
275
276/**
277 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
278 */
279static DECLCALLBACK(int) drvHostVaKitAudioStreamCapture(PPDMIHOSTAUDIO pInterface,
280 PPDMAUDIOBACKENDSTREAM pStream, void *pvBuf, uint32_t cxBuf,
281 uint32_t *pcxRead)
282{
283 RT_NOREF(pInterface, pStream, pvBuf, cxBuf);
284
285 /* Never capture anything. */
286 if (pcxRead)
287 *pcxRead = 0;
288
289 return VINF_SUCCESS;
290}
291
292
293static int vakitDestroyStreamIn(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
294{
295 RT_NOREF(pDrv, pStreamDbg);
296 return VINF_SUCCESS;
297}
298
299
300static int vakitDestroyStreamOut(PDRVHOSTVAKITAUDIO pDrv, PVAKITAUDIOSTREAM pStreamDbg)
301{
302 RT_NOREF(pDrv);
303
304 if (pStreamDbg->Out.pu8PlayBuffer)
305 {
306 RTMemFree(pStreamDbg->Out.pu8PlayBuffer);
307 pStreamDbg->Out.pu8PlayBuffer = NULL;
308 }
309
310 if (pStreamDbg->pFile)
311 {
312 size_t cbDataSize = DrvAudioHlpFileGetDataSize(pStreamDbg->pFile);
313 if (cbDataSize)
314 LogRel(("VaKitAudio: Created output file '%s' (%zu bytes)\n", pStreamDbg->pFile->szName, cbDataSize));
315
316 DrvAudioHlpFileDestroy(pStreamDbg->pFile);
317 pStreamDbg->pFile = NULL;
318 }
319
320 return VINF_SUCCESS;
321}
322
323
324static DECLCALLBACK(int) drvHostVaKitAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
325{
326 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
327
328 PDRVHOSTVAKITAUDIO pDrv = RT_FROM_MEMBER(pInterface, DRVHOSTVAKITAUDIO, IHostAudio);
329 PVAKITAUDIOSTREAM pStreamDbg = (PVAKITAUDIOSTREAM)pStream;
330
331 if (!pStreamDbg->pCfg) /* Not (yet) configured? Skip. */
332 return VINF_SUCCESS;
333
334 int rc;
335 if (pStreamDbg->pCfg->enmDir == PDMAUDIODIR_IN)
336 rc = vakitDestroyStreamIn (pDrv, pStreamDbg);
337 else
338 rc = vakitDestroyStreamOut(pDrv, pStreamDbg);
339
340 if (RT_SUCCESS(rc))
341 {
342 DrvAudioHlpStreamCfgFree(pStreamDbg->pCfg);
343 pStreamDbg->pCfg = NULL;
344 }
345
346 return rc;
347}
348
349static DECLCALLBACK(int) drvHostVaKitAudioStreamControl(PPDMIHOSTAUDIO pInterface,
350 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
351{
352 RT_NOREF(enmStreamCmd);
353 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
354 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
355
356 return VINF_SUCCESS;
357}
358
359/**
360 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
361 */
362static DECLCALLBACK(uint32_t) drvHostVaKitAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
363{
364 RT_NOREF(pInterface, pStream);
365
366 return UINT32_MAX;
367}
368
369
370/**
371 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
372 */
373static DECLCALLBACK(uint32_t) drvHostVaKitAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
374{
375 RT_NOREF(pInterface, pStream);
376
377 return UINT32_MAX;
378}
379
380static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostVaKitAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
381{
382 RT_NOREF(pInterface, pStream);
383
384 return (PDMAUDIOSTREAMSTS_FLAG_INITIALIZED | PDMAUDIOSTREAMSTS_FLAG_ENABLED);
385}
386
387static DECLCALLBACK(int) drvHostVaKitAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
388{
389 RT_NOREF(pInterface, pStream);
390 return VINF_SUCCESS;
391}
392
393
394/**
395 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
396 */
397static DECLCALLBACK(void *) drvHostVaKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
398{
399 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
400 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
401
402 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
403 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
404 return NULL;
405}
406
407
408/**
409 * Constructs a VaKit audio driver instance.
410 *
411 * @copydoc FNPDMDRVCONSTRUCT
412 */
413static DECLCALLBACK(int) drvHostVaKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
414{
415 RT_NOREF(pCfg, fFlags);
416 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
417 PDRVHOSTVAKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVAKITAUDIO);
418 LogRel(("Audio: Initializing VAKIT driver\n"));
419
420 /*
421 * Init the static parts.
422 */
423 pThis->pDrvIns = pDrvIns;
424 /* IBase */
425 pDrvIns->IBase.pfnQueryInterface = drvHostVaKitAudioQueryInterface;
426 /* IHostAudio */
427 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostVaKitAudio);
428
429 return VINF_SUCCESS;
430}
431
432/**
433 * Char driver registration record.
434 */
435const PDMDRVREG g_DrvHostValidationKitAudio =
436{
437 /* u32Version */
438 PDM_DRVREG_VERSION,
439 /* szName */
440 "ValidationKitAudio",
441 /* szRCMod */
442 "",
443 /* szR0Mod */
444 "",
445 /* pszDescription */
446 "ValidationKitAudio audio host driver",
447 /* fFlags */
448 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
449 /* fClass. */
450 PDM_DRVREG_CLASS_AUDIO,
451 /* cMaxInstances */
452 ~0U,
453 /* cbInstance */
454 sizeof(DRVHOSTVAKITAUDIO),
455 /* pfnConstruct */
456 drvHostVaKitAudioConstruct,
457 /* pfnDestruct */
458 NULL,
459 /* pfnRelocate */
460 NULL,
461 /* pfnIOCtl */
462 NULL,
463 /* pfnPowerOn */
464 NULL,
465 /* pfnReset */
466 NULL,
467 /* pfnSuspend */
468 NULL,
469 /* pfnResume */
470 NULL,
471 /* pfnAttach */
472 NULL,
473 /* pfnDetach */
474 NULL,
475 /* pfnPowerOff */
476 NULL,
477 /* pfnSoftReset */
478 NULL,
479 /* u32EndVersion */
480 PDM_DRVREG_VERSION
481};
482
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