VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioValidationKit.cpp@ 89847

Last change on this file since 89847 was 89838, checked in by vboxsync, 4 years ago

Audio/ValKit: Factored out cleaning up the registered tests in the ValKit audio driver and also do so when ending a test set [build fix]. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.2 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 89838 2021-06-22 14:20:52Z vboxsync $ */
2/** @file
3 * Host audio driver - ValidationKit - For dumping and injecting audio data from/to the device emulation.
4 */
5
6/*
7 * Copyright (C) 2016-2021 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Defined Constants And Macros *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
23#include <iprt/dir.h>
24#include <iprt/env.h>
25#include <iprt/mem.h>
26#include <iprt/path.h>
27#include <iprt/stream.h>
28#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
29
30#include <VBox/log.h>
31#include <VBox/vmm/pdmaudioifs.h>
32#include <VBox/vmm/pdmaudioinline.h>
33
34#include "VBoxDD.h"
35#include "AudioHlp.h"
36#include "AudioTest.h"
37#include "AudioTestService.h"
38
39
40/*********************************************************************************************************************************
41* Structures and Typedefs *
42*********************************************************************************************************************************/
43/**
44 * Structure for keeping a Validation Kit input/output stream.
45 */
46typedef struct VALKITAUDIOSTREAM
47{
48 /** Common part. */
49 PDMAUDIOBACKENDSTREAM Core;
50 /** The stream's acquired configuration. */
51 PDMAUDIOSTREAMCFG Cfg;
52} VALKITAUDIOSTREAM;
53/** Pointer to a Validation Kit stream. */
54typedef VALKITAUDIOSTREAM *PVALKITAUDIOSTREAM;
55
56/**
57 * Test tone-specific instance data.
58 */
59typedef struct VALKITTESTTONEDATA
60{
61 union
62 {
63 struct
64 {
65 /** How many bytes to write. */
66 uint64_t cbToWrite;
67 /** How many bytes already written. */
68 uint64_t cbWritten;
69 } Rec;
70 struct
71 {
72 /** How many bytes to read. */
73 uint64_t cbToRead;
74 /** How many bytes already read. */
75 uint64_t cbRead;
76 } Play;
77 } u;
78 /** The test tone instance to use. */
79 AUDIOTESTTONE Tone;
80 /** The test tone parameters to use. */
81 AUDIOTESTTONEPARMS Parms;
82} VALKITTESTTONEDATA;
83
84/**
85 * Structure keeping a single Validation Kit test.
86 */
87typedef struct VALKITTESTDATA
88{
89 /** The list node. */
90 RTLISTNODE Node;
91 /** Index in test sequence (0-based). */
92 uint32_t idxTest;
93 /** Current test set entry to process. */
94 PAUDIOTESTENTRY pEntry;
95 /** Current test object to process. */
96 PAUDIOTESTOBJ pObj;
97 /** Stream configuration to use for this test. */
98 PDMAUDIOSTREAMCFG StreamCfg;
99 union
100 {
101 /** Test tone-specific data. */
102 VALKITTESTTONEDATA TestTone;
103 } t;
104 /** Time stamp (real, in ms) when test started. */
105 uint64_t msStartedTS;
106} VALKITTESTDATA;
107/** Pointer to Validation Kit test data. */
108typedef VALKITTESTDATA *PVALKITTESTDATA;
109
110/**
111 * Validation Kit audio driver instance data.
112 * @implements PDMIAUDIOCONNECTOR
113 */
114typedef struct DRVHOSTVALKITAUDIO
115{
116 /** Pointer to the driver instance structure. */
117 PPDMDRVINS pDrvIns;
118 /** Pointer to host audio interface. */
119 PDMIHOSTAUDIO IHostAudio;
120 /** Temporary path to use. */
121 char szPathTemp[RTPATH_MAX];
122 /** Output path to use. */
123 char szPathOut[RTPATH_MAX];
124 /** Current test set being handled. */
125 AUDIOTESTSET Set;
126 /** Number of total tests created. */
127 uint32_t cTestsTotal;
128 /** Number of tests in \a lstTestsRec. */
129 uint32_t cTestsRec;
130 /** List keeping the recording tests (FIFO). */
131 RTLISTANCHOR lstTestsRec;
132 /** Number of tests in \a lstTestsPlay. */
133 uint32_t cTestsPlay;
134 /** List keeping the recording tests (FIFO). */
135 RTLISTANCHOR lstTestsPlay;
136 /** Pointer to current test being processed. */
137 PVALKITTESTDATA pTestCur;
138 /** Critical section for serializing access across threads. */
139 RTCRITSECT CritSect;
140 /** The Audio Test Service (ATS) instance. */
141 ATSSERVER Srv;
142 /** Absolute path to the packed up test set archive.
143 * Keep it simple for now and only support one (open) archive at a time. */
144 char szTestSetArchive[RTPATH_MAX];
145 /** File handle to the (opened) test set archive for reading. */
146 RTFILE hTestSetArchive;
147
148} DRVHOSTVALKITAUDIO;
149/** Pointer to a Validation Kit host audio driver instance. */
150typedef DRVHOSTVALKITAUDIO *PDRVHOSTVALKITAUDIO;
151
152
153/*********************************************************************************************************************************
154* Internal test handling code *
155*********************************************************************************************************************************/
156
157/**
158 * Unregisters a ValKit test, common code.
159 *
160 * @param pTst Test to unregister.
161 * The pointer will be invalid afterwards.
162 */
163static void drvHostValKiUnregisterTest(PVALKITTESTDATA pTst)
164{
165 AssertPtrReturnVoid(pTst);
166
167 RTListNodeRemove(&pTst->Node);
168
169 AudioTestSetObjClose(pTst->pObj);
170 pTst->pObj = NULL;
171
172 if (pTst->pEntry) /* Set set entry assign? Mark as done. */
173 {
174 AssertPtrReturnVoid(pTst->pEntry);
175 AudioTestSetTestDone(pTst->pEntry);
176 pTst->pEntry = NULL;
177 }
178
179 RTMemFree(pTst);
180 pTst = NULL;
181}
182
183/**
184 * Unregisters a ValKit recording test.
185 *
186 * @param pThis ValKit audio driver instance.
187 * @param pTst Test to unregister.
188 * The pointer will be invalid afterwards.
189 */
190static void drvHostValKiUnregisterRecTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
191{
192 drvHostValKiUnregisterTest(pTst);
193
194 Assert(pThis->cTestsRec);
195 pThis->cTestsRec--;
196}
197
198/**
199 * Unregisters a ValKit playback test.
200 *
201 * @param pThis ValKit audio driver instance.
202 * @param pTst Test to unregister.
203 * The pointer will be invalid afterwards.
204 */
205static void drvHostValKiUnregisterPlayTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
206{
207 drvHostValKiUnregisterTest(pTst);
208
209 Assert(pThis->cTestsPlay);
210 pThis->cTestsPlay--;
211}
212
213/**
214 * Performs some internal cleanup / housekeeping of all registered tests.
215 *
216 * @param pThis ValKit audio driver instance.
217 */
218static void drvHostValKitCleanup(PDRVHOSTVALKITAUDIO pThis)
219{
220 LogRel(("Audio: Validation Kit: Cleaning up ...\n"));
221
222 if (pThis->cTestsRec)
223 LogRel(("Audio: Validation Kit: Warning: %RU32 guest recording tests still outstanding:\n", pThis->cTestsRec));
224
225 PVALKITTESTDATA pTst, pTstNext;
226 RTListForEachSafe(&pThis->lstTestsRec, pTst, pTstNext, VALKITTESTDATA, Node)
227 {
228 size_t const cbOutstanding = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
229 if (cbOutstanding)
230 LogRel(("Audio: Validation Kit: \tRecording test #%RU32 has %RU64 bytes (%RU32ms) outstanding\n",
231 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding)));
232 drvHostValKiUnregisterRecTest(pThis, pTst);
233 }
234
235 if (pThis->cTestsPlay)
236 LogRel(("Audio: Validation Kit: Warning: %RU32 guest playback tests still outstanding:\n", pThis->cTestsPlay));
237
238 RTListForEachSafe(&pThis->lstTestsPlay, pTst, pTstNext, VALKITTESTDATA, Node)
239 {
240 size_t const cbOutstanding = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
241 if (cbOutstanding)
242 LogRel(("Audio: Validation Kit: \tPlayback test #%RU32 has %RU64 bytes (%RU32ms) outstanding\n",
243 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding)));
244 drvHostValKiUnregisterPlayTest(pThis, pTst);
245 }
246
247 Assert(pThis->cTestsRec == 0);
248 Assert(pThis->cTestsPlay == 0);
249}
250
251
252/*********************************************************************************************************************************
253* ATS callback implementations *
254*********************************************************************************************************************************/
255
256/** @copydoc ATSCALLBACKS::pfnTestSetBegin */
257static DECLCALLBACK(int) drvHostValKitTestSetBegin(void const *pvUser, const char *pszTag)
258{
259 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
260
261 LogRel(("Audio: Validation Kit: Beginning test set '%s'\n", pszTag));
262 return AudioTestSetCreate(&pThis->Set, pThis->szPathTemp, pszTag);
263}
264
265/** @copydoc ATSCALLBACKS::pfnTestSetEnd */
266static DECLCALLBACK(int) drvHostValKitTestSetEnd(void const *pvUser, const char *pszTag)
267{
268 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
269
270 const PAUDIOTESTSET pSet = &pThis->Set;
271
272 LogRel(("Audio: Validation Kit: Ending test set '%s'\n", pszTag));
273
274 /* Close the test set first. */
275 AudioTestSetClose(pSet);
276
277 /* Before destroying the test environment, pack up the test set so
278 * that it's ready for transmission. */
279 int rc = AudioTestSetPack(pSet, pThis->szPathOut, pThis->szTestSetArchive, sizeof(pThis->szTestSetArchive));
280 if (RT_SUCCESS(rc))
281 LogRel(("Audio: Validation Kit: Packed up to '%s'\n", pThis->szTestSetArchive));
282
283 /* Do some internal housekeeping. */
284 drvHostValKitCleanup(pThis);
285
286 int rc2 = AudioTestSetWipe(pSet);
287 if (RT_SUCCESS(rc))
288 rc = rc2;
289
290 AudioTestSetDestroy(pSet);
291
292 if (RT_FAILURE(rc))
293 LogRel(("Audio: Validation Kit: Ending test set failed with %Rrc\n", rc));
294
295 return rc;
296}
297
298/** @copydoc ATSCALLBACKS::pfnTonePlay
299 *
300 * Creates and registers a new test tone guest recording test.
301 * This backend will play (inject) input data to the guest.
302 */
303static DECLCALLBACK(int) drvHostValKitRegisterGuestRecTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
304{
305 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
306
307 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
308 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
309
310 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
311
312 AudioTestToneInit(&pTestData->t.TestTone.Tone, &pToneParms->Props, pTestData->t.TestTone.Parms.dbFreqHz);
313
314 pTestData->t.TestTone.u.Rec.cbToWrite = PDMAudioPropsMilliToBytes(&pToneParms->Props,
315 pTestData->t.TestTone.Parms.msDuration);
316 int rc = RTCritSectEnter(&pThis->CritSect);
317 if (RT_SUCCESS(rc))
318 {
319 LogRel(("Audio: Validation Kit: Registered guest recording test #%RU32 (%RU32ms, %RU64 bytes)\n",
320 pThis->cTestsTotal, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Rec.cbToWrite));
321
322 RTListAppend(&pThis->lstTestsRec, &pTestData->Node);
323
324 pTestData->idxTest = pThis->cTestsTotal++;
325
326 pThis->cTestsRec++;
327
328 int rc2 = RTCritSectLeave(&pThis->CritSect);
329 AssertRC(rc2);
330 }
331
332 return VINF_SUCCESS;
333}
334
335/** @copydoc ATSCALLBACKS::pfnToneRecord
336 *
337 * Creates and registers a new test tone guest playback test.
338 * This backend will record the guest output data.
339 */
340static DECLCALLBACK(int) drvHostValKitRegisterGuestPlayTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
341{
342 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
343
344 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
345 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
346
347 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
348
349 pTestData->t.TestTone.u.Play.cbToRead = PDMAudioPropsMilliToBytes(&pToneParms->Props,
350 pTestData->t.TestTone.Parms.msDuration);
351 int rc = RTCritSectEnter(&pThis->CritSect);
352 if (RT_SUCCESS(rc))
353 {
354 LogRel(("Audio: Validation Kit: Registered guest playback test #%RU32 (%RU32ms, %RU64 bytes)\n",
355 pThis->cTestsTotal, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Play.cbToRead));
356
357 RTListAppend(&pThis->lstTestsPlay, &pTestData->Node);
358
359 pTestData->idxTest = pThis->cTestsTotal++;
360
361 pThis->cTestsPlay++;
362
363 int rc2 = RTCritSectLeave(&pThis->CritSect);
364 AssertRC(rc2);
365 }
366
367 return VINF_SUCCESS;
368}
369
370/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
371static DECLCALLBACK(int) drvHostValKitTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
372{
373 RT_NOREF(pszTag);
374
375 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
376
377 if (!RTFileExists(pThis->szTestSetArchive)) /* Has the archive successfully been created yet? */
378 return VERR_WRONG_ORDER;
379
380 int rc = RTFileOpen(&pThis->hTestSetArchive, pThis->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
381 if (RT_SUCCESS(rc))
382 {
383 uint64_t uSize;
384 rc = RTFileQuerySize(pThis->hTestSetArchive, &uSize);
385 if (RT_SUCCESS(rc))
386 LogRel(("Audio: Validation Kit: Sending test set '%s' (%zu bytes)\n", pThis->szTestSetArchive, uSize));
387 }
388
389 return rc;
390}
391
392/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
393static DECLCALLBACK(int) drvHostValKitTestSetSendReadCallback(void const *pvUser,
394 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
395{
396 RT_NOREF(pszTag);
397
398 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
399
400 return RTFileRead(pThis->hTestSetArchive, pvBuf, cbBuf, pcbRead);
401}
402
403/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
404static DECLCALLBACK(int) drvHostValKitTestSetSendEndCallback(void const *pvUser, const char *pszTag)
405{
406 RT_NOREF(pszTag);
407
408 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
409
410 int rc = RTFileClose(pThis->hTestSetArchive);
411 if (RT_SUCCESS(rc))
412 {
413 pThis->hTestSetArchive = NIL_RTFILE;
414 }
415
416 return rc;
417}
418
419
420/*********************************************************************************************************************************
421* PDMIHOSTAUDIO interface implementation *
422*********************************************************************************************************************************/
423
424/**
425 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
426 */
427static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
428{
429 RT_NOREF(pInterface);
430 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
431
432 /*
433 * Fill in the config structure.
434 */
435 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
436 pBackendCfg->cbStream = sizeof(VALKITAUDIOSTREAM);
437 pBackendCfg->fFlags = 0;
438 pBackendCfg->cMaxStreamsOut = 1; /* Output (Playback). */
439 pBackendCfg->cMaxStreamsIn = 1; /* Input (Recording). */
440
441 return VINF_SUCCESS;
442}
443
444
445/**
446 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
447 */
448static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
449{
450 RT_NOREF(enmDir);
451 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
452
453 return PDMAUDIOBACKENDSTS_RUNNING;
454}
455
456
457/**
458 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
459 */
460static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
461 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
462{
463 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
464 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
465 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
466 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
467 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
468 RT_NOREF(pThis);
469
470 int rc = VINF_SUCCESS;
471 PDMAudioStrmCfgCopy(&pStreamDbg->Cfg, pCfgAcq);
472 return rc;
473}
474
475/**
476 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
477 */
478static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
479 bool fImmediate)
480{
481 RT_NOREF(pInterface, fImmediate);
482 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
483 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
484
485 return VINF_SUCCESS;
486}
487
488
489/**
490 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
491 */
492static DECLCALLBACK(int) drvHostValKitAudioHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
493{
494 RT_NOREF(pInterface, pStream);
495 return VINF_SUCCESS;
496}
497
498
499/**
500 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
501 */
502static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
503{
504 RT_NOREF(pInterface, pStream);
505 return VINF_SUCCESS;
506}
507
508
509/**
510 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
511 */
512static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
513{
514 RT_NOREF(pInterface, pStream);
515 return VINF_SUCCESS;
516}
517
518
519/**
520 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
521 */
522static DECLCALLBACK(int) drvHostValKitAudioHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
523{
524 RT_NOREF(pInterface, pStream);
525 return VINF_SUCCESS;
526}
527
528
529/**
530 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
531 */
532static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
533{
534 RT_NOREF(pInterface, pStream);
535 return VINF_SUCCESS;
536}
537
538
539/**
540 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
541 */
542static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
543{
544 RT_NOREF(pStream);
545
546 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
547
548 int rc = RTCritSectEnter(&pThis->CritSect);
549 if (RT_SUCCESS(rc))
550 {
551 pThis->pTestCur = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
552
553 int rc2 = RTCritSectLeave(&pThis->CritSect);
554 AssertRC(rc2);
555 }
556
557 if (pThis->pTestCur == NULL) /* Empty list? */
558 return 0;
559
560 PVALKITTESTDATA const pTst = pThis->pTestCur;
561
562 Assert(pTst->t.TestTone.u.Rec.cbToWrite >= pTst->t.TestTone.u.Rec.cbWritten);
563 return pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
564}
565
566
567/**
568 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
569 */
570static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
571{
572 RT_NOREF(pInterface, pStream);
573 return UINT32_MAX;
574}
575
576
577/**
578 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
579 */
580static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
581 PPDMAUDIOBACKENDSTREAM pStream)
582{
583 RT_NOREF(pInterface);
584 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
585 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
586}
587
588
589/**
590 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
591 */
592static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
593 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
594{
595 RT_NOREF(pStream);
596
597 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
598
599 int rc = RTCritSectEnter(&pThis->CritSect);
600 if (RT_SUCCESS(rc))
601 {
602 pThis->pTestCur = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
603
604 int rc2 = RTCritSectLeave(&pThis->CritSect);
605 AssertRC(rc2);
606 }
607
608 if (pThis->pTestCur == NULL) /* Empty list? */
609 {
610 LogRelMax(64, ("Audio: Validation Kit: Warning: Guest is playing back data when no playback test is active\n"));
611
612 *pcbWritten = cbBuf;
613 return VINF_SUCCESS;
614 }
615
616 PVALKITTESTDATA pTst = pThis->pTestCur;
617
618 if (pTst->t.TestTone.u.Play.cbRead == 0)
619 {
620 AUDIOTESTPARMS Parms;
621 RT_ZERO(Parms);
622 Parms.enmDir = PDMAUDIODIR_IN;
623 Parms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
624 Parms.TestTone = pTst->t.TestTone.Parms;
625
626 Assert(pTst->pEntry == NULL);
627 rc = AudioTestSetTestBegin(&pThis->Set, "Recording audio data from guest",
628 &Parms, &pTst->pEntry);
629 if (RT_SUCCESS(rc))
630 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-rec.pcm", &pTst->pObj);
631
632 if (RT_SUCCESS(rc))
633 {
634 pTst->msStartedTS = RTTimeMilliTS();
635 LogRel(("Audio: Validation Kit: Recording audio data (%RU16Hz, %RU32ms) started\n",
636 (uint16_t)Parms.TestTone.dbFreqHz, Parms.TestTone.msDuration));
637 }
638 }
639
640 uint32_t cbWritten = 0;
641
642 if (RT_SUCCESS(rc))
643 {
644 uint32_t cbToRead = RT_MIN(cbBuf,
645 pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead);
646
647 rc = AudioTestSetObjWrite(pTst->pObj, pvBuf, cbToRead);
648 if (RT_SUCCESS(rc))
649 {
650 pTst->t.TestTone.u.Play.cbRead += cbToRead;
651 Assert(pTst->t.TestTone.u.Play.cbRead <= pTst->t.TestTone.u.Play.cbToRead);
652
653 const bool fComplete = pTst->t.TestTone.u.Play.cbToRead == pTst->t.TestTone.u.Play.cbRead;
654
655 if (fComplete)
656 {
657 LogRel(("Audio: Validation Kit: Recording audio data done (took %RU32ms)\n",
658 RTTimeMilliTS() - pTst->msStartedTS));
659
660 rc = RTCritSectEnter(&pThis->CritSect);
661 if (RT_SUCCESS(rc))
662 {
663 drvHostValKiUnregisterPlayTest(pThis, pTst);
664
665 pThis->pTestCur = NULL;
666
667 int rc2 = RTCritSectLeave(&pThis->CritSect);
668 AssertRC(rc2);
669 }
670 }
671
672 cbWritten = cbToRead;
673 }
674 }
675
676 if (RT_FAILURE(rc))
677 {
678 if (pTst->pEntry)
679 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
680 LogRel(("Audio: Validation Kit: Recording audio data failed with %Rrc\n", rc));
681 }
682
683 *pcbWritten = cbWritten;
684
685 return VINF_SUCCESS; /** @todo Return rc here? */
686}
687
688
689/**
690 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
691 */
692static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
693 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
694{
695 RT_NOREF(pStream);
696
697 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
698
699 int rc = RTCritSectEnter(&pThis->CritSect);
700 if (RT_SUCCESS(rc))
701 {
702 pThis->pTestCur = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
703
704 int rc2 = RTCritSectLeave(&pThis->CritSect);
705 AssertRC(rc2);
706 }
707
708 if (pThis->pTestCur == NULL) /* Empty list? */
709 {
710 LogRelMax(64, ("Audio: Validation Kit: Warning: Guest is recording audio data when no recording test is active\n"));
711
712 *pcbRead = 0;
713 return VINF_SUCCESS;
714 }
715
716 PVALKITTESTDATA pTst = pThis->pTestCur;
717
718 if (pTst->t.TestTone.u.Rec.cbWritten == 0)
719 {
720 AUDIOTESTPARMS Parms;
721 RT_ZERO(Parms);
722 Parms.enmDir = PDMAUDIODIR_OUT;
723 Parms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
724 Parms.TestTone = pTst->t.TestTone.Parms;
725
726 Assert(pTst->pEntry == NULL);
727 rc = AudioTestSetTestBegin(&pThis->Set, "Injecting audio input data to guest",
728 &Parms, &pTst->pEntry);
729 if (RT_SUCCESS(rc))
730 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-play.pcm", &pTst->pObj);
731
732 if (RT_SUCCESS(rc))
733 {
734 pTst->msStartedTS = RTTimeMilliTS();
735 LogRel(("Audio: Validation Kit: Injecting audio input data (%RU16Hz, %RU32ms) started\n",
736 (uint16_t)pTst->t.TestTone.Tone.rdFreqHz,
737 pTst->t.TestTone.Parms.msDuration));
738 }
739 }
740
741 uint32_t cbRead = 0;
742
743 if (RT_SUCCESS(rc))
744 {
745 uint32_t cbToWrite = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
746 if (cbToWrite)
747 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, RT_MIN(cbToWrite, cbBuf), &cbRead);
748 if ( RT_SUCCESS(rc)
749 && cbRead)
750 {
751 rc = AudioTestSetObjWrite(pTst->pObj, pvBuf, cbRead);
752 if (RT_SUCCESS(rc))
753 {
754 pTst->t.TestTone.u.Rec.cbWritten += cbRead;
755 Assert(pTst->t.TestTone.u.Rec.cbWritten <= pTst->t.TestTone.u.Rec.cbToWrite);
756
757 const bool fComplete = pTst->t.TestTone.u.Rec.cbToWrite == pTst->t.TestTone.u.Rec.cbWritten;
758
759 if (fComplete)
760 {
761 LogRel(("Audio: Validation Kit: Injecting audio input data done (took %RU32ms)\n",
762 RTTimeMilliTS() - pTst->msStartedTS));
763
764 rc = RTCritSectEnter(&pThis->CritSect);
765 if (RT_SUCCESS(rc))
766 {
767 drvHostValKiUnregisterRecTest(pThis, pTst);
768
769 pThis->pTestCur = NULL;
770
771 int rc2 = RTCritSectLeave(&pThis->CritSect);
772 AssertRC(rc2);
773 }
774 }
775 }
776 }
777 }
778
779 if (RT_FAILURE(rc))
780 {
781 if (pTst->pEntry)
782 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
783 LogRel(("Audio: Validation Kit: Injecting audio input data failed with %Rrc\n", rc));
784 }
785
786 *pcbRead = cbRead;
787
788 return VINF_SUCCESS; /** @todo Return rc here? */
789}
790
791
792/**
793 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
794 */
795static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
796{
797 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
798 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
799
800 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
801 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
802 return NULL;
803}
804
805
806/**
807 * Constructs a VaKit audio driver instance.
808 *
809 * @copydoc FNPDMDRVCONSTRUCT
810 */
811static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
812{
813 RT_NOREF(pCfg, fFlags);
814 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
815 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
816 LogRel(("Audio: Initializing VALKIT driver\n"));
817
818 /*
819 * Init the static parts.
820 */
821 pThis->pDrvIns = pDrvIns;
822 /* IBase */
823 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
824 /* IHostAudio */
825 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
826 pThis->IHostAudio.pfnGetDevices = NULL;
827 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
828 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
829 pThis->IHostAudio.pfnStreamConfigHint = NULL;
830 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
831 pThis->IHostAudio.pfnStreamInitAsync = NULL;
832 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
833 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
834 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
835 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
836 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
837 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
838 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
839 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
840 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
841 pThis->IHostAudio.pfnStreamGetPending = NULL;
842 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
843 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
844 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
845
846 RTListInit(&pThis->lstTestsRec);
847 pThis->cTestsRec = 0;
848 RTListInit(&pThis->lstTestsPlay);
849 pThis->cTestsPlay = 0;
850
851 ATSCALLBACKS Callbacks;
852 RT_ZERO(Callbacks);
853 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
854 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
855 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
856 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
857 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
858 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
859 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
860 Callbacks.pvUser = pThis;
861
862 /** @todo Make this configurable via CFGM. */
863 const char *pszTcpAddr = ATS_TCP_HOST_DEFAULT_ADDR_STR;
864 uint32_t uTcpPort = ATS_TCP_HOST_DEFAULT_PORT;
865
866 LogRel(("Audio: Validation Kit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
867 pszTcpAddr, uTcpPort));
868
869 int rc = AudioTestSvcInit(&pThis->Srv,
870 /* We only allow connections from localhost for now. */
871 pszTcpAddr, uTcpPort, &Callbacks);
872 if (RT_SUCCESS(rc))
873 rc = AudioTestSvcStart(&pThis->Srv);
874
875 if (RT_SUCCESS(rc))
876 {
877 LogRel(("Audio: Validation Kit: Audio Test Service (ATS) running\n"));
878
879 /** @todo Let the following be customizable by CFGM later. */
880 rc = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
881 if (RT_SUCCESS(rc))
882 {
883 LogRel(("Audio: Validation Kit: Using temp dir '%s'\n", pThis->szPathTemp));
884 rc = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
885 if (RT_SUCCESS(rc))
886 LogRel(("Audio: Validation Kit: Using output dir '%s'\n", pThis->szPathOut));
887 }
888 }
889
890 if (RT_SUCCESS(rc))
891 rc = RTCritSectInit(&pThis->CritSect);
892
893 if (RT_FAILURE(rc))
894 LogRel(("Audio: Validation Kit: Initialization failed, rc=%Rrc\n", rc));
895
896 return rc;
897}
898
899static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
900{
901 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
902 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
903
904 LogRel(("Audio: Validation Kit: Shutting down Audio Test Service (ATS) ...\n"));
905
906 int rc = AudioTestSvcShutdown(&pThis->Srv);
907 if (RT_SUCCESS(rc))
908 rc = AudioTestSvcDestroy(&pThis->Srv);
909
910 if (RT_SUCCESS(rc))
911 {
912 LogRel(("Audio: Validation Kit: Shutdown of Audio Test Service (ATS) complete\n"));
913 drvHostValKitCleanup(pThis);
914 }
915 else
916 LogRel(("Audio: Validation Kit: Shutdown of Audio Test Service (ATS) failed, rc=%Rrc\n", rc));
917
918 /* Try cleaning up a bit. */
919 RTDirRemove(pThis->szPathTemp);
920 RTDirRemove(pThis->szPathOut);
921
922 if (RTCritSectIsInitialized(&pThis->CritSect))
923 {
924 int rc2 = RTCritSectDelete(&pThis->CritSect);
925 if (RT_SUCCESS(rc))
926 rc = rc2;
927 }
928
929 if (RT_FAILURE(rc))
930 LogRel(("Audio: Validation Kit: Destruction failed, rc=%Rrc\n", rc));
931}
932
933/**
934 * Char driver registration record.
935 */
936const PDMDRVREG g_DrvHostValidationKitAudio =
937{
938 /* u32Version */
939 PDM_DRVREG_VERSION,
940 /* szName */
941 "ValidationKitAudio",
942 /* szRCMod */
943 "",
944 /* szR0Mod */
945 "",
946 /* pszDescription */
947 "ValidationKitAudio audio host driver",
948 /* fFlags */
949 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
950 /* fClass. */
951 PDM_DRVREG_CLASS_AUDIO,
952 /* cMaxInstances */
953 ~0U,
954 /* cbInstance */
955 sizeof(DRVHOSTVALKITAUDIO),
956 /* pfnConstruct */
957 drvHostValKitAudioConstruct,
958 /* pfnDestruct */
959 drvHostValKitAudioDestruct,
960 /* pfnRelocate */
961 NULL,
962 /* pfnIOCtl */
963 NULL,
964 /* pfnPowerOn */
965 NULL,
966 /* pfnReset */
967 NULL,
968 /* pfnSuspend */
969 NULL,
970 /* pfnResume */
971 NULL,
972 /* pfnAttach */
973 NULL,
974 /* pfnDetach */
975 NULL,
976 /* pfnPowerOff */
977 NULL,
978 /* pfnSoftReset */
979 NULL,
980 /* u32EndVersion */
981 PDM_DRVREG_VERSION
982};
983
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