VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTestService.cpp@ 90867

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

Audio/VKAT: Fixed naming (PATSSERVERINST -> PATSCLIENTINST). ​bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.2 KB
Line 
1/* $Id: AudioTestService.cpp 90867 2021-08-25 09:16:25Z vboxsync $ */
2/** @file
3 * AudioTestService - Audio test execution server.
4 */
5
6/*
7 * Copyright (C) 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* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_AUDIO_TEST
23
24#include <iprt/alloca.h>
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/critsect.h>
28#include <iprt/crc.h>
29#include <iprt/ctype.h>
30#include <iprt/dir.h>
31#include <iprt/env.h>
32#include <iprt/err.h>
33#include <iprt/file.h>
34#include <iprt/getopt.h>
35#include <iprt/handle.h>
36#include <iprt/initterm.h>
37#include <iprt/json.h>
38#include <iprt/list.h>
39#include <iprt/log.h>
40#include <iprt/mem.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/pipe.h>
45#include <iprt/poll.h>
46#include <iprt/process.h>
47#include <iprt/stream.h>
48#include <iprt/string.h>
49#include <iprt/thread.h>
50
51#include <VBox/log.h>
52
53#include "AudioTestService.h"
54#include "AudioTestServiceInternal.h"
55
56
57/*********************************************************************************************************************************
58* Structures and Typedefs *
59*********************************************************************************************************************************/
60/**
61 * A generic ATS reply, used by the client
62 * to process the incoming packets.
63 */
64typedef struct ATSSRVREPLY
65{
66 char szOp[ATSPKT_OPCODE_MAX_LEN];
67 void *pvPayload;
68 size_t cbPayload;
69} ATSSRVREPLY;
70/** Pointer to a generic ATS reply. */
71typedef struct ATSSRVREPLY *PATSSRVREPLY;
72
73
74/*********************************************************************************************************************************
75* Global Variables *
76*********************************************************************************************************************************/
77/**
78 * Transport layers.
79 */
80const PCATSTRANSPORT g_apTransports[] =
81{
82 &g_TcpTransport
83};
84/** Number of transport layers in \a g_apTransports. */
85const size_t g_cTransports = RT_ELEMENTS(g_apTransports);
86
87/**
88 * ATS client state.
89 */
90typedef enum ATSCLIENTSTATE
91{
92 /** Invalid client state. */
93 ATSCLIENTSTATE_INVALID = 0,
94 /** Client is initialising, only the HOWDY and BYE packets are allowed. */
95 ATSCLIENTSTATE_INITIALISING,
96 /** Client is in fully cuntional state and ready to process all requests. */
97 ATSCLIENTSTATE_READY,
98 /** Client is destroying. */
99 ATSCLIENTSTATE_DESTROYING,
100 /** 32bit hack. */
101 ATSCLIENTSTATE_32BIT_HACK = 0x7fffffff
102} ATSCLIENTSTATE;
103
104/**
105 * ATS client instance.
106 */
107typedef struct ATSCLIENTINST
108{
109 /** List node for new clients. */
110 RTLISTNODE NdLst;
111 /** The current client state. */
112 ATSCLIENTSTATE enmState;
113 /** Transport backend specific data. */
114 PATSTRANSPORTCLIENT pTransportClient;
115 /** Client hostname. */
116 char *pszHostname;
117} ATSCLIENTINST;
118/** Pointer to a ATS client instance. */
119typedef ATSCLIENTINST *PATSCLIENTINST;
120
121/**
122 * Returns the string represenation of the given state.
123 */
124static const char *atsClientStateStringify(ATSCLIENTSTATE enmState)
125{
126 switch (enmState)
127 {
128 case ATSCLIENTSTATE_INVALID:
129 return "INVALID";
130 case ATSCLIENTSTATE_INITIALISING:
131 return "INITIALISING";
132 case ATSCLIENTSTATE_READY:
133 return "READY";
134 case ATSCLIENTSTATE_DESTROYING:
135 return "DESTROYING";
136 case ATSCLIENTSTATE_32BIT_HACK:
137 default:
138 break;
139 }
140
141 AssertMsgFailed(("Unknown state %#x\n", enmState));
142 return "UNKNOWN";
143}
144
145/**
146 * Calculates the checksum value, zero any padding space and send the packet.
147 *
148 * @returns IPRT status code.
149 * @param pThis The ATS instance.
150 * @param pInst The ATS client structure.
151 * @param pPkt The packet to send. Must point to a correctly
152 * aligned buffer.
153 */
154static int atsSendPkt(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPkt)
155{
156 Assert(pPkt->cb >= sizeof(*pPkt));
157 pPkt->uCrc32 = RTCrc32(pPkt->achOpcode, pPkt->cb - RT_UOFFSETOF(ATSPKTHDR, achOpcode));
158 if (pPkt->cb != RT_ALIGN_32(pPkt->cb, ATSPKT_ALIGNMENT))
159 memset((uint8_t *)pPkt + pPkt->cb, '\0', RT_ALIGN_32(pPkt->cb, ATSPKT_ALIGNMENT) - pPkt->cb);
160
161 LogFlowFunc(("cb=%RU32 (%#x), payload=%RU32 (%#x), opcode=%.8s\n",
162 pPkt->cb, pPkt->cb, pPkt->cb - sizeof(ATSPKTHDR), pPkt->cb - sizeof(ATSPKTHDR), pPkt->achOpcode));
163 int rc = pThis->pTransport->pfnSendPkt(pThis->pTransportInst, pInst->pTransportClient, pPkt);
164 while (RT_UNLIKELY(rc == VERR_INTERRUPTED) && !pThis->fTerminate)
165 rc = pThis->pTransport->pfnSendPkt(pThis->pTransportInst, pInst->pTransportClient, pPkt);
166
167 return rc;
168}
169
170/**
171 * Sends a babble reply and disconnects the client (if applicable).
172 *
173 * @param pThis The ATS instance.
174 * @param pInst The ATS server instance.
175 * @param pszOpcode The BABBLE opcode.
176 */
177static void atsReplyBabble(PATSSERVER pThis, PATSCLIENTINST pInst, const char *pszOpcode)
178{
179 ATSPKTHDR Reply;
180 Reply.cb = sizeof(Reply);
181 Reply.uCrc32 = 0;
182 memcpy(Reply.achOpcode, pszOpcode, sizeof(Reply.achOpcode));
183
184 pThis->pTransport->pfnBabble(pThis->pTransportInst, pInst->pTransportClient, &Reply, 20*1000);
185}
186
187/**
188 * Receive and validate a packet.
189 *
190 * Will send bable responses to malformed packets that results in a error status
191 * code.
192 *
193 * @returns IPRT status code.
194 * @param pThis The ATS instance.
195 * @param pInst The opaque ATS instance structure.
196 * @param ppPktHdr Where to return the packet on success. Free
197 * with RTMemFree.
198 * @param fAutoRetryOnFailure Whether to retry on error.
199 */
200static int atsRecvPkt(PATSSERVER pThis, PATSCLIENTINST pInst, PPATSPKTHDR ppPktHdr, bool fAutoRetryOnFailure)
201{
202 for (;;)
203 {
204 PATSPKTHDR pPktHdr;
205 int rc = pThis->pTransport->pfnRecvPkt(pThis->pTransportInst, pInst->pTransportClient, &pPktHdr);
206 if (RT_SUCCESS(rc))
207 {
208 /* validate the packet. */
209 if ( pPktHdr->cb >= sizeof(ATSPKTHDR)
210 && pPktHdr->cb < ATSPKT_MAX_SIZE)
211 {
212 Log2Func(("pPktHdr=%p cb=%#x crc32=%#x opcode=%.8s\n",
213 pPktHdr, pPktHdr->cb, pPktHdr->uCrc32, pPktHdr->achOpcode));
214 uint32_t uCrc32Calc = pPktHdr->uCrc32 != 0
215 ? RTCrc32(&pPktHdr->achOpcode[0], pPktHdr->cb - RT_UOFFSETOF(ATSPKTHDR, achOpcode))
216 : 0;
217 if (pPktHdr->uCrc32 == uCrc32Calc)
218 {
219 AssertCompileMemberSize(ATSPKTHDR, achOpcode, 8);
220 if ( RT_C_IS_UPPER(pPktHdr->achOpcode[0])
221 && RT_C_IS_UPPER(pPktHdr->achOpcode[1])
222 && (RT_C_IS_UPPER(pPktHdr->achOpcode[2]) || pPktHdr->achOpcode[2] == ' ')
223 && (RT_C_IS_PRINT(pPktHdr->achOpcode[3]) || pPktHdr->achOpcode[3] == ' ')
224 && (RT_C_IS_PRINT(pPktHdr->achOpcode[4]) || pPktHdr->achOpcode[4] == ' ')
225 && (RT_C_IS_PRINT(pPktHdr->achOpcode[5]) || pPktHdr->achOpcode[5] == ' ')
226 && (RT_C_IS_PRINT(pPktHdr->achOpcode[6]) || pPktHdr->achOpcode[6] == ' ')
227 && (RT_C_IS_PRINT(pPktHdr->achOpcode[7]) || pPktHdr->achOpcode[7] == ' ')
228 )
229 {
230 Log(("cb=%#x opcode=%.8s\n", pPktHdr->cb, pPktHdr->achOpcode));
231 *ppPktHdr = pPktHdr;
232 return rc;
233 }
234
235 rc = VERR_IO_BAD_COMMAND;
236 }
237 else
238 {
239 Log(("cb=%#x opcode=%.8s crc32=%#x actual=%#x\n",
240 pPktHdr->cb, pPktHdr->achOpcode, pPktHdr->uCrc32, uCrc32Calc));
241 rc = VERR_IO_CRC;
242 }
243 }
244 else
245 rc = VERR_IO_BAD_LENGTH;
246
247 /* Send babble reply and disconnect the client if the transport is
248 connection oriented. */
249 if (rc == VERR_IO_BAD_LENGTH)
250 atsReplyBabble(pThis, pInst, "BABBLE L");
251 else if (rc == VERR_IO_CRC)
252 atsReplyBabble(pThis, pInst, "BABBLE C");
253 else if (rc == VERR_IO_BAD_COMMAND)
254 atsReplyBabble(pThis, pInst, "BABBLE O");
255 else
256 atsReplyBabble(pThis, pInst, "BABBLE ");
257 RTMemFree(pPktHdr);
258 }
259
260 /* Try again or return failure? */
261 if ( pThis->fTerminate
262 || rc != VERR_INTERRUPTED
263 || !fAutoRetryOnFailure
264 )
265 {
266 Log(("rc=%Rrc\n", rc));
267 return rc;
268 }
269 }
270}
271
272/**
273 * Make a simple reply, only status opcode.
274 *
275 * @returns IPRT status code of the send.
276 * @param pThis The ATS instance.
277 * @param pInst The opaque ATS instance structure.
278 * @param pReply The reply packet.
279 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
280 * with space.
281 * @param cbExtra Bytes in addition to the header.
282 */
283static int atsReplyInternal(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pReply, const char *pszOpcode, size_t cbExtra)
284{
285 /* copy the opcode, don't be too strict in case of a padding screw up. */
286 size_t cchOpcode = strlen(pszOpcode);
287 if (RT_LIKELY(cchOpcode == sizeof(pReply->achOpcode)))
288 memcpy(pReply->achOpcode, pszOpcode, sizeof(pReply->achOpcode));
289 else
290 {
291 Assert(cchOpcode == sizeof(pReply->achOpcode));
292 while (cchOpcode > 0 && pszOpcode[cchOpcode - 1] == ' ')
293 cchOpcode--;
294 AssertMsgReturn(cchOpcode < sizeof(pReply->achOpcode), ("%d/'%.8s'\n", cchOpcode, pszOpcode), VERR_INTERNAL_ERROR_4);
295 memcpy(pReply->achOpcode, pszOpcode, cchOpcode);
296 memset(&pReply->achOpcode[cchOpcode], ' ', sizeof(pReply->achOpcode) - cchOpcode);
297 }
298
299 pReply->cb = (uint32_t)sizeof(ATSPKTHDR) + (uint32_t)cbExtra;
300 pReply->uCrc32 = 0;
301
302 return atsSendPkt(pThis, pInst, pReply);
303}
304
305/**
306 * Make a simple reply, only status opcode.
307 *
308 * @returns IPRT status code of the send.
309 * @param pThis The ATS instance.
310 * @param pInst The opaque ATS instance structure.
311 * @param pPktHdr The original packet (for future use).
312 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
313 * with space.
314 */
315static int atsReplySimple(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, const char *pszOpcode)
316{
317 return atsReplyInternal(pThis, pInst, pPktHdr, pszOpcode, 0);
318}
319
320/**
321 * Acknowledges a packet with success.
322 *
323 * @returns IPRT status code of the send.
324 * @param pThis The ATS instance.
325 * @param pInst The opaque ATS instance structure.
326 * @param pPktHdr The original packet (for future use).
327 */
328static int atsReplyAck(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
329{
330 return atsReplySimple(pThis, pInst, pPktHdr, "ACK ");
331}
332
333/**
334 * Replies with a failure.
335 *
336 * @returns IPRT status code of the send.
337 * @param pThis The ATS instance.
338 * @param pInst The opaque ATS instance structure.
339 * @param pPktHdr The original packet (for future use).
340 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
341 * with space.
342 * @param rcReq The status code of the request.
343 * @param pszDetailFmt Longer description of the problem (format string).
344 * @param va Format arguments.
345 */
346static int atsReplyFailureV(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr,
347 const char *pszOpcode, int rcReq, const char *pszDetailFmt, va_list va)
348{
349 RT_NOREF(pPktHdr);
350 union
351 {
352 ATSPKTHDR Hdr;
353 int rc;
354 char ach[256];
355 } uPkt;
356 RT_ZERO(uPkt);
357
358 size_t cchDetail = RTStrPrintfV(&uPkt.ach[sizeof(ATSPKTHDR)],
359 sizeof(uPkt) - sizeof(ATSPKTHDR),
360 pszDetailFmt, va);
361
362 uPkt.rc = rcReq;
363
364 return atsReplyInternal(pThis, pInst, &uPkt.Hdr, pszOpcode, sizeof(int) + cchDetail + 1);
365}
366
367/**
368 * Replies with a failure.
369 *
370 * @returns IPRT status code of the send.
371 * @param pThis The ATS instance.
372 * @param pInst The opaque ATS instance structure.
373 * @param pPktHdr The original packet (for future use).
374 * @param pszOpcode The status opcode. Exactly 8 chars long, padd
375 * with space.
376 * @param rcReq Status code.
377 * @param pszDetailFmt Longer description of the problem (format string).
378 * @param ... Format arguments.
379 */
380static int atsReplyFailure(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr,
381 const char *pszOpcode, int rcReq, const char *pszDetailFmt, ...)
382{
383 va_list va;
384 va_start(va, pszDetailFmt);
385 int rc = atsReplyFailureV(pThis, pInst, pPktHdr, pszOpcode, rcReq, pszDetailFmt, va);
386 va_end(va);
387 return rc;
388}
389
390/**
391 * Replies according to the return code.
392 *
393 * @returns IPRT status code of the send.
394 * @param pThis The ATS instance.
395 * @param pInst The opaque ATS instance structure.
396 * @param pPktHdr The packet to reply to.
397 * @param rcOperation The status code to report.
398 * @param pszOperationFmt The operation that failed. Typically giving the
399 * function call with important arguments.
400 * @param ... Arguments to the format string.
401 */
402static int atsReplyRC(PATSSERVER pThis,
403 PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, int rcOperation, const char *pszOperationFmt, ...)
404{
405 if (RT_SUCCESS(rcOperation))
406 return atsReplyAck(pThis, pInst, pPktHdr);
407
408 char szOperation[128];
409 va_list va;
410 va_start(va, pszOperationFmt);
411 RTStrPrintfV(szOperation, sizeof(szOperation), pszOperationFmt, va);
412 va_end(va);
413
414 return atsReplyFailure(pThis, pInst, pPktHdr, "FAILED ", rcOperation, "%s failed with rc=%Rrc (opcode '%.8s')",
415 szOperation, rcOperation, pPktHdr->achOpcode);
416}
417
418/**
419 * Signal a bad packet exact size.
420 *
421 * @returns IPRT status code of the send.
422 * @param pThis The ATS instance.
423 * @param pInst The opaque ATS instance structure.
424 * @param pPktHdr The packet to reply to.
425 * @param cb The wanted size.
426 */
427static int atsReplyBadSize(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr, size_t cb)
428{
429 return atsReplyFailure(pThis, pInst, pPktHdr, "BAD SIZE", VERR_INVALID_PARAMETER, "Expected at %zu bytes, got %u (opcode '%.8s')",
430 cb, pPktHdr->cb, pPktHdr->achOpcode);
431}
432
433/**
434 * Deals with a unknown command.
435 *
436 * @returns IPRT status code of the send.
437 * @param pThis The ATS instance.
438 * @param pInst The opaque ATS instance structure.
439 * @param pPktHdr The packet to reply to.
440 */
441static int atsReplyUnknown(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
442{
443 return atsReplyFailure(pThis, pInst, pPktHdr, "UNKNOWN ", VERR_NOT_FOUND, "Opcode '%.8s' is not known", pPktHdr->achOpcode);
444}
445
446/**
447 * Deals with a command sent in an invalid client state.
448 *
449 * @returns IPRT status code of the send.
450 * @param pThis The ATS instance.
451 * @param pInst The opaque ATS instance structure.
452 * @param pPktHdr The packet containing the unterminated string.
453 */
454static int atsReplyInvalidState(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
455{
456 return atsReplyFailure(pThis, pInst, pPktHdr, "INVSTATE", VERR_INVALID_STATE, "Opcode '%.8s' is not supported at client state '%s",
457 pPktHdr->achOpcode, atsClientStateStringify(pInst->enmState));
458}
459
460/**
461 * Verifies and acknowledges a "BYE" request.
462 *
463 * @returns IPRT status code.
464 * @param pThis The ATS instance.
465 * @param pInst The opaque ATS instance structure.
466 * @param pPktHdr The bye packet.
467 */
468static int atsDoBye(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
469{
470 int rc;
471 if (pPktHdr->cb == sizeof(ATSPKTHDR))
472 {
473 if (pThis->Callbacks.pfnBye)
474 {
475 rc = pThis->Callbacks.pfnBye(pThis->Callbacks.pvUser);
476 }
477 else
478 rc = VINF_SUCCESS;
479
480 if (RT_SUCCESS(rc))
481 {
482 rc = atsReplyAck(pThis, pInst, pPktHdr);
483 }
484 else
485 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Shutting down server failed");
486
487 if (RT_SUCCESS(rc))
488 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pInst->pTransportClient);
489 }
490 else
491 rc = atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTHDR));
492 return rc;
493}
494
495/**
496 * Verifies and acknowledges a "HOWDY" request.
497 *
498 * @returns IPRT status code.
499 * @param pThis The ATS instance.
500 * @param pInst The opaque ATS instance structure.
501 * @param pPktHdr The howdy packet.
502 */
503static int atsDoHowdy(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
504{
505 int rc = VINF_SUCCESS;
506
507 if (pPktHdr->cb != sizeof(ATSPKTREQHOWDY))
508 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQHOWDY));
509
510 if (pInst->enmState != ATSCLIENTSTATE_INITIALISING)
511 return atsReplyInvalidState(pThis, pInst, pPktHdr);
512
513 PATSPKTREQHOWDY pReq = (PATSPKTREQHOWDY)pPktHdr;
514
515 if (pReq->uVersion != ATS_PROTOCOL_VS)
516 return atsReplyRC(pThis, pInst, pPktHdr, VERR_VERSION_MISMATCH, "The given version %#x is not supported", pReq->uVersion);
517
518 ATSPKTREPHOWDY Rep;
519 RT_ZERO(Rep);
520
521 Rep.uVersion = ATS_PROTOCOL_VS;
522
523 rc = atsReplyInternal(pThis, pInst, &Rep.Hdr, "ACK ", sizeof(Rep) - sizeof(ATSPKTHDR));
524 if (RT_SUCCESS(rc))
525 {
526 pThis->pTransport->pfnNotifyHowdy(pThis->pTransportInst, pInst->pTransportClient);
527
528 if (pThis->Callbacks.pfnHowdy)
529 rc = pThis->Callbacks.pfnHowdy(pThis->Callbacks.pvUser);
530
531 if (RT_SUCCESS(rc))
532 pInst->enmState = ATSCLIENTSTATE_READY;
533 }
534
535 return rc;
536}
537
538/**
539 * Verifies and acknowledges a "TSET BEG" request.
540 *
541 * @returns IPRT status code.
542 * @param pThis The ATS instance.
543 * @param pInst The opaque ATS instance structure.
544 * @param pPktHdr The test set begin packet.
545 */
546static int atsDoTestSetBegin(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
547{
548 if (pPktHdr->cb != sizeof(ATSPKTREQTSETBEG))
549 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETBEG));
550
551 PATSPKTREQTSETBEG pReq = (PATSPKTREQTSETBEG)pPktHdr;
552
553 int rc = VINF_SUCCESS;
554
555 if (pThis->Callbacks.pfnTestSetBegin)
556 {
557 rc = pThis->Callbacks.pfnTestSetBegin(pThis->Callbacks.pvUser, pReq->szTag);
558 if (RT_FAILURE(rc))
559 return atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning test set '%s' failed", pReq->szTag);
560 }
561
562 if (RT_SUCCESS(rc))
563 {
564 rc = atsReplyAck(pThis, pInst, pPktHdr);
565 }
566 else
567 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning test set failed");
568
569 return rc;
570}
571
572/**
573 * Verifies and acknowledges a "TSET END" request.
574 *
575 * @returns IPRT status code.
576 * @param pThis The ATS instance.
577 * @param pInst The opaque ATS instance structure.
578 * @param pPktHdr The test set end packet.
579 */
580static int atsDoTestSetEnd(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
581{
582 if (pPktHdr->cb != sizeof(ATSPKTREQTSETEND))
583 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETEND));
584
585 PATSPKTREQTSETEND pReq = (PATSPKTREQTSETEND)pPktHdr;
586
587 int rc = VINF_SUCCESS;
588
589 if (pThis->Callbacks.pfnTestSetEnd)
590 {
591 rc = pThis->Callbacks.pfnTestSetEnd(pThis->Callbacks.pvUser, pReq->szTag);
592 if (RT_FAILURE(rc))
593 return atsReplyRC(pThis, pInst, pPktHdr, rc, "Ending test set '%s' failed", pReq->szTag);
594 }
595 if (RT_SUCCESS(rc))
596 {
597 rc = atsReplyAck(pThis, pInst, pPktHdr);
598 }
599 else
600 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Ending test set failed");
601
602 return rc;
603}
604
605/**
606 * Used by atsDoTestSetSend to wait for a reply ACK from the client.
607 *
608 * @returns VINF_SUCCESS on ACK, VERR_GENERAL_FAILURE on NACK,
609 * VERR_NET_NOT_CONNECTED on unknown response (sending a bable reply),
610 * or whatever atsRecvPkt returns.
611 * @param pThis The ATS instance.
612 * @param pInst The opaque ATS instance structure.
613 * @param pPktHdr The original packet (for future use).
614 */
615static int atsWaitForAck(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
616{
617 RT_NOREF(pPktHdr);
618 /** @todo timeout? */
619 PATSPKTHDR pReply;
620 int rc = atsRecvPkt(pThis, pInst, &pReply, false /*fAutoRetryOnFailure*/);
621 if (RT_SUCCESS(rc))
622 {
623 if (atsIsSameOpcode(pReply, "ACK"))
624 rc = VINF_SUCCESS;
625 else if (atsIsSameOpcode(pReply, "NACK"))
626 rc = VERR_GENERAL_FAILURE;
627 else
628 {
629 atsReplyBabble(pThis, pInst, "BABBLE ");
630 rc = VERR_NET_NOT_CONNECTED;
631 }
632 RTMemFree(pReply);
633 }
634 return rc;
635}
636
637/**
638 * Verifies and acknowledges a "TSET SND" request.
639 *
640 * @returns IPRT status code.
641 * @param pThis The ATS instance.
642 * @param pInst The opaque ATS instance structure.
643 * @param pPktHdr The test set end packet.
644 */
645static int atsDoTestSetSend(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
646{
647 if (pPktHdr->cb != sizeof(ATSPKTREQTSETSND))
648 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTSETSND));
649
650 PATSPKTREQTSETSND pReq = (PATSPKTREQTSETSND)pPktHdr;
651
652 int rc = VINF_SUCCESS;
653
654 if (!pThis->Callbacks.pfnTestSetSendRead)
655 return atsReplyRC(pThis, pInst, pPktHdr, VERR_NOT_SUPPORTED, "Sending test set not implemented");
656
657 if (pThis->Callbacks.pfnTestSetSendBegin)
658 {
659 rc = pThis->Callbacks.pfnTestSetSendBegin(pThis->Callbacks.pvUser, pReq->szTag);
660 if (RT_FAILURE(rc))
661 return atsReplyRC(pThis, pInst, pPktHdr, rc, "Beginning sending test set '%s' failed", pReq->szTag);
662 }
663
664 for (;;)
665 {
666 uint32_t uMyCrc32 = RTCrc32Start();
667 struct
668 {
669 ATSPKTHDR Hdr;
670 uint32_t uCrc32;
671 char ab[_64K];
672 char abPadding[ATSPKT_ALIGNMENT];
673 } Pkt;
674#ifdef DEBUG
675 RT_ZERO(Pkt);
676#endif
677 size_t cbRead = 0;
678 rc = pThis->Callbacks.pfnTestSetSendRead(pThis->Callbacks.pvUser, pReq->szTag, &Pkt.ab, sizeof(Pkt.ab), &cbRead);
679 if ( RT_FAILURE(rc)
680 || cbRead == 0)
681 {
682 if ( rc == VERR_EOF
683 || (RT_SUCCESS(rc) && cbRead == 0))
684 {
685 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
686 rc = atsReplyInternal(pThis, pInst, &Pkt.Hdr, "DATA EOF", sizeof(uint32_t) /* uCrc32 */);
687 if (RT_SUCCESS(rc))
688 rc = atsWaitForAck(pThis, pInst, &Pkt.Hdr);
689 }
690 else
691 rc = atsReplyRC(pThis, pInst, pPktHdr, rc, "Sending data for test set '%s' failed", pReq->szTag);
692 break;
693 }
694
695 uMyCrc32 = RTCrc32Process(uMyCrc32, &Pkt.ab[0], cbRead);
696 Pkt.uCrc32 = RTCrc32Finish(uMyCrc32);
697
698 Log2Func(("cbRead=%zu -> uCrc32=%#x\n", cbRead, Pkt.uCrc32));
699
700 Assert(cbRead <= sizeof(Pkt.ab));
701
702 rc = atsReplyInternal(pThis, pInst, &Pkt.Hdr, "DATA ", sizeof(uint32_t) /* uCrc32 */ + cbRead);
703 if (RT_FAILURE(rc))
704 break;
705
706 rc = atsWaitForAck(pThis, pInst, &Pkt.Hdr);
707 if (RT_FAILURE(rc))
708 break;
709 }
710
711 if (pThis->Callbacks.pfnTestSetSendEnd)
712 {
713 int rc2 = pThis->Callbacks.pfnTestSetSendEnd(pThis->Callbacks.pvUser, pReq->szTag);
714 if (RT_FAILURE(rc2))
715 return atsReplyRC(pThis, pInst, pPktHdr, rc2, "Ending sending test set '%s' failed", pReq->szTag);
716 }
717
718 return rc;
719}
720
721/**
722 * Verifies and processes a "TN PLY" request.
723 *
724 * @returns IPRT status code.
725 * @param pThis The ATS instance.
726 * @param pInst The opaque ATS instance structure.
727 * @param pPktHdr The packet header.
728 */
729static int atsDoTonePlay(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
730{
731 int rc = VINF_SUCCESS;
732
733 if (pPktHdr->cb < sizeof(ATSPKTREQTONEPLAY))
734 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTONEPLAY));
735
736 if (pInst->enmState != ATSCLIENTSTATE_READY)
737 return atsReplyInvalidState(pThis, pInst, pPktHdr);
738
739 if (!pThis->Callbacks.pfnTonePlay)
740 return atsReplyRC(pThis, pInst, pPktHdr, VERR_NOT_SUPPORTED, "Playing tones not supported");
741
742 PATSPKTREQTONEPLAY pReq = (PATSPKTREQTONEPLAY)pPktHdr;
743 rc = pThis->Callbacks.pfnTonePlay(pThis->Callbacks.pvUser, &pReq->ToneParms);
744
745 int rc2 = atsReplyAck(pThis, pInst, pPktHdr);
746 if (RT_SUCCESS(rc))
747 rc = rc2;
748
749 return rc;
750}
751
752/**
753 * Verifies and processes a "TN REC" request.
754 *
755 * @returns IPRT status code.
756 * @param pThis The ATS instance.
757 * @param pInst The opaque ATS instance structure.
758 * @param pPktHdr The packet header.
759 */
760static int atsDoToneRecord(PATSSERVER pThis, PATSCLIENTINST pInst, PATSPKTHDR pPktHdr)
761{
762 int rc = VINF_SUCCESS;
763
764 if (pPktHdr->cb < sizeof(ATSPKTREQTONEREC))
765 return atsReplyBadSize(pThis, pInst, pPktHdr, sizeof(ATSPKTREQTONEREC));
766
767 if (pInst->enmState != ATSCLIENTSTATE_READY)
768 return atsReplyInvalidState(pThis, pInst, pPktHdr);
769
770 if (!pThis->Callbacks.pfnToneRecord)
771 return atsReplyRC(pThis, pInst, pPktHdr, VERR_NOT_SUPPORTED, "Recording tones not supported");
772
773 PATSPKTREQTONEREC pReq = (PATSPKTREQTONEREC)pPktHdr;
774 rc = pThis->Callbacks.pfnToneRecord(pThis->Callbacks.pvUser, &pReq->ToneParms);
775
776 int rc2 = atsReplyAck(pThis, pInst, pPktHdr);
777 if (RT_SUCCESS(rc))
778 rc = rc2;
779
780 return rc;
781}
782
783/**
784 * Main request processing routine for each client.
785 *
786 * @returns IPRT status code.
787 * @param pThis The ATS instance.
788 * @param pInst The ATS client structure sending the request.
789 */
790static int atsClientReqProcess(PATSSERVER pThis, PATSCLIENTINST pInst)
791{
792 /*
793 * Read client command packet and process it.
794 */
795 PATSPKTHDR pPktHdr = NULL;
796 int rc = atsRecvPkt(pThis, pInst, &pPktHdr, true /*fAutoRetryOnFailure*/);
797 if (RT_FAILURE(rc))
798 return rc;
799
800 /*
801 * Do a string switch on the opcode bit.
802 */
803 /* Connection: */
804 if ( atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_HOWDY))
805 rc = atsDoHowdy(pThis, pInst, pPktHdr);
806 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_BYE))
807 rc = atsDoBye(pThis, pInst, pPktHdr);
808 /* Test set handling: */
809 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_BEGIN))
810 rc = atsDoTestSetBegin(pThis, pInst, pPktHdr);
811 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_END))
812 rc = atsDoTestSetEnd(pThis, pInst, pPktHdr);
813 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TESTSET_SEND))
814 rc = atsDoTestSetSend(pThis, pInst, pPktHdr);
815 /* Audio testing: */
816 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TONE_PLAY))
817 rc = atsDoTonePlay(pThis, pInst, pPktHdr);
818 else if (atsIsSameOpcode(pPktHdr, ATSPKT_OPCODE_TONE_RECORD))
819 rc = atsDoToneRecord(pThis, pInst, pPktHdr);
820 /* Misc: */
821 else
822 rc = atsReplyUnknown(pThis, pInst, pPktHdr);
823
824 RTMemFree(pPktHdr);
825
826 return rc;
827}
828
829/**
830 * Destroys a client instance.
831 *
832 * @returns nothing.
833 * @param pInst The opaque ATS instance structure.
834 */
835static void atsClientDestroy(PATSCLIENTINST pInst)
836{
837 if (pInst->pszHostname)
838 RTStrFree(pInst->pszHostname);
839 RTMemFree(pInst);
840}
841
842/**
843 * The main thread worker serving the clients.
844 */
845static DECLCALLBACK(int) atsClientWorker(RTTHREAD hThread, void *pvUser)
846{
847 RT_NOREF(hThread);
848
849 PATSSERVER pThis = (PATSSERVER)pvUser;
850 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
851
852 unsigned cClientsMax = 0;
853 unsigned cClientsCur = 0;
854 PATSCLIENTINST *papInsts = NULL;
855
856 /* Add the pipe to the poll set. */
857 int rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 0);
858 if (RT_SUCCESS(rc))
859 {
860 while (!pThis->fTerminate)
861 {
862 uint32_t fEvts;
863 uint32_t uId;
864 rc = RTPoll(pThis->hPollSet, RT_INDEFINITE_WAIT, &fEvts, &uId);
865 if (RT_SUCCESS(rc))
866 {
867 if (uId == 0)
868 {
869 if (fEvts & RTPOLL_EVT_ERROR)
870 break;
871
872 /* We got woken up because of a new client. */
873 Assert(fEvts & RTPOLL_EVT_READ);
874
875 uint8_t bRead;
876 size_t cbRead = 0;
877 rc = RTPipeRead(pThis->hPipeR, &bRead, 1, &cbRead);
878 AssertRC(rc);
879
880 RTCritSectEnter(&pThis->CritSectClients);
881 /* Walk the list and add all new clients. */
882 PATSCLIENTINST pIt, pItNext;
883 RTListForEachSafe(&pThis->LstClientsNew, pIt, pItNext, ATSCLIENTINST, NdLst)
884 {
885 RTListNodeRemove(&pIt->NdLst);
886 Assert(cClientsCur <= cClientsMax);
887 if (cClientsCur == cClientsMax)
888 {
889 /* Realloc to accommodate for the new clients. */
890 PATSCLIENTINST *papInstsNew = (PATSCLIENTINST *)RTMemRealloc(papInsts, (cClientsMax + 10) * sizeof(PATSCLIENTINST));
891 if (RT_LIKELY(papInstsNew))
892 {
893 cClientsMax += 10;
894 papInsts = papInstsNew;
895 }
896 }
897 if (cClientsCur < cClientsMax)
898 {
899 /* Find a free slot in the client array. */
900 unsigned idxSlt = 0;
901 while ( idxSlt < cClientsMax
902 && papInsts[idxSlt] != NULL)
903 idxSlt++;
904
905 rc = pThis->pTransport->pfnPollSetAdd(pThis->pTransportInst, pThis->hPollSet, pIt->pTransportClient, idxSlt + 1);
906 if (RT_SUCCESS(rc))
907 {
908 cClientsCur++;
909 papInsts[idxSlt] = pIt;
910 }
911 else
912 {
913 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pIt->pTransportClient);
914 atsClientDestroy(pIt);
915 }
916 }
917 else
918 {
919 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pIt->pTransportClient);
920 atsClientDestroy(pIt);
921 }
922 }
923 RTCritSectLeave(&pThis->CritSectClients);
924 }
925 else
926 {
927 /* Client sends a request, pick the right client and process it. */
928 PATSCLIENTINST pInst = papInsts[uId - 1];
929 AssertPtr(pInst);
930 if (fEvts & RTPOLL_EVT_READ)
931 rc = atsClientReqProcess(pThis, pInst);
932
933 if ( (fEvts & RTPOLL_EVT_ERROR)
934 || RT_FAILURE(rc))
935 {
936 /* Close connection and remove client from array. */
937 rc = pThis->pTransport->pfnPollSetRemove(pThis->pTransportInst, pThis->hPollSet, pInst->pTransportClient, uId);
938 AssertRC(rc);
939
940 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pInst->pTransportClient);
941 pInst->pTransportClient = NULL;
942 papInsts[uId - 1] = NULL;
943 cClientsCur--;
944 atsClientDestroy(pInst);
945 }
946 }
947 }
948 }
949 }
950
951 return rc;
952}
953
954/**
955 * The main thread waiting for new client connections.
956 *
957 * @returns VBox status code.
958 */
959static DECLCALLBACK(int) atsMainThread(RTTHREAD hThread, void *pvUser)
960{
961 RT_NOREF(hThread);
962
963 PATSSERVER pThis = (PATSSERVER)pvUser;
964 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
965
966 int rc = RTThreadUserSignal(hThread);
967 AssertRCReturn(rc, rc);
968
969 while (!pThis->fTerminate)
970 {
971 /*
972 * Wait for new connection and spin off a new thread
973 * for every new client.
974 */
975 PATSTRANSPORTCLIENT pTransportClient;
976 rc = pThis->pTransport->pfnWaitForConnect(pThis->pTransportInst, &pTransportClient);
977 if (RT_FAILURE(rc))
978 continue;
979
980 /*
981 * New connection, create new client structure and spin of
982 * the request handling thread.
983 */
984 PATSCLIENTINST pInst = (PATSCLIENTINST)RTMemAllocZ(sizeof(ATSCLIENTINST));
985 if (RT_LIKELY(pInst))
986 {
987 pInst->enmState = ATSCLIENTSTATE_INITIALISING;
988 pInst->pTransportClient = pTransportClient;
989 pInst->pszHostname = NULL;
990
991 /* Add client to the new list and inform the worker thread. */
992 RTCritSectEnter(&pThis->CritSectClients);
993 RTListAppend(&pThis->LstClientsNew, &pInst->NdLst);
994 RTCritSectLeave(&pThis->CritSectClients);
995
996 size_t cbWritten = 0;
997 rc = RTPipeWrite(pThis->hPipeW, "", 1, &cbWritten);
998 if (RT_FAILURE(rc))
999 RTMsgError("Failed to inform worker thread of a new client");
1000 }
1001 else
1002 {
1003 RTMsgError("Creating new client structure failed with out of memory error\n");
1004 pThis->pTransport->pfnNotifyBye(pThis->pTransportInst, pTransportClient);
1005 }
1006 }
1007
1008 return rc;
1009}
1010
1011/**
1012 * Creates an ATS instance.
1013 *
1014 * @returns VBox status code.
1015 * @param pThis The ATS instance to create.
1016 */
1017int AudioTestSvcCreate(PATSSERVER pThis)
1018{
1019 /*
1020 * The default transporter is the first one.
1021 */
1022 pThis->pTransport = g_apTransports[0]; /** @todo Make this dynamic. */
1023
1024 return pThis->pTransport->pfnCreate(&pThis->pTransportInst);
1025}
1026
1027/**
1028 * Initializes an ATS instance.
1029 *
1030 * @returns VBox status code.
1031 * @param pThis The ATS instance.
1032 * @param pCallbacks The callbacks table to use.
1033 */
1034int AudioTestSvcInit(PATSSERVER pThis, PCATSCALLBACKS pCallbacks)
1035{
1036 memcpy(&pThis->Callbacks, pCallbacks, sizeof(ATSCALLBACKS));
1037
1038 pThis->fStarted = false;
1039 pThis->fTerminate = false;
1040
1041 pThis->hPipeR = NIL_RTPIPE;
1042 pThis->hPipeW = NIL_RTPIPE;
1043
1044 RTListInit(&pThis->LstClientsNew);
1045
1046 /*
1047 * Initialize the transport layer.
1048 */
1049 int rc = RTCritSectInit(&pThis->CritSectClients);
1050 if (RT_SUCCESS(rc))
1051 {
1052 rc = RTPollSetCreate(&pThis->hPollSet);
1053 if (RT_SUCCESS(rc))
1054 {
1055 rc = RTPipeCreate(&pThis->hPipeR, &pThis->hPipeW, 0);
1056 if (RT_SUCCESS(rc))
1057 {
1058 /* Spin off the thread serving connections. */
1059 rc = RTThreadCreate(&pThis->hThreadServing, atsClientWorker, pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
1060 "AUDTSTSRVC");
1061 if (RT_SUCCESS(rc))
1062 return VINF_SUCCESS;
1063 else
1064 RTMsgError("Creating the client worker thread failed with %Rrc\n", rc);
1065
1066 RTPipeClose(pThis->hPipeR);
1067 RTPipeClose(pThis->hPipeW);
1068 }
1069 else
1070 RTMsgError("Creating communications pipe failed with %Rrc\n", rc);
1071
1072 RTPollSetDestroy(pThis->hPollSet);
1073 }
1074 else
1075 RTMsgError("Creating pollset failed with %Rrc\n", rc);
1076
1077 RTCritSectDelete(&pThis->CritSectClients);
1078 }
1079 else
1080 RTMsgError("Creating global critical section failed with %Rrc\n", rc);
1081
1082 return rc;
1083}
1084
1085/**
1086 * Handles a command line option.
1087 *
1088 * @returns VBox status code.
1089 * @param pThis The ATS instance to handle option for.
1090 * @param ch Option (short) to handle.
1091 * @param pVal Option union to store the result in on success.
1092 */
1093int AudioTestSvcHandleOption(PATSSERVER pThis, int ch, PCRTGETOPTUNION pVal)
1094{
1095 AssertPtrReturn(pThis->pTransport, VERR_WRONG_ORDER); /* Must be creatd first. */
1096 if (!pThis->pTransport->pfnOption)
1097 return VERR_GETOPT_UNKNOWN_OPTION;
1098 return pThis->pTransport->pfnOption(pThis->pTransportInst, ch, pVal);
1099}
1100
1101/**
1102 * Starts a formerly initialized ATS instance.
1103 *
1104 * @returns VBox status code.
1105 * @param pThis The ATS instance to start.
1106 */
1107int AudioTestSvcStart(PATSSERVER pThis)
1108{
1109 int rc = pThis->pTransport->pfnStart(pThis->pTransportInst);
1110 if (RT_SUCCESS(rc))
1111 {
1112 /* Spin off the connection thread. */
1113 rc = RTThreadCreate(&pThis->hThreadMain, atsMainThread, pThis, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
1114 "AUDTSTSRVM");
1115 if (RT_SUCCESS(rc))
1116 {
1117 rc = RTThreadUserWait(pThis->hThreadMain, RT_MS_30SEC);
1118 if (RT_SUCCESS(rc))
1119 pThis->fStarted = true;
1120 }
1121 }
1122
1123 return rc;
1124}
1125
1126/**
1127 * Shuts down a formerly started ATS instance.
1128 *
1129 * @returns VBox status code.
1130 * @param pThis The ATS instance.
1131 */
1132int AudioTestSvcShutdown(PATSSERVER pThis)
1133{
1134 if (!pThis->fStarted)
1135 return VINF_SUCCESS;
1136
1137 ASMAtomicXchgBool(&pThis->fTerminate, true);
1138
1139 if (pThis->pTransport)
1140 pThis->pTransport->pfnTerm(pThis->pTransportInst);
1141
1142 size_t cbWritten;
1143 int rc = RTPipeWrite(pThis->hPipeW, "", 1, &cbWritten);
1144 AssertRCReturn(rc, rc);
1145
1146 /* First close serving thread. */
1147 int rcThread;
1148 rc = RTThreadWait(pThis->hThreadServing, RT_MS_30SEC, &rcThread);
1149 if (RT_SUCCESS(rc))
1150 {
1151 rc = rcThread;
1152 if (RT_SUCCESS(rc))
1153 {
1154 /* Close the main thread last. */
1155 rc = RTThreadWait(pThis->hThreadMain, RT_MS_30SEC, &rcThread);
1156 if (RT_SUCCESS(rc))
1157 rc = rcThread;
1158
1159 if (rc == VERR_TCP_SERVER_DESTROYED)
1160 rc = VINF_SUCCESS;
1161 }
1162 }
1163
1164 if (RT_SUCCESS(rc))
1165 pThis->fStarted = false;
1166
1167 return rc;
1168}
1169
1170/**
1171 * Destroys an ATS instance, internal version.
1172 *
1173 * @returns VBox status code.
1174 * @param pThis ATS instance to destroy.
1175 */
1176static int audioTestSvcDestroyInternal(PATSSERVER pThis)
1177{
1178 int rc = VINF_SUCCESS;
1179
1180 if (pThis->hPipeR != NIL_RTPIPE)
1181 {
1182 rc = RTPipeClose(pThis->hPipeR);
1183 AssertRCReturn(rc, rc);
1184 pThis->hPipeR = NIL_RTPIPE;
1185 }
1186
1187 if (pThis->hPipeW != NIL_RTPIPE)
1188 {
1189 rc = RTPipeClose(pThis->hPipeW);
1190 AssertRCReturn(rc, rc);
1191 pThis->hPipeW = NIL_RTPIPE;
1192 }
1193
1194 RTPollSetDestroy(pThis->hPollSet);
1195 pThis->hPollSet = NIL_RTPOLLSET;
1196
1197 PATSCLIENTINST pIt, pItNext;
1198 RTListForEachSafe(&pThis->LstClientsNew, pIt, pItNext, ATSCLIENTINST, NdLst)
1199 {
1200 RTListNodeRemove(&pIt->NdLst);
1201
1202 RTMemFree(pIt);
1203 pIt = NULL;
1204 }
1205
1206 if (RTCritSectIsInitialized(&pThis->CritSectClients))
1207 {
1208 rc = RTCritSectDelete(&pThis->CritSectClients);
1209 AssertRCReturn(rc, rc);
1210 }
1211
1212 return rc;
1213}
1214
1215/**
1216 * Destroys an ATS instance.
1217 *
1218 * @returns VBox status code.
1219 * @param pThis ATS instance to destroy.
1220 */
1221int AudioTestSvcDestroy(PATSSERVER pThis)
1222{
1223 int rc = audioTestSvcDestroyInternal(pThis);
1224 if (RT_SUCCESS(rc))
1225 {
1226 if (pThis->pTransport)
1227 {
1228 if ( pThis->pTransport->pfnDestroy
1229 && pThis->pTransportInst)
1230 {
1231 pThis->pTransport->pfnDestroy(pThis->pTransportInst);
1232 pThis->pTransportInst = NULL;
1233 }
1234 }
1235 }
1236
1237 return rc;
1238}
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