VirtualBox

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

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

Audio/ValKit: Windows build fix for r148172. Also, 'off' is recognized hungarian, so skip the 'u'. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.2 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 92327 2021-11-10 15:21:18Z 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 uint8_t *auBuf = (uint8_t *)pvBuf;
1097
1098 while (cbWritten < cbBuf)
1099 {
1100 switch (pTst->enmState)
1101 {
1102 case AUDIOTESTSTATE_PRE:
1103 RT_FALL_THROUGH();
1104 case AUDIOTESTSTATE_POST:
1105 {
1106 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1107
1108 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) beacon data remaining\n",
1109 pTst->idxTest,
1110 AudioTestBeaconGetRemaining(pBeacon),
1111 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, AudioTestBeaconGetRemaining(pBeacon))));
1112
1113 if ( AudioTestBeaconGetSize(pBeacon)
1114 && !AudioTestBeaconIsComplete(pBeacon))
1115 {
1116 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1117
1118 size_t off = 0; /* Points at the data right *after the found beacon data on return. */
1119 rc2 = AudioTestBeaconAddConsecutive(pBeacon, auBuf, cbBuf - cbWritten, &off);
1120 if (RT_SUCCESS(rc2))
1121 {
1122 cbWritten += (uint32_t)off;
1123 auBuf += off;
1124 }
1125
1126 if (fStarted)
1127 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon started (%RU64ms played so far)\n",
1128 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType),
1129 PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedTotal)));
1130 if (AudioTestBeaconIsComplete(pBeacon))
1131 {
1132 LogRel2(("ValKit: Test #%RU32: Detection of %s beacon ended\n",
1133 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1134
1135 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1136 pTst->enmState = AUDIOTESTSTATE_RUN;
1137 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1138 pTst->enmState = AUDIOTESTSTATE_DONE;
1139 }
1140 }
1141 else /* Go to the next state. */
1142 pTst->enmState = AUDIOTESTSTATE_RUN;
1143 break;
1144 }
1145
1146 case AUDIOTESTSTATE_RUN:
1147 {
1148 uint32_t const cbRemaining = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
1149
1150 LogRel3(("ValKit: Test #%RU32: %RU32 bytes (%RU64ms) audio data remaining\n",
1151 pTst->idxTest, cbRemaining, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbRemaining)));
1152
1153 /* Whether we count all silence as recorded data or not.
1154 * Currently we don't, as otherwise consequtively played tones will be cut off in the end. */
1155 if (!fIsAllSilence)
1156 {
1157 /* Don't read more than we're told to.
1158 * After the actual test tone data there might come a post beacon which also
1159 * needs to be handled in the AUDIOTESTSTATE_POST state then. */
1160 const uint32_t cbData = RT_MIN(cbBuf - cbWritten, cbRemaining);
1161
1162 pTst->t.TestTone.u.Play.cbRead += cbData;
1163
1164 cbWritten += cbData;
1165 auBuf += cbData;
1166 }
1167
1168 const bool fComplete = pTst->t.TestTone.u.Play.cbRead >= pTst->t.TestTone.u.Play.cbToRead;
1169 if (fComplete)
1170 {
1171 LogRel(("ValKit: Test #%RU32: Recording audio data ended (took %RU32ms)\n",
1172 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1173
1174 if (pTst->t.TestTone.u.Play.cbRead > pTst->t.TestTone.u.Play.cbToRead)
1175 LogRel(("ValKit: Test #%RU32: Warning: Read %RU32 bytes more than announced\n",
1176 pTst->idxTest, pTst->t.TestTone.u.Play.cbRead - pTst->t.TestTone.u.Play.cbToRead));
1177
1178 pTst->enmState = AUDIOTESTSTATE_POST;
1179 /* Re-use the beacon object, but this time it's the post beacon. */
1180 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, AUDIOTESTTONEBEACONTYPE_PLAY_POST, &pTst->t.TestTone.Parms.Props);
1181 }
1182 break;
1183 }
1184
1185 case AUDIOTESTSTATE_DONE:
1186 {
1187 /* Handled below. */
1188 break;
1189 }
1190
1191 default:
1192 AssertFailed();
1193 break;
1194 }
1195
1196 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1197 break;
1198 }
1199
1200 LogRel3(("ValKit: Test #%RU32: Played %RU32/%RU32 bytes\n", pTst->idxTest, cbWritten, cbBuf));
1201
1202 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbWritten);
1203 AssertRC(rc);
1204
1205 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1206 {
1207 AudioTestSetTestDone(pTst->pEntry);
1208
1209 rc = RTCritSectEnter(&pThis->CritSect);
1210 if (RT_SUCCESS(rc))
1211 {
1212 drvHostValKiUnregisterPlayTest(pThis, pTst);
1213
1214 pThis->pTestCurPlay = NULL;
1215 pTst = NULL;
1216
1217 rc2 = RTCritSectLeave(&pThis->CritSect);
1218 if (RT_SUCCESS(rc))
1219 rc = rc2;
1220 }
1221 }
1222
1223 if (RT_FAILURE(rc))
1224 {
1225 if ( pTst
1226 && pTst->pEntry)
1227 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
1228 LogRel(("ValKit: Recording audio data failed with %Rrc\n", rc));
1229 }
1230
1231 *pcbWritten = cbWritten;
1232
1233 return VINF_SUCCESS; /** @todo Return rc here? */
1234}
1235
1236
1237/**
1238 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1239 */
1240static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1241 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1242{
1243 RT_NOREF(pStream);
1244
1245 if (cbBuf == 0)
1246 {
1247 /* Fend off draining calls. */
1248 *pcbRead = 0;
1249 return VINF_SUCCESS;
1250 }
1251
1252 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1253 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
1254 PVALKITTESTDATA pTst = NULL;
1255
1256 LogRel3(("ValKit: Capturing stream '%s' (%RU32 bytes / %RU64ms -- %RU64 bytes / %RU64ms total so far) ...\n",
1257 pStream->pStream->Cfg.szName,
1258 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf),
1259 pThis->cbRecordedTotal, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbRecordedTotal)));
1260
1261 int rc = RTCritSectEnter(&pThis->CritSect);
1262 if (RT_SUCCESS(rc))
1263 {
1264 if (pThis->pTestCurRec == NULL)
1265 {
1266 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
1267 if (pThis->pTestCurRec)
1268 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
1269 }
1270
1271 pTst = pThis->pTestCurRec;
1272
1273 int rc2 = RTCritSectLeave(&pThis->CritSect);
1274 AssertRC(rc2);
1275 }
1276
1277 if (pTst == NULL) /* Empty list? */
1278 {
1279 LogRel(("ValKit: Warning: Guest is trying to record audio data when no recording test is active (%RU32 bytes available)\n",
1280 pStrmValKit->cbAvail));
1281
1282 /** @todo Not sure yet why this happens after all data has been captured sometimes,
1283 * but the guest side just will record silence and the audio test verification
1284 * will have to deal with (and/or report) it then. */
1285 PDMAudioPropsClearBuffer(&pStream->pStream->Cfg.Props, pvBuf, cbBuf,
1286 PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf));
1287
1288 *pcbRead = cbBuf; /* Just report back stuff as being "recorded" (silence). */
1289 return VINF_SUCCESS;
1290 }
1291
1292 uint32_t cbWritten = 0;
1293
1294 switch (pTst->enmState)
1295 {
1296 case AUDIOTESTSTATE_PRE:
1297 RT_FALL_THROUGH();
1298 case AUDIOTESTSTATE_POST:
1299 {
1300 PAUDIOTESTTONEBEACON pBeacon = &pTst->t.TestTone.Beacon;
1301 if ( AudioTestBeaconGetSize(pBeacon)
1302 && !AudioTestBeaconIsComplete(pBeacon))
1303 {
1304 bool const fStarted = AudioTestBeaconGetRemaining(pBeacon) == AudioTestBeaconGetSize(pBeacon);
1305
1306 uint32_t const cbBeaconRemaining = AudioTestBeaconGetRemaining(pBeacon);
1307 AssertBreakStmt(cbBeaconRemaining, VERR_WRONG_ORDER);
1308
1309 /* Limit to exactly one beacon (pre or post). */
1310 uint32_t const cbToWrite = RT_MIN(cbBuf, cbBeaconRemaining);
1311
1312 rc = AudioTestBeaconWrite(pBeacon, pvBuf, cbToWrite);
1313 if (RT_SUCCESS(rc))
1314 cbWritten = cbToWrite;
1315
1316 if (fStarted)
1317 LogRel2(("ValKit: Test #%RU32: Writing %s beacon begin\n",
1318 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1319 if (AudioTestBeaconIsComplete(pBeacon))
1320 {
1321 LogRel2(("ValKit: Test #%RU32: Writing %s beacon end\n",
1322 pTst->idxTest, AudioTestBeaconTypeGetName(pBeacon->enmType)));
1323
1324 if (pTst->enmState == AUDIOTESTSTATE_PRE)
1325 pTst->enmState = AUDIOTESTSTATE_RUN;
1326 else if (pTst->enmState == AUDIOTESTSTATE_POST)
1327 pTst->enmState = AUDIOTESTSTATE_DONE;
1328 }
1329 }
1330 else /* Go to the next state. */
1331 pTst->enmState = AUDIOTESTSTATE_RUN;
1332 break;
1333 }
1334
1335 case AUDIOTESTSTATE_RUN:
1336 {
1337 uint32_t const cbToWrite = RT_MIN(cbBuf, pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten);
1338 if (cbToWrite)
1339 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, cbToWrite, &cbWritten);
1340 if ( RT_SUCCESS(rc)
1341 && cbWritten)
1342 {
1343 Assert(cbWritten == cbToWrite);
1344
1345 if (cbWritten > pStrmValKit->cbAvail)
1346 LogRel(("ValKit: Warning: Test #%RU32: Reading more from capturing stream than availabe for (%RU32 vs. %RU32)\n",
1347 pTst->idxTest, cbWritten, pStrmValKit->cbAvail));
1348
1349 pTst->t.TestTone.u.Rec.cbWritten += cbWritten;
1350 }
1351
1352 LogRel3(("ValKit: Test #%RU32: Supplied %RU32 bytes of (capturing) audio data (%RU32 bytes left)\n",
1353 pTst->idxTest, cbWritten, pStrmValKit->cbAvail));
1354
1355 const bool fComplete = pTst->t.TestTone.u.Rec.cbWritten >= pTst->t.TestTone.u.Rec.cbToWrite;
1356 if (fComplete)
1357 {
1358 LogRel(("ValKit: Test #%RU32: Recording done (took %RU32ms)\n",
1359 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1360
1361 pTst->enmState = AUDIOTESTSTATE_POST;
1362 /* Re-use the beacon object, but this time it's the post beacon. */
1363 AudioTestBeaconInit(&pTst->t.TestTone.Beacon, AUDIOTESTTONEBEACONTYPE_PLAY_POST, &pTst->t.TestTone.Parms.Props);
1364 pStrmValKit->cbAvail += AudioTestBeaconGetSize(&pTst->t.TestTone.Beacon);
1365 }
1366 break;
1367 }
1368
1369 case AUDIOTESTSTATE_DONE:
1370 {
1371 /* Handled below. */
1372 break;
1373 }
1374
1375 default:
1376 AssertFailed();
1377 break;
1378 }
1379
1380 if (RT_SUCCESS(rc))
1381 {
1382 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbWritten);
1383 if (RT_SUCCESS(rc))
1384 pStrmValKit->cbAvail -= RT_MIN(pStrmValKit->cbAvail, cbWritten);
1385 }
1386
1387 if (pTst->enmState == AUDIOTESTSTATE_DONE)
1388 {
1389 AudioTestSetTestDone(pTst->pEntry);
1390
1391 rc = RTCritSectEnter(&pThis->CritSect);
1392 if (RT_SUCCESS(rc))
1393 {
1394 drvHostValKiUnregisterRecTest(pThis, pTst);
1395
1396 pThis->pTestCurRec = NULL;
1397 pTst = NULL;
1398
1399 int rc2 = RTCritSectLeave(&pThis->CritSect);
1400 AssertRC(rc2);
1401 }
1402 }
1403
1404 if (RT_FAILURE(rc))
1405 {
1406 if (pTst->pEntry)
1407 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
1408 LogRel(("ValKit: Test #%RU32: Failed with %Rrc\n", pTst->idxTest, rc));
1409 }
1410
1411 pThis->cbRecordedTotal += cbWritten; /* Do a bit of accounting. */
1412
1413 *pcbRead = cbWritten;
1414
1415 return VINF_SUCCESS; /** @todo Return rc here? */
1416}
1417
1418
1419/**
1420 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1421 */
1422static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1423{
1424 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1425 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1426
1427 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1428 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1429 return NULL;
1430}
1431
1432
1433/**
1434 * Constructs a VaKit audio driver instance.
1435 *
1436 * @copydoc FNPDMDRVCONSTRUCT
1437 */
1438static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1439{
1440 RT_NOREF(pCfg, fFlags);
1441 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1442 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1443 LogRel(("Audio: Initializing VALKIT driver\n"));
1444
1445 /*
1446 * Init the static parts.
1447 */
1448 pThis->pDrvIns = pDrvIns;
1449 /* IBase */
1450 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
1451 /* IHostAudio */
1452 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
1453 pThis->IHostAudio.pfnGetDevices = NULL;
1454 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
1455 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
1456 pThis->IHostAudio.pfnStreamConfigHint = NULL;
1457 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
1458 pThis->IHostAudio.pfnStreamInitAsync = NULL;
1459 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
1460 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
1461 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
1462 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
1463 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
1464 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
1465 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
1466 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
1467 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
1468 pThis->IHostAudio.pfnStreamGetPending = NULL;
1469 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
1470 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
1471 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
1472
1473 int rc = RTCritSectInit(&pThis->CritSect);
1474 AssertRCReturn(rc, rc);
1475 rc = RTSemEventCreate(&pThis->EventSemEnded);
1476 AssertRCReturn(rc, rc);
1477
1478 pThis->cbPlayedTotal = 0;
1479 pThis->cbRecordedTotal = 0;
1480 pThis->cbPlayedSilence = 0;
1481 pThis->cbPlayedNoTest = 0;
1482
1483 pThis->cTestsTotal = 0;
1484 pThis->fTestSetEnd = false;
1485
1486 RTListInit(&pThis->lstTestsRec);
1487 pThis->cTestsRec = 0;
1488 RTListInit(&pThis->lstTestsPlay);
1489 pThis->cTestsPlay = 0;
1490
1491 ATSCALLBACKS Callbacks;
1492 RT_ZERO(Callbacks);
1493 Callbacks.pfnHowdy = drvHostValKitHowdy;
1494 Callbacks.pfnBye = drvHostValKitBye;
1495 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
1496 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
1497 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
1498 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
1499 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
1500 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
1501 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
1502 Callbacks.pvUser = pThis;
1503
1504 /** @todo Make this configurable via CFGM. */
1505 const char *pszBindAddr = "127.0.0.1"; /* Only reachable for localhost for now. */
1506 uint32_t uBindPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
1507
1508 LogRel2(("ValKit: Debug logging enabled\n"));
1509
1510 LogRel(("ValKit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
1511 pszBindAddr, uBindPort));
1512
1513 /* Dont' use rc here, as this will be reported back to PDM and will prevent VBox
1514 * from starting -- not critical but warn the user though. */
1515 int rc2 = AudioTestSvcInit(&pThis->Srv, &Callbacks);
1516 if (RT_SUCCESS(rc2))
1517 {
1518 RTGETOPTUNION Val;
1519 RT_ZERO(Val);
1520
1521 Val.u32 = ATSCONNMODE_SERVER; /** @todo No client connection mode needed here (yet). Make this configurable via CFGM. */
1522 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_CONN_MODE, &Val);
1523 AssertRC(rc2);
1524
1525 Val.psz = pszBindAddr;
1526 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_ADDRESS, &Val);
1527 AssertRC(rc2);
1528
1529 Val.u16 = uBindPort;
1530 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_PORT, &Val);
1531 AssertRC(rc2);
1532
1533 rc2 = AudioTestSvcStart(&pThis->Srv);
1534 }
1535
1536 if (RT_SUCCESS(rc2))
1537 {
1538 LogRel(("ValKit: Audio Test Service (ATS) running\n"));
1539
1540 /** @todo Let the following be customizable by CFGM later. */
1541 rc2 = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
1542 if (RT_SUCCESS(rc2))
1543 {
1544 LogRel(("ValKit: Using temp dir '%s'\n", pThis->szPathTemp));
1545 rc2 = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
1546 if (RT_SUCCESS(rc2))
1547 LogRel(("ValKit: Using output dir '%s'\n", pThis->szPathOut));
1548 }
1549 }
1550
1551 if (RT_FAILURE(rc2))
1552 LogRel(("ValKit: Error starting Audio Test Service (ATS), rc=%Rrc -- tests *will* fail!\n", rc2));
1553
1554 if (RT_FAILURE(rc)) /* This one *is* critical though. */
1555 LogRel(("ValKit: Initialization failed, rc=%Rrc\n", rc));
1556
1557 return rc;
1558}
1559
1560static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
1561{
1562 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1563 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1564
1565 LogRel(("ValKit: Shutting down Audio Test Service (ATS) ...\n"));
1566
1567 int rc = AudioTestSvcStop(&pThis->Srv);
1568 if (RT_SUCCESS(rc))
1569 rc = AudioTestSvcDestroy(&pThis->Srv);
1570
1571 if (RT_SUCCESS(rc))
1572 {
1573 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) complete\n"));
1574 drvHostValKitCleanup(pThis);
1575 }
1576 else
1577 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) failed, rc=%Rrc\n", rc));
1578
1579 /* Try cleaning up a bit. */
1580 RTDirRemove(pThis->szPathTemp);
1581 RTDirRemove(pThis->szPathOut);
1582
1583 RTSemEventDestroy(pThis->EventSemEnded);
1584
1585 if (RTCritSectIsInitialized(&pThis->CritSect))
1586 {
1587 int rc2 = RTCritSectDelete(&pThis->CritSect);
1588 if (RT_SUCCESS(rc))
1589 rc = rc2;
1590 }
1591
1592 if (RT_FAILURE(rc))
1593 LogRel(("ValKit: Destruction failed, rc=%Rrc\n", rc));
1594}
1595
1596/**
1597 * Char driver registration record.
1598 */
1599const PDMDRVREG g_DrvHostValidationKitAudio =
1600{
1601 /* u32Version */
1602 PDM_DRVREG_VERSION,
1603 /* szName */
1604 "ValidationKitAudio",
1605 /* szRCMod */
1606 "",
1607 /* szR0Mod */
1608 "",
1609 /* pszDescription */
1610 "ValidationKitAudio audio host driver",
1611 /* fFlags */
1612 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1613 /* fClass. */
1614 PDM_DRVREG_CLASS_AUDIO,
1615 /* cMaxInstances */
1616 ~0U,
1617 /* cbInstance */
1618 sizeof(DRVHOSTVALKITAUDIO),
1619 /* pfnConstruct */
1620 drvHostValKitAudioConstruct,
1621 /* pfnDestruct */
1622 drvHostValKitAudioDestruct,
1623 /* pfnRelocate */
1624 NULL,
1625 /* pfnIOCtl */
1626 NULL,
1627 /* pfnPowerOn */
1628 NULL,
1629 /* pfnReset */
1630 NULL,
1631 /* pfnSuspend */
1632 NULL,
1633 /* pfnResume */
1634 NULL,
1635 /* pfnAttach */
1636 NULL,
1637 /* pfnDetach */
1638 NULL,
1639 /* pfnPowerOff */
1640 NULL,
1641 /* pfnSoftReset */
1642 NULL,
1643 /* u32EndVersion */
1644 PDM_DRVREG_VERSION
1645};
1646
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