VirtualBox

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

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

scm --update-copyright-year

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