VirtualBox

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

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

Audio/ValKit: Implemented support for downloading (host) test sets. ​bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.7 KB
Line 
1/* $Id: DrvHostAudioValidationKit.cpp 89685 2021-06-14 15:41:09Z 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/stream.h>
28#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
29
30#include <VBox/log.h>
31#include <VBox/vmm/pdmaudioifs.h>
32#include <VBox/vmm/pdmaudioinline.h>
33
34#include "VBoxDD.h"
35#include "AudioHlp.h"
36#include "AudioTest.h"
37#include "AudioTestService.h"
38
39
40/*********************************************************************************************************************************
41* Structures and Typedefs *
42*********************************************************************************************************************************/
43/**
44 * Structure for keeping a Validation Kit input/output stream.
45 */
46typedef struct VALKITAUDIOSTREAM
47{
48 /** Common part. */
49 PDMAUDIOBACKENDSTREAM Core;
50 /** The stream's acquired configuration. */
51 PDMAUDIOSTREAMCFG Cfg;
52} VALKITAUDIOSTREAM;
53/** Pointer to a Validation Kit stream. */
54typedef VALKITAUDIOSTREAM *PVALKITAUDIOSTREAM;
55
56/**
57 * Test tone-specific instance data.
58 */
59typedef struct VALKITTESTTONEDATA
60{
61 union
62 {
63 struct
64 {
65 /** How many bytes to write. */
66 uint64_t cbToWrite;
67 /** How many bytes already written. */
68 uint64_t cbWritten;
69 } Rec;
70 struct
71 {
72 /** How many bytes to read. */
73 uint64_t cbToRead;
74 /** How many bytes already read. */
75 uint64_t cbRead;
76 } Play;
77 } u;
78 /** The test tone instance to use. */
79 AUDIOTESTTONE Tone;
80 /** The test tone parameters to use. */
81 AUDIOTESTTONEPARMS Parms;
82} VALKITTESTTONEDATA;
83
84/**
85 * Structure keeping a single Validation Kit test.
86 */
87typedef struct VALKITTESTDATA
88{
89 /** The list node. */
90 RTLISTNODE Node;
91 /** Index in test sequence (0-based). */
92 uint32_t idxTest;
93 /** Current test set entry to process. */
94 PAUDIOTESTENTRY pEntry;
95 /** Current test object to process. */
96 PAUDIOTESTOBJ pObj;
97 /** Stream configuration to use for this test. */
98 PDMAUDIOSTREAMCFG StreamCfg;
99 union
100 {
101 /** Test tone-specific data. */
102 VALKITTESTTONEDATA TestTone;
103 } t;
104 /** Time stamp (real, in ms) when test started. */
105 uint64_t msStartedTS;
106} VALKITTESTDATA;
107/** Pointer to Validation Kit test data. */
108typedef VALKITTESTDATA *PVALKITTESTDATA;
109
110/**
111 * Validation Kit audio driver instance data.
112 * @implements PDMIAUDIOCONNECTOR
113 */
114typedef struct DRVHOSTVALKITAUDIO
115{
116 /** Pointer to the driver instance structure. */
117 PPDMDRVINS pDrvIns;
118 /** Pointer to host audio interface. */
119 PDMIHOSTAUDIO IHostAudio;
120 /** Temporary path to use. */
121 char szPathTemp[RTPATH_MAX];
122 /** Output path to use. */
123 char szPathOut[RTPATH_MAX];
124 /** Current test set being handled. */
125 AUDIOTESTSET Set;
126 /** Number of total tests created. */
127 uint32_t cTestsTotal;
128 /** Number of tests in \a lstTestsRec. */
129 uint32_t cTestsRec;
130 /** List keeping the recording tests (FIFO). */
131 RTLISTANCHOR lstTestsRec;
132 /** Number of tests in \a lstTestsPlay. */
133 uint32_t cTestsPlay;
134 /** List keeping the recording tests (FIFO). */
135 RTLISTANCHOR lstTestsPlay;
136 /** Pointer to current test being processed. */
137 PVALKITTESTDATA pTestCur;
138 /** Critical section for serializing access across threads. */
139 RTCRITSECT CritSect;
140 /** The Audio Test Service (ATS) instance. */
141 ATSSERVER Srv;
142 /** Absolute path to the packed up test set archive.
143 * Keep it simple for now and only support one (open) archive at a time. */
144 char szTestSetArchive[RTPATH_MAX];
145 /** File handle to the (opened) test set archive for reading. */
146 RTFILE hTestSetArchive;
147
148} DRVHOSTVALKITAUDIO;
149/** Pointer to a Validation Kit host audio driver instance. */
150typedef DRVHOSTVALKITAUDIO *PDRVHOSTVALKITAUDIO;
151
152
153/*********************************************************************************************************************************
154* Internal test handling code *
155*********************************************************************************************************************************/
156
157/**
158 * Unregisters a ValKit test, common code.
159 *
160 * @param pTst Test to unregister.
161 * The pointer will be invalid afterwards.
162 */
163static void drvHostValKiUnregisterTest(PVALKITTESTDATA pTst)
164{
165 AssertPtrReturnVoid(pTst);
166
167 RTListNodeRemove(&pTst->Node);
168
169 AudioTestSetObjClose(pTst->pObj);
170 pTst->pObj = NULL;
171
172 if (pTst->pEntry) /* Set set entry assign? Mark as done. */
173 {
174 AssertPtrReturnVoid(pTst->pEntry);
175 AudioTestSetTestDone(pTst->pEntry);
176 pTst->pEntry = NULL;
177 }
178
179 RTMemFree(pTst);
180 pTst = NULL;
181}
182
183/**
184 * Unregisters a ValKit recording test.
185 *
186 * @param pThis ValKit audio driver instance.
187 * @param pTst Test to unregister.
188 * The pointer will be invalid afterwards.
189 */
190static void drvHostValKiUnregisterRecTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
191{
192 drvHostValKiUnregisterTest(pTst);
193
194 Assert(pThis->cTestsRec);
195 pThis->cTestsRec--;
196}
197
198/**
199 * Unregisters a ValKit playback test.
200 *
201 * @param pThis ValKit audio driver instance.
202 * @param pTst Test to unregister.
203 * The pointer will be invalid afterwards.
204 */
205static void drvHostValKiUnregisterPlayTest(PDRVHOSTVALKITAUDIO pThis, PVALKITTESTDATA pTst)
206{
207 drvHostValKiUnregisterTest(pTst);
208
209 Assert(pThis->cTestsPlay);
210 pThis->cTestsPlay--;
211}
212
213/** @copydoc ATSCALLBACKS::pfnTestSetBegin */
214static DECLCALLBACK(int) drvHostValKitTestSetBegin(void const *pvUser, const char *pszTag)
215{
216 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
217
218 LogRel(("Audio: Validation Kit: Beginning test set '%s'\n", pszTag));
219 return AudioTestSetCreate(&pThis->Set, pThis->szPathTemp, pszTag);
220}
221
222
223/*********************************************************************************************************************************
224* ATS callback implementations *
225*********************************************************************************************************************************/
226
227/** @copydoc ATSCALLBACKS::pfnTestSetEnd */
228static DECLCALLBACK(int) drvHostValKitTestSetEnd(void const *pvUser, const char *pszTag)
229{
230 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
231
232 const PAUDIOTESTSET pSet = &pThis->Set;
233
234 LogRel(("Audio: Validation Kit: Ending test set '%s'\n", pszTag));
235
236 /* Close the test set first. */
237 AudioTestSetClose(pSet);
238
239 /* Before destroying the test environment, pack up the test set so
240 * that it's ready for transmission. */
241 int rc = AudioTestSetPack(pSet, pThis->szPathOut, pThis->szTestSetArchive, sizeof(pThis->szTestSetArchive));
242 if (RT_SUCCESS(rc))
243 LogRel(("Audio: Validation Kit: Packed up to '%s'\n", pThis->szTestSetArchive));
244
245 int rc2 = AudioTestSetWipe(pSet);
246 if (RT_SUCCESS(rc))
247 rc = rc2;
248
249 AudioTestSetDestroy(pSet);
250
251 if (RT_FAILURE(rc))
252 LogRel(("Audio: Validation Kit: Ending test set failed with %Rrc\n", rc));
253
254 return rc;
255}
256
257/** @copydoc ATSCALLBACKS::pfnTonePlay
258 *
259 * Creates and registers a new test tone guest recording test.
260 * This backend will play (inject) input data to the guest.
261 */
262static DECLCALLBACK(int) drvHostValKitRegisterGuestRecTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
263{
264 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
265
266 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
267 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
268
269 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
270
271 AudioTestToneInit(&pTestData->t.TestTone.Tone, &pToneParms->Props, pTestData->t.TestTone.Parms.dbFreqHz);
272
273 pTestData->t.TestTone.u.Rec.cbToWrite = PDMAudioPropsMilliToBytes(&pToneParms->Props,
274 pTestData->t.TestTone.Parms.msDuration);
275 int rc = RTCritSectEnter(&pThis->CritSect);
276 if (RT_SUCCESS(rc))
277 {
278 LogRel(("Audio: Validation Kit: Registered guest recording test #%RU32 (%RU32ms, %RU64 bytes)\n",
279 pThis->cTestsTotal, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Rec.cbToWrite));
280
281 RTListAppend(&pThis->lstTestsRec, &pTestData->Node);
282
283 pTestData->idxTest = pThis->cTestsTotal++;
284
285 pThis->cTestsRec++;
286
287 int rc2 = RTCritSectLeave(&pThis->CritSect);
288 AssertRC(rc2);
289 }
290
291 return VINF_SUCCESS;
292}
293
294/** @copydoc ATSCALLBACKS::pfnToneRecord
295 *
296 * Creates and registers a new test tone guest playback test.
297 * This backend will record the guest output data.
298 */
299static DECLCALLBACK(int) drvHostValKitRegisterGuestPlayTest(void const *pvUser, PAUDIOTESTTONEPARMS pToneParms)
300{
301 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
302
303 PVALKITTESTDATA pTestData = (PVALKITTESTDATA)RTMemAllocZ(sizeof(VALKITTESTDATA));
304 AssertPtrReturn(pTestData, VERR_NO_MEMORY);
305
306 memcpy(&pTestData->t.TestTone.Parms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
307
308 pTestData->t.TestTone.u.Play.cbToRead = PDMAudioPropsMilliToBytes(&pToneParms->Props,
309 pTestData->t.TestTone.Parms.msDuration);
310 int rc = RTCritSectEnter(&pThis->CritSect);
311 if (RT_SUCCESS(rc))
312 {
313 LogRel(("Audio: Validation Kit: Registered guest playback test #%RU32 (%RU32ms, %RU64 bytes)\n",
314 pThis->cTestsTotal, pTestData->t.TestTone.Parms.msDuration, pTestData->t.TestTone.u.Play.cbToRead));
315
316 RTListAppend(&pThis->lstTestsPlay, &pTestData->Node);
317
318 pTestData->idxTest = pThis->cTestsTotal++;
319
320 pThis->cTestsPlay++;
321
322 int rc2 = RTCritSectLeave(&pThis->CritSect);
323 AssertRC(rc2);
324 }
325
326 return VINF_SUCCESS;
327}
328
329/** @copydoc ATSCALLBACKS::pfnTestSetSendBegin */
330static DECLCALLBACK(int) drvHostValKitTestSetSendBeginCallback(void const *pvUser, const char *pszTag)
331{
332 RT_NOREF(pszTag);
333
334 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
335
336 if (!RTFileExists(pThis->szTestSetArchive)) /* Has the archive successfully been created yet? */
337 return VERR_WRONG_ORDER;
338
339 int rc = RTFileOpen(&pThis->hTestSetArchive, pThis->szTestSetArchive, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
340 if (RT_SUCCESS(rc))
341 {
342 uint64_t uSize;
343 rc = RTFileQuerySize(pThis->hTestSetArchive, &uSize);
344 if (RT_SUCCESS(rc))
345 LogRel(("Audio: Validation Kit: Sending test set '%s' (%zu bytes)\n", pThis->szTestSetArchive, uSize));
346 }
347
348 return rc;
349}
350
351/** @copydoc ATSCALLBACKS::pfnTestSetSendRead */
352static DECLCALLBACK(int) drvHostValKitTestSetSendReadCallback(void const *pvUser,
353 const char *pszTag, void *pvBuf, size_t cbBuf, size_t *pcbRead)
354{
355 RT_NOREF(pszTag);
356
357 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
358
359 return RTFileRead(pThis->hTestSetArchive, pvBuf, cbBuf, pcbRead);
360}
361
362/** @copydoc ATSCALLBACKS::pfnTestSetSendEnd */
363static DECLCALLBACK(int) drvHostValKitTestSetSendEndCallback(void const *pvUser, const char *pszTag)
364{
365 RT_NOREF(pszTag);
366
367 PDRVHOSTVALKITAUDIO pThis = (PDRVHOSTVALKITAUDIO)pvUser;
368
369 int rc = RTFileClose(pThis->hTestSetArchive);
370 if (RT_SUCCESS(rc))
371 {
372 pThis->hTestSetArchive = NIL_RTFILE;
373 }
374
375 return rc;
376}
377
378
379/*********************************************************************************************************************************
380* PDMIHOSTAUDIO interface implementation *
381*********************************************************************************************************************************/
382
383/**
384 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
385 */
386static DECLCALLBACK(int) drvHostValKitAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
387{
388 RT_NOREF(pInterface);
389 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
390
391 /*
392 * Fill in the config structure.
393 */
394 RTStrCopy(pBackendCfg->szName, sizeof(pBackendCfg->szName), "Validation Kit");
395 pBackendCfg->cbStream = sizeof(VALKITAUDIOSTREAM);
396 pBackendCfg->fFlags = 0;
397 pBackendCfg->cMaxStreamsOut = 1; /* Output (Playback). */
398 pBackendCfg->cMaxStreamsIn = 1; /* Input (Recording). */
399
400 return VINF_SUCCESS;
401}
402
403
404/**
405 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
406 */
407static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostValKitAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
408{
409 RT_NOREF(enmDir);
410 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
411
412 return PDMAUDIOBACKENDSTS_RUNNING;
413}
414
415
416/**
417 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
418 */
419static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
420 PCPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
421{
422 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
423 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
424 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
425 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
426 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
427 RT_NOREF(pThis);
428
429 int rc = VINF_SUCCESS;
430 PDMAudioStrmCfgCopy(&pStreamDbg->Cfg, pCfgAcq);
431 return rc;
432}
433
434/**
435 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
436 */
437static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
438 bool fImmediate)
439{
440 RT_NOREF(pInterface, fImmediate);
441 PVALKITAUDIOSTREAM pStreamDbg = (PVALKITAUDIOSTREAM)pStream;
442 AssertPtrReturn(pStreamDbg, VERR_INVALID_POINTER);
443
444 return VINF_SUCCESS;
445}
446
447
448/**
449 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamEnable}
450 */
451static DECLCALLBACK(int) drvHostValKitAudioHA_StreamEnable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
452{
453 RT_NOREF(pInterface, pStream);
454 return VINF_SUCCESS;
455}
456
457
458/**
459 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDisable}
460 */
461static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDisable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
462{
463 RT_NOREF(pInterface, pStream);
464 return VINF_SUCCESS;
465}
466
467
468/**
469 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPause}
470 */
471static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPause(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
472{
473 RT_NOREF(pInterface, pStream);
474 return VINF_SUCCESS;
475}
476
477
478/**
479 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamResume}
480 */
481static DECLCALLBACK(int) drvHostValKitAudioHA_StreamResume(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
482{
483 RT_NOREF(pInterface, pStream);
484 return VINF_SUCCESS;
485}
486
487
488/**
489 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDrain}
490 */
491static DECLCALLBACK(int) drvHostValKitAudioHA_StreamDrain(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
492{
493 RT_NOREF(pInterface, pStream);
494 return VINF_SUCCESS;
495}
496
497
498/**
499 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
500 */
501static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
502{
503 RT_NOREF(pStream);
504
505 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
506
507 int rc = RTCritSectEnter(&pThis->CritSect);
508 if (RT_SUCCESS(rc))
509 {
510 pThis->pTestCur = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
511
512 int rc2 = RTCritSectLeave(&pThis->CritSect);
513 AssertRC(rc2);
514 }
515
516 if (pThis->pTestCur == NULL) /* Empty list? */
517 return 0;
518
519 PVALKITTESTDATA const pTst = pThis->pTestCur;
520
521 Assert(pTst->t.TestTone.u.Rec.cbToWrite >= pTst->t.TestTone.u.Rec.cbWritten);
522 return pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
523}
524
525
526/**
527 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
528 */
529static DECLCALLBACK(uint32_t) drvHostValKitAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
530{
531 RT_NOREF(pInterface, pStream);
532 return UINT32_MAX;
533}
534
535
536/**
537 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetState}
538 */
539static DECLCALLBACK(PDMHOSTAUDIOSTREAMSTATE) drvHostValKitAudioHA_StreamGetState(PPDMIHOSTAUDIO pInterface,
540 PPDMAUDIOBACKENDSTREAM pStream)
541{
542 RT_NOREF(pInterface);
543 AssertPtrReturn(pStream, PDMHOSTAUDIOSTREAMSTATE_INVALID);
544 return PDMHOSTAUDIOSTREAMSTATE_OKAY;
545}
546
547
548/**
549 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
550 */
551static DECLCALLBACK(int) drvHostValKitAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
552 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
553{
554 RT_NOREF(pStream);
555
556 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
557
558 int rc = RTCritSectEnter(&pThis->CritSect);
559 if (RT_SUCCESS(rc))
560 {
561 pThis->pTestCur = RTListGetFirst(&pThis->lstTestsPlay, VALKITTESTDATA, Node);
562
563 int rc2 = RTCritSectLeave(&pThis->CritSect);
564 AssertRC(rc2);
565 }
566
567 if (pThis->pTestCur == NULL) /* Empty list? */
568 {
569 LogRelMax(64, ("Audio: Validation Kit: Warning: Guest is playing back data when no playback test is active\n"));
570
571 *pcbWritten = cbBuf;
572 return VINF_SUCCESS;
573 }
574
575 PVALKITTESTDATA pTst = pThis->pTestCur;
576
577 if (pTst->t.TestTone.u.Play.cbRead == 0)
578 {
579 AUDIOTESTPARMS Parms;
580 RT_ZERO(Parms);
581 Parms.enmDir = PDMAUDIODIR_IN;
582 Parms.enmType = AUDIOTESTTYPE_TESTTONE_RECORD;
583 Parms.TestTone = pTst->t.TestTone.Parms;
584
585 Assert(pTst->pEntry == NULL);
586 rc = AudioTestSetTestBegin(&pThis->Set, "Recording audio data from guest",
587 &Parms, &pTst->pEntry);
588 if (RT_SUCCESS(rc))
589 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-rec.pcm", &pTst->pObj);
590
591 if (RT_SUCCESS(rc))
592 {
593 pTst->msStartedTS = RTTimeMilliTS();
594 LogRel(("Audio: Validation Kit: Recording audio data (%RU16Hz, %RU32ms) started\n",
595 (uint16_t)Parms.TestTone.dbFreqHz, Parms.TestTone.msDuration));
596 }
597 }
598
599 uint32_t cbWritten = 0;
600
601 if (RT_SUCCESS(rc))
602 {
603 uint32_t cbToRead = RT_MIN(cbBuf,
604 pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead);
605
606 rc = AudioTestSetObjWrite(pTst->pObj, pvBuf, cbToRead);
607 if (RT_SUCCESS(rc))
608 {
609 pTst->t.TestTone.u.Play.cbRead += cbToRead;
610 Assert(pTst->t.TestTone.u.Play.cbRead <= pTst->t.TestTone.u.Play.cbToRead);
611
612 const bool fComplete = pTst->t.TestTone.u.Play.cbToRead == pTst->t.TestTone.u.Play.cbRead;
613
614 if (fComplete)
615 {
616 LogRel(("Audio: Validation Kit: Recording audio data done (took %RU32ms)\n",
617 RTTimeMilliTS() - pTst->msStartedTS));
618
619 rc = RTCritSectEnter(&pThis->CritSect);
620 if (RT_SUCCESS(rc))
621 {
622 drvHostValKiUnregisterPlayTest(pThis, pTst);
623
624 pThis->pTestCur = NULL;
625
626 int rc2 = RTCritSectLeave(&pThis->CritSect);
627 AssertRC(rc2);
628 }
629 }
630
631 cbWritten = cbToRead;
632 }
633 }
634
635 if (RT_FAILURE(rc))
636 {
637 if (pTst->pEntry)
638 AudioTestSetTestFailed(pTst->pEntry, rc, "Recording audio data failed");
639 LogRel(("Audio: Validation Kit: Recording audio data failed with %Rrc\n", rc));
640 }
641
642 *pcbWritten = cbWritten;
643
644 return VINF_SUCCESS; /** @todo Return rc here? */
645}
646
647
648/**
649 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
650 */
651static DECLCALLBACK(int) drvHostValKitAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
652 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
653{
654 RT_NOREF(pStream);
655
656 PDRVHOSTVALKITAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTVALKITAUDIO, IHostAudio);
657
658 int rc = RTCritSectEnter(&pThis->CritSect);
659 if (RT_SUCCESS(rc))
660 {
661 pThis->pTestCur = RTListGetFirst(&pThis->lstTestsRec, VALKITTESTDATA, Node);
662
663 int rc2 = RTCritSectLeave(&pThis->CritSect);
664 AssertRC(rc2);
665 }
666
667 if (pThis->pTestCur == NULL) /* Empty list? */
668 {
669 LogRelMax(64, ("Audio: Validation Kit: Warning: Guest is recording audio data when no recording test is active\n"));
670
671 *pcbRead = 0;
672 return VINF_SUCCESS;
673 }
674
675 PVALKITTESTDATA pTst = pThis->pTestCur;
676
677 if (pTst->t.TestTone.u.Rec.cbWritten == 0)
678 {
679 AUDIOTESTPARMS Parms;
680 RT_ZERO(Parms);
681 Parms.enmDir = PDMAUDIODIR_OUT;
682 Parms.enmType = AUDIOTESTTYPE_TESTTONE_PLAY;
683 Parms.TestTone = pTst->t.TestTone.Parms;
684
685 Assert(pTst->pEntry == NULL);
686 rc = AudioTestSetTestBegin(&pThis->Set, "Injecting audio input data to guest",
687 &Parms, &pTst->pEntry);
688 if (RT_SUCCESS(rc))
689 rc = AudioTestSetObjCreateAndRegister(&pThis->Set, "host-tone-play.pcm", &pTst->pObj);
690
691 if (RT_SUCCESS(rc))
692 {
693 pTst->msStartedTS = RTTimeMilliTS();
694 LogRel(("Audio: Validation Kit: Injecting audio input data (%RU16Hz, %RU32ms) started\n",
695 (uint16_t)pTst->t.TestTone.Tone.rdFreqHz,
696 pTst->t.TestTone.Parms.msDuration));
697 }
698 }
699
700 uint32_t cbRead = 0;
701
702 if (RT_SUCCESS(rc))
703 {
704 uint32_t cbToWrite = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
705 if (cbToWrite)
706 rc = AudioTestToneGenerate(&pTst->t.TestTone.Tone, pvBuf, RT_MIN(cbToWrite, cbBuf), &cbRead);
707 if ( RT_SUCCESS(rc)
708 && cbRead)
709 {
710 rc = AudioTestSetObjWrite(pTst->pObj, pvBuf, cbRead);
711 if (RT_SUCCESS(rc))
712 {
713 pTst->t.TestTone.u.Rec.cbWritten += cbRead;
714 Assert(pTst->t.TestTone.u.Rec.cbWritten <= pTst->t.TestTone.u.Rec.cbToWrite);
715
716 const bool fComplete = pTst->t.TestTone.u.Rec.cbToWrite == pTst->t.TestTone.u.Rec.cbWritten;
717
718 if (fComplete)
719 {
720 LogRel(("Audio: Validation Kit: Injecting audio input data done (took %RU32ms)\n",
721 RTTimeMilliTS() - pTst->msStartedTS));
722
723 rc = RTCritSectEnter(&pThis->CritSect);
724 if (RT_SUCCESS(rc))
725 {
726 drvHostValKiUnregisterRecTest(pThis, pTst);
727
728 pThis->pTestCur = NULL;
729
730 int rc2 = RTCritSectLeave(&pThis->CritSect);
731 AssertRC(rc2);
732 }
733 }
734 }
735 }
736 }
737
738 if (RT_FAILURE(rc))
739 {
740 if (pTst->pEntry)
741 AudioTestSetTestFailed(pTst->pEntry, rc, "Injecting audio input data failed");
742 LogRel(("Audio: Validation Kit: Injecting audio input data failed with %Rrc\n", rc));
743 }
744
745 *pcbRead = cbRead;
746
747 return VINF_SUCCESS; /** @todo Return rc here? */
748}
749
750
751/**
752 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
753 */
754static DECLCALLBACK(void *) drvHostValKitAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
755{
756 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
757 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
758
759 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
760 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
761 return NULL;
762}
763
764
765/**
766 * Constructs a VaKit audio driver instance.
767 *
768 * @copydoc FNPDMDRVCONSTRUCT
769 */
770static DECLCALLBACK(int) drvHostValKitAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
771{
772 RT_NOREF(pCfg, fFlags);
773 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
774 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
775 LogRel(("Audio: Initializing VALKIT driver\n"));
776
777 /*
778 * Init the static parts.
779 */
780 pThis->pDrvIns = pDrvIns;
781 /* IBase */
782 pDrvIns->IBase.pfnQueryInterface = drvHostValKitAudioQueryInterface;
783 /* IHostAudio */
784 pThis->IHostAudio.pfnGetConfig = drvHostValKitAudioHA_GetConfig;
785 pThis->IHostAudio.pfnGetDevices = NULL;
786 pThis->IHostAudio.pfnGetStatus = drvHostValKitAudioHA_GetStatus;
787 pThis->IHostAudio.pfnDoOnWorkerThread = NULL;
788 pThis->IHostAudio.pfnStreamConfigHint = NULL;
789 pThis->IHostAudio.pfnStreamCreate = drvHostValKitAudioHA_StreamCreate;
790 pThis->IHostAudio.pfnStreamInitAsync = NULL;
791 pThis->IHostAudio.pfnStreamDestroy = drvHostValKitAudioHA_StreamDestroy;
792 pThis->IHostAudio.pfnStreamNotifyDeviceChanged = NULL;
793 pThis->IHostAudio.pfnStreamEnable = drvHostValKitAudioHA_StreamEnable;
794 pThis->IHostAudio.pfnStreamDisable = drvHostValKitAudioHA_StreamDisable;
795 pThis->IHostAudio.pfnStreamPause = drvHostValKitAudioHA_StreamPause;
796 pThis->IHostAudio.pfnStreamResume = drvHostValKitAudioHA_StreamResume;
797 pThis->IHostAudio.pfnStreamDrain = drvHostValKitAudioHA_StreamDrain;
798 pThis->IHostAudio.pfnStreamGetReadable = drvHostValKitAudioHA_StreamGetReadable;
799 pThis->IHostAudio.pfnStreamGetWritable = drvHostValKitAudioHA_StreamGetWritable;
800 pThis->IHostAudio.pfnStreamGetPending = NULL;
801 pThis->IHostAudio.pfnStreamGetState = drvHostValKitAudioHA_StreamGetState;
802 pThis->IHostAudio.pfnStreamPlay = drvHostValKitAudioHA_StreamPlay;
803 pThis->IHostAudio.pfnStreamCapture = drvHostValKitAudioHA_StreamCapture;
804
805 RTListInit(&pThis->lstTestsRec);
806 pThis->cTestsRec = 0;
807 RTListInit(&pThis->lstTestsPlay);
808 pThis->cTestsPlay = 0;
809
810 ATSCALLBACKS Callbacks;
811 RT_ZERO(Callbacks);
812 Callbacks.pfnTestSetBegin = drvHostValKitTestSetBegin;
813 Callbacks.pfnTestSetEnd = drvHostValKitTestSetEnd;
814 Callbacks.pfnTonePlay = drvHostValKitRegisterGuestRecTest;
815 Callbacks.pfnToneRecord = drvHostValKitRegisterGuestPlayTest;
816 Callbacks.pfnTestSetSendBegin = drvHostValKitTestSetSendBeginCallback;
817 Callbacks.pfnTestSetSendRead = drvHostValKitTestSetSendReadCallback;
818 Callbacks.pfnTestSetSendEnd = drvHostValKitTestSetSendEndCallback;
819 Callbacks.pvUser = pThis;
820
821 /** @todo Make this configurable via CFGM. */
822 const char *pszTcpAddr = ATS_TCP_HOST_DEFAULT_ADDR_STR;
823 uint32_t uTcpPort = ATS_TCP_HOST_DEFAULT_PORT;
824
825 LogRel(("Audio: Validation Kit: Starting Audio Test Service (ATS) at %s:%RU32...\n",
826 pszTcpAddr, uTcpPort));
827
828 int rc = AudioTestSvcInit(&pThis->Srv,
829 /* We only allow connections from localhost for now. */
830 pszTcpAddr, uTcpPort, &Callbacks);
831 if (RT_SUCCESS(rc))
832 rc = AudioTestSvcStart(&pThis->Srv);
833
834 if (RT_SUCCESS(rc))
835 {
836 LogRel(("Audio: Validation Kit: Audio Test Service (ATS) running\n"));
837
838 /** @todo Let the following be customizable by CFGM later. */
839 rc = AudioTestPathCreateTemp(pThis->szPathTemp, sizeof(pThis->szPathTemp), "ValKitAudio");
840 if (RT_SUCCESS(rc))
841 {
842 LogRel(("Audio: Validation Kit: Using temp dir '%s'\n", pThis->szPathTemp));
843 rc = AudioTestPathGetTemp(pThis->szPathOut, sizeof(pThis->szPathOut));
844 if (RT_SUCCESS(rc))
845 LogRel(("Audio: Validation Kit: Using output dir '%s'\n", pThis->szPathOut));
846 }
847 }
848
849 if (RT_SUCCESS(rc))
850 rc = RTCritSectInit(&pThis->CritSect);
851
852 if (RT_FAILURE(rc))
853 LogRel(("Audio: Validation Kit: Initialization failed, rc=%Rrc\n", rc));
854
855 return rc;
856}
857
858static DECLCALLBACK(void) drvHostValKitAudioDestruct(PPDMDRVINS pDrvIns)
859{
860 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
861 PDRVHOSTVALKITAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTVALKITAUDIO);
862
863 LogRel(("Audio: Validation Kit: Shutting down Audio Test Service (ATS) ...\n"));
864
865 int rc = AudioTestSvcShutdown(&pThis->Srv);
866 if (RT_SUCCESS(rc))
867 rc = AudioTestSvcDestroy(&pThis->Srv);
868
869 if (RT_SUCCESS(rc))
870 {
871 LogRel(("Audio: Validation Kit: Shutdown of Audio Test Service complete\n"));
872
873 if (pThis->cTestsRec)
874 LogRel(("Audio: Validation Kit: Warning: %RU32 guest recording tests still outstanding:\n", pThis->cTestsRec));
875
876 PVALKITTESTDATA pTst, pTstNext;
877 RTListForEachSafe(&pThis->lstTestsRec, pTst, pTstNext, VALKITTESTDATA, Node)
878 {
879 size_t const cbOutstanding = pTst->t.TestTone.u.Rec.cbToWrite - pTst->t.TestTone.u.Rec.cbWritten;
880 if (cbOutstanding)
881 LogRel(("Audio: Validation Kit: \tRecording test #%RU32 has %RU64 bytes outstanding\n", pTst->idxTest, cbOutstanding));
882 drvHostValKiUnregisterRecTest(pThis, pTst);
883 }
884
885 if (pThis->cTestsPlay)
886 LogRel(("Audio: Validation Kit: Warning: %RU32 guest playback tests still outstanding:\n", pThis->cTestsPlay));
887
888 RTListForEachSafe(&pThis->lstTestsPlay, pTst, pTstNext, VALKITTESTDATA, Node)
889 {
890 size_t const cbOutstanding = pTst->t.TestTone.u.Play.cbToRead - pTst->t.TestTone.u.Play.cbRead;
891 if (cbOutstanding)
892 LogRel(("Audio: Validation Kit: \tPlayback test #%RU32 has %RU64 bytes outstanding\n", pTst->idxTest, cbOutstanding));
893 drvHostValKiUnregisterPlayTest(pThis, pTst);
894 }
895
896 Assert(pThis->cTestsRec == 0);
897 Assert(pThis->cTestsPlay == 0);
898 }
899 else
900 LogRel(("Audio: Validation Kit: Shutdown of Audio Test Service failed, rc=%Rrc\n", rc));
901
902 /* Try cleaning up a bit. */
903 RTDirRemove(pThis->szPathTemp);
904 RTDirRemove(pThis->szPathOut);
905
906 if (RTCritSectIsInitialized(&pThis->CritSect))
907 {
908 int rc2 = RTCritSectDelete(&pThis->CritSect);
909 if (RT_SUCCESS(rc))
910 rc = rc2;
911 }
912
913 if (RT_FAILURE(rc))
914 LogRel(("Audio: Validation Kit: Destruction failed, rc=%Rrc\n", rc));
915}
916
917/**
918 * Char driver registration record.
919 */
920const PDMDRVREG g_DrvHostValidationKitAudio =
921{
922 /* u32Version */
923 PDM_DRVREG_VERSION,
924 /* szName */
925 "ValidationKitAudio",
926 /* szRCMod */
927 "",
928 /* szR0Mod */
929 "",
930 /* pszDescription */
931 "ValidationKitAudio audio host driver",
932 /* fFlags */
933 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
934 /* fClass. */
935 PDM_DRVREG_CLASS_AUDIO,
936 /* cMaxInstances */
937 ~0U,
938 /* cbInstance */
939 sizeof(DRVHOSTVALKITAUDIO),
940 /* pfnConstruct */
941 drvHostValKitAudioConstruct,
942 /* pfnDestruct */
943 drvHostValKitAudioDestruct,
944 /* pfnRelocate */
945 NULL,
946 /* pfnIOCtl */
947 NULL,
948 /* pfnPowerOn */
949 NULL,
950 /* pfnReset */
951 NULL,
952 /* pfnSuspend */
953 NULL,
954 /* pfnResume */
955 NULL,
956 /* pfnAttach */
957 NULL,
958 /* pfnDetach */
959 NULL,
960 /* pfnPowerOff */
961 NULL,
962 /* pfnSoftReset */
963 NULL,
964 /* u32EndVersion */
965 PDM_DRVREG_VERSION
966};
967
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