VirtualBox

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

最後變更 在這個檔案從91039是 91039,由 vboxsync 提交於 4 年 前

Audio/Validation Kit: More code for resolving the connection problems. ​bugref:10008

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette