VirtualBox

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

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

Audio/Validation Kit: Return the Validation Kit audio driver's stream state as always "working", in the hope to fix failing tests. bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.0 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 91206 2021-09-10 15:27:12Z 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)\n",
474 pThis->idxTest, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Rec.cbToWrite));
475
476 RTListAppend(&pThis->lstTestsRec, &pTestData->Node);
477
478 pTestData->msRegisteredTS = RTTimeMilliTS();
479 pTestData->idxTest = pThis->idxTest++;
480
481 pThis->cTestsRec++;
482 pThis->cTestsTotal++;
483
484 int rc2 = RTCritSectLeave(&pThis->CritSect);
485 AssertRC(rc2);
486 }
487
488 return VINF_SUCCESS;
489}
490
491/** @copydoc ATSCALLBACKS::pfnToneRecord
492 *
493 * Creates and registers a new test tone guest playback test.
494 * This backend will record the guest output data.
495 */
496static DECLCALLBACK(int) drvHostValKitRegisterGuestPlayTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
497{
498 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
499
500 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
501 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
502
503 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
504
505 AssertReturn(pTestData->t.TestTone.Parms.msDuration, VERR_INVALID_PARAMETER);
506 AssertReturn(PDMAudioPropsAreValid(&pTestData->t.TestTone.Parms.Props), VERR_INVALID_PARAMETER);
507
508 pTestData->t.TestTone.u.Play.cbToRead = PDMAudioPropsMilliToBytes(&pTestData->t.TestTone.Parms.Props,
509 pTestData->t.TestTone.Parms.msDuration);
510 int rc = RTCritSectEnter(&pThis->CritSect);
511 if (RT_SUCCESS(rc))
512 {
513 LogRel(("ValKit: Registering guest playback test #%RU32 (%RU32ms, %RU64 bytes)\n",
514 pThis->idxTest, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Play.cbToRead));
515
516 RTListAppend(&pThis->lstTestsPlay, &pTestData->Node);
517
518 pTestData->msRegisteredTS = RTTimeMilliTS();
519 pTestData->idxTest = pThis->idxTest++;
520
521 pThis->cTestsTotal++;
522 pThis->cTestsPlay++;
523
524 int rc2 = RTCritSectLeave(&pThis->CritSect);
525 AssertRC(rc2);
526 }
527
528 return VINF_SUCCESS;
529}
530
531/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
532static DECLCALLBACK(int) drvHostValKitTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
533{
534 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
535
536 int rc = RTCritSectEnter(&pThis->CritSect);
537 if (RT_SUCCESS(rc))
538 {
539 if (RTFileExists(pThis->szTestSetArchive)) /* Has the archive successfully been created yet? */
540 {
541 rc = RTFileOpen(&pThis->hTestSetArchive, pThis->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
542 if (RT_SUCCESS(rc))
543 {
544 uint64_t uSize;
545 rc = RTFileQuerySize(pThis->hTestSetArchive, &uSize);
546 if (RT_SUCCESS(rc))
547 LogRel(("ValKit: Sending test set '%s' (%zu bytes)\n", pThis->szTestSetArchive, uSize));
548 }
549 }
550 else
551 rc = VERR_FILE_NOT_FOUND;
552
553 int rc2 = RTCritSectLeave(&pThis->CritSect);
554 if (RT_SUCCESS(rc))
555 rc = rc2;
556 }
557
558 if (RT_FAILURE(rc))
559 LogRel(("ValKit: Beginning to send test set '%s' failed with %Rrc\n", pszTag, rc));
560
561 return rc;
562}
563
564/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
565static DECLCALLBACK(int) drvHostValKitTestSetSendReadCallback(void const *pvUser,
566 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
567{
568 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
569
570 int rc = RTCritSectEnter(&pThis->CritSect);
571 if (RT_SUCCESS(rc))
572 {
573 if (RTFileIsValid(pThis->hTestSetArchive))
574 {
575 rc = RTFileRead(pThis->hTestSetArchive, pvBuf, cbBuf, pcbRead);
576 }
577 else
578 rc = VERR_WRONG_ORDER;
579
580 int rc2 = RTCritSectLeave(&pThis->CritSect);
581 if (RT_SUCCESS(rc))
582 rc = rc2;
583 }
584
585 if (RT_FAILURE(rc))
586 LogRel(("ValKit: Reading from test set '%s' failed with %Rrc\n", pszTag, rc));
587
588 return rc;
589}
590
591/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
592static DECLCALLBACK(int) drvHostValKitTestSetSendEndCallback(void const *pvUser, const char *pszTag)
593{
594 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
595
596 int rc = RTCritSectEnter(&pThis->CritSect);
597 if (RT_SUCCESS(rc))
598 {
599 if (RTFileIsValid(pThis->hTestSetArchive))
600 {
601 rc = RTFileClose(pThis->hTestSetArchive);
602 if (RT_SUCCESS(rc))
603 pThis->hTestSetArchive = NIL_RTFILE;
604 }
605
606 int rc2 = RTCritSectLeave(&pThis->CritSect);
607 if (RT_SUCCESS(rc))
608 rc = rc2;
609 }
610
611 if (RT_FAILURE(rc))
612 LogRel(("ValKit: Ending to send test set '%s' failed with %Rrc\n", pszTag, rc));
613
614 return rc;
615}
616
617
618/*********************************************************************************************************************************
619* PDMIHOSTAUDIO interface implementation *
620*********************************************************************************************************************************/
621
622/**
623 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
624 */
625static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
626{
627 RT_NOREF(pInterface);
628 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
629
630 /*
631 * Fill in the config structure.
632 */
633 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
634 pBackendCfg->cbStream = sizeof(VALKITAUDIOSTREAM);
635 pBackendCfg->fFlags = 0;
636 pBackendCfg->cMaxStreamsOut = 1; /* Output (Playback). */
637 pBackendCfg->cMaxStreamsIn = 1; /* Input (Recording). */
638
639 return VINF_SUCCESS;
640}
641
642
643/**
644 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
645 */
646static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
647{
648 RT_NOREF(enmDir);
649 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
650
651 return PDMAUDIOBACKENDSTS_RUNNING;
652}
653
654
655/**
656 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
657 */
658static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
659 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
660{
661 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
662 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
663 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
664 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
665 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
666 RT_NOREF(pThis);
667
668 int rc = VINF_SUCCESS;
669 PDMAudioStrmCfgCopy(&pStreamDbg->Cfg, pCfgAcq);
670 return rc;
671}
672
673/**
674 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
675 */
676static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
677 bool fImmediate)
678{
679 RT_NOREF(pInterface, fImmediate);
680 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
681 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
682
683 return VINF_SUCCESS;
684}
685
686
687/**
688 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
689 */
690static DECLCALLBACK(int) drvHostValKitAudioHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
691{
692 RT_NOREF(pInterface, pStream);
693 return VINF_SUCCESS;
694}
695
696
697/**
698 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
699 */
700static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
701{
702 RT_NOREF(pInterface, pStream);
703 return VINF_SUCCESS;
704}
705
706
707/**
708 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
709 */
710static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
711{
712 RT_NOREF(pInterface, pStream);
713 return VINF_SUCCESS;
714}
715
716
717/**
718 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
719 */
720static DECLCALLBACK(int) drvHostValKitAudioHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
721{
722 RT_NOREF(pInterface, pStream);
723 return VINF_SUCCESS;
724}
725
726
727/**
728 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
729 */
730static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
731{
732 RT_NOREF(pStream);
733
734 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
735
736 int rc = RTCritSectEnter(&pThis->CritSect);
737 if (RT_SUCCESS(rc))
738 {
739 PVALKITTESTDATA pTst = pThis->pTestCurPlay;
740
741 if (pTst)
742 {
743 LogRel(("ValKit: Test #%RU32: Recording audio data ended (took %RU32ms)\n",
744 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
745
746 if (pTst->t.TestTone.u.Play.cbRead > pTst->t.TestTone.u.Play.cbToRead)
747 LogRel(("ValKit: Warning: Test #%RU32 read %RU32 bytes more than announced\n",
748 pTst->idxTest, pTst->t.TestTone.u.Play.cbRead - pTst->t.TestTone.u.Play.cbToRead));
749
750 AudioTestSetTestDone(pTst->pEntry);
751
752 pThis->pTestCurPlay = NULL;
753 pTst = NULL;
754
755 if (ASMAtomicReadBool(&pThis->fTestSetEnd))
756 rc = RTSemEventSignal(pThis->EventSemEnded);
757 }
758
759 int rc2 = RTCritSectLeave(&pThis->CritSect);
760 AssertRC(rc2);
761 }
762
763 return VINF_SUCCESS;
764}
765
766
767/**
768 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
769 */
770static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
771{
772 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
773 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
774 PVALKITTESTDATA pTst = NULL;
775
776 int rc = RTCritSectEnter(&pThis->CritSect);
777 if (RT_SUCCESS(rc))
778 {
779 if (pThis->pTestCurRec == NULL)
780 {
781 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
782 if (pThis->pTestCurRec)
783 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
784 }
785
786 pTst = pThis->pTestCurRec;
787
788 int rc2 = RTCritSectLeave(&pThis->CritSect);
789 AssertRC(rc2);
790 }
791
792 if ( pTst
793 && pTst->pEntry == NULL) /* Test not started yet? */
794 {
795 AUDIOTESTPARMS Parms;
796 RT_ZERO(Parms);
797 Parms.enmDir = PDMAUDIODIR_OUT;
798 Parms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
799 Parms.TestTone = pTst->t.TestTone.Parms;
800
801 rc = AudioTestSetTestBegin(&pThis->Set, "Injecting audio input data to guest",
802 &Parms, &pTst->pEntry);
803 if (RT_SUCCESS(rc))
804 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-play.pcm", &pTst->Obj);
805
806 if (RT_SUCCESS(rc))
807 {
808 pTst->msStartedTS = RTTimeMilliTS();
809 LogRel(("ValKit: Injecting audio input data (%RU16Hz, %RU32ms, %RU32 bytes) for test #%RU32 started (delay is %RU32ms)\n",
810 (uint16_t)pTst->t.TestTone.Tone.rdFreqHz,
811 pTst->t.TestTone.Parms.msDuration, pTst->t.TestTone.u.Rec.cbToWrite,
812 Parms.TestTone.Hdr.idxSeq, RTTimeMilliTS() - pTst->msRegisteredTS));
813
814 char szTimeCreated[RTTIME_STR_LEN];
815 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
816 LogRel(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
817 }
818
819 pStrmValKit->cbAvail += pTst->t.TestTone.u.Rec.cbToWrite;
820 LogRel(("ValKit: Now total of %RU32 bytes available for capturing\n", pStrmValKit->cbAvail));
821 }
822
823 LogRel(("ValKit: Test #%RU32: Reporting %RU32 bytes as available\n",
824 pTst ? pTst->idxTest : 9999, pStrmValKit->cbAvail));
825 return pStrmValKit->cbAvail;
826}
827
828
829/**
830 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
831 */
832static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
833{
834 RT_NOREF(pInterface, pStream);
835 return UINT32_MAX;
836}
837
838
839/**
840 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
841 */
842static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
843 PPDMAUDIOBACKENDSTREAM pStream)
844{
845 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
846
847#if 0
848 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
849 PDMHOSTAUDIOSTREAMSTATE enmState = PDMHOSTAUDIOSTREAMSTATE_NOT_WORKING;
850
851 if (pStream->pStream->Cfg.enmDir == PDMAUDIODIR_IN)
852 {
853 int rc2 = RTCritSectEnter(&pThis->CritSect);
854 if (RT_SUCCESS(rc2))
855 {
856 enmState = pThis->cTestsRec == 0
857 ? PDMHOSTAUDIOSTREAMSTATE_INACTIVE : PDMHOSTAUDIOSTREAMSTATE_OKAY;
858
859 rc2 = RTCritSectLeave(&pThis->CritSect);
860 AssertRC(rc2);
861 }
862 }
863 else
864 enmState = PDMHOSTAUDIOSTREAMSTATE_OKAY;
865
866 return enmState;
867#else
868 RT_NOREF(pInterface, pStream);
869 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
870#endif
871}
872
873
874/**
875 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
876 */
877static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
878 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
879{
880 if (cbBuf == 0)
881 {
882 /* Fend off draining calls. */
883 *pcbWritten = 0;
884 return VINF_SUCCESS;
885 }
886
887 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
888 PVALKITTESTDATA pTst = NULL;
889
890 pThis->cbPlayedTotal += cbBuf; /* Do a bit of accounting. */
891
892 bool const fIsSilence = PDMAudioPropsIsBufferSilence(&pStream->pStream->Cfg.Props, pvBuf, cbBuf);
893
894 int rc = RTCritSectEnter(&pThis->CritSect);
895 if (RT_SUCCESS(rc))
896 {
897 if (pThis->pTestCurPlay == NULL)
898 {
899 pThis->pTestCurPlay = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
900 if (pThis->pTestCurPlay)
901 LogRel(("ValKit: Next guest playback test in queue is test #%RU32\n", pThis->pTestCurPlay->idxTest));
902 }
903
904 pTst = pThis->pTestCurPlay;
905
906 int rc2 = RTCritSectLeave(&pThis->CritSect);
907 AssertRC(rc2);
908 }
909
910 if (pTst == NULL) /* Empty list? */
911 {
912 LogRel2(("ValKit: Warning: Guest is playing back audio (%RU32 bytes, %RU64ms) when no playback test is active\n",
913 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf)));
914
915 pThis->cbPlayedNoTest += cbBuf;
916
917 *pcbWritten = cbBuf;
918 return VINF_SUCCESS;
919 }
920
921 if (pThis->cbPlayedNoTest)
922 {
923 LogRel(("ValKit: Warning: Guest was playing back audio (%RU32 bytes, %RU64ms) when no playback test is active\n",
924 pThis->cbPlayedNoTest, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedNoTest)));
925 pThis->cbPlayedNoTest = 0;
926 }
927
928 if (fIsSilence)
929 {
930 pThis->cbPlayedSilence += cbBuf;
931 }
932 else /* Audible data */
933 {
934 if (pThis->cbPlayedSilence)
935 LogRel(("ValKit: Guest was playing back %RU32 bytes (%RU64ms) of silence\n",
936 pThis->cbPlayedSilence, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, pThis->cbPlayedSilence)));
937 pThis->cbPlayedSilence = 0;
938 }
939
940 bool fHandleSilence = true;
941
942 if (pTst->pEntry == NULL) /* Test not started yet? */
943 {
944 AUDIOTESTPARMS Parms;
945 RT_ZERO(Parms);
946 Parms.enmDir = PDMAUDIODIR_IN;
947 Parms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
948 Parms.TestTone = pTst->t.TestTone.Parms;
949
950 rc = AudioTestSetTestBegin(&pThis->Set, "Recording audio data from guest",
951 &Parms, &pTst->pEntry);
952 if (RT_SUCCESS(rc))
953 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-rec.pcm", &pTst->Obj);
954
955 if (RT_SUCCESS(rc))
956 {
957 pTst->msStartedTS = RTTimeMilliTS();
958 LogRel(("ValKit: Test #%RU32: Recording audio data (%RU16Hz, %RU32ms) for test #%RU32 started (delay is %RU32ms)\n",
959 pTst->idxTest, (uint16_t)Parms.TestTone.dbFreqHz, Parms.TestTone.msDuration,
960 Parms.TestTone.Hdr.idxSeq, RTTimeMilliTS() - pTst->msRegisteredTS));
961
962 char szTimeCreated[RTTIME_STR_LEN];
963 RTTimeToString(&Parms.TestTone.Hdr.tsCreated, szTimeCreated, sizeof(szTimeCreated));
964 LogRel(("ValKit: Test created (caller UTC): %s\n", szTimeCreated));
965 }
966 }
967
968 uint32_t cbWritten = 0;
969
970 if (RT_SUCCESS(rc))
971 {
972 if ( !fIsSilence
973 || (fIsSilence && fHandleSilence))
974 {
975 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbBuf);
976 pTst->t.TestTone.u.Play.cbRead += cbBuf;
977
978 const bool fComplete = pTst->t.TestTone.u.Play.cbRead >= pTst->t.TestTone.u.Play.cbToRead;
979 if (fComplete)
980 {
981 LogRel(("ValKit: Test #%RU32: Recording audio data ended (took %RU32ms)\n",
982 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
983
984 if (pTst->t.TestTone.u.Play.cbRead > pTst->t.TestTone.u.Play.cbToRead)
985 LogRel(("ValKit: Warning: Test #%RU32 read %RU32 bytes more than announced\n",
986 pTst->idxTest, pTst->t.TestTone.u.Play.cbRead - pTst->t.TestTone.u.Play.cbToRead));
987
988 AudioTestSetTestDone(pTst->pEntry);
989
990 rc = RTCritSectEnter(&pThis->CritSect);
991 if (RT_SUCCESS(rc))
992 {
993 drvHostValKiUnregisterPlayTest(pThis, pTst);
994
995 pThis->pTestCurPlay = NULL;
996 pTst = NULL;
997
998 int rc2 = RTCritSectLeave(&pThis->CritSect);
999 if (RT_SUCCESS(rc))
1000 rc = rc2;
1001 }
1002 }
1003 }
1004
1005 /* Always report everything as being played. */
1006 cbWritten = cbBuf;
1007 }
1008
1009 if (RT_FAILURE(rc))
1010 {
1011 if ( pTst
1012 && pTst->pEntry)
1013 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
1014 LogRel(("ValKit: Recording audio data failed with %Rrc\n", rc));
1015 }
1016
1017 *pcbWritten = cbWritten;
1018
1019 return VINF_SUCCESS; /** @todo Return rc here? */
1020}
1021
1022
1023/**
1024 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1025 */
1026static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1027 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1028{
1029 RT_NOREF(pStream);
1030
1031 if (cbBuf == 0)
1032 {
1033 /* Fend off draining calls. */
1034 *pcbRead = 0;
1035 return VINF_SUCCESS;
1036 }
1037
1038 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
1039 PVALKITAUDIOSTREAM pStrmValKit = (PVALKITAUDIOSTREAM)pStream;
1040 PVALKITTESTDATA pTst = NULL;
1041
1042 pThis->cbRecordedTotal += cbBuf; /* Do a bit of accounting. */
1043
1044 int rc = RTCritSectEnter(&pThis->CritSect);
1045 if (RT_SUCCESS(rc))
1046 {
1047 if (pThis->pTestCurRec == NULL)
1048 {
1049 pThis->pTestCurRec = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
1050 if (pThis->pTestCurRec)
1051 LogRel(("ValKit: Next guest recording test in queue is test #%RU32\n", pThis->pTestCurRec->idxTest));
1052 }
1053
1054 pTst = pThis->pTestCurRec;
1055
1056 int rc2 = RTCritSectLeave(&pThis->CritSect);
1057 AssertRC(rc2);
1058 }
1059
1060 if (pTst == NULL) /* Empty list? */
1061 {
1062 LogRel(("ValKit: Warning: Guest is trying to record %RU32 bytes (%RU32ms) of audio data when no recording test is active (%RU32 bytes available)\n",
1063 cbBuf, PDMAudioPropsBytesToMilli(&pStream->pStream->Cfg.Props, cbBuf), pStrmValKit->cbAvail));
1064
1065 /** @todo Not sure yet why this happens after all data has been captured sometimes,
1066 * but the guest side just will record silence and the audio test verification
1067 * will have to deal with (and/or report) it then. */
1068 PDMAudioPropsClearBuffer(&pStream->pStream->Cfg.Props, pvBuf, cbBuf,
1069 PDMAudioPropsBytesToFrames(&pStream->pStream->Cfg.Props, cbBuf));
1070
1071 *pcbRead = cbBuf; /* Just report back stuff as being "recorded" (silence). */
1072 return VINF_SUCCESS;
1073 }
1074
1075 uint32_t cbRead = 0;
1076
1077 if (RT_SUCCESS(rc))
1078 {
1079 uint32_t cbToWrite = RT_MIN(cbBuf,
1080 pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten);
1081 if (cbToWrite)
1082 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, cbToWrite, &cbRead);
1083 if ( RT_SUCCESS(rc)
1084 && cbRead)
1085 {
1086 Assert(cbRead == cbToWrite);
1087
1088 if (cbRead > pStrmValKit->cbAvail)
1089 LogRel(("ValKit: Warning: Test #%RU32: Reading more from capturing stream than availabe for (%RU32 vs. %RU32)\n",
1090 pTst->idxTest, cbRead, pStrmValKit->cbAvail));
1091
1092 pStrmValKit->cbAvail -= RT_MIN(pStrmValKit->cbAvail, cbRead);
1093
1094 rc = AudioTestObjWrite(pTst->Obj, pvBuf, cbRead);
1095 if (RT_SUCCESS(rc))
1096 {
1097 pTst->t.TestTone.u.Rec.cbWritten += cbRead;
1098 Assert(pTst->t.TestTone.u.Rec.cbWritten <= pTst->t.TestTone.u.Rec.cbToWrite);
1099
1100 LogRel(("ValKit: Test #%RU32: Read %RU32 bytes of (capturing) audio data (%RU32 bytes left)\n",
1101 pTst->idxTest, cbRead, pStrmValKit->cbAvail));
1102
1103 const bool fComplete = pTst->t.TestTone.u.Rec.cbWritten >= pTst->t.TestTone.u.Rec.cbToWrite;
1104 if (fComplete)
1105 {
1106 LogRel(("ValKit: Test #%RU32: Recording done (took %RU32ms)\n",
1107 pTst->idxTest, RTTimeMilliTS() - pTst->msStartedTS));
1108
1109 AudioTestSetTestDone(pTst->pEntry);
1110
1111 rc = RTCritSectEnter(&pThis->CritSect);
1112 if (RT_SUCCESS(rc))
1113 {
1114 drvHostValKiUnregisterRecTest(pThis, pTst);
1115
1116 pThis->pTestCurRec = NULL;
1117 pTst = NULL;
1118
1119 int rc2 = RTCritSectLeave(&pThis->CritSect);
1120 AssertRC(rc2);
1121 }
1122 }
1123 }
1124 }
1125 }
1126
1127 if (RT_FAILURE(rc))
1128 {
1129 if (pTst->pEntry)
1130 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
1131 LogRel(("ValKit: Test #%RU32: Failed with %Rrc\n", pTst->idxTest, rc));
1132 }
1133
1134 *pcbRead = cbRead;
1135
1136 return VINF_SUCCESS; /** @todo Return rc here? */
1137}
1138
1139
1140/**
1141 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1142 */
1143static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1144{
1145 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1146 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1147
1148 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1149 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1150 return NULL;
1151}
1152
1153
1154/**
1155 * Constructs a VaKit audio driver instance.
1156 *
1157 * @copydoc FNPDMDRVCONSTRUCT
1158 */
1159static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1160{
1161 RT_NOREF(pCfg, fFlags);
1162 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1163 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1164 LogRel(("Audio: Initializing VALKIT driver\n"));
1165
1166 /*
1167 * Init the static parts.
1168 */
1169 pThis->pDrvIns = pDrvIns;
1170 /* IBase */
1171 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
1172 /* IHostAudio */
1173 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
1174 pThis->IHostAudio.pfnGetDevices = NULL;
1175 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
1176 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
1177 pThis->IHostAudio.pfnStreamConfigHint = NULL;
1178 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
1179 pThis->IHostAudio.pfnStreamInitAsync = NULL;
1180 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
1181 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
1182 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
1183 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
1184 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
1185 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
1186 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
1187 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
1188 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
1189 pThis->IHostAudio.pfnStreamGetPending = NULL;
1190 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
1191 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
1192 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
1193
1194 int rc = RTCritSectInit(&pThis->CritSect);
1195 AssertRCReturn(rc, rc);
1196 rc = RTSemEventCreate(&pThis->EventSemEnded);
1197 AssertRCReturn(rc, rc);
1198
1199 pThis->cbPlayedTotal = 0;
1200 pThis->cbRecordedTotal = 0;
1201 pThis->cbPlayedSilence = 0;
1202 pThis->cbPlayedNoTest = 0;
1203
1204 pThis->cTestsTotal = 0;
1205 pThis->cTestsPlay = 0;
1206 pThis->cTestsRec = 0;
1207 pThis->fTestSetEnd = false;
1208
1209 RTListInit(&pThis->lstTestsRec);
1210 pThis->cTestsRec = 0;
1211 RTListInit(&pThis->lstTestsPlay);
1212 pThis->cTestsPlay = 0;
1213
1214 ATSCALLBACKS Callbacks;
1215 RT_ZERO(Callbacks);
1216 Callbacks.pfnHowdy = drvHostValKitHowdy;
1217 Callbacks.pfnBye = drvHostValKitBye;
1218 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
1219 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
1220 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
1221 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
1222 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
1223 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
1224 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
1225 Callbacks.pvUser = pThis;
1226
1227 /** @todo Make this configurable via CFGM. */
1228 const char *pszBindAddr = "127.0.0.1"; /* Only reachable for localhost for now. */
1229 uint32_t uBindPort = ATS_TCP_DEF_BIND_PORT_VALKIT;
1230
1231 LogRel2(("ValKit: Debug logging enabled\n"));
1232
1233 LogRel(("ValKit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
1234 pszBindAddr, uBindPort));
1235
1236 /* Dont' use rc here, as this will be reported back to PDM and will prevent VBox
1237 * from starting -- not critical but warn the user though. */
1238 int rc2 = AudioTestSvcInit(&pThis->Srv, &Callbacks);
1239 if (RT_SUCCESS(rc2))
1240 {
1241 RTGETOPTUNION Val;
1242 RT_ZERO(Val);
1243
1244 Val.u32 = ATSCONNMODE_SERVER; /** @todo No client connection mode needed here (yet). Make this configurable via CFGM. */
1245 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_CONN_MODE, &Val);
1246 AssertRC(rc2);
1247
1248 Val.psz = pszBindAddr;
1249 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_ADDRESS, &Val);
1250 AssertRC(rc2);
1251
1252 Val.u16 = uBindPort;
1253 rc2 = AudioTestSvcHandleOption(&pThis->Srv, ATSTCPOPT_BIND_PORT, &Val);
1254 AssertRC(rc2);
1255
1256 rc2 = AudioTestSvcStart(&pThis->Srv);
1257 }
1258
1259 if (RT_SUCCESS(rc2))
1260 {
1261 LogRel(("ValKit: Audio Test Service (ATS) running\n"));
1262
1263 /** @todo Let the following be customizable by CFGM later. */
1264 rc2 = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
1265 if (RT_SUCCESS(rc2))
1266 {
1267 LogRel(("ValKit: Using temp dir '%s'\n", pThis->szPathTemp));
1268 rc2 = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
1269 if (RT_SUCCESS(rc2))
1270 LogRel(("ValKit: Using output dir '%s'\n", pThis->szPathOut));
1271 }
1272 }
1273
1274 if (RT_FAILURE(rc2))
1275 LogRel(("ValKit: Error starting Audio Test Service (ATS), rc=%Rrc -- tests *will* fail!\n", rc2));
1276
1277 if (RT_FAILURE(rc)) /* This one *is* critical though. */
1278 LogRel(("ValKit: Initialization failed, rc=%Rrc\n", rc));
1279
1280 return rc;
1281}
1282
1283static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
1284{
1285 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
1286 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
1287
1288 LogRel(("ValKit: Shutting down Audio Test Service (ATS) ...\n"));
1289
1290 int rc = AudioTestSvcStop(&pThis->Srv);
1291 if (RT_SUCCESS(rc))
1292 rc = AudioTestSvcDestroy(&pThis->Srv);
1293
1294 if (RT_SUCCESS(rc))
1295 {
1296 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) complete\n"));
1297 drvHostValKitCleanup(pThis);
1298 }
1299 else
1300 LogRel(("ValKit: Shutdown of Audio Test Service (ATS) failed, rc=%Rrc\n", rc));
1301
1302 /* Try cleaning up a bit. */
1303 RTDirRemove(pThis->szPathTemp);
1304 RTDirRemove(pThis->szPathOut);
1305
1306 RTSemEventDestroy(pThis->EventSemEnded);
1307
1308 if (RTCritSectIsInitialized(&pThis->CritSect))
1309 {
1310 int rc2 = RTCritSectDelete(&pThis->CritSect);
1311 if (RT_SUCCESS(rc))
1312 rc = rc2;
1313 }
1314
1315 if (RT_FAILURE(rc))
1316 LogRel(("ValKit: Destruction failed, rc=%Rrc\n", rc));
1317}
1318
1319/**
1320 * Char driver registration record.
1321 */
1322const PDMDRVREG g_DrvHostValidationKitAudio =
1323{
1324 /* u32Version */
1325 PDM_DRVREG_VERSION,
1326 /* szName */
1327 "ValidationKitAudio",
1328 /* szRCMod */
1329 "",
1330 /* szR0Mod */
1331 "",
1332 /* pszDescription */
1333 "ValidationKitAudio audio host driver",
1334 /* fFlags */
1335 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1336 /* fClass. */
1337 PDM_DRVREG_CLASS_AUDIO,
1338 /* cMaxInstances */
1339 ~0U,
1340 /* cbInstance */
1341 sizeof(DRVHOSTVALKITAUDIO),
1342 /* pfnConstruct */
1343 drvHostValKitAudioConstruct,
1344 /* pfnDestruct */
1345 drvHostValKitAudioDestruct,
1346 /* pfnRelocate */
1347 NULL,
1348 /* pfnIOCtl */
1349 NULL,
1350 /* pfnPowerOn */
1351 NULL,
1352 /* pfnReset */
1353 NULL,
1354 /* pfnSuspend */
1355 NULL,
1356 /* pfnResume */
1357 NULL,
1358 /* pfnAttach */
1359 NULL,
1360 /* pfnDetach */
1361 NULL,
1362 /* pfnPowerOff */
1363 NULL,
1364 /* pfnSoftReset */
1365 NULL,
1366 /* u32EndVersion */
1367 PDM_DRVREG_VERSION
1368};
1369
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