VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/AudioTestServiceClient.cpp@ 90778

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

Audio/ATS: Implemented audioTestSvcClientDoBye(). bugref:10008

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
RevLine 
[89205]1/* $Id: AudioTestServiceClient.cpp 90764 2021-08-20 15:54:46Z vboxsync $ */
2/** @file
[89285]3 * AudioTestServiceClient - Audio Test Service (ATS), Client helpers.
[89205]4 *
5 * Note: Only does TCP/IP as transport layer for now.
6 */
7
8/*
9 * Copyright (C) 2021 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
[90048]24#define LOG_GROUP LOG_GROUP_AUDIO_TEST
[89614]25
[89205]26#include <iprt/crc.h>
27#include <iprt/err.h>
[89614]28#include <iprt/file.h>
[89205]29#include <iprt/mem.h>
30#include <iprt/string.h>
31#include <iprt/tcp.h>
32
[89962]33#include <VBox/log.h>
34
[89399]35#include "AudioTestService.h"
[89962]36#include "AudioTestServiceInternal.h"
[89205]37#include "AudioTestServiceClient.h"
38
39/** @todo Use common defines between server protocol and this client. */
40
[89285]41/**
42 * A generic ATS reply, used by the client
43 * to process the incoming packets.
44 */
[89205]45typedef struct ATSSRVREPLY
46{
[89614]47 char szOp[ATSPKT_OPCODE_MAX_LEN];
[89962]48 /** Pointer to payload data.
49 * This does *not* include the header! */
[89205]50 void *pvPayload;
[89962]51 /** Size (in bytes) of the payload data.
52 * This does *not* include the header! */
[89205]53 size_t cbPayload;
54} ATSSRVREPLY;
[89285]55/** Pointer to a generic ATS reply. */
[89205]56typedef struct ATSSRVREPLY *PATSSRVREPLY;
57
58
[89285]59/**
60 * Initializes an ATS client, internal version.
61 *
62 * @param pClient Client to initialize.
63 */
64static void audioTestSvcClientInit(PATSCLIENT pClient)
[89205]65{
[89962]66 RT_BZERO(pClient, sizeof(ATSCLIENT));
[89205]67}
68
[89285]69/**
[89614]70 * Destroys an ATS server reply.
71 *
72 * @param pReply Reply to destroy.
[89285]73 */
[89614]74static void audioTestSvcClientReplyDestroy(PATSSRVREPLY pReply)
[89205]75{
76 if (!pReply)
77 return;
78
79 if (pReply->pvPayload)
80 {
81 Assert(pReply->cbPayload);
82 RTMemFree(pReply->pvPayload);
83 pReply->pvPayload = NULL;
84 }
85
86 pReply->cbPayload = 0;
87}
88
[89285]89/**
90 * Receives a reply from an ATS server.
91 *
92 * @returns VBox status code.
93 * @param pClient Client to receive reply for.
94 * @param pReply Where to store the reply.
[89614]95 * The reply must be destroyed with audioTestSvcClientReplyDestroy() then.
[89285]96 * @param fNoDataOk If it's okay that the reply is not expected to have any payload.
97 */
[89205]98static int audioTestSvcClientRecvReply(PATSCLIENT pClient, PATSSRVREPLY pReply, bool fNoDataOk)
99{
[89802]100 LogFlowFuncEnter();
101
[89962]102 PATSPKTHDR pPktHdr;
103 int rc = pClient->pTransport->pfnRecvPkt(pClient->pTransportInst, pClient->pTransportClient, &pPktHdr);
104 if (RT_SUCCESS(rc))
[89205]105 {
[89962]106 AssertReturn(pPktHdr->cb >= sizeof(ATSPKTHDR), VERR_NET_PROTOCOL_ERROR);
107 pReply->cbPayload = pPktHdr->cb - sizeof(ATSPKTHDR);
108 Log3Func(("szOp=%.8s, cb=%RU32\n", pPktHdr->achOpcode, pPktHdr->cb));
109 if (pReply->cbPayload)
110 {
111 pReply->pvPayload = RTMemDup((uint8_t *)pPktHdr + sizeof(ATSPKTHDR), pReply->cbPayload);
112 }
113 else
114 pReply->pvPayload = NULL;
[89205]115
[89962]116 if ( !pReply->cbPayload
117 && !fNoDataOk)
[89205]118 {
[89962]119 rc = VERR_NET_PROTOCOL_ERROR;
[89205]120 }
[89802]121 else
[89962]122 {
123 memcpy(&pReply->szOp, &pPktHdr->achOpcode, sizeof(pReply->szOp));
124 }
125
126 RTMemFree(pPktHdr);
127 pPktHdr = NULL;
[89205]128 }
129
[89802]130 LogFlowFuncLeaveRC(rc);
[89205]131 return rc;
132}
133
[89285]134/**
135 * Receives a reply for an ATS server and checks if it is an acknowledge (success) one.
136 *
137 * @returns VBox status code.
138 * @retval VERR_NET_PROTOCOL_ERROR if the reply indicates a failure.
139 * @param pClient Client to receive reply for.
140 */
[89205]141static int audioTestSvcClientRecvAck(PATSCLIENT pClient)
142{
143 ATSSRVREPLY Reply;
144 RT_ZERO(Reply);
145
146 int rc = audioTestSvcClientRecvReply(pClient, &Reply, true /* fNoDataOk */);
147 if (RT_SUCCESS(rc))
148 {
[89614]149 if (RTStrNCmp(Reply.szOp, "ACK ", ATSPKT_OPCODE_MAX_LEN) != 0)
[89205]150 rc = VERR_NET_PROTOCOL_ERROR;
151
[89614]152 audioTestSvcClientReplyDestroy(&Reply);
[89205]153 }
154
155 return rc;
156}
157
[89285]158/**
159 * Sends a message plus optional payload to an ATS server.
160 *
161 * @returns VBox status code.
162 * @param pClient Client to send message for.
163 * @param pvHdr Pointer to header data to send.
164 * @param cbHdr Size (in bytes) of \a pvHdr to send.
165 */
[89962]166static int audioTestSvcClientSendMsg(PATSCLIENT pClient, void *pvHdr, size_t cbHdr)
[89205]167{
[89962]168 RT_NOREF(cbHdr);
169 AssertPtrReturn(pClient->pTransport, VERR_WRONG_ORDER);
170 AssertPtrReturn(pClient->pTransportInst, VERR_WRONG_ORDER);
171 AssertPtrReturn(pClient->pTransportClient, VERR_NET_NOT_CONNECTED);
172 return pClient->pTransport->pfnSendPkt(pClient->pTransportInst, pClient->pTransportClient, (PCATSPKTHDR)pvHdr);
[89205]173}
174
[89285]175/**
176 * Initializes a client request header.
177 *
178 * @returns VBox status code.
179 * @param pReqHdr Request header to initialize.
180 * @param cbReq Size (in bytes) the request will have (does *not* include payload).
181 * @param pszOp Operation to perform with the request.
182 * @param cbPayload Size (in bytes) of payload that will follow the header. Optional and can be 0.
183 */
[89205]184DECLINLINE (void) audioTestSvcClientReqHdrInit(PATSPKTHDR pReqHdr, size_t cbReq, const char *pszOp, size_t cbPayload)
185{
186 AssertReturnVoid(strlen(pszOp) >= 2);
[89614]187 AssertReturnVoid(strlen(pszOp) <= ATSPKT_OPCODE_MAX_LEN);
[89205]188
189 /** @todo Validate opcode. */
190
[89614]191 RT_BZERO(pReqHdr, sizeof(ATSPKTHDR));
192
193 memcpy(pReqHdr->achOpcode, pszOp, strlen(pszOp));
[89205]194 pReqHdr->uCrc32 = 0; /** @todo Do CRC-32 calculation. */
[89206]195 pReqHdr->cb = (uint32_t)cbReq + (uint32_t)cbPayload;
[89614]196
197 Assert(pReqHdr->cb <= ATSPKT_MAX_SIZE);
[89205]198}
199
[89285]200/**
[89614]201 * Sends an acknowledege response back to the server.
202 *
203 * @returns VBox status code.
204 * @param pClient Client to send command for.
205 */
206static int audioTestSvcClientSendAck(PATSCLIENT pClient)
207{
208 ATSPKTHDR Req;
209 audioTestSvcClientReqHdrInit(&Req, sizeof(Req), "ACK ", 0);
210
[89962]211 return audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
[89614]212}
213
214/**
[89285]215 * Sends a greeting command (handshake) to an ATS server.
216 *
217 * @returns VBox status code.
218 * @param pClient Client to send command for.
219 */
[89205]220static int audioTestSvcClientDoGreet(PATSCLIENT pClient)
221{
222 ATSPKTREQHOWDY Req;
223 Req.uVersion = ATS_PROTOCOL_VS;
224 audioTestSvcClientReqHdrInit(&Req.Hdr, sizeof(Req), ATSPKT_OPCODE_HOWDY, 0);
[89962]225 int rc = audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
[89205]226 if (RT_SUCCESS(rc))
227 rc = audioTestSvcClientRecvAck(pClient);
228 return rc;
229}
230
[89285]231/**
[90764]232 * Tells the ATS server that we want disconnect.
233 *
234 * @returns VBox status code.
235 * @param pClient Client to disconnect.
236 */
237static int audioTestSvcClientDoBye(PATSCLIENT pClient)
238{
239 ATSPKTREQHOWDY Req;
240 Req.uVersion = ATS_PROTOCOL_VS;
241 audioTestSvcClientReqHdrInit(&Req.Hdr, sizeof(Req), ATSPKT_OPCODE_BYE, 0);
242 int rc = audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
243 if (RT_SUCCESS(rc))
244 rc = audioTestSvcClientRecvAck(pClient);
245 return rc;
246}
247
248/**
[89962]249 * Creates an ATS client.
[89285]250 *
251 * @returns VBox status code.
[89962]252 * @param pClient Client to create.
[89285]253 */
[89962]254int AudioTestSvcClientCreate(PATSCLIENT pClient)
[89205]255{
[89962]256 audioTestSvcClientInit(pClient);
[89205]257
[89962]258 /*
259 * The default transporter is the first one.
260 */
261 pClient->pTransport = g_apTransports[0]; /** @todo Make this dynamic. */
262
263 return pClient->pTransport->pfnCreate(&pClient->pTransportInst);
[89205]264}
265
[89285]266/**
[89962]267 * Destroys an ATS client.
[89285]268 *
269 * @returns VBox status code.
[89962]270 * @param pClient Client to destroy.
271 */
272void AudioTestSvcClientDestroy(PATSCLIENT pClient)
273{
[90764]274 /* ignore rc */ audioTestSvcClientDoBye(pClient);
275
[89962]276 if (pClient->pTransport)
277 pClient->pTransport->pfnTerm(pClient->pTransportInst);
278}
279
280/**
281 * Handles a command line option.
282 *
283 * @returns VBox status code.
284 * @param pClient Client to handle option for.
285 * @param ch Option (short) to handle.
286 * @param pVal Option union to store the result in on success.
287 */
288int AudioTestSvcClientHandleOption(PATSCLIENT pClient, int ch, PCRTGETOPTUNION pVal)
289{
290 AssertPtrReturn(pClient->pTransport, VERR_WRONG_ORDER); /* Must be created first via AudioTestSvcClientCreate(). */
291 if (!pClient->pTransport->pfnOption)
292 return VERR_GETOPT_UNKNOWN_OPTION;
293 return pClient->pTransport->pfnOption(pClient->pTransportInst, ch, pVal);
294}
295
296/**
297 * Connects to an ATS peer.
298 *
299 * @returns VBox status code.
[89285]300 * @param pClient Client to connect.
301 */
[89962]302int AudioTestSvcClientConnect(PATSCLIENT pClient)
[89205]303{
[89962]304 int rc = pClient->pTransport->pfnStart(pClient->pTransportInst);
[89205]305 if (RT_SUCCESS(rc))
306 {
[89962]307 rc = pClient->pTransport->pfnWaitForConnect(pClient->pTransportInst, &pClient->pTransportClient);
308 if (RT_SUCCESS(rc))
309 {
310 rc = audioTestSvcClientDoGreet(pClient);
311 }
[89205]312 }
313
314 return rc;
315}
316
[89285]317/**
[89431]318 * Tells the server to begin a new test set.
319 *
320 * @returns VBox status code.
321 * @param pClient Client to issue command for.
322 * @param pszTag Tag to use for the test set to begin.
323 */
324int AudioTestSvcClientTestSetBegin(PATSCLIENT pClient, const char *pszTag)
325{
326 ATSPKTREQTSETBEG Req;
327
328 int rc = RTStrCopy(Req.szTag, sizeof(Req.szTag), pszTag);
329 AssertRCReturn(rc, rc);
330
331 audioTestSvcClientReqHdrInit(&Req.Hdr, sizeof(Req), ATSPKT_OPCODE_TESTSET_BEGIN, 0);
332
[89962]333 rc = audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
[89431]334 if (RT_SUCCESS(rc))
335 rc = audioTestSvcClientRecvAck(pClient);
336
337 return rc;
338}
339
340/**
341 * Tells the server to end a runing test set.
342 *
343 * @returns VBox status code.
344 * @param pClient Client to issue command for.
345 * @param pszTag Tag of test set to end.
346 */
347int AudioTestSvcClientTestSetEnd(PATSCLIENT pClient, const char *pszTag)
348{
349 ATSPKTREQTSETEND Req;
350
351 int rc = RTStrCopy(Req.szTag, sizeof(Req.szTag), pszTag);
352 AssertRCReturn(rc, rc);
353
354 audioTestSvcClientReqHdrInit(&Req.Hdr, sizeof(Req), ATSPKT_OPCODE_TESTSET_END, 0);
355
[89962]356 rc = audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
[89431]357 if (RT_SUCCESS(rc))
358 rc = audioTestSvcClientRecvAck(pClient);
359
360 return rc;
361}
362
363/**
[89285]364 * Tells the server to play a (test) tone.
365 *
366 * @returns VBox status code.
367 * @param pClient Client to issue command for.
368 * @param pToneParms Tone parameters to use.
369 * @note How (and if) the server plays a tone depends on the actual implementation side.
370 */
[89458]371int AudioTestSvcClientTonePlay(PATSCLIENT pClient, PAUDIOTESTTONEPARMS pToneParms)
[89226]372{
373 ATSPKTREQTONEPLAY Req;
374
375 memcpy(&Req.ToneParms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
376
377 audioTestSvcClientReqHdrInit(&Req.Hdr, sizeof(Req), ATSPKT_OPCODE_TONE_PLAY, 0);
378
[89962]379 int rc = audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
[89226]380 if (RT_SUCCESS(rc))
381 rc = audioTestSvcClientRecvAck(pClient);
382
383 return rc;
384}
385
[89285]386/**
[89456]387 * Tells the server to record a (test) tone.
388 *
389 * @returns VBox status code.
390 * @param pClient Client to issue command for.
391 * @param pToneParms Tone parameters to use.
392 * @note How (and if) the server plays a tone depends on the actual implementation side.
393 */
[89458]394int AudioTestSvcClientToneRecord(PATSCLIENT pClient, PAUDIOTESTTONEPARMS pToneParms)
[89456]395{
396 ATSPKTREQTONEREC Req;
397
398 memcpy(&Req.ToneParms, pToneParms, sizeof(AUDIOTESTTONEPARMS));
399
400 audioTestSvcClientReqHdrInit(&Req.Hdr, sizeof(Req), ATSPKT_OPCODE_TONE_RECORD, 0);
401
[89962]402 int rc = audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
[89456]403 if (RT_SUCCESS(rc))
404 rc = audioTestSvcClientRecvAck(pClient);
405
406 return rc;
407}
408
409/**
[89614]410 * Tells the server to send (download) a (packed up) test set archive.
411 * The test set must not be running / open anymore.
412 *
413 * @returns VBox status code.
414 * @param pClient Client to issue command for.
415 * @param pszTag Tag of test set to send.
[89618]416 * @param pszPathOutAbs Absolute path where to store the downloaded test set archive.
[89614]417 */
418int AudioTestSvcClientTestSetDownload(PATSCLIENT pClient, const char *pszTag, const char *pszPathOutAbs)
419{
420 ATSPKTREQTSETSND Req;
421
422 int rc = RTStrCopy(Req.szTag, sizeof(Req.szTag), pszTag);
423 AssertRCReturn(rc, rc);
424
425 audioTestSvcClientReqHdrInit(&Req.Hdr, sizeof(Req), ATSPKT_OPCODE_TESTSET_SEND, 0);
426
427 RTFILE hFile;
428 rc = RTFileOpen(&hFile, pszPathOutAbs, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
429 AssertRCReturn(rc, rc);
430
[89962]431 rc = audioTestSvcClientSendMsg(pClient, &Req, sizeof(Req));
[89614]432 while (RT_SUCCESS(rc))
433 {
434 ATSSRVREPLY Reply;
435 RT_ZERO(Reply);
[89962]436
[89614]437 rc = audioTestSvcClientRecvReply(pClient, &Reply, false /* fNoDataOk */);
[89802]438 if (RT_SUCCESS(rc))
439 {
[89962]440 /* Extract received CRC32 checksum. */
441 const size_t cbCrc32 = sizeof(uint32_t); /* Skip CRC32 in payload for actual CRC verification. */
[89614]442
[89962]443 uint32_t uSrcCrc32;
444 memcpy(&uSrcCrc32, Reply.pvPayload, cbCrc32);
[89802]445
[89962]446 if (uSrcCrc32)
447 {
448 const uint32_t uDstCrc32 = RTCrc32((uint8_t *)Reply.pvPayload + cbCrc32, Reply.cbPayload - cbCrc32);
449
450 Log2Func(("uSrcCrc32=%#x, cbRead=%zu -> uDstCrc32=%#x\n"
451 "%.*Rhxd\n",
452 uSrcCrc32, Reply.cbPayload - cbCrc32, uDstCrc32,
453 RT_MIN(64, Reply.cbPayload - cbCrc32), (uint8_t *)Reply.pvPayload + cbCrc32));
454
455 if (uSrcCrc32 != uDstCrc32)
456 rc = VERR_TAR_CHKSUM_MISMATCH; /** @todo Fudge! */
457 }
458
[89802]459 if (RT_SUCCESS(rc))
460 {
461 if ( RTStrNCmp(Reply.szOp, "DATA ", ATSPKT_OPCODE_MAX_LEN) == 0
462 && Reply.pvPayload
463 && Reply.cbPayload)
464 {
[89962]465 rc = RTFileWrite(hFile, (uint8_t *)Reply.pvPayload + cbCrc32, Reply.cbPayload - cbCrc32, NULL);
[89802]466 }
467 else if (RTStrNCmp(Reply.szOp, "DATA EOF", ATSPKT_OPCODE_MAX_LEN) == 0)
468 {
469 rc = VINF_EOF;
470 }
471 else
472 {
473 AssertMsgFailed(("Got unexpected reply '%s'", Reply.szOp));
474 rc = VERR_NOT_SUPPORTED;
475 }
476 }
[89614]477 }
478
479 audioTestSvcClientReplyDestroy(&Reply);
480
481 int rc2 = audioTestSvcClientSendAck(pClient);
482 if (rc == VINF_SUCCESS) /* Might be VINF_EOF already. */
483 rc = rc2;
484
485 if (rc == VINF_EOF)
486 break;
487 }
488
489 int rc2 = RTFileClose(hFile);
490 if (RT_SUCCESS(rc))
491 rc = rc2;
492
493 return rc;
494}
495
496/**
[89285]497 * Disconnects from an ATS server.
498 *
499 * @returns VBox status code.
500 * @param pClient Client to disconnect.
501 */
[89205]502int AudioTestSvcClientClose(PATSCLIENT pClient)
503{
[89962]504 pClient->pTransport->pfnNotifyBye(pClient->pTransportInst, pClient->pTransportClient);
[89205]505
[89962]506 return VINF_SUCCESS;
[89205]507}
508
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use