VirtualBox

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

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

Audio/Validation Kit: More bugfixes. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.1 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 91209 2021-09-10 16:39:07Z 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/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44/**
45 * Structure for keeping a Validation Kit input/output stream.
46 */
47typedef struct VALKITAUDIOSTREAM
48{
49 /** Common part. */
50 PDMAUDIOBACKENDSTREAM Core;
51 /** The stream's acquired configuration. */
52 PDMAUDIOSTREAMCFG Cfg;
53 /** How much bytes are available to read (only for capturing streams). */
54 uint32_t cbAvail;
55} VALKITAUDIOSTREAM;
56/** Pointer to a Validation Kit stream. */
57typedef VALKITAUDIOSTREAM *PVALKITAUDIOSTREAM;
58
59/**
60 * Test tone-specific instance data.
61 */
62typedef struct VALKITTESTTONEDATA
63{
64 union
65 {
66 struct
67 {
68 /** How many bytes to write. */
69 uint64_t cbToWrite;
70 /** How many bytes already written. */
71 uint64_t cbWritten;
72 } Rec;
73 struct
74 {
75 /** How many bytes to read. */
76 uint64_t cbToRead;
77 /** How many bytes already read. */
78 uint64_t cbRead;
79 } Play;
80 } u;
81 /** The test tone instance to use. */
82 AUDIOTESTTONE Tone;
83 /** The test tone parameters to use. */
84 AUDIOTESTTONEPARMS Parms;
85} VALKITTESTTONEDATA;
86
87/**
88 * Structure keeping a single Validation Kit test.
89 */
90typedef struct VALKITTESTDATA
91{
92 /** The list node. */
93 RTLISTNODE Node;
94 /** Index in test sequence (0-based). */
95 uint32_t idxTest;
96 /** Current test set entry to process. */
97 PAUDIOTESTENTRY pEntry;
98 /** Current test object to process. */
99 AUDIOTESTOBJ Obj;
100 /** Stream configuration to use for this test. */
101 PDMAUDIOSTREAMCFG StreamCfg;
102 union
103 {
104 /** Test tone-specific data. */
105 VALKITTESTTONEDATA TestTone;
106 } t;
107 /** Time stamp (real, in ms) when test got registered. */
108 uint64_t msRegisteredTS;
109 /** Time stamp (real, in ms) when test started. */
110 uint64_t msStartedTS;
111} VALKITTESTDATA;
112/** Pointer to Validation Kit test data. */
113typedef VALKITTESTDATA *PVALKITTESTDATA;
114
115/**
116 * Validation Kit audio driver instance data.
117 * @implements PDMIAUDIOCONNECTOR
118 */
119typedef struct DRVHOSTVALKITAUDIO
120{
121 /** Pointer to the driver instance structure. */
122 PPDMDRVINS pDrvIns;
123 /** Pointer to host audio interface. */
124 PDMIHOSTAUDIO IHostAudio;
125 /** Total number of bytes played since driver construction. */
126 uint64_t cbPlayedTotal;
127 /** Total number of bytes recorded since driver construction. */
128 uint64_t cbRecordedTotal;
129 /** Total number of bytes silence was played in a consequtive block so far.
130 * Will be reset once audible data is being played (again). */
131 uint64_t cbPlayedSilence;
132 /** Total number of bytes audio (audible or not) was played while no active
133 * audio test was registered / available. */
134 uint64_t cbPlayedNoTest;
135 /** Temporary path to use. */
136 char szPathTemp[RTPATH_MAX];
137 /** Output path to use. */
138 char szPathOut[RTPATH_MAX];
139 /** Current test set being handled.
140 * At the moment only one test set can be around at a time. */
141 AUDIOTESTSET Set;
142 /** Number of total tests in \a lstTestsRec and \a lstTestsPlay. */
143 uint32_t cTestsTotal;
144 /** Increasing number to identify tests. */
145 uint32_t idxTest;
146 /** Number of tests in \a lstTestsRec. */
147 uint32_t cTestsRec;
148 /** List keeping the recording tests (FIFO). */
149 RTLISTANCHOR lstTestsRec;
150 /** Pointer to current recording test being processed.
151 * NULL if no current test active. */
152 PVALKITTESTDATA pTestCurRec;
153 /** Number of tests in \a lstTestsPlay. */
154 uint32_t cTestsPlay;
155 /** List keeping the recording tests (FIFO). */
156 RTLISTANCHOR lstTestsPlay;
157 /** Pointer to current playback test being processed.
158 * NULL if no current test active. */
159 PVALKITTESTDATA pTestCurPlay;
160 /** Critical section for serializing access across threads. */
161 RTCRITSECT CritSect;
162 /** Whether the test set needs to end.
163 * Needed for packing up (to archive) and termination, as capturing and playback
164 * can run in asynchronous threads. */
165 bool fTestSetEnd;
166 /** Event semaphore for waiting on the current test set to end. */
167 RTSEMEVENT EventSemEnded;
168 /** The Audio Test Service (ATS) instance. */
169 ATSSERVER Srv;
170 /** Absolute path to the packed up test set archive.
171 * Keep it simple for now and only support one (open) archive at a time. */
172 char szTestSetArchive[RTPATH_MAX];
173 /** File handle to the (opened) test set archive for reading. */
174 RTFILE hTestSetArchive;
175
176} DRVHOSTVALKITAUDIO;
177/** Pointer to a Validation Kit host audio driver instance. */
178typedef DRVHOSTVALKITAUDIO *PDRVHOSTVALKITAUDIO;
179
180
181/*********************************************************************************************************************************
182* Internal test handling code *
183*********************************************************************************************************************************/
184
185/**
186 * Unregisters a ValKit test, common code.
187 *
188 * @param pThis ValKit audio driver instance.
189 * @param pTst Test to unregister.
190 * The pointer will be invalid afterwards.
191 */
192static void drvHostValKiUnregisterTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
193{
194 AssertPtrReturnVoid(pTst);
195
196 RTListNodeRemove(&pTst->Node);
197
198 AudioTestObjClose(pTst->Obj);
199 pTst->Obj = NULL;
200
201 if (pTst->pEntry) /* Set set entry assign? Mark as done. */
202 {
203 AssertPtrReturnVoid(pTst->pEntry);
204 pTst->pEntry = NULL;
205 }
206
207 RTMemFree(pTst);
208 pTst = NULL;
209
210 Assert(pThis->cTestsTotal);
211 pThis->cTestsTotal--;
212 if (pThis->cTestsTotal == 0)
213 {
214 if (ASMAtomicReadBool(&pThis->fTestSetEnd))
215 {
216 int rc2 = RTSemEventSignal(pThis->EventSemEnded);
217 AssertRC(rc2);
218 }
219 }
220}
221
222/**
223 * Unregisters a ValKit recording test.
224 *
225 * @param pThis ValKit audio driver instance.
226 * @param pTst Test to unregister.
227 * The pointer will be invalid afterwards.
228 */
229static void drvHostValKiUnregisterRecTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
230{
231 Assert(pThis->cTestsRec);
232 pThis->cTestsRec--;
233
234 drvHostValKiUnregisterTest(pThis, pTst);
235}
236
237/**
238 * Unregisters a ValKit playback 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 drvHostValKiUnregisterPlayTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
245{
246 Assert(pThis->cTestsPlay);
247 pThis->cTestsPlay--;
248
249 drvHostValKiUnregisterTest(pThis, pTst);
250}
251
252/**
253 * Performs some internal cleanup / housekeeping of all registered tests.
254 *
255 * @param pThis ValKit audio driver instance.
256 */
257static void drvHostValKitCleanup(PDRVHOSTVALKITAUDIO pThis)
258{
259 LogRel(("ValKit: Cleaning up ...\n"));
260
261 if ( pThis->cTestsTotal
262 && ( !pThis->cbPlayedTotal
263 && !pThis->cbRecordedTotal)
264 )
265 {
266 LogRel(("ValKit: Warning: Did not get any audio data to play or record altough tests were configured\n\n"));
267 LogRel(("ValKit: Hints:\n"
268 "ValKit: - Audio device emulation configured and enabled for the VM?\n"
269 "ValKit: - Audio input and/or output enabled for the VM?\n"
270 "ValKit: - Is the guest able to play / record sound at all?\n"
271 "ValKit: - Is the guest's audio mixer or input / output sinks muted?\n"
272 "ValKit: - Audio stack misconfiguration / bug?\n\n"));
273 }
274
275 if (pThis->cTestsRec)
276 LogRel(("ValKit: Warning: %RU32 guest recording tests still outstanding:\n", pThis->cTestsRec));
277
278 PVALKITTESTDATA pTst, pTstNext;
279 RTListForEachSafe(&pThis->lstTestsRec, pTst, pTstNext, VALKITTESTDATA, Node)
280 {
281 size_t const cbOutstanding = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
282 if (cbOutstanding)
283 LogRel(("ValKit: \tRecording test #%RU32 has %RU64 bytes (%RU32ms) outstanding (%RU8%% left)\n",
284 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding),
285 100 - (pTst->t.TestTone.u.Rec.cbWritten * 100) / RT_MAX(pTst->t.TestTone.u.Rec.cbToWrite, 1)));
286 drvHostValKiUnregisterRecTest(pThis, pTst);
287 }
288
289 if (pThis->cTestsPlay)
290 LogRel(("ValKit: Warning: %RU32 guest playback tests still outstanding:\n", pThis->cTestsPlay));
291
292 RTListForEachSafe(&pThis->lstTestsPlay, pTst, pTstNext, VALKITTESTDATA, Node)
293 {
294 size_t const cbOutstanding = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
295 if (cbOutstanding)
296 LogRel(("ValKit: \tPlayback test #%RU32 has %RU64 bytes (%RU32ms) outstanding (%RU8%% left)\n",
297 pTst->idxTest, cbOutstanding, PDMAudioPropsBytesToMilli(&pTst->t.TestTone.Parms.Props, (uint32_t)cbOutstanding),
298 100 - (pTst->t.TestTone.u.Play.cbRead * 100) / RT_MAX(pTst->t.TestTone.u.Play.cbToRead, 1)));
299 drvHostValKiUnregisterPlayTest(pThis, pTst);
300 }
301
302 Assert(pThis->cTestsRec == 0);
303 Assert(pThis->cTestsPlay == 0);
304}
305
306
307/*********************************************************************************************************************************
308* ATS callback implementations *
309*********************************************************************************************************************************/
310
311/** @copydoc ATSCALLBACKS::pfnHowdy */
312static DECLCALLBACK(int) drvHostValKitHowdy(void const *pvUser)
313{
314 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
315 RT_NOREF(pThis);
316
317 LogRel(("ValKit: Client connected\n"));
318
319 return VINF_SUCCESS;
320}
321
322/** @copydoc ATSCALLBACKS::pfnBye */
323static DECLCALLBACK(int) drvHostValKitBye(void const *pvUser)
324{
325 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
326 RT_NOREF(pThis);
327
328 LogRel(("ValKit: Client disconnected\n"));
329
330 return VINF_SUCCESS;
331}
332
333/** @copydoc ATSCALLBACKS::pfnTestSetBegin */
334static DECLCALLBACK(int) drvHostValKitTestSetBegin(void const *pvUser, const char *pszTag)
335{
336 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
337
338 LogRel(("ValKit: Beginning test set '%s'\n", pszTag));
339
340 int rc = RTCritSectEnter(&pThis->CritSect);
341 if (RT_SUCCESS(rc))
342 {
343 rc = AudioTestSetCreate(&pThis->Set, pThis->szPathTemp, pszTag);
344
345 int rc2 = RTCritSectLeave(&pThis->CritSect);
346 if (RT_SUCCESS(rc))
347 rc = rc2;
348 }
349
350 if (RT_FAILURE(rc))
351 LogRel(("ValKit: Beginning test set failed with %Rrc\n", rc));
352
353 return rc;
354}
355
356/** @copydoc ATSCALLBACKS::pfnTestSetEnd */
357static DECLCALLBACK(int) drvHostValKitTestSetEnd(void const *pvUser, const char *pszTag)
358{
359 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
360
361 LogRel(("ValKit: Ending test set '%s'\n", pszTag));
362
363 int rc = RTCritSectEnter(&pThis->CritSect);
364 if (RT_SUCCESS(rc))
365 {
366 const PAUDIOTESTSET pSet = &pThis->Set;
367
368 LogRel(("ValKit: Test set has %RU32 tests total, %RU32 (still) running, %RU32 failures total so far\n",
369 AudioTestSetGetTestsTotal(pSet), AudioTestSetGetTestsRunning(pSet), AudioTestSetGetTotalFailures(pSet)));
370 LogRel(("ValKit: %RU32 tests still registered total (%RU32 play, %RU32 record)\n",
371 pThis->cTestsTotal, pThis->cTestsPlay, pThis->cTestsRec));
372
373 if ( AudioTestSetIsRunning(pSet)
374 || pThis->cTestsTotal)
375 {
376 ASMAtomicWriteBool(&pThis->fTestSetEnd, true);
377
378 rc = RTCritSectLeave(&pThis->CritSect);
379 if (RT_SUCCESS(rc))
380 {
381 LogRel(("ValKit: Waiting for all tests of set '%s' to end ...\n", pszTag));
382 rc = RTSemEventWait(pThis->EventSemEnded, RT_MS_1MIN);
383 if (RT_FAILURE(rc))
384 {
385 LogRel(("ValKit: Waiting for tests of set '%s' to end failed with %Rrc\n", pszTag, rc));
386
387 /* The verification on the host will tell us later which tests did run and which didn't (anymore).
388 * So continue and pack (plus transfer) the test set to the host. */
389 if (rc == VERR_TIMEOUT)
390 rc = VINF_SUCCESS;
391 }
392
393 int rc2 = RTCritSectEnter(&pThis->CritSect);
394 if (RT_SUCCESS(rc))
395 rc = rc2;
396 }
397 }
398
399 if (RT_SUCCESS(rc))
400 {
401 LogRel(("ValKit: Closing test set '%s' ...\n", pszTag));
402
403 /* Close the test set first. */
404 rc = AudioTestSetClose(pSet);
405 if (RT_SUCCESS(rc))
406 {
407 /* Before destroying the test environment, pack up the test set so
408 * that it's ready for transmission. */
409 rc = AudioTestSetPack(pSet, pThis->szPathOut, pThis->szTestSetArchive, sizeof(pThis->szTestSetArchive));
410 if (RT_SUCCESS(rc))
411 {
412 LogRel(("ValKit: Packed up to '%s'\n", pThis->szTestSetArchive));
413 }
414 else
415 LogRel(("ValKit: Packing up test set failed with %Rrc\n", rc));
416
417 /* Do some internal housekeeping. */
418 drvHostValKitCleanup(pThis);
419
420#ifndef DEBUG_andy
421 int rc2 = AudioTestSetWipe(pSet);
422 if (RT_SUCCESS(rc))
423 rc = rc2;
424#endif
425 }
426 else
427 LogRel(("ValKit: Closing test set failed with %Rrc\n", rc));
428
429 int rc2 = AudioTestSetDestroy(pSet);
430 if (RT_FAILURE(rc2))
431 {
432 LogRel(("ValKit: Destroying test set failed with %Rrc\n", rc));
433 if (RT_SUCCESS(rc))
434 rc = rc2;
435 }
436 }
437
438 int rc2 = RTCritSectLeave(&pThis->CritSect);
439 if (RT_SUCCESS(rc))
440 rc = rc2;
441 }
442
443 if (RT_FAILURE(rc))
444 LogRel(("ValKit: Ending test set failed with %Rrc\n", rc));
445
446 return rc;
447}
448
449/** @copydoc ATSCALLBACKS::pfnTonePlay
450 *
451 * Creates and registers a new test tone guest recording test.
452 * This backend will play (inject) input data to the guest.
453 */
454static DECLCALLBACK(int) drvHostValKitRegisterGuestRecTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
455{
456 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
457
458 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
459 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
460
461 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
462
463 AssertReturn(pTestData->t.TestTone.Parms.msDuration, VERR_INVALID_PARAMETER);
464 AssertReturn(PDMAudioPropsAreValid(&pTestData->t.TestTone.Parms.Props), VERR_INVALID_PARAMETER);
465
466 AudioTestToneInit(&pTestData->t.TestTone.Tone, &pTestData->t.TestTone.Parms.Props, pTestData->t.TestTone.Parms.dbFreqHz);
467
468 pTestData->t.TestTone.u.Rec.cbToWrite = PDMAudioPropsMilliToBytes(&pTestData->t.TestTone.Parms.Props,
469 pTestData->t.TestTone.Parms.msDuration);
470 int rc = RTCritSectEnter(&pThis->CritSect);
471 if (RT_SUCCESS(rc))
472 {
473 LogRel(("ValKit: Registering guest recording test #%RU32 (%RU32ms, %RU64 bytes, host test #%RU32)\n",
474 pThis->idxTest, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Rec.cbToWrite,
475 pToneParms->Hdr.idxSeq));
476
477 RTListAppend(&pThis->lstTestsRec, &pTestData->Node);
478
479 pTestData->msRegisteredTS = RTTimeMilliTS();
480 pTestData->idxTest = pThis->idxTest++;
481
482 pThis->cTestsRec++;
483 pThis->cTestsTotal++;
484
485 int rc2 = RTCritSectLeave(&pThis->CritSect);
486 AssertRC(rc2);
487 }
488
489 return VINF_SUCCESS;
490}
491
492/** @copydoc ATSCALLBACKS::pfnToneRecord
493 *
494 * Creates and registers a new test tone guest playback test.
495 * This backend will record the guest output data.
496 */
497static DECLCALLBACK(int) drvHostValKitRegisterGuestPlayTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
498{
499 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
500
501 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
502 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
503
504 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
505
506 AssertReturn(pTestData->t.TestTone.Parms.msDuration, VERR_INVALID_PARAMETER);
507 AssertReturn(PDMAudioPropsAreValid(&pTestData->t.TestTone.Parms.Props), VERR_INVALID_PARAMETER);
508
509 pTestData->t.TestTone.u.Play.cbToRead = PDMAudioPropsMilliToBytes(&pTestData->t.TestTone.Parms.Props,
510 pTestData->t.TestTone.Parms.msDuration);
511 int rc = RTCritSectEnter(&pThis->CritSect);
512 if (RT_SUCCESS(rc))
513 {
514 LogRel(("ValKit: Registering guest playback test #%RU32 (%RU32ms, %RU64 bytes, host test #%RU32)\n",
515 pThis->idxTest, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Play.cbToRead,
516 pToneParms->Hdr.idxSeq));
517
518 RTListAppend(&pThis->lstTestsPlay, &pTestData->Node);
519
520 pTestData->msRegisteredTS = RTTimeMilliTS();
521 pTestData->idxTest = pThis->idxTest++;
522
523 pThis->cTestsTotal++;
524 pThis->cTestsPlay++;
525
526 int rc2 = RTCritSectLeave(&pThis->CritSect);
527 AssertRC(rc2);
528 }
529
530 return VINF_SUCCESS;
531}
532
533/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
534static DECLCALLBACK(int) drvHostValKitTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
535{
536 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
537
538 int rc = RTCritSectEnter(&pThis->CritSect);
539 if (RT_SUCCESS(rc))
540 {
541 if (RTFileExists(pThis->szTestSetArchive)) /* Has the archive successfully been created yet? */
542 {
543 rc = RTFileOpen(&pThis->hTestSetArchive, pThis->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
544 if (RT_SUCCESS(rc))
545 {
546 uint64_t uSize;
547 rc = RTFileQuerySize(pThis->hTestSetArchive, &uSize);
548 if (RT_SUCCESS(rc))
549 LogRel(("ValKit: Sending test set '%s' (%zu bytes)\n", pThis->szTestSetArchive, uSize));
550 }
551 }
552 else
553 rc = VERR_FILE_NOT_FOUND;
554
555 int rc2 = RTCritSectLeave(&pThis->CritSect);
556 if (RT_SUCCESS(rc))
557 rc = rc2;
558 }
559
560 if (RT_FAILURE(rc))
561 LogRel(("ValKit: Beginning to send test set '%s' failed with %Rrc\n", pszTag, rc));
562
563 return rc;
564}
565
566/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
567static DECLCALLBACK(int) drvHostValKitTestSetSendReadCallback(void const *pvUser,
568 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
569{
570 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
571
572 int rc = RTCritSectEnter(&pThis->CritSect);
573 if (RT_SUCCESS(rc))
574 {
575 if (RTFileIsValid(pThis->hTestSetArchive))
576 {
577 rc = RTFileRead(pThis->hTestSetArchive, pvBuf, cbBuf, pcbRead);
578 }
579 else
580 rc = VERR_WRONG_ORDER;
581
582 int rc2 = RTCritSectLeave(&pThis->CritSect);
583 if (RT_SUCCESS(rc))
584 rc = rc2;
585 }
586
587 if (RT_FAILURE(rc))
588 LogRel(("ValKit: Reading from test set '%s' failed with %Rrc\n", pszTag, rc));
589
590 return rc;
591}
592
593/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
594static DECLCALLBACK(int) drvHostValKitTestSetSendEndCallback(void const *pvUser, const char *pszTag)
595{
596 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
597
598 int rc = RTCritSectEnter(&pThis->CritSect);
599 if (RT_SUCCESS(rc))
600 {
601 if (RTFileIsValid(pThis->hTestSetArchive))
602 {
603 rc = RTFileClose(pThis->hTestSetArchive);
604 if (RT_SUCCESS(rc))
605 pThis->hTestSetArchive = NIL_RTFILE;
606 }
607
608 int rc2 = RTCritSectLeave(&pThis->CritSect);
609 if (RT_SUCCESS(rc))
610 rc = rc2;
611 }
612
613 if (RT_FAILURE(rc))
614 LogRel(("ValKit: Ending to send test set '%s' failed with %Rrc\n", pszTag, rc));
615
616 return rc;
617}
618
619
620/*********************************************************************************************************************************
621* PDMIHOSTAUDIO interface implementation *
622*********************************************************************************************************************************/
623
624/**
625 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
626 */
627static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
628{
629 RT_NOREF(pInterface);
630 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
631
632 /*
633 * Fill in the config structure.
634 */
635 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
636 pBackendCfg->cbStream = sizeof(VALKITAUDIOSTREAM);
637 pBackendCfg->fFlags = 0;
638 pBackendCfg->cMaxStreamsOut = 1; /* Output (Playback). */
639 pBackendCfg->cMaxStreamsIn = 1; /* Input (Recording). */
640
641 return VINF_SUCCESS;
642}
643
644
645/**
646 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
647 */
648static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
649{
650 RT_NOREF(enmDir);
651 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
652
653 return PDMAUDIOBACKENDSTS_RUNNING;
654}
655
656
657/**
658 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
659 */
660static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
661 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
662{
663 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
664 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
665 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
666 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
667 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
668 RT_NOREF(pThis);
669
670 int rc = VINF_SUCCESS;
671 PDMAudioStrmCfgCopy(&pStreamDbg->Cfg, pCfgAcq);
672 return rc;
673}
674
675/**
676 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
677 */
678static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
679 bool fImmediate)
680{
681 RT_NOREF(pInterface, fImmediate);
682 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
683 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
684
685 return VINF_SUCCESS;
686}
687
688
689/**
690 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
691 */
692static DECLCALLBACK(int) drvHostValKitAudioHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
693{
694 RT_NOREF(pInterface, pStream);
695 return VINF_SUCCESS;
696}
697
698
699/**
700 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
701 */
702static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
703{
704 RT_NOREF(pInterface, pStream);
705 return VINF_SUCCESS;
706}
707
708
709/**
710 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
711 */
712static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
713{
714 RT_NOREF(pInterface, pStream);
715 return VINF_SUCCESS;
716}
717
718
719/**
720 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
721 */
722static DECLCALLBACK(int) drvHostValKitAudioHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
723{
724 RT_NOREF(pInterface, pStream);
725 return VINF_SUCCESS;
726}
727
728
729/**
730 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
731 */
732static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
733{
734 RT_NOREF(pInterface, pStream);
735 return VINF_SUCCESS;
736}
737
738
739/**
740 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
741 */
742static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
743{
744 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
745 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
746 PVALKITTESTDATA pTst = NULL;
747
748 int rc = RTCritSectEnter(&pThis->CritSect);
749 if (RT_SUCCESS(rc))
750 {
751 if (pThis->pTestCurRec == NULL)
752 {
753 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
754 if (pThis->pTestCurRec)
755 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
756 }
757
758 pTst = pThis->pTestCurRec;
759
760 int rc2 = RTCritSectLeave(&pThis->CritSect);
761 AssertRC(rc2);
762 }
763
764 if ( pTst
765 && pTst->pEntry == NULL) /* Test not started yet? */
766 {
767 AUDIOTESTPARMS Parms;
768 RT_ZERO(Parms);
769 Parms.enmDir = PDMAUDIODIR_OUT;
770 Parms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
771 Parms.TestTone = pTst->t.TestTone.Parms;
772
773 rc = AudioTestSetTestBegin(&pThis->Set, "Injecting audio input data to guest",
774 &Parms, &pTst->pEntry);
775 if (RT_SUCCESS(rc))
776 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-play.pcm", &pTst->Obj);
777
778 if (RT_SUCCESS(rc))
779 {
780 pTst->msStartedTS = RTTimeMilliTS();
781 LogRel(("ValKit: Test #%RU32: Injecting audio input data (%RU16Hz, %RU32ms, %RU32 bytes) for host test #%RU32 started (delay is %RU32ms)\n",
782 pTst->idxTest, (uint16_t)pTst->t.TestTone.Tone.rdFreqHz,
783 pTst->t.TestTone.Parms.msDuration, pTst->t.TestTone.u.Rec.cbToWrite,
784 Parms.TestTone.Hdr.idxSeq, RTTimeMilliTS() - pTst->msRegisteredTS));
785
786 char szTimeCreated[RTTIME_STR_LEN];
787 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
788 LogRel(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
789 }
790
791 pStrmValKit->cbAvail += pTst->t.TestTone.u.Rec.cbToWrite;
792 LogRel(("ValKit: Now total of %RU32 bytes available for capturing\n", pStrmValKit->cbAvail));
793 }
794
795 return pStrmValKit->cbAvail;
796}
797
798
799/**
800 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
801 */
802static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
803{
804 RT_NOREF(pInterface, pStream);
805 return UINT32_MAX;
806}
807
808
809/**
810 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
811 */
812static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
813 PPDMAUDIOBACKENDSTREAM pStream)
814{
815 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
816
817#if 0
818 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
819 PDMHOSTAUDIOSTREAMSTATE enmState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
820
821 if (pStream->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
822 {
823 int rc2 = RTCritSectEnter(&pThis->CritSect);
824 if (RT_SUCCESS(rc2))
825 {
826 enmState = pThis->cTestsRec == 0
827 ? PDMHOSTAUDIOSTREAMSTATE_INACTIVE : PDMHOSTAUDIOSTREAMSTATE_OKAY;
828
829 rc2 = RTCritSectLeave(&pThis->CritSect);
830 AssertRC(rc2);
831 }
832 }
833 else
834 enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
835
836 return enmState;
837#else
838 RT_NOREF(pInterface, pStream);
839 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
840#endif
841}
842
843
844/**
845 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
846 */
847static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
848 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
849{
850 if (cbBuf == 0)
851 {
852 /* Fend off draining calls. */
853 *pcbWritten = 0;
854 return VINF_SUCCESS;
855 }
856
857 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
858 PVALKITTESTDATA pTst = NULL;
859
860 bool const fIsSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, pvBuf, cbBuf);
861
862 LogRel2(("ValKit: Playing stream '%s' ...\n", pStream->pStream->Cfg.szName));
863
864 int rc = RTCritSectEnter(&pThis->CritSect);
865 if (RT_SUCCESS(rc))
866 {
867 pThis->cbPlayedTotal += cbBuf; /* Do a bit of accounting. */
868
869 if (pThis->pTestCurPlay == NULL)
870 {
871 pThis->pTestCurPlay = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
872 if (pThis->pTestCurPlay)
873 LogRel(("ValKit: Next guest playback test in queue is test #%RU32\n", pThis->pTestCurPlay->idxTest));
874 }
875
876 pTst = pThis->pTestCurPlay;
877
878 int rc2 = RTCritSectLeave(&pThis->CritSect);
879 AssertRC(rc2);
880 }
881
882 if (pTst == NULL) /* Empty list? */
883 {
884 LogRel2(("ValKit: Warning: Guest is playing back audio (%RU32 bytes, %RU64ms) when no playback test is active\n",
885 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf)));
886
887 pThis->cbPlayedNoTest += cbBuf;
888
889 *pcbWritten = cbBuf;
890 return VINF_SUCCESS;
891 }
892
893 if (pThis->cbPlayedNoTest)
894 {
895 LogRel(("ValKit: Warning: Guest was playing back audio (%RU32 bytes, %RU64ms) when no playback test is active\n",
896 pThis->cbPlayedNoTest, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedNoTest)));
897 pThis->cbPlayedNoTest = 0;
898 }
899
900 if (fIsSilence)
901 {
902 pThis->cbPlayedSilence += cbBuf;
903 }
904 else /* Audible data */
905 {
906 if (pThis->cbPlayedSilence)
907 LogRel(("ValKit: Guest was playing back %RU32 bytes (%RU64ms) of silence\n",
908 pThis->cbPlayedSilence, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedSilence)));
909 pThis->cbPlayedSilence = 0;
910 }
911
912 bool fHandleSilence = true;
913
914 if (pTst->pEntry == NULL) /* Test not started yet? */
915 {
916 AUDIOTESTPARMS Parms;
917 RT_ZERO(Parms);
918 Parms.enmDir = PDMAUDIODIR_IN;
919 Parms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
920 Parms.TestTone = pTst->t.TestTone.Parms;
921
922 rc = AudioTestSetTestBegin(&pThis->Set, "Recording audio data from guest",
923 &Parms, &pTst->pEntry);
924 if (RT_SUCCESS(rc))
925 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-rec.pcm", &pTst->Obj);
926
927 if (RT_SUCCESS(rc))
928 {
929 pTst->msStartedTS = RTTimeMilliTS();
930 LogRel(("ValKit: Test #%RU32: Recording audio data (%RU16Hz, %RU32ms) for host test #%RU32 started (delay is %RU32ms)\n",
931 pTst->idxTest, (uint16_t)Parms.TestTone.dbFreqHz, Parms.TestTone.msDuration,
932 Parms.TestTone.Hdr.idxSeq, RTTimeMilliTS() - pTst->msRegisteredTS));
933
934 char szTimeCreated[RTTIME_STR_LEN];
935 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
936 LogRel(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
937 }
938 }
939
940 uint32_t cbWritten = 0;
941
942 if (RT_SUCCESS(rc))
943 {
944 if ( !fIsSilence
945 || (fIsSilence && fHandleSilence))
946 {
947 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbBuf);
948 pTst->t.TestTone.u.Play.cbRead += cbBuf;
949
950 const bool fComplete = pTst->t.TestTone.u.Play.cbRead >= pTst->t.TestTone.u.Play.cbToRead;
951 if (fComplete)
952 {
953 LogRel(("ValKit: Test #%RU32: Recording audio data ended (took %RU32ms)\n",
954 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
955
956 if (pTst->t.TestTone.u.Play.cbRead > pTst->t.TestTone.u.Play.cbToRead)
957 LogRel(("ValKit: Warning: Test #%RU32 read %RU32 bytes more than announced\n",
958 pTst->idxTest, pTst->t.TestTone.u.Play.cbRead - pTst->t.TestTone.u.Play.cbToRead));
959
960 AudioTestSetTestDone(pTst->pEntry);
961
962 rc = RTCritSectEnter(&pThis->CritSect);
963 if (RT_SUCCESS(rc))
964 {
965 drvHostValKiUnregisterPlayTest(pThis, pTst);
966
967 pThis->pTestCurPlay = NULL;
968 pTst = NULL;
969
970 int rc2 = RTCritSectLeave(&pThis->CritSect);
971 if (RT_SUCCESS(rc))
972 rc = rc2;
973 }
974 }
975 }
976
977 /* Always report everything as being played. */
978 cbWritten = cbBuf;
979 }
980
981 if (RT_FAILURE(rc))
982 {
983 if ( pTst
984 && pTst->pEntry)
985 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
986 LogRel(("ValKit: Recording audio data failed with %Rrc\n", rc));
987 }
988
989 *pcbWritten = cbWritten;
990
991 return VINF_SUCCESS; /** @todo Return rc here? */
992}
993
994
995/**
996 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
997 */
998static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
999 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1000{
1001 RT_NOREF(pStream);
1002
1003 if (cbBuf == 0)
1004 {
1005 /* Fend off draining calls. */
1006 *pcbRead = 0;
1007 return VINF_SUCCESS;
1008 }
1009
1010 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1011 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
1012 PVALKITTESTDATA pTst = NULL;
1013
1014 LogRel2(("ValKit: Capturing stream '%s' ...\n", pStream->pStream->Cfg.szName));
1015
1016 int rc = RTCritSectEnter(&pThis->CritSect);
1017 if (RT_SUCCESS(rc))
1018 {
1019 pThis->cbRecordedTotal += cbBuf; /* Do a bit of accounting. */
1020
1021 if (pThis->pTestCurRec == NULL)
1022 {
1023 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
1024 if (pThis->pTestCurRec)
1025 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
1026 }
1027
1028 pTst = pThis->pTestCurRec;
1029
1030 int rc2 = RTCritSectLeave(&pThis->CritSect);
1031 AssertRC(rc2);
1032 }
1033
1034 if (pTst == NULL) /* Empty list? */
1035 {
1036 LogRel(("ValKit: Warning: Guest is trying to record %RU32 bytes (%RU32ms) of audio data when no recording test is active (%RU32 bytes available)\n",
1037 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf), pStrmValKit->cbAvail));
1038
1039 /** @todo Not sure yet why this happens after all data has been captured sometimes,
1040 * but the guest side just will record silence and the audio test verification
1041 * will have to deal with (and/or report) it then. */
1042 PDMAudioPropsClearBuffer(&pStream->pStream->Cfg.Props, pvBuf, cbBuf,
1043 PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf));
1044
1045 *pcbRead = cbBuf; /* Just report back stuff as being "recorded" (silence). */
1046 return VINF_SUCCESS;
1047 }
1048
1049 uint32_t cbRead = 0;
1050
1051 if (RT_SUCCESS(rc))
1052 {
1053 uint32_t cbToWrite = RT_MIN(cbBuf,
1054 pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten);
1055 if (cbToWrite)
1056 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, cbToWrite, &cbRead);
1057 if ( RT_SUCCESS(rc)
1058 && cbRead)
1059 {
1060 Assert(cbRead == cbToWrite);
1061
1062 if (cbRead > pStrmValKit->cbAvail)
1063 LogRel(("ValKit: Warning: Test #%RU32: Reading more from capturing stream than availabe for (%RU32 vs. %RU32)\n",
1064 pTst->idxTest, cbRead, pStrmValKit->cbAvail));
1065
1066 pStrmValKit->cbAvail -= RT_MIN(pStrmValKit->cbAvail, cbRead);
1067
1068 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbRead);
1069 if (RT_SUCCESS(rc))
1070 {
1071 pTst->t.TestTone.u.Rec.cbWritten += cbRead;
1072 Assert(pTst->t.TestTone.u.Rec.cbWritten <= pTst->t.TestTone.u.Rec.cbToWrite);
1073
1074 LogRel(("ValKit: Test #%RU32: Read %RU32 bytes of (capturing) audio data (%RU32 bytes left)\n",
1075 pTst->idxTest, cbRead, pStrmValKit->cbAvail));
1076
1077 const bool fComplete = pTst->t.TestTone.u.Rec.cbWritten >= pTst->t.TestTone.u.Rec.cbToWrite;
1078 if (fComplete)
1079 {
1080 LogRel(("ValKit: Test #%RU32: Recording done (took %RU32ms)\n",
1081 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1082
1083 AudioTestSetTestDone(pTst->pEntry);
1084
1085 rc = RTCritSectEnter(&pThis->CritSect);
1086 if (RT_SUCCESS(rc))
1087 {
1088 drvHostValKiUnregisterRecTest(pThis, pTst);
1089
1090 pThis->pTestCurRec = NULL;
1091 pTst = NULL;
1092
1093 int rc2 = RTCritSectLeave(&pThis->CritSect);
1094 AssertRC(rc2);
1095 }
1096 }
1097 }
1098 }
1099 }
1100
1101 if (RT_FAILURE(rc))
1102 {
1103 if (pTst->pEntry)
1104 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
1105 LogRel(("ValKit: Test #%RU32: Failed with %Rrc\n", pTst->idxTest, rc));
1106 }
1107
1108 *pcbRead = cbRead;
1109
1110 return VINF_SUCCESS; /** @todo Return rc here? */
1111}
1112
1113
1114/**
1115 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1116 */
1117static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1118{
1119 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1120 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1121
1122 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1123 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1124 return NULL;
1125}
1126
1127
1128/**
1129 * Constructs a VaKit audio driver instance.
1130 *
1131 * @copydoc FNPDMDRVCONSTRUCT
1132 */
1133static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1134{
1135 RT_NOREF(pCfg, fFlags);
1136 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1137 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1138 LogRel(("Audio: Initializing VALKIT driver\n"));
1139
1140 /*
1141 * Init the static parts.
1142 */
1143 pThis->pDrvIns = pDrvIns;
1144 /* IBase */
1145 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
1146 /* IHostAudio */
1147 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
1148 pThis->IHostAudio.pfnGetDevices = NULL;
1149 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
1150 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
1151 pThis->IHostAudio.pfnStreamConfigHint = NULL;
1152 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
1153 pThis->IHostAudio.pfnStreamInitAsync = NULL;
1154 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
1155 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
1156 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
1157 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
1158 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
1159 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
1160 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
1161 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
1162 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
1163 pThis->IHostAudio.pfnStreamGetPending = NULL;
1164 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
1165 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
1166 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
1167
1168 int rc = RTCritSectInit(&pThis->CritSect);
1169 AssertRCReturn(rc, rc);
1170 rc = RTSemEventCreate(&pThis->EventSemEnded);
1171 AssertRCReturn(rc, rc);
1172
1173 pThis->cbPlayedTotal = 0;
1174 pThis->cbRecordedTotal = 0;
1175 pThis->cbPlayedSilence = 0;
1176 pThis->cbPlayedNoTest = 0;
1177
1178 pThis->cTestsTotal = 0;
1179 pThis->fTestSetEnd = false;
1180
1181 RTListInit(&pThis->lstTestsRec);
1182 pThis->cTestsRec = 0;
1183 RTListInit(&pThis->lstTestsPlay);
1184 pThis->cTestsPlay = 0;
1185
1186 ATSCALLBACKS Callbacks;
1187 RT_ZERO(Callbacks);
1188 Callbacks.pfnHowdy = drvHostValKitHowdy;
1189 Callbacks.pfnBye = drvHostValKitBye;
1190 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
1191 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
1192 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
1193 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
1194 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
1195 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
1196 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
1197 Callbacks.pvUser = pThis;
1198
1199 /** @todo Make this configurable via CFGM. */
1200 const char *pszBindAddr = "127.0.0.1"; /* Only reachable for localhost for now. */
1201 uint32_t uBindPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
1202
1203 LogRel2(("ValKit: Debug logging enabled\n"));
1204
1205 LogRel(("ValKit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
1206 pszBindAddr, uBindPort));
1207
1208 /* Dont' use rc here, as this will be reported back to PDM and will prevent VBox
1209 * from starting -- not critical but warn the user though. */
1210 int rc2 = AudioTestSvcInit(&pThis->Srv, &Callbacks);
1211 if (RT_SUCCESS(rc2))
1212 {
1213 RTGETOPTUNION Val;
1214 RT_ZERO(Val);
1215
1216 Val.u32 = ATSCONNMODE_SERVER; /** @todo No client connection mode needed here (yet). Make this configurable via CFGM. */
1217 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_CONN_MODE, &Val);
1218 AssertRC(rc2);
1219
1220 Val.psz = pszBindAddr;
1221 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_ADDRESS, &Val);
1222 AssertRC(rc2);
1223
1224 Val.u16 = uBindPort;
1225 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_PORT, &Val);
1226 AssertRC(rc2);
1227
1228 rc2 = AudioTestSvcStart(&pThis->Srv);
1229 }
1230
1231 if (RT_SUCCESS(rc2))
1232 {
1233 LogRel(("ValKit: Audio Test Service (ATS) running\n"));
1234
1235 /** @todo Let the following be customizable by CFGM later. */
1236 rc2 = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
1237 if (RT_SUCCESS(rc2))
1238 {
1239 LogRel(("ValKit: Using temp dir '%s'\n", pThis->szPathTemp));
1240 rc2 = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
1241 if (RT_SUCCESS(rc2))
1242 LogRel(("ValKit: Using output dir '%s'\n", pThis->szPathOut));
1243 }
1244 }
1245
1246 if (RT_FAILURE(rc2))
1247 LogRel(("ValKit: Error starting Audio Test Service (ATS), rc=%Rrc -- tests *will* fail!\n", rc2));
1248
1249 if (RT_FAILURE(rc)) /* This one *is* critical though. */
1250 LogRel(("ValKit: Initialization failed, rc=%Rrc\n", rc));
1251
1252 return rc;
1253}
1254
1255static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
1256{
1257 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1258 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1259
1260 LogRel(("ValKit: Shutting down Audio Test Service (ATS) ...\n"));
1261
1262 int rc = AudioTestSvcStop(&pThis->Srv);
1263 if (RT_SUCCESS(rc))
1264 rc = AudioTestSvcDestroy(&pThis->Srv);
1265
1266 if (RT_SUCCESS(rc))
1267 {
1268 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) complete\n"));
1269 drvHostValKitCleanup(pThis);
1270 }
1271 else
1272 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) failed, rc=%Rrc\n", rc));
1273
1274 /* Try cleaning up a bit. */
1275 RTDirRemove(pThis->szPathTemp);
1276 RTDirRemove(pThis->szPathOut);
1277
1278 RTSemEventDestroy(pThis->EventSemEnded);
1279
1280 if (RTCritSectIsInitialized(&pThis->CritSect))
1281 {
1282 int rc2 = RTCritSectDelete(&pThis->CritSect);
1283 if (RT_SUCCESS(rc))
1284 rc = rc2;
1285 }
1286
1287 if (RT_FAILURE(rc))
1288 LogRel(("ValKit: Destruction failed, rc=%Rrc\n", rc));
1289}
1290
1291/**
1292 * Char driver registration record.
1293 */
1294const PDMDRVREG g_DrvHostValidationKitAudio =
1295{
1296 /* u32Version */
1297 PDM_DRVREG_VERSION,
1298 /* szName */
1299 "ValidationKitAudio",
1300 /* szRCMod */
1301 "",
1302 /* szR0Mod */
1303 "",
1304 /* pszDescription */
1305 "ValidationKitAudio audio host driver",
1306 /* fFlags */
1307 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1308 /* fClass. */
1309 PDM_DRVREG_CLASS_AUDIO,
1310 /* cMaxInstances */
1311 ~0U,
1312 /* cbInstance */
1313 sizeof(DRVHOSTVALKITAUDIO),
1314 /* pfnConstruct */
1315 drvHostValKitAudioConstruct,
1316 /* pfnDestruct */
1317 drvHostValKitAudioDestruct,
1318 /* pfnRelocate */
1319 NULL,
1320 /* pfnIOCtl */
1321 NULL,
1322 /* pfnPowerOn */
1323 NULL,
1324 /* pfnReset */
1325 NULL,
1326 /* pfnSuspend */
1327 NULL,
1328 /* pfnResume */
1329 NULL,
1330 /* pfnAttach */
1331 NULL,
1332 /* pfnDetach */
1333 NULL,
1334 /* pfnPowerOff */
1335 NULL,
1336 /* pfnSoftReset */
1337 NULL,
1338 /* u32EndVersion */
1339 PDM_DRVREG_VERSION
1340};
1341
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