VirtualBox

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

Last change on this file since 92320 was 92320, checked in by vboxsync, 3 years ago

Audio/Validation Kit: More optional release logging. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 60.7 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 92320 2021-11-10 13:48:36Z 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/semaphore.h>
28#include <iprt/stream.h>
29#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
30
31#include <VBox/log.h>
32#include <VBox/vmm/pdmaudioifs.h>
33#include <VBox/vmm/pdmaudioinline.h>
34
35#include "VBoxDD.h"
36#include "AudioHlp.h"
37#include "AudioTest.h"
38#include "AudioTestService.h"
39
40
41#ifdef DEBUG_andy
42/** Enables dumping audio streams to the temporary directory for debugging. */
43# define VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
44#endif
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50/**
51 * Structure for keeping a Validation Kit input/output stream.
52 */
53typedef struct VALKITAUDIOSTREAM
54{
55 /** Common part. */
56 PDMAUDIOBACKENDSTREAM Core;
57 /** The stream's acquired configuration. */
58 PDMAUDIOSTREAMCFG Cfg;
59 /** How much bytes are available to read (only for capturing streams). */
60 uint32_t cbAvail;
61#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
62 /** Audio file to dump output to. */
63 PAUDIOHLPFILE pFile;
64#endif
65} VALKITAUDIOSTREAM;
66/** Pointer to a Validation Kit stream. */
67typedef VALKITAUDIOSTREAM *PVALKITAUDIOSTREAM;
68
69/**
70 * Test tone-specific instance data.
71 */
72typedef struct VALKITTESTTONEDATA
73{
74 /* Test tone beacon to use.
75 * Will be re-used for pre/post beacons. */
76 AUDIOTESTTONEBEACON Beacon;
77 union
78 {
79 struct
80 {
81 /** How many bytes to write. */
82 uint64_t cbToWrite;
83 /** How many bytes already written. */
84 uint64_t cbWritten;
85 } Rec;
86 struct
87 {
88 /** How many bytes to read. */
89 uint64_t cbToRead;
90 /** How many bytes already read. */
91 uint64_t cbRead;
92 } Play;
93 } u;
94 /** The test tone instance to use. */
95 AUDIOTESTTONE Tone;
96 /** The test tone parameters to use. */
97 AUDIOTESTTONEPARMS Parms;
98} VALKITTESTTONEDATA;
99
100/**
101 * Structure keeping a single Validation Kit test.
102 */
103typedef struct VALKITTESTDATA
104{
105 /** The list node. */
106 RTLISTNODE Node;
107 /** Index in test sequence (0-based). */
108 uint32_t idxTest;
109 /** Current test set entry to process. */
110 PAUDIOTESTENTRY pEntry;
111 /** Current test state. */
112 AUDIOTESTSTATE enmState;
113 /** Current test object to process. */
114 AUDIOTESTOBJ Obj;
115 /** Stream configuration to use for this test. */
116 PDMAUDIOSTREAMCFG StreamCfg;
117 union
118 {
119 /** Test tone-specific data. */
120 VALKITTESTTONEDATA TestTone;
121 } t;
122 /** Time stamp (real, in ms) when test got registered. */
123 uint64_t msRegisteredTS;
124 /** Time stamp (real, in ms) when test started. */
125 uint64_t msStartedTS;
126} VALKITTESTDATA;
127/** Pointer to Validation Kit test data. */
128typedef VALKITTESTDATA *PVALKITTESTDATA;
129
130/**
131 * Validation Kit audio driver instance data.
132 * @implements PDMIAUDIOCONNECTOR
133 */
134typedef struct DRVHOSTVALKITAUDIO
135{
136 /** Pointer to the driver instance structure. */
137 PPDMDRVINS pDrvIns;
138 /** Pointer to host audio interface. */
139 PDMIHOSTAUDIO IHostAudio;
140 /** Total number of bytes played since driver construction. */
141 uint64_t cbPlayedTotal;
142 /** Total number of bytes recorded since driver construction. */
143 uint64_t cbRecordedTotal;
144 /** Total number of bytes silence was played in a consequtive block so far.
145 * Will be reset once audible data is being played (again). */
146 uint64_t cbPlayedSilence;
147 /** Total number of bytes audio (audible or not) was played while no active
148 * audio test was registered / available. */
149 uint64_t cbPlayedNoTest;
150 /** Temporary path to use. */
151 char szPathTemp[RTPATH_MAX];
152 /** Output path to use. */
153 char szPathOut[RTPATH_MAX];
154 /** Current test set being handled.
155 * At the moment only one test set can be around at a time. */
156 AUDIOTESTSET Set;
157 /** Number of total tests in \a lstTestsRec and \a lstTestsPlay. */
158 uint32_t cTestsTotal;
159 /** Increasing number to identify tests. */
160 uint32_t idxTest;
161 /** Number of tests in \a lstTestsRec. */
162 uint32_t cTestsRec;
163 /** List keeping the recording tests (FIFO). */
164 RTLISTANCHOR lstTestsRec;
165 /** Pointer to current recording test being processed.
166 * NULL if no current test active. */
167 PVALKITTESTDATA pTestCurRec;
168 /** Number of tests in \a lstTestsPlay. */
169 uint32_t cTestsPlay;
170 /** List keeping the recording tests (FIFO). */
171 RTLISTANCHOR lstTestsPlay;
172 /** Pointer to current playback test being processed.
173 * NULL if no current test active. */
174 PVALKITTESTDATA pTestCurPlay;
175 /** Critical section for serializing access across threads. */
176 RTCRITSECT CritSect;
177 /** Whether the test set needs to end.
178 * Needed for packing up (to archive) and termination, as capturing and playback
179 * can run in asynchronous threads. */
180 bool fTestSetEnd;
181 /** Event semaphore for waiting on the current test set to end. */
182 RTSEMEVENT EventSemEnded;
183 /** The Audio Test Service (ATS) instance. */
184 ATSSERVER Srv;
185 /** Absolute path to the packed up test set archive.
186 * Keep it simple for now and only support one (open) archive at a time. */
187 char szTestSetArchive[RTPATH_MAX];
188 /** File handle to the (opened) test set archive for reading. */
189 RTFILE hTestSetArchive;
190
191} DRVHOSTVALKITAUDIO;
192/** Pointer to a Validation Kit host audio driver instance. */
193typedef DRVHOSTVALKITAUDIO *PDRVHOSTVALKITAUDIO;
194
195
196/*********************************************************************************************************************************
197* Internal test handling code *
198*********************************************************************************************************************************/
199
200/**
201 * Unregisters a ValKit test, common code.
202 *
203 * @param pThis ValKit audio driver instance.
204 * @param pTst Test to unregister.
205 * The pointer will be invalid afterwards.
206 */
207static void drvHostValKiUnregisterTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
208{
209 AssertPtrReturnVoid(pTst);
210
211 RTListNodeRemove(&pTst->Node);
212
213 AudioTestObjClose(pTst->Obj);
214 pTst->Obj = NULL;
215
216 if (pTst->pEntry) /* Set set entry assign? Mark as done. */
217 {
218 AssertPtrReturnVoid(pTst->pEntry);
219 pTst->pEntry = NULL;
220 }
221
222 RTMemFree(pTst);
223 pTst = NULL;
224
225 Assert(pThis->cTestsTotal);
226 pThis->cTestsTotal--;
227 if (pThis->cTestsTotal == 0)
228 {
229 if (ASMAtomicReadBool(&pThis->fTestSetEnd))
230 {
231 int rc2 = RTSemEventSignal(pThis->EventSemEnded);
232 AssertRC(rc2);
233 }
234 }
235}
236
237/**
238 * Unregisters a ValKit recording test.
239 *
240 * @param pThis ValKit audio driver instance.
241 * @param pTst Test to unregister.
242 * The pointer will be invalid afterwards.
243 */
244static void drvHostValKiUnregisterRecTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
245{
246 Assert(pThis->cTestsRec);
247 pThis->cTestsRec--;
248
249 drvHostValKiUnregisterTest(pThis, pTst);
250}
251
252/**
253 * Unregisters a ValKit playback test.
254 *
255 * @param pThis ValKit audio driver instance.
256 * @param pTst Test to unregister.
257 * The pointer will be invalid afterwards.
258 */
259static void drvHostValKiUnregisterPlayTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
260{
261 Assert(pThis->cTestsPlay);
262 pThis->cTestsPlay--;
263
264 drvHostValKiUnregisterTest(pThis, pTst);
265}
266
267/**
268 * Performs some internal cleanup / housekeeping of all registered tests.
269 *
270 * @param pThis ValKit audio driver instance.
271 */
272static void drvHostValKitCleanup(PDRVHOSTVALKITAUDIO pThis)
273{
274 LogRel(("ValKit: Cleaning up ...\n"));
275
276 if ( pThis->cTestsTotal
277 && ( !pThis->cbPlayedTotal
278 && !pThis->cbRecordedTotal)
279 )
280 {
281 LogRel(("ValKit: Warning: Did not get any audio data to play or record altough tests were configured\n\n"));
282 LogRel(("ValKit: Hints:\n"
283 "ValKit: - Audio device emulation configured and enabled for the VM?\n"
284 "ValKit: - Audio input and/or output enabled for the VM?\n"
285 "ValKit: - Is the guest able to play / record sound at all?\n"
286 "ValKit: - Is the guest's audio mixer or input / output sinks muted?\n"
287 "ValKit: - Audio stack misconfiguration / bug?\n\n"));
288 }
289
290 if (pThis->cTestsRec)
291 LogRel(("ValKit: Warning: %RU32 guest recording tests still outstanding:\n", pThis->cTestsRec));
292
293 PVALKITTESTDATA pTst, pTstNext;
294 RTListForEachSafe(&pThis->lstTestsRec, pTst, pTstNext, VALKITTESTDATA, Node)
295 {
296 if (pTst->enmState != AUDIOTESTSTATE_DONE)
297 LogRel(("ValKit: \tWarning: Test #%RU32 (recording) not done yet (state is '%s')\n",
298 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
299
300 if (pTst->t.TestTone.u.Rec.cbToWrite > pTst->t.TestTone.u.Rec.cbWritten)
301 {
302 size_t const cbOutstanding = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
303 if (cbOutstanding)
304 LogRel(("ValKit: \tWarning: Recording test #%RU32 has %RU64 bytes (%RU64ms) outstanding (%RU8%% left)\n",
305 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding),
306 100 - (pTst->t.TestTone.u.Rec.cbWritten * 100) / RT_MAX(pTst->t.TestTone.u.Rec.cbToWrite, 1)));
307 }
308 drvHostValKiUnregisterRecTest(pThis, pTst);
309 }
310
311 if (pThis->cTestsPlay)
312 LogRel(("ValKit: Warning: %RU32 guest playback tests still outstanding:\n", pThis->cTestsPlay));
313
314 RTListForEachSafe(&pThis->lstTestsPlay, pTst, pTstNext, VALKITTESTDATA, Node)
315 {
316 if (pTst->enmState != AUDIOTESTSTATE_DONE)
317 LogRel(("ValKit: \tWarning: Test #%RU32 (playback) not done yet (state is '%s')\n",
318 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
319
320 if (pTst->t.TestTone.u.Play.cbToRead > pTst->t.TestTone.u.Play.cbRead)
321 {
322 size_t const cbOutstanding = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
323 if (cbOutstanding)
324 LogRel(("ValKit: \tWarning: Playback test #%RU32 has %RU64 bytes (%RU64ms) outstanding (%RU8%% left)\n",
325 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding),
326 100 - (pTst->t.TestTone.u.Play.cbRead * 100) / RT_MAX(pTst->t.TestTone.u.Play.cbToRead, 1)));
327 }
328 drvHostValKiUnregisterPlayTest(pThis, pTst);
329 }
330
331 Assert(pThis->cTestsRec == 0);
332 Assert(pThis->cTestsPlay == 0);
333
334 if (pThis->cbPlayedNoTest)
335 {
336 LogRel2(("ValKit: Warning: Guest was playing back audio when no playback test is active (%RU64 bytes total)\n",
337 pThis->cbPlayedNoTest));
338 pThis->cbPlayedNoTest = 0;
339 }
340}
341
342
343/*********************************************************************************************************************************
344* ATS callback implementations *
345*********************************************************************************************************************************/
346
347/** @copydoc ATSCALLBACKS::pfnHowdy */
348static DECLCALLBACK(int) drvHostValKitHowdy(void const *pvUser)
349{
350 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
351 RT_NOREF(pThis);
352
353 LogRel(("ValKit: Client connected\n"));
354
355 return VINF_SUCCESS;
356}
357
358/** @copydoc ATSCALLBACKS::pfnBye */
359static DECLCALLBACK(int) drvHostValKitBye(void const *pvUser)
360{
361 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
362 RT_NOREF(pThis);
363
364 LogRel(("ValKit: Client disconnected\n"));
365
366 return VINF_SUCCESS;
367}
368
369/** @copydoc ATSCALLBACKS::pfnTestSetBegin */
370static DECLCALLBACK(int) drvHostValKitTestSetBegin(void const *pvUser, const char *pszTag)
371{
372 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
373
374 LogRel(("ValKit: Beginning test set '%s'\n", pszTag));
375
376 int rc = RTCritSectEnter(&pThis->CritSect);
377 if (RT_SUCCESS(rc))
378 {
379 rc = AudioTestSetCreate(&pThis->Set, pThis->szPathTemp, pszTag);
380
381 int rc2 = RTCritSectLeave(&pThis->CritSect);
382 if (RT_SUCCESS(rc))
383 rc = rc2;
384 }
385
386 if (RT_FAILURE(rc))
387 LogRel(("ValKit: Beginning test set failed with %Rrc\n", rc));
388
389 return rc;
390}
391
392/** @copydoc ATSCALLBACKS::pfnTestSetEnd */
393static DECLCALLBACK(int) drvHostValKitTestSetEnd(void const *pvUser, const char *pszTag)
394{
395 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
396
397 LogRel(("ValKit: Ending test set '%s'\n", pszTag));
398
399 int rc = RTCritSectEnter(&pThis->CritSect);
400 if (RT_SUCCESS(rc))
401 {
402 const PAUDIOTESTSET pSet = &pThis->Set;
403
404 const char *pszTagSet = AudioTestSetGetTag(pSet);
405 if (RTStrCmp(pszTagSet, pszTag) != 0)
406 {
407 LogRel(("ValKit: Error: Current test does not match test set to end ('%s' vs '%s')\n", pszTagSet, pszTag));
408
409 int rc2 = RTCritSectLeave(&pThis->CritSect);
410 AssertRC(rc2);
411
412 return VERR_NOT_FOUND; /* Return to the caller. */
413 }
414
415 LogRel(("ValKit: Test set has %RU32 tests total, %RU32 (still) running, %RU32 failures total so far\n",
416 AudioTestSetGetTestsTotal(pSet), AudioTestSetGetTestsRunning(pSet), AudioTestSetGetTotalFailures(pSet)));
417 LogRel(("ValKit: %RU32 tests still registered total (%RU32 play, %RU32 record)\n",
418 pThis->cTestsTotal, pThis->cTestsPlay, pThis->cTestsRec));
419
420 if ( AudioTestSetIsRunning(pSet)
421 || pThis->cTestsTotal)
422 {
423 ASMAtomicWriteBool(&pThis->fTestSetEnd, true);
424
425 rc = RTCritSectLeave(&pThis->CritSect);
426 if (RT_SUCCESS(rc))
427 {
428 LogRel(("ValKit: Waiting for all tests of set '%s' to end ...\n", pszTag));
429 rc = RTSemEventWait(pThis->EventSemEnded, RT_MS_5SEC);
430 if (RT_FAILURE(rc))
431 {
432 LogRel(("ValKit: Waiting for tests of set '%s' to end failed with %Rrc\n", pszTag, rc));
433
434 /* The verification on the host will tell us later which tests did run and which didn't (anymore).
435 * So continue and pack (plus transfer) the test set to the host. */
436 if (rc == VERR_TIMEOUT)
437 rc = VINF_SUCCESS;
438 }
439
440 int rc2 = RTCritSectEnter(&pThis->CritSect);
441 if (RT_SUCCESS(rc))
442 rc = rc2;
443 }
444 }
445
446 if (RT_SUCCESS(rc))
447 {
448 LogRel(("ValKit: Closing test set '%s' ...\n", pszTag));
449
450 /* Close the test set first. */
451 rc = AudioTestSetClose(pSet);
452 if (RT_SUCCESS(rc))
453 {
454 /* Before destroying the test environment, pack up the test set so
455 * that it's ready for transmission. */
456 rc = AudioTestSetPack(pSet, pThis->szPathOut, pThis->szTestSetArchive, sizeof(pThis->szTestSetArchive));
457 if (RT_SUCCESS(rc))
458 {
459 LogRel(("ValKit: Packed up to '%s'\n", pThis->szTestSetArchive));
460 }
461 else
462 LogRel(("ValKit: Packing up test set failed with %Rrc\n", rc));
463
464 /* Do some internal housekeeping. */
465 drvHostValKitCleanup(pThis);
466
467#ifndef DEBUG_andy
468 int rc2 = AudioTestSetWipe(pSet);
469 if (RT_SUCCESS(rc))
470 rc = rc2;
471#endif
472 }
473 else
474 LogRel(("ValKit: Closing test set failed with %Rrc\n", rc));
475
476 int rc2 = AudioTestSetDestroy(pSet);
477 if (RT_FAILURE(rc2))
478 {
479 LogRel(("ValKit: Destroying test set failed with %Rrc\n", rc));
480 if (RT_SUCCESS(rc))
481 rc = rc2;
482 }
483 }
484
485 int rc2 = RTCritSectLeave(&pThis->CritSect);
486 if (RT_SUCCESS(rc))
487 rc = rc2;
488 }
489
490 if (RT_FAILURE(rc))
491 LogRel(("ValKit: Ending test set failed with %Rrc\n", rc));
492
493 return rc;
494}
495
496/** @copydoc ATSCALLBACKS::pfnTonePlay
497 *
498 * Creates and registers a new test tone guest recording test.
499 * This backend will play (inject) input data to the guest.
500 */
501static DECLCALLBACK(int) drvHostValKitRegisterGuestRecTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
502{
503 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
504
505 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
506 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
507
508 pTestData->enmState = AUDIOTESTSTATE_INIT;
509
510 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
511
512 PPDMAUDIOPCMPROPS const pProps = &pTestData->t.TestTone.Parms.Props;
513
514 AssertReturn(pTestData->t.TestTone.Parms.msDuration, VERR_INVALID_PARAMETER);
515 AssertReturn(PDMAudioPropsAreValid(pProps), VERR_INVALID_PARAMETER);
516
517 AudioTestToneInit(&pTestData->t.TestTone.Tone, pProps, pTestData->t.TestTone.Parms.dbFreqHz);
518
519 pTestData->t.TestTone.u.Rec.cbToWrite = PDMAudioPropsMilliToBytes(pProps,
520 pTestData->t.TestTone.Parms.msDuration);
521
522 /* We inject a pre + post beacon before + after the actual test tone.
523 * We always start with the pre beacon. */
524 AudioTestBeaconInit(&pTestData->t.TestTone.Beacon, AUDIOTESTTONEBEACONTYPE_PLAY_PRE, pProps);
525
526 int rc = RTCritSectEnter(&pThis->CritSect);
527 if (RT_SUCCESS(rc))
528 {
529 LogRel(("ValKit: Registering guest recording test #%RU32 (%RU32ms, %RU64 bytes, host test #%RU32)\n",
530 pThis->idxTest, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Rec.cbToWrite,
531 pToneParms->Hdr.idxSeq));
532
533 const uint32_t cbBeacon = AudioTestBeaconGetSize(&pTestData->t.TestTone.Beacon);
534 if (cbBeacon)
535 LogRel2(("ValKit: Guest recording test #%RU32 includes 2 x %RU32 bytes of pre/post beacons\n",
536 pThis->idxTest, cbBeacon));
537
538 RTListAppend(&pThis->lstTestsRec, &pTestData->Node);
539
540 pTestData->msRegisteredTS = RTTimeMilliTS();
541 pTestData->idxTest = pThis->idxTest++;
542
543 pThis->cTestsRec++;
544 pThis->cTestsTotal++;
545
546 int rc2 = RTCritSectLeave(&pThis->CritSect);
547 AssertRC(rc2);
548 }
549
550 return VINF_SUCCESS;
551}
552
553/** @copydoc ATSCALLBACKS::pfnToneRecord
554 *
555 * Creates and registers a new test tone guest playback test.
556 * This backend will record the guest output data.
557 */
558static DECLCALLBACK(int) drvHostValKitRegisterGuestPlayTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
559{
560 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
561
562 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
563 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
564
565 pTestData->enmState = AUDIOTESTSTATE_INIT;
566
567 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
568
569 PPDMAUDIOPCMPROPS const pProps = &pTestData->t.TestTone.Parms.Props;
570
571 AssertReturn(pTestData->t.TestTone.Parms.msDuration, VERR_INVALID_PARAMETER);
572 AssertReturn(PDMAudioPropsAreValid(pProps), VERR_INVALID_PARAMETER);
573
574 pTestData->t.TestTone.u.Play.cbToRead = PDMAudioPropsMilliToBytes(pProps,
575 pTestData->t.TestTone.Parms.msDuration);
576
577 /* We play a pre + post beacon before + after the actual test tone.
578 * We always start with the pre beacon. */
579 AudioTestBeaconInit(&pTestData->t.TestTone.Beacon, AUDIOTESTTONEBEACONTYPE_PLAY_PRE, pProps);
580
581 int rc = RTCritSectEnter(&pThis->CritSect);
582 if (RT_SUCCESS(rc))
583 {
584 LogRel(("ValKit: Registering guest playback test #%RU32 (%RU32ms, %RU64 bytes, host test #%RU32)\n",
585 pThis->idxTest, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Play.cbToRead,
586 pToneParms->Hdr.idxSeq));
587
588 const uint32_t cbBeacon = AudioTestBeaconGetSize(&pTestData->t.TestTone.Beacon);
589 if (cbBeacon)
590 LogRel2(("ValKit: Guest playback test #%RU32 includes 2 x %RU32 bytes of pre/post beacons\n",
591 pThis->idxTest, cbBeacon));
592
593 RTListAppend(&pThis->lstTestsPlay, &pTestData->Node);
594
595 pTestData->msRegisteredTS = RTTimeMilliTS();
596 pTestData->idxTest = pThis->idxTest++;
597
598 pThis->cTestsTotal++;
599 pThis->cTestsPlay++;
600
601 int rc2 = RTCritSectLeave(&pThis->CritSect);
602 AssertRC(rc2);
603 }
604
605 return VINF_SUCCESS;
606}
607
608/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
609static DECLCALLBACK(int) drvHostValKitTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
610{
611 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
612
613 int rc = RTCritSectEnter(&pThis->CritSect);
614 if (RT_SUCCESS(rc))
615 {
616 if (RTFileExists(pThis->szTestSetArchive)) /* Has the archive successfully been created yet? */
617 {
618 rc = RTFileOpen(&pThis->hTestSetArchive, pThis->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
619 if (RT_SUCCESS(rc))
620 {
621 uint64_t uSize;
622 rc = RTFileQuerySize(pThis->hTestSetArchive, &uSize);
623 if (RT_SUCCESS(rc))
624 LogRel(("ValKit: Sending test set '%s' (%zu bytes)\n", pThis->szTestSetArchive, uSize));
625 }
626 }
627 else
628 rc = VERR_FILE_NOT_FOUND;
629
630 int rc2 = RTCritSectLeave(&pThis->CritSect);
631 if (RT_SUCCESS(rc))
632 rc = rc2;
633 }
634
635 if (RT_FAILURE(rc))
636 LogRel(("ValKit: Beginning to send test set '%s' failed with %Rrc\n", pszTag, rc));
637
638 return rc;
639}
640
641/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
642static DECLCALLBACK(int) drvHostValKitTestSetSendReadCallback(void const *pvUser,
643 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
644{
645 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
646
647 int rc = RTCritSectEnter(&pThis->CritSect);
648 if (RT_SUCCESS(rc))
649 {
650 if (RTFileIsValid(pThis->hTestSetArchive))
651 {
652 rc = RTFileRead(pThis->hTestSetArchive, pvBuf, cbBuf, pcbRead);
653 }
654 else
655 rc = VERR_WRONG_ORDER;
656
657 int rc2 = RTCritSectLeave(&pThis->CritSect);
658 if (RT_SUCCESS(rc))
659 rc = rc2;
660 }
661
662 if (RT_FAILURE(rc))
663 LogRel(("ValKit: Reading from test set '%s' failed with %Rrc\n", pszTag, rc));
664
665 return rc;
666}
667
668/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
669static DECLCALLBACK(int) drvHostValKitTestSetSendEndCallback(void const *pvUser, const char *pszTag)
670{
671 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
672
673 int rc = RTCritSectEnter(&pThis->CritSect);
674 if (RT_SUCCESS(rc))
675 {
676 if (RTFileIsValid(pThis->hTestSetArchive))
677 {
678 rc = RTFileClose(pThis->hTestSetArchive);
679 if (RT_SUCCESS(rc))
680 pThis->hTestSetArchive = NIL_RTFILE;
681 }
682
683 int rc2 = RTCritSectLeave(&pThis->CritSect);
684 if (RT_SUCCESS(rc))
685 rc = rc2;
686 }
687
688 if (RT_FAILURE(rc))
689 LogRel(("ValKit: Ending to send test set '%s' failed with %Rrc\n", pszTag, rc));
690
691 return rc;
692}
693
694
695/*********************************************************************************************************************************
696* PDMIHOSTAUDIO interface implementation *
697*********************************************************************************************************************************/
698
699/**
700 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
701 */
702static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
703{
704 RT_NOREF(pInterface);
705 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
706
707 /*
708 * Fill in the config structure.
709 */
710 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
711 pBackendCfg->cbStream = sizeof(VALKITAUDIOSTREAM);
712 pBackendCfg->fFlags = 0;
713 pBackendCfg->cMaxStreamsOut = 1; /* Output (Playback). */
714 pBackendCfg->cMaxStreamsIn = 1; /* Input (Recording). */
715
716 return VINF_SUCCESS;
717}
718
719
720/**
721 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
722 */
723static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
724{
725 RT_NOREF(enmDir);
726 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
727
728 return PDMAUDIOBACKENDSTS_RUNNING;
729}
730
731
732/**
733 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
734 */
735static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
736 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
737{
738 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
739 PVALKITAUDIOSTREAM pStreamValKit = (PVALKITAUDIOSTREAM)pStream;
740 AssertPtrReturn(pStreamValKit, VERR_INVALID_POINTER);
741 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
742 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
743 RT_NOREF(pThis);
744
745 int rc = VINF_SUCCESS;
746 PDMAudioStrmCfgCopy(&pStreamValKit->Cfg, pCfgAcq);
747
748#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
749 int rc2 = AudioHlpFileCreateAndOpenEx(&pStreamValKit->pFile, AUDIOHLPFILETYPE_WAV, NULL /*use temp dir*/,
750 pThis->pDrvIns->iInstance, AUDIOHLPFILENAME_FLAGS_NONE, AUDIOHLPFILE_FLAGS_NONE,
751 &pCfgReq->Props, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
752 pCfgReq->enmDir == PDMAUDIODIR_IN ? "ValKitAudioIn" : "ValKitAudioOut");
753 if (RT_FAILURE(rc2))
754 LogRel(("ValKit: Failed to creating debug file for %s stream '%s' in the temp directory: %Rrc\n",
755 pCfgReq->enmDir == PDMAUDIODIR_IN ? "input" : "output", pCfgReq->szName, rc2));
756#endif
757
758 return rc;
759}
760
761/**
762 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
763 */
764static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
765 bool fImmediate)
766{
767 RT_NOREF(pInterface, fImmediate);
768 PVALKITAUDIOSTREAM pStreamValKit = (PVALKITAUDIOSTREAM)pStream;
769 AssertPtrReturn(pStreamValKit, VERR_INVALID_POINTER);
770
771#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
772 if (pStreamValKit->pFile)
773 {
774 AudioHlpFileDestroy(pStreamValKit->pFile);
775 pStreamValKit->pFile = NULL;
776 }
777#endif
778
779 return VINF_SUCCESS;
780}
781
782
783/**
784 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
785 */
786static DECLCALLBACK(int) drvHostValKitAudioHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
787{
788 RT_NOREF(pInterface, pStream);
789 return VINF_SUCCESS;
790}
791
792
793/**
794 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
795 */
796static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
797{
798 RT_NOREF(pInterface, pStream);
799 return VINF_SUCCESS;
800}
801
802
803/**
804 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
805 */
806static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
807{
808 RT_NOREF(pInterface, pStream);
809 return VINF_SUCCESS;
810}
811
812
813/**
814 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
815 */
816static DECLCALLBACK(int) drvHostValKitAudioHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
817{
818 RT_NOREF(pInterface, pStream);
819 return VINF_SUCCESS;
820}
821
822
823/**
824 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
825 */
826static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
827{
828 RT_NOREF(pInterface, pStream);
829 return VINF_SUCCESS;
830}
831
832
833/**
834 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
835 */
836static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
837{
838 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
839 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
840 PVALKITTESTDATA pTst = NULL;
841
842 int rc = RTCritSectEnter(&pThis->CritSect);
843 if (RT_SUCCESS(rc))
844 {
845 if (pThis->pTestCurRec == NULL)
846 {
847 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
848 if (pThis->pTestCurRec)
849 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
850 }
851
852 pTst = pThis->pTestCurRec;
853
854 int rc2 = RTCritSectLeave(&pThis->CritSect);
855 AssertRC(rc2);
856 }
857
858 if ( pTst
859 && pTst->enmState == AUDIOTESTSTATE_INIT) /* Test not started yet? */
860 {
861 AUDIOTESTPARMS Parms;
862 RT_ZERO(Parms);
863 Parms.enmDir = PDMAUDIODIR_OUT;
864 Parms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
865 Parms.TestTone = pTst->t.TestTone.Parms;
866
867 rc = AudioTestSetTestBegin(&pThis->Set, "Injecting audio input data to guest",
868 &Parms, &pTst->pEntry);
869 if (RT_SUCCESS(rc))
870 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-play.pcm", &pTst->Obj);
871
872 if (RT_SUCCESS(rc))
873 {
874 pTst->msStartedTS = RTTimeMilliTS();
875 LogRel(("ValKit: Test #%RU32: Injecting audio input data (%RU16Hz, %RU32ms, %RU32 bytes) for host test #%RU32 started (delay is %RU32ms)\n",
876 pTst->idxTest, (uint16_t)pTst->t.TestTone.Tone.rdFreqHz,
877 pTst->t.TestTone.Parms.msDuration, pTst->t.TestTone.u.Rec.cbToWrite,
878 Parms.TestTone.Hdr.idxSeq, RTTimeMilliTS() - pTst->msRegisteredTS));
879
880 char szTimeCreated[RTTIME_STR_LEN];
881 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
882 LogRel2(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
883 }
884
885 pStrmValKit->cbAvail += pTst->t.TestTone.u.Rec.cbToWrite;
886 pStrmValKit->cbAvail += AudioTestBeaconGetSize(&pTst->t.TestTone.Beacon); /* Add beacon data, if any. */
887 LogRel2(("ValKit: Now total of %RU32 bytes available for capturing\n", pStrmValKit->cbAvail));
888
889 pTst->enmState = AUDIOTESTSTATE_PRE;
890 }
891
892 return pStrmValKit->cbAvail;
893}
894
895
896/**
897 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
898 */
899static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
900{
901 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
902 RT_NOREF(pStream);
903
904 uint32_t cbWritable = UINT32_MAX;
905 PVALKITTESTDATA pTst = NULL;
906
907 int rc = RTCritSectEnter(&pThis->CritSect);
908 if (RT_SUCCESS(rc))
909 {
910 pTst = pThis->pTestCurPlay;
911
912 if (pTst)
913 {
914 switch (pTst->enmState)
915 {
916 case AUDIOTESTSTATE_PRE:
917 RT_FALL_THROUGH();
918 case AUDIOTESTSTATE_POST:
919 {
920 cbWritable = AudioTestBeaconGetRemaining(&pTst->t.TestTone.Beacon);
921 break;
922 }
923
924 case AUDIOTESTSTATE_RUN:
925 {
926 AssertReturn(pTst->t.TestTone.u.Play.cbToRead >= pTst->t.TestTone.u.Play.cbRead, 0);
927 cbWritable = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
928 break;
929 }
930
931 default:
932 break;
933 }
934
935 LogRel2(("ValKit: Test #%RU32: Reporting %RU32 bytes writable (state is '%s')\n",
936 pTst->idxTest, cbWritable, AudioTestStateToStr(pTst->enmState)));
937
938 if (cbWritable == 0)
939 {
940 LogRel2(("ValKit: Test #%RU32: Warning: Not writable anymore (state is '%s'), returning UINT32_MAX\n",
941 pTst->idxTest, AudioTestStateToStr(pTst->enmState)));
942 cbWritable = UINT32_MAX;
943 }
944 }
945 else
946 LogRel2(("ValKit: Reporting UINT32_MAX bytes writable (no playback test running)\n"));
947
948 int rc2 = RTCritSectLeave(&pThis->CritSect);
949 AssertRC(rc2);
950 }
951
952 return cbWritable;
953}
954
955
956/**
957 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
958 */
959static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
960 PPDMAUDIOBACKENDSTREAM pStream)
961{
962 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
963
964#if 0
965 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
966 PDMHOSTAUDIOSTREAMSTATE enmState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
967
968 if (pStream->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
969 {
970 int rc2 = RTCritSectEnter(&pThis->CritSect);
971 if (RT_SUCCESS(rc2))
972 {
973 enmState = pThis->cTestsRec == 0
974 ? PDMHOSTAUDIOSTREAMSTATE_INACTIVE : PDMHOSTAUDIOSTREAMSTATE_OKAY;
975
976 rc2 = RTCritSectLeave(&pThis->CritSect);
977 AssertRC(rc2);
978 }
979 }
980 else
981 enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
982
983 return enmState;
984#else
985 RT_NOREF(pInterface, pStream);
986 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
987#endif
988}
989
990
991/**
992 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
993 */
994static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
995 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
996{
997 if (cbBuf == 0)
998 {
999 /* Fend off draining calls. */
1000 *pcbWritten = 0;
1001 return VINF_SUCCESS;
1002 }
1003
1004 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1005 PVALKITTESTDATA pTst = NULL;
1006
1007 int rc2;
1008#ifdef VBOX_WITH_AUDIO_VALKIT_DUMP_STREAMS
1009 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
1010 rc2 = AudioHlpFileWrite(pStrmValKit->pFile, pvBuf, cbBuf);
1011 AssertRC(rc2);
1012#endif
1013
1014 /* Flag indicating whether the whole block we're going to play is silence or not. */
1015 bool const fIsAllSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, pvBuf, cbBuf);
1016
1017 int rc = RTCritSectEnter(&pThis->CritSect);
1018 if (RT_SUCCESS(rc))
1019 {
1020 pThis->cbPlayedTotal += cbBuf; /* Do a bit of accounting. */
1021
1022 if (pThis->pTestCurPlay == NULL)
1023 {
1024 pThis->pTestCurPlay = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
1025 if (pThis->pTestCurPlay)
1026 LogRel(("ValKit: Next guest playback test in queue is test #%RU32\n", pThis->pTestCurPlay->idxTest));
1027 }
1028
1029 pTst = pThis->pTestCurPlay;
1030
1031 rc2 = RTCritSectLeave(&pThis->CritSect);
1032 AssertRC(rc2);
1033 }
1034
1035 if (pTst == NULL) /* Empty list? */
1036 {
1037 pThis->cbPlayedNoTest += cbBuf;
1038
1039 *pcbWritten = cbBuf;
1040 return VINF_SUCCESS;
1041 }
1042
1043 if (pThis->cbPlayedNoTest)
1044 {
1045 LogRel(("ValKit: Warning: Guest was playing back audio (%RU64 bytes, %RU64ms) when no playback test is active\n",
1046 pThis->cbPlayedNoTest, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedNoTest)));
1047 pThis->cbPlayedNoTest = 0;
1048 }
1049
1050 if (fIsAllSilence)
1051 {
1052 pThis->cbPlayedSilence += cbBuf;
1053 }
1054 else /* Audible data */
1055 {
1056 if (pThis->cbPlayedSilence)
1057 LogRel(("ValKit: Guest was playing back %RU64 bytes (%RU64ms) of silence\n",
1058 pThis->cbPlayedSilence, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedSilence)));
1059 pThis->cbPlayedSilence = 0;
1060 }
1061
1062 if (pTst->enmState == AUDIOTESTSTATE_INIT) /* Test not started yet? */
1063 {
1064 AUDIOTESTPARMS Parms;
1065 RT_ZERO(Parms);
1066 Parms.enmDir = PDMAUDIODIR_IN;
1067 Parms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
1068 Parms.TestTone = pTst->t.TestTone.Parms;
1069
1070 rc = AudioTestSetTestBegin(&pThis->Set, "Recording audio data from guest",
1071 &Parms, &pTst->pEntry);
1072 if (RT_SUCCESS(rc))
1073 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-rec.pcm", &pTst->Obj);
1074
1075 if (RT_SUCCESS(rc))
1076 {
1077 pTst->msStartedTS = RTTimeMilliTS();
1078 LogRel(("ValKit: Test #%RU32: Recording audio data (%RU16Hz, %RU32ms) for host test #%RU32 started (delay is %RU32ms)\n",
1079 pTst->idxTest, (uint16_t)Parms.TestTone.dbFreqHz, Parms.TestTone.msDuration,
1080 Parms.TestTone.Hdr.idxSeq, RTTimeMilliTS() - pTst->msRegisteredTS));
1081
1082 char szTimeCreated[RTTIME_STR_LEN];
1083 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
1084 LogRel(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
1085
1086 pTst->enmState = AUDIOTESTSTATE_PRE;
1087 }
1088 }
1089
1090 LogRel3(("ValKit: Test #%RU32: Playing stream '%s' (%RU32 bytes / %RU64ms) -- state is '%s' ...\n",
1091 pTst->idxTest, pStream->pStream->Cfg.szName,
1092 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf),
1093 AudioTestStateToStr(pTst->enmState)));
1094
1095 uint32_t cbWritten = 0;
1096
1097 if (RT_SUCCESS(rc))
1098 {
1099 switch (pTst->enmState)
1100 {
1101 case AUDIOTESTSTATE_PRE:
1102 RT_FALL_THROUGH();
1103 case AUDIOTESTSTATE_POST:
1104 {
1105 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1106
1107 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) beacon data remaining\n",
1108 pTst->idxTest,
1109 AudioTestBeaconGetRemaining(pBeacon),
1110 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, AudioTestBeaconGetRemaining(pBeacon))));
1111
1112 if ( AudioTestBeaconGetSize(pBeacon)
1113 && !AudioTestBeaconIsComplete(pBeacon))
1114 {
1115 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1116
1117 size_t uOff; /* Points at the data right *after the found beacon data. */
1118 rc2 = AudioTestBeaconAddConsecutive(pBeacon, (uint8_t *)pvBuf, cbBuf, &uOff);
1119 if (RT_SUCCESS(rc2))
1120 {
1121 /*
1122 * When being in the AUDIOTESTSTATE_PRE state, we might get more audio data
1123 * than we need for the pre-beacon to complete. In other words, that "more data"
1124 * needs to be counted to the actual recorded test tone data then.
1125 */
1126 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1127 {
1128 uint32_t const cbRemaining = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
1129 uint32_t cbData = cbBuf - (uint32_t)uOff;
1130
1131 if (cbData > cbRemaining) /* Overflow handling -- should never happen! */
1132 {
1133 LogRel(("ValKit: Test #%RU32: Warning: Pre-beacon data contains more data than remaining test "
1134 "expects (%RU32 bytes) -- %RU32 bytes will be *lost* !!!\n",
1135 pTst->idxTest, cbRemaining, cbData - cbRemaining));
1136 cbData = cbRemaining;
1137 }
1138
1139 pTst->t.TestTone.u.Play.cbRead += cbData;
1140 }
1141 }
1142
1143 if (fStarted)
1144 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon started (%RU64ms played so far)\n",
1145 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType),
1146 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedTotal)));
1147 if (AudioTestBeaconIsComplete(pBeacon))
1148 {
1149 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon ended\n",
1150 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1151
1152 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1153 pTst->enmState = AUDIOTESTSTATE_RUN;
1154 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1155 pTst->enmState = AUDIOTESTSTATE_DONE;
1156 }
1157 }
1158 else /* Go to the next state. */
1159 pTst->enmState = AUDIOTESTSTATE_RUN;
1160 break;
1161 }
1162
1163 case AUDIOTESTSTATE_RUN:
1164 {
1165 uint32_t const cbRemaining = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
1166
1167 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) audio data remaining\n",
1168 pTst->idxTest, cbRemaining, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbRemaining)));
1169
1170 /* Whether we count all silence as recorded data or not.
1171 * Currently we don't, as otherwise consequtively played tones will be cut off in the end. */
1172 if (!fIsAllSilence)
1173 {
1174 /* Don't read more than we're told to.
1175 * After the actual test tone data there might come a post beacon which also
1176 * needs to be handled in the AUDIOTESTSTATE_POST state then. */
1177 if (cbBuf > cbRemaining)
1178 {
1179 LogRel(("ValKit: Test #%RU32: Warning: Audio data contains more data than remaining test "
1180 "expects (%RU32 bytes) -- %RU32 bytes will be *lost* !!!\n",
1181 pTst->idxTest, cbRemaining, cbBuf - cbRemaining));
1182 cbBuf = cbRemaining;
1183 }
1184
1185 pTst->t.TestTone.u.Play.cbRead += cbBuf;
1186 }
1187
1188 const bool fComplete = pTst->t.TestTone.u.Play.cbRead >= pTst->t.TestTone.u.Play.cbToRead;
1189 if (fComplete)
1190 {
1191 LogRel(("ValKit: Test #%RU32: Recording audio data ended (took %RU32ms)\n",
1192 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1193
1194 if (pTst->t.TestTone.u.Play.cbRead > pTst->t.TestTone.u.Play.cbToRead)
1195 LogRel(("ValKit: Warning: Test #%RU32 read %RU32 bytes more than announced\n",
1196 pTst->idxTest, pTst->t.TestTone.u.Play.cbRead - pTst->t.TestTone.u.Play.cbToRead));
1197
1198 pTst->enmState = AUDIOTESTSTATE_POST;
1199 /* Re-use the beacon object, but this time it's the post beacon. */
1200 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, AUDIOTESTTONEBEACONTYPE_PLAY_POST, &pTst->t.TestTone.Parms.Props);
1201 }
1202 break;
1203 }
1204
1205 case AUDIOTESTSTATE_DONE:
1206 {
1207 /* Handled below. */
1208 break;
1209 }
1210
1211 default:
1212 AssertFailed();
1213 break;
1214 }
1215
1216 /* Always write (record) everything, no matter if the current audio contains complete silence or not.
1217 * Might be also become handy later if we want to have a look at start/stop timings and so on. */
1218 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbBuf);
1219 AssertRC(rc);
1220
1221 /* Always report everything as being played. */
1222 cbWritten = cbBuf;
1223 }
1224
1225 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1226 {
1227 AudioTestSetTestDone(pTst->pEntry);
1228
1229 rc = RTCritSectEnter(&pThis->CritSect);
1230 if (RT_SUCCESS(rc))
1231 {
1232 drvHostValKiUnregisterPlayTest(pThis, pTst);
1233
1234 pThis->pTestCurPlay = NULL;
1235 pTst = NULL;
1236
1237 rc2 = RTCritSectLeave(&pThis->CritSect);
1238 if (RT_SUCCESS(rc))
1239 rc = rc2;
1240 }
1241 }
1242
1243 if (RT_FAILURE(rc))
1244 {
1245 if ( pTst
1246 && pTst->pEntry)
1247 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
1248 LogRel(("ValKit: Recording audio data failed with %Rrc\n", rc));
1249 }
1250
1251 *pcbWritten = cbWritten;
1252
1253 return VINF_SUCCESS; /** @todo Return rc here? */
1254}
1255
1256
1257/**
1258 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1259 */
1260static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1261 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1262{
1263 RT_NOREF(pStream);
1264
1265 if (cbBuf == 0)
1266 {
1267 /* Fend off draining calls. */
1268 *pcbRead = 0;
1269 return VINF_SUCCESS;
1270 }
1271
1272 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1273 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
1274 PVALKITTESTDATA pTst = NULL;
1275
1276 LogRel3(("ValKit: Capturing stream '%s' (%RU32 bytes / %RU64ms -- %RU64 bytes / %RU64ms total so far) ...\n",
1277 pStream->pStream->Cfg.szName,
1278 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf),
1279 pThis->cbRecordedTotal, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbRecordedTotal)));
1280
1281 int rc = RTCritSectEnter(&pThis->CritSect);
1282 if (RT_SUCCESS(rc))
1283 {
1284 if (pThis->pTestCurRec == NULL)
1285 {
1286 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
1287 if (pThis->pTestCurRec)
1288 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
1289 }
1290
1291 pTst = pThis->pTestCurRec;
1292
1293 int rc2 = RTCritSectLeave(&pThis->CritSect);
1294 AssertRC(rc2);
1295 }
1296
1297 if (pTst == NULL) /* Empty list? */
1298 {
1299 LogRel(("ValKit: Warning: Guest is trying to record audio data when no recording test is active (%RU32 bytes available)\n",
1300 pStrmValKit->cbAvail));
1301
1302 /** @todo Not sure yet why this happens after all data has been captured sometimes,
1303 * but the guest side just will record silence and the audio test verification
1304 * will have to deal with (and/or report) it then. */
1305 PDMAudioPropsClearBuffer(&pStream->pStream->Cfg.Props, pvBuf, cbBuf,
1306 PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf));
1307
1308 *pcbRead = cbBuf; /* Just report back stuff as being "recorded" (silence). */
1309 return VINF_SUCCESS;
1310 }
1311
1312 uint32_t cbWritten = 0;
1313
1314 switch (pTst->enmState)
1315 {
1316 case AUDIOTESTSTATE_PRE:
1317 RT_FALL_THROUGH();
1318 case AUDIOTESTSTATE_POST:
1319 {
1320 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1321 if ( AudioTestBeaconGetSize(pBeacon)
1322 && !AudioTestBeaconIsComplete(pBeacon))
1323 {
1324 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1325
1326 uint32_t const cbBeaconRemaining = AudioTestBeaconGetRemaining(pBeacon);
1327 AssertBreakStmt(cbBeaconRemaining, VERR_WRONG_ORDER);
1328
1329 /* Limit to exactly one beacon (pre or post). */
1330 uint32_t const cbToWrite = RT_MIN(cbBuf, cbBeaconRemaining);
1331
1332 rc = AudioTestBeaconWrite(pBeacon, pvBuf, cbToWrite);
1333 if (RT_SUCCESS(rc))
1334 cbWritten = cbToWrite;
1335
1336 if (fStarted)
1337 LogRel2(("ValKit: Test #%RU32: Writing %s beacon begin\n",
1338 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1339 if (AudioTestBeaconIsComplete(pBeacon))
1340 {
1341 LogRel2(("ValKit: Test #%RU32: Writing %s beacon end\n",
1342 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1343
1344 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1345 pTst->enmState = AUDIOTESTSTATE_RUN;
1346 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1347 pTst->enmState = AUDIOTESTSTATE_DONE;
1348 }
1349 }
1350 else /* Go to the next state. */
1351 pTst->enmState = AUDIOTESTSTATE_RUN;
1352 break;
1353 }
1354
1355 case AUDIOTESTSTATE_RUN:
1356 {
1357 uint32_t const cbToWrite = RT_MIN(cbBuf, pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten);
1358 if (cbToWrite)
1359 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, cbToWrite, &cbWritten);
1360 if ( RT_SUCCESS(rc)
1361 && cbWritten)
1362 {
1363 Assert(cbWritten == cbToWrite);
1364
1365 if (cbWritten > pStrmValKit->cbAvail)
1366 LogRel(("ValKit: Warning: Test #%RU32: Reading more from capturing stream than availabe for (%RU32 vs. %RU32)\n",
1367 pTst->idxTest, cbWritten, pStrmValKit->cbAvail));
1368
1369 pTst->t.TestTone.u.Rec.cbWritten += cbWritten;
1370 }
1371
1372 LogRel3(("ValKit: Test #%RU32: Supplied %RU32 bytes of (capturing) audio data (%RU32 bytes left)\n",
1373 pTst->idxTest, cbWritten, pStrmValKit->cbAvail));
1374
1375 const bool fComplete = pTst->t.TestTone.u.Rec.cbWritten >= pTst->t.TestTone.u.Rec.cbToWrite;
1376 if (fComplete)
1377 {
1378 LogRel(("ValKit: Test #%RU32: Recording done (took %RU32ms)\n",
1379 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1380
1381 pTst->enmState = AUDIOTESTSTATE_POST;
1382 /* Re-use the beacon object, but this time it's the post beacon. */
1383 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, AUDIOTESTTONEBEACONTYPE_PLAY_POST, &pTst->t.TestTone.Parms.Props);
1384 pStrmValKit->cbAvail += AudioTestBeaconGetSize(&pTst->t.TestTone.Beacon);
1385 }
1386 break;
1387 }
1388
1389 case AUDIOTESTSTATE_DONE:
1390 {
1391 /* Handled below. */
1392 break;
1393 }
1394
1395 default:
1396 AssertFailed();
1397 break;
1398 }
1399
1400 if (RT_SUCCESS(rc))
1401 {
1402 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbWritten);
1403 if (RT_SUCCESS(rc))
1404 pStrmValKit->cbAvail -= RT_MIN(pStrmValKit->cbAvail, cbWritten);
1405 }
1406
1407 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1408 {
1409 AudioTestSetTestDone(pTst->pEntry);
1410
1411 rc = RTCritSectEnter(&pThis->CritSect);
1412 if (RT_SUCCESS(rc))
1413 {
1414 drvHostValKiUnregisterRecTest(pThis, pTst);
1415
1416 pThis->pTestCurRec = NULL;
1417 pTst = NULL;
1418
1419 int rc2 = RTCritSectLeave(&pThis->CritSect);
1420 AssertRC(rc2);
1421 }
1422 }
1423
1424 if (RT_FAILURE(rc))
1425 {
1426 if (pTst->pEntry)
1427 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
1428 LogRel(("ValKit: Test #%RU32: Failed with %Rrc\n", pTst->idxTest, rc));
1429 }
1430
1431 pThis->cbRecordedTotal += cbWritten; /* Do a bit of accounting. */
1432
1433 *pcbRead = cbWritten;
1434
1435 return VINF_SUCCESS; /** @todo Return rc here? */
1436}
1437
1438
1439/**
1440 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1441 */
1442static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1443{
1444 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1445 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1446
1447 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1448 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1449 return NULL;
1450}
1451
1452
1453/**
1454 * Constructs a VaKit audio driver instance.
1455 *
1456 * @copydoc FNPDMDRVCONSTRUCT
1457 */
1458static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1459{
1460 RT_NOREF(pCfg, fFlags);
1461 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1462 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1463 LogRel(("Audio: Initializing VALKIT driver\n"));
1464
1465 /*
1466 * Init the static parts.
1467 */
1468 pThis->pDrvIns = pDrvIns;
1469 /* IBase */
1470 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
1471 /* IHostAudio */
1472 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
1473 pThis->IHostAudio.pfnGetDevices = NULL;
1474 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
1475 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
1476 pThis->IHostAudio.pfnStreamConfigHint = NULL;
1477 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
1478 pThis->IHostAudio.pfnStreamInitAsync = NULL;
1479 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
1480 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
1481 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
1482 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
1483 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
1484 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
1485 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
1486 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
1487 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
1488 pThis->IHostAudio.pfnStreamGetPending = NULL;
1489 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
1490 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
1491 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
1492
1493 int rc = RTCritSectInit(&pThis->CritSect);
1494 AssertRCReturn(rc, rc);
1495 rc = RTSemEventCreate(&pThis->EventSemEnded);
1496 AssertRCReturn(rc, rc);
1497
1498 pThis->cbPlayedTotal = 0;
1499 pThis->cbRecordedTotal = 0;
1500 pThis->cbPlayedSilence = 0;
1501 pThis->cbPlayedNoTest = 0;
1502
1503 pThis->cTestsTotal = 0;
1504 pThis->fTestSetEnd = false;
1505
1506 RTListInit(&pThis->lstTestsRec);
1507 pThis->cTestsRec = 0;
1508 RTListInit(&pThis->lstTestsPlay);
1509 pThis->cTestsPlay = 0;
1510
1511 ATSCALLBACKS Callbacks;
1512 RT_ZERO(Callbacks);
1513 Callbacks.pfnHowdy = drvHostValKitHowdy;
1514 Callbacks.pfnBye = drvHostValKitBye;
1515 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
1516 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
1517 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
1518 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
1519 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
1520 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
1521 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
1522 Callbacks.pvUser = pThis;
1523
1524 /** @todo Make this configurable via CFGM. */
1525 const char *pszBindAddr = "127.0.0.1"; /* Only reachable for localhost for now. */
1526 uint32_t uBindPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
1527
1528 LogRel2(("ValKit: Debug logging enabled\n"));
1529
1530 LogRel(("ValKit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
1531 pszBindAddr, uBindPort));
1532
1533 /* Dont' use rc here, as this will be reported back to PDM and will prevent VBox
1534 * from starting -- not critical but warn the user though. */
1535 int rc2 = AudioTestSvcInit(&pThis->Srv, &Callbacks);
1536 if (RT_SUCCESS(rc2))
1537 {
1538 RTGETOPTUNION Val;
1539 RT_ZERO(Val);
1540
1541 Val.u32 = ATSCONNMODE_SERVER; /** @todo No client connection mode needed here (yet). Make this configurable via CFGM. */
1542 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_CONN_MODE, &Val);
1543 AssertRC(rc2);
1544
1545 Val.psz = pszBindAddr;
1546 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_ADDRESS, &Val);
1547 AssertRC(rc2);
1548
1549 Val.u16 = uBindPort;
1550 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_PORT, &Val);
1551 AssertRC(rc2);
1552
1553 rc2 = AudioTestSvcStart(&pThis->Srv);
1554 }
1555
1556 if (RT_SUCCESS(rc2))
1557 {
1558 LogRel(("ValKit: Audio Test Service (ATS) running\n"));
1559
1560 /** @todo Let the following be customizable by CFGM later. */
1561 rc2 = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
1562 if (RT_SUCCESS(rc2))
1563 {
1564 LogRel(("ValKit: Using temp dir '%s'\n", pThis->szPathTemp));
1565 rc2 = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
1566 if (RT_SUCCESS(rc2))
1567 LogRel(("ValKit: Using output dir '%s'\n", pThis->szPathOut));
1568 }
1569 }
1570
1571 if (RT_FAILURE(rc2))
1572 LogRel(("ValKit: Error starting Audio Test Service (ATS), rc=%Rrc -- tests *will* fail!\n", rc2));
1573
1574 if (RT_FAILURE(rc)) /* This one *is* critical though. */
1575 LogRel(("ValKit: Initialization failed, rc=%Rrc\n", rc));
1576
1577 return rc;
1578}
1579
1580static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
1581{
1582 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1583 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1584
1585 LogRel(("ValKit: Shutting down Audio Test Service (ATS) ...\n"));
1586
1587 int rc = AudioTestSvcStop(&pThis->Srv);
1588 if (RT_SUCCESS(rc))
1589 rc = AudioTestSvcDestroy(&pThis->Srv);
1590
1591 if (RT_SUCCESS(rc))
1592 {
1593 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) complete\n"));
1594 drvHostValKitCleanup(pThis);
1595 }
1596 else
1597 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) failed, rc=%Rrc\n", rc));
1598
1599 /* Try cleaning up a bit. */
1600 RTDirRemove(pThis->szPathTemp);
1601 RTDirRemove(pThis->szPathOut);
1602
1603 RTSemEventDestroy(pThis->EventSemEnded);
1604
1605 if (RTCritSectIsInitialized(&pThis->CritSect))
1606 {
1607 int rc2 = RTCritSectDelete(&pThis->CritSect);
1608 if (RT_SUCCESS(rc))
1609 rc = rc2;
1610 }
1611
1612 if (RT_FAILURE(rc))
1613 LogRel(("ValKit: Destruction failed, rc=%Rrc\n", rc));
1614}
1615
1616/**
1617 * Char driver registration record.
1618 */
1619const PDMDRVREG g_DrvHostValidationKitAudio =
1620{
1621 /* u32Version */
1622 PDM_DRVREG_VERSION,
1623 /* szName */
1624 "ValidationKitAudio",
1625 /* szRCMod */
1626 "",
1627 /* szR0Mod */
1628 "",
1629 /* pszDescription */
1630 "ValidationKitAudio audio host driver",
1631 /* fFlags */
1632 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1633 /* fClass. */
1634 PDM_DRVREG_CLASS_AUDIO,
1635 /* cMaxInstances */
1636 ~0U,
1637 /* cbInstance */
1638 sizeof(DRVHOSTVALKITAUDIO),
1639 /* pfnConstruct */
1640 drvHostValKitAudioConstruct,
1641 /* pfnDestruct */
1642 drvHostValKitAudioDestruct,
1643 /* pfnRelocate */
1644 NULL,
1645 /* pfnIOCtl */
1646 NULL,
1647 /* pfnPowerOn */
1648 NULL,
1649 /* pfnReset */
1650 NULL,
1651 /* pfnSuspend */
1652 NULL,
1653 /* pfnResume */
1654 NULL,
1655 /* pfnAttach */
1656 NULL,
1657 /* pfnDetach */
1658 NULL,
1659 /* pfnPowerOff */
1660 NULL,
1661 /* pfnSoftReset */
1662 NULL,
1663 /* u32EndVersion */
1664 PDM_DRVREG_VERSION
1665};
1666
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