VirtualBox

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

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

Audio/Validation Kit: Added some simple cumulative accounting for silent audio data blocks and audio data which does not belong to any registered audio tests. Silence now also will be treated as to-be-recorded audio data [build fix]. ​bugref:10008

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