VirtualBox

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

Last change on this file since 98638 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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