VirtualBox

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

Last change on this file since 89554 was 89541, checked in by vboxsync, 4 years ago

Audio/ValKit: More code for completely self-contained (self) testing. bugref:10008

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