VirtualBox

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

Last change on this file since 89693 was 89616, checked in by vboxsync, 4 years ago

Audio/ValKit: Implemented support for downloading (guest) test sets [Doxygen fix]. bugref:10008

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