VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/serial/SerialTest.cpp@ 86351

Last change on this file since 86351 was 86016, checked in by vboxsync, 4 years ago

ValidationKit/SerialTest: Add simple echo service sending data back to where it was received from

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.7 KB
Line 
1/* $Id: SerialTest.cpp 86016 2020-09-03 08:19:40Z vboxsync $ */
2/** @file
3 * SerialTest - Serial port testing utility.
4 */
5
6/*
7 * Copyright (C) 2017-2020 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/errcore.h>
32#include <iprt/getopt.h>
33#include <iprt/mem.h>
34#include <iprt/path.h>
35#include <iprt/param.h>
36#include <iprt/process.h>
37#include <iprt/rand.h>
38#include <iprt/serialport.h>
39#include <iprt/stream.h>
40#include <iprt/string.h>
41#include <iprt/test.h>
42
43
44/*********************************************************************************************************************************
45* Defined Constants And Macros *
46*********************************************************************************************************************************/
47
48/** Number of times to toggle the status lines during the test. */
49#define SERIALTEST_STS_LINE_TOGGLE_COUNT 100
50
51
52/*********************************************************************************************************************************
53* Structures and Typedefs *
54*********************************************************************************************************************************/
55
56
57/**
58 * Serial test mode.
59 */
60typedef enum SERIALTESTMODE
61{
62 /** Invalid mode. */
63 SERIALTESTMODE_INVALID = 0,
64 /** Serial port is looped back to itself */
65 SERIALTESTMODE_LOOPBACK,
66 /** A secondary serial port is used with a null modem cable in between. */
67 SERIALTESTMODE_SECONDARY,
68 /** The serial port is connected externally over which we have no control. */
69 SERIALTESTMODE_EXTERNAL,
70 /** Usual 32bit hack. */
71 SERIALTESTMODE_32BIT_HACK = 0x7fffffff
72} SERIALTESTMODE;
73/** Pointer to a serial test mode. */
74typedef SERIALTESTMODE *PSERIALTESTMDOE;
75
76/** Pointer to the serial test data instance. */
77typedef struct SERIALTEST *PSERIALTEST;
78
79/**
80 * Test callback function.
81 *
82 * @returns IPRT status code.
83 * @param pSerialTest The serial test instance data.
84 */
85typedef DECLCALLBACKTYPE(int, FNSERIALTESTRUN,(PSERIALTEST pSerialTest));
86/** Pointer to the serial test callback. */
87typedef FNSERIALTESTRUN *PFNSERIALTESTRUN;
88
89
90/**
91 * The serial test instance data.
92 */
93typedef struct SERIALTEST
94{
95 /** The assigned test handle. */
96 RTTEST hTest;
97 /** The assigned serial port. */
98 RTSERIALPORT hSerialPort;
99 /** The currently active config. */
100 PCRTSERIALPORTCFG pSerialCfg;
101} SERIALTEST;
102
103
104/**
105 * Test descriptor.
106 */
107typedef struct SERIALTESTDESC
108{
109 /** Test ID. */
110 const char *pszId;
111 /** Test description. */
112 const char *pszDesc;
113 /** Test run callback. */
114 PFNSERIALTESTRUN pfnRun;
115} SERIALTESTDESC;
116/** Pointer to a test descriptor. */
117typedef SERIALTESTDESC *PSERIALTESTDESC;
118/** Pointer to a constant test descriptor. */
119typedef const SERIALTESTDESC *PCSERIALTESTDESC;
120
121
122/**
123 * TX/RX buffer containing a simple counter.
124 */
125typedef struct SERIALTESTTXRXBUFCNT
126{
127 /** The current counter value. */
128 uint32_t iCnt;
129 /** Number of bytes left to receive/transmit. */
130 size_t cbTxRxLeft;
131 /** The offset into the buffer to receive to/send from. */
132 size_t offBuf;
133 /** Maximum size to send/receive before processing is needed again. */
134 size_t cbTxRxMax;
135 /** The data buffer. */
136 uint8_t abBuf[_1K];
137} SERIALTESTTXRXBUFCNT;
138/** Pointer to a TX/RX buffer. */
139typedef SERIALTESTTXRXBUFCNT *PSERIALTESTTXRXBUFCNT;
140
141
142/*********************************************************************************************************************************
143* Global Variables *
144*********************************************************************************************************************************/
145
146
147/** Command line parameters */
148static const RTGETOPTDEF g_aCmdOptions[] =
149{
150 {"--device", 'd', RTGETOPT_REQ_STRING },
151 {"--baudrate", 'b', RTGETOPT_REQ_UINT32 },
152 {"--parity", 'p', RTGETOPT_REQ_STRING },
153 {"--databits", 'c', RTGETOPT_REQ_UINT32 },
154 {"--stopbits", 's', RTGETOPT_REQ_STRING },
155 {"--mode", 'm', RTGETOPT_REQ_STRING },
156 {"--secondarydevice", 'l', RTGETOPT_REQ_STRING },
157 {"--tests", 't', RTGETOPT_REQ_STRING },
158 {"--txbytes", 'x', RTGETOPT_REQ_UINT32 },
159 {"--abort-on-error", 'a', RTGETOPT_REQ_NOTHING},
160 {"--verbose", 'v', RTGETOPT_REQ_NOTHING},
161 {"--help", 'h', RTGETOPT_REQ_NOTHING}
162};
163
164
165static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest);
166static DECLCALLBACK(int) serialTestRunWrite(PSERIALTEST pSerialTest);
167static DECLCALLBACK(int) serialTestRunReadVerify(PSERIALTEST pSerialTest);
168static DECLCALLBACK(int) serialTestRunStsLines(PSERIALTEST pSerialTest);
169static DECLCALLBACK(int) serialTestRunEcho(PSERIALTEST pSerialTest);
170
171/** Implemented tests. */
172static const SERIALTESTDESC g_aSerialTests[] =
173{
174 {"readwrite", "Simple Read/Write test on the same serial port", serialTestRunReadWrite },
175 {"write", "Simple write test (verification done somewhere else)", serialTestRunWrite },
176 {"readverify", "Counterpart to write test (reads and verifies data)", serialTestRunReadVerify },
177 {"stslines", "Testing the status line setting and receiving", serialTestRunStsLines },
178 {"echo", "Echoes received data back to the sender (not real test)", serialTestRunEcho },
179};
180
181/** Verbosity value. */
182static unsigned g_cVerbosity = 0;
183/** The test handle. */
184static RTTEST g_hTest = NIL_RTTEST;
185/** The serial test mode. */
186static SERIALTESTMODE g_enmMode = SERIALTESTMODE_LOOPBACK;
187/** Random number generator. */
188static RTRAND g_hRand = NIL_RTRAND;
189/** The serial port handle. */
190static RTSERIALPORT g_hSerialPort = NIL_RTSERIALPORT;
191/** The loopback serial port handle if configured. */
192static RTSERIALPORT g_hSerialPortSecondary = NIL_RTSERIALPORT;
193/** Number of bytes to transmit for read/write tests. */
194static size_t g_cbTx = _1M;
195/** Flag whether to abort the tool when encountering the first error. */
196static bool g_fAbortOnError = false;
197/** The config used. */
198static RTSERIALPORTCFG g_SerialPortCfg =
199{
200 /* uBaudRate */
201 115200,
202 /* enmParity */
203 RTSERIALPORTPARITY_NONE,
204 /* enmDataBitCount */
205 RTSERIALPORTDATABITS_8BITS,
206 /* enmStopBitCount */
207 RTSERIALPORTSTOPBITS_ONE
208};
209
210
211/**
212 * RTTestFailed() wrapper which aborts the program if the option is set.
213 */
214static void serialTestFailed(RTTEST hTest, const char *pszFmt, ...)
215{
216 va_list va;
217 va_start(va, pszFmt);
218 RTTestFailedV(hTest, pszFmt, va);
219 va_end(va);
220 if (g_fAbortOnError)
221 RT_BREAKPOINT();
222}
223
224
225/**
226 * Initializes a TX buffer.
227 *
228 * @returns nothing.
229 * @param pSerBuf The serial buffer to initialize.
230 * @param cbTx Maximum number of bytes to transmit.
231 */
232static void serialTestTxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbTx)
233{
234 pSerBuf->iCnt = 0;
235 pSerBuf->offBuf = 0;
236 pSerBuf->cbTxRxMax = 0;
237 pSerBuf->cbTxRxLeft = cbTx;
238 RT_ZERO(pSerBuf->abBuf);
239}
240
241
242/**
243 * Initializes a RX buffer.
244 *
245 * @returns nothing.
246 * @param pSerBuf The serial buffer to initialize.
247 * @param cbRx Maximum number of bytes to receive.
248 */
249static void serialTestRxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbRx)
250{
251 pSerBuf->iCnt = 0;
252 pSerBuf->offBuf = 0;
253 pSerBuf->cbTxRxMax = sizeof(pSerBuf->abBuf);
254 pSerBuf->cbTxRxLeft = cbRx;
255 RT_ZERO(pSerBuf->abBuf);
256}
257
258
259/**
260 * Prepares the given TX buffer with data for sending it out.
261 *
262 * @returns nothing.
263 * @param pSerBuf The TX buffer pointer.
264 */
265static void serialTestTxBufPrepare(PSERIALTESTTXRXBUFCNT pSerBuf)
266{
267 /* Move the data to the front to make room at the end to fill. */
268 if (pSerBuf->offBuf)
269 {
270 memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[pSerBuf->offBuf], sizeof(pSerBuf->abBuf) - pSerBuf->offBuf);
271 pSerBuf->offBuf = 0;
272 }
273
274 /* Fill up with data. */
275 uint32_t offData = 0;
276 while (pSerBuf->cbTxRxMax + sizeof(uint32_t) <= sizeof(pSerBuf->abBuf))
277 {
278 pSerBuf->iCnt++;
279 *(uint32_t *)&pSerBuf->abBuf[pSerBuf->offBuf + offData] = pSerBuf->iCnt;
280 pSerBuf->cbTxRxMax += sizeof(uint32_t);
281 offData += sizeof(uint32_t);
282 }
283}
284
285
286/**
287 * Sends a new batch of data from the TX buffer preapring new data if required.
288 *
289 * @returns IPRT status code.
290 * @param hSerialPort The serial port handle to send the data to.
291 * @param pSerBuf The TX buffer pointer.
292 */
293static int serialTestTxBufSend(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
294{
295 int rc = VINF_SUCCESS;
296
297 if (pSerBuf->cbTxRxLeft)
298 {
299 if (!pSerBuf->cbTxRxMax)
300 serialTestTxBufPrepare(pSerBuf);
301
302 size_t cbToWrite = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
303 size_t cbWritten = 0;
304 rc = RTSerialPortWriteNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToWrite, &cbWritten);
305 if (RT_SUCCESS(rc))
306 {
307 pSerBuf->cbTxRxMax -= cbWritten;
308 pSerBuf->offBuf += cbWritten;
309 pSerBuf->cbTxRxLeft -= cbWritten;
310 }
311 }
312
313 return rc;
314}
315
316
317/**
318 * Receives dat from the given serial port into the supplied RX buffer and does some validity checking.
319 *
320 * @returns IPRT status code.
321 * @param hSerialPort The serial port handle to receive data from.
322 * @param pSerBuf The RX buffer pointer.
323 */
324static int serialTestRxBufRecv(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
325{
326 int rc = VINF_SUCCESS;
327
328 if (pSerBuf->cbTxRxLeft)
329 {
330 size_t cbToRead = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
331 size_t cbRead = 0;
332 rc = RTSerialPortReadNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToRead, &cbRead);
333 if (RT_SUCCESS(rc))
334 {
335 pSerBuf->offBuf += cbRead;
336 pSerBuf->cbTxRxMax -= cbRead;
337 pSerBuf->cbTxRxLeft -= cbRead;
338 }
339 }
340
341 return rc;
342}
343
344
345/**
346 * Verifies the data in the given RX buffer for correct transmission.
347 *
348 * @returns Flag whether verification failed.
349 * @param hTest The test handle to report errors to.
350 * @param pSerBuf The RX buffer pointer.
351 * @param iCntTx The current TX counter value the RX buffer should never get ahead of,
352 * UINT32_MAX disables this check.
353 */
354static bool serialTestRxBufVerify(RTTEST hTest, PSERIALTESTTXRXBUFCNT pSerBuf, uint32_t iCntTx)
355{
356 uint32_t offRx = 0;
357 bool fFailed = false;
358
359 while (offRx + sizeof(uint32_t) < pSerBuf->offBuf)
360 {
361 uint32_t u32Val = *(uint32_t *)&pSerBuf->abBuf[offRx];
362 offRx += sizeof(uint32_t);
363
364 if (RT_UNLIKELY(u32Val != ++pSerBuf->iCnt))
365 {
366 fFailed = true;
367 if (g_cVerbosity > 0)
368 serialTestFailed(hTest, "Data corruption/loss detected, expected counter value %u got %u\n",
369 pSerBuf->iCnt, u32Val);
370 }
371 }
372
373 if (RT_UNLIKELY(pSerBuf->iCnt > iCntTx))
374 {
375 fFailed = true;
376 serialTestFailed(hTest, "Overtook the send buffer, expected maximum counter value %u got %u\n",
377 iCntTx, pSerBuf->iCnt);
378 }
379
380 /* Remove processed data from the buffer and move the rest to the front. */
381 if (offRx)
382 {
383 memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[offRx], sizeof(pSerBuf->abBuf) - offRx);
384 pSerBuf->offBuf -= offRx;
385 pSerBuf->cbTxRxMax += offRx;
386 }
387
388 return fFailed;
389}
390
391
392DECLINLINE(bool) serialTestRndTrue(void)
393{
394 return RTRandAdvU32Ex(g_hRand, 0, 1) == 1;
395}
396
397
398/**
399 * Runs a simple read/write test.
400 *
401 * @returns IPRT status code.
402 * @param pSerialTest The serial test configuration.
403 */
404static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest)
405{
406 uint64_t tsStart = RTTimeNanoTS();
407 bool fFailed = false;
408 SERIALTESTTXRXBUFCNT SerBufTx;
409 SERIALTESTTXRXBUFCNT SerBufRx;
410
411 serialTestTxBufInit(&SerBufTx, g_cbTx);
412 serialTestRxBufInit(&SerBufRx, g_cbTx);
413
414 int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
415 while ( RT_SUCCESS(rc)
416 && ( SerBufTx.cbTxRxLeft
417 || SerBufRx.cbTxRxLeft))
418 {
419 uint32_t fEvts = 0;
420 uint32_t fEvtsQuery = 0;
421 if (SerBufTx.cbTxRxLeft)
422 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX;
423 if (SerBufRx.cbTxRxLeft)
424 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX;
425
426 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
427 if (RT_FAILURE(rc))
428 break;
429
430 if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
431 {
432 rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx);
433 if (RT_FAILURE(rc))
434 break;
435
436 bool fRes = serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, SerBufTx.iCnt);
437 if (fRes && !fFailed)
438 {
439 fFailed = true;
440 serialTestFailed(pSerialTest->hTest, "Data corruption/loss detected\n");
441 }
442 }
443 if ( RT_SUCCESS(rc)
444 && (fEvts & RTSERIALPORT_EVT_F_DATA_TX))
445 rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
446 }
447
448 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
449 size_t cNsPerByte = tsRuntime / g_cbTx;
450 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
451 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
452
453 return rc;
454}
455
456
457/**
458 * Runs a simple write test without doing any verification.
459 *
460 * @returns IPRT status code.
461 * @param pSerialTest The serial test configuration.
462 */
463static DECLCALLBACK(int) serialTestRunWrite(PSERIALTEST pSerialTest)
464{
465 uint64_t tsStart = RTTimeNanoTS();
466 SERIALTESTTXRXBUFCNT SerBufTx;
467
468 serialTestTxBufInit(&SerBufTx, g_cbTx);
469
470 int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
471 while ( RT_SUCCESS(rc)
472 && SerBufTx.cbTxRxLeft)
473 {
474 uint32_t fEvts = 0;
475
476 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, RTSERIALPORT_EVT_F_DATA_TX, &fEvts, RT_INDEFINITE_WAIT);
477 if (RT_FAILURE(rc))
478 break;
479
480 if (fEvts & RTSERIALPORT_EVT_F_DATA_TX)
481 rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
482 }
483
484 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
485 size_t cNsPerByte = tsRuntime / g_cbTx;
486 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
487 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
488
489 return rc;
490}
491
492
493/**
494 * Runs the counterpart to the write test, reading and verifying data.
495 *
496 * @returns IPRT status code.
497 * @param pSerialTest The serial test configuration.
498 */
499static DECLCALLBACK(int) serialTestRunReadVerify(PSERIALTEST pSerialTest)
500{
501 int rc = VINF_SUCCESS;
502 uint64_t tsStart = RTTimeNanoTS();
503 bool fFailed = false;
504 SERIALTESTTXRXBUFCNT SerBufRx;
505
506 serialTestRxBufInit(&SerBufRx, g_cbTx);
507
508 while ( RT_SUCCESS(rc)
509 && SerBufRx.cbTxRxLeft)
510 {
511 uint32_t fEvts = 0;
512 uint32_t fEvtsQuery = RTSERIALPORT_EVT_F_DATA_RX;
513
514 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
515 if (RT_FAILURE(rc))
516 break;
517
518 if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
519 {
520 rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx);
521 if (RT_FAILURE(rc))
522 break;
523
524 bool fRes = serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, UINT32_MAX);
525 if (fRes && !fFailed)
526 {
527 fFailed = true;
528 serialTestFailed(pSerialTest->hTest, "Data corruption/loss detected\n");
529 }
530 }
531 }
532
533 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
534 size_t cNsPerByte = tsRuntime / g_cbTx;
535 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
536 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
537
538 return rc;
539}
540
541
542/**
543 * Tests setting status lines and getting notified about status line changes.
544 *
545 * @returns IPRT status code.
546 * @param pSerialTest The serial test configuration.
547 */
548static DECLCALLBACK(int) serialTestRunStsLines(PSERIALTEST pSerialTest)
549{
550 int rc = VINF_SUCCESS;
551
552 if (g_enmMode == SERIALTESTMODE_LOOPBACK)
553 {
554 uint32_t fStsLinesQueriedOld = 0;
555
556 rc = RTSerialPortChgStatusLines(pSerialTest->hSerialPort,
557 RTSERIALPORT_CHG_STS_LINES_F_RTS | RTSERIALPORT_CHG_STS_LINES_F_DTR,
558 0);
559 if (RT_SUCCESS(rc))
560 {
561 rc = RTSerialPortQueryStatusLines(pSerialTest->hSerialPort, &fStsLinesQueriedOld);
562 if (RT_SUCCESS(rc))
563 {
564 /* Everything should be clear at this stage. */
565 if (!fStsLinesQueriedOld)
566 {
567 uint32_t fStsLinesSetOld = 0;
568
569 for (uint32_t i = 0; i < SERIALTEST_STS_LINE_TOGGLE_COUNT; i++)
570 {
571 uint32_t fStsLinesSet = 0;
572 uint32_t fStsLinesClear = 0;
573
574 /* Change RTS? */
575 if (serialTestRndTrue())
576 {
577 /* Clear, if set previously otherwise set it. */
578 if (fStsLinesSetOld & RTSERIALPORT_CHG_STS_LINES_F_RTS)
579 fStsLinesClear |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
580 else
581 fStsLinesSet |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
582 }
583
584 /* Change DTR? */
585 if (serialTestRndTrue())
586 {
587 /* Clear, if set previously otherwise set it. */
588 if (fStsLinesSetOld & RTSERIALPORT_CHG_STS_LINES_F_DTR)
589 fStsLinesClear |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
590 else
591 fStsLinesSet |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
592 }
593
594 rc = RTSerialPortChgStatusLines(pSerialTest->hSerialPort, fStsLinesClear, fStsLinesSet);
595 if (RT_FAILURE(rc))
596 {
597 serialTestFailed(g_hTest, "Changing status lines failed with %Rrc on iteration %u (fSet=%#x fClear=%#x)\n",
598 rc, i, fStsLinesSet, fStsLinesClear);
599 break;
600 }
601
602 /* Wait for status line monitor event. */
603 uint32_t fEvtsRecv = 0;
604 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED,
605 &fEvtsRecv, RT_MS_1SEC);
606 if ( RT_FAILURE(rc)
607 && (rc != VERR_TIMEOUT && !fStsLinesSet && !fStsLinesClear))
608 {
609 serialTestFailed(g_hTest, "Waiting for status line change failed with %Rrc on iteration %u\n",
610 rc, i);
611 break;
612 }
613
614 uint32_t fStsLinesQueried = 0;
615 rc = RTSerialPortQueryStatusLines(pSerialTest->hSerialPort, &fStsLinesQueried);
616 if (RT_FAILURE(rc))
617 {
618 serialTestFailed(g_hTest, "Querying status lines failed with %Rrc on iteration %u\n",
619 rc, i);
620 break;
621 }
622
623 /* Compare expected and real result. */
624 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
625 != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_DSR))
626 {
627 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
628 && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR))
629 serialTestFailed(g_hTest, "DSR line got set when it shouldn't be on iteration %u\n", i);
630 else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
631 && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
632 serialTestFailed(g_hTest, "DSR line got cleared when it shouldn't be on iteration %u\n", i);
633 }
634 else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
635 || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
636 serialTestFailed(g_hTest, "DSR line didn't change when it should have on iteration %u\n", i);
637
638 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
639 != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_DCD))
640 {
641 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
642 && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR))
643 serialTestFailed(g_hTest, "DCD line got set when it shouldn't be on iteration %u\n", i);
644 else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
645 && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
646 serialTestFailed(g_hTest, "DCD line got cleared when it shouldn't be on iteration %u\n", i);
647 }
648 else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
649 || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
650 serialTestFailed(g_hTest, "DCD line didn't change when it should have on iteration %u\n", i);
651
652 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
653 != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_CTS))
654 {
655 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
656 && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_RTS))
657 serialTestFailed(g_hTest, "CTS line got set when it shouldn't be on iteration %u\n", i);
658 else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
659 && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
660 serialTestFailed(g_hTest, "CTS line got cleared when it shouldn't be on iteration %u\n", i);
661 }
662 else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)
663 || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
664 serialTestFailed(g_hTest, "CTS line didn't change when it should have on iteration %u\n", i);
665
666 if (RTTestErrorCount(g_hTest) > 0)
667 break;
668
669 fStsLinesSetOld |= fStsLinesSet;
670 fStsLinesSetOld &= ~fStsLinesClear;
671 fStsLinesQueriedOld = fStsLinesQueried;
672 }
673 }
674 else
675 serialTestFailed(g_hTest, "Status lines active which should be clear (%#x, but expected %#x)\n",
676 fStsLinesQueriedOld, 0);
677 }
678 else
679 serialTestFailed(g_hTest, "Querying status lines failed with %Rrc\n", rc);
680 }
681 else
682 serialTestFailed(g_hTest, "Clearing status lines failed with %Rrc\n", rc);
683 }
684 else
685 rc = VERR_NOT_IMPLEMENTED;
686
687 return rc;
688}
689
690
691/**
692 * Runs a simple echo service (not a real test on its own).
693 *
694 * @returns IPRT status code.
695 * @param pSerialTest The serial test configuration.
696 */
697static DECLCALLBACK(int) serialTestRunEcho(PSERIALTEST pSerialTest)
698{
699 int rc = VINF_SUCCESS;
700 uint64_t tsStart = RTTimeNanoTS();
701 uint8_t abBuf[_1K];
702 size_t cbLeft = g_cbTx;
703 size_t cbInBuf = 0;
704
705 while ( RT_SUCCESS(rc)
706 && ( cbLeft
707 || cbInBuf))
708 {
709 uint32_t fEvts = 0;
710 uint32_t fEvtsQuery = 0;
711 if (cbInBuf)
712 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX;
713 if (cbLeft && cbInBuf < sizeof(abBuf))
714 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX;
715
716 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
717 if (RT_FAILURE(rc))
718 break;
719
720 if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
721 {
722 size_t cbThisRead = RT_MIN(cbLeft, sizeof(abBuf) - cbInBuf);
723 size_t cbRead = 0;
724 rc = RTSerialPortReadNB(pSerialTest->hSerialPort, &abBuf[cbInBuf], cbThisRead, &cbRead);
725 if (RT_SUCCESS(rc))
726 {
727 cbInBuf += cbRead;
728 cbLeft -= cbRead;
729 }
730 else if (RT_FAILURE(rc))
731 break;
732 }
733
734 if (fEvts & RTSERIALPORT_EVT_F_DATA_TX)
735 {
736 size_t cbWritten = 0;
737 rc = RTSerialPortWriteNB(pSerialTest->hSerialPort, &abBuf[0], cbInBuf, &cbWritten);
738 if (RT_SUCCESS(rc))
739 {
740 memmove(&abBuf[0], &abBuf[cbWritten], cbInBuf - cbWritten);
741 cbInBuf -= cbWritten;
742 }
743 }
744 }
745
746 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
747 size_t cNsPerByte = tsRuntime / g_cbTx;
748 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
749 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
750
751 return rc;
752}
753
754
755/**
756 * Returns an array of test descriptors get from the given string.
757 *
758 * @returns Pointer to the array of test descriptors.
759 * @param pszTests The string containing the tests separated with ':'.
760 */
761static PSERIALTESTDESC serialTestSelectFromCmdLine(const char *pszTests)
762{
763 size_t cTests = 1;
764
765 const char *pszNext = strchr(pszTests, ':');
766 while (pszNext)
767 {
768 pszNext++;
769 cTests++;
770 pszNext = strchr(pszNext, ':');
771 }
772
773 PSERIALTESTDESC paTests = (PSERIALTESTDESC)RTMemAllocZ((cTests + 1) * sizeof(SERIALTESTDESC));
774 if (RT_LIKELY(paTests))
775 {
776 uint32_t iTest = 0;
777
778 pszNext = strchr(pszTests, ':');
779 while (pszNext)
780 {
781 bool fFound = false;
782
783 pszNext++; /* Skip : character. */
784
785 for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
786 {
787 if (!RTStrNICmp(pszTests, g_aSerialTests[i].pszId, pszNext - pszTests - 1))
788 {
789 memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
790 fFound = true;
791 break;
792 }
793 }
794
795 if (RT_UNLIKELY(!fFound))
796 {
797 RTPrintf("Testcase \"%.*s\" not known\n", pszNext - pszTests - 1, pszTests);
798 RTMemFree(paTests);
799 return NULL;
800 }
801
802 pszTests = pszNext;
803 pszNext = strchr(pszTests, ':');
804 }
805
806 /* Fill last descriptor. */
807 bool fFound = false;
808 for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
809 {
810 if (!RTStrICmp(pszTests, g_aSerialTests[i].pszId))
811 {
812 memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
813 fFound = true;
814 break;
815 }
816 }
817
818 if (RT_UNLIKELY(!fFound))
819 {
820 RTPrintf("Testcase \"%s\" not known\n", pszTests);
821 RTMemFree(paTests);
822 paTests = NULL;
823 }
824 }
825 else
826 RTPrintf("Failed to allocate test descriptors for %u selected tests\n", cTests);
827
828 return paTests;
829}
830
831
832/**
833 * Shows tool usage text.
834 */
835static void serialTestUsage(PRTSTREAM pStrm)
836{
837 char szExec[RTPATH_MAX];
838 RTStrmPrintf(pStrm, "usage: %s [options]\n",
839 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
840 RTStrmPrintf(pStrm, "\n");
841 RTStrmPrintf(pStrm, "options: \n");
842
843
844 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
845 {
846 const char *pszHelp;
847 switch (g_aCmdOptions[i].iShort)
848 {
849 case 'h':
850 pszHelp = "Displays this help and exit";
851 break;
852 case 'd':
853 pszHelp = "Use the specified serial port device";
854 break;
855 case 'b':
856 pszHelp = "Use the given baudrate";
857 break;
858 case 'p':
859 pszHelp = "Use the given parity, valid modes are: none, even, odd, mark, space";
860 break;
861 case 'c':
862 pszHelp = "Use the given data bitcount, valid are: 5, 6, 7, 8";
863 break;
864 case 's':
865 pszHelp = "Use the given stop bitcount, valid are: 1, 1.5, 2";
866 break;
867 case 'm':
868 pszHelp = "Mode of the serial port, valid are: loopback, secondary, external";
869 break;
870 case 'l':
871 pszHelp = "Use the given serial port device as the secondary device";
872 break;
873 case 't':
874 pszHelp = "The tests to run separated by ':'";
875 break;
876 case 'x':
877 pszHelp = "Number of bytes to transmit during read/write tests";
878 break;
879 default:
880 pszHelp = "Option undocumented";
881 break;
882 }
883 char szOpt[256];
884 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
885 RTStrmPrintf(pStrm, " %-30s%s\n", szOpt, pszHelp);
886 }
887}
888
889
890int main(int argc, char *argv[])
891{
892 /*
893 * Init IPRT and globals.
894 */
895 int rc = RTTestInitAndCreate("SerialTest", &g_hTest);
896 if (rc)
897 return rc;
898
899 /*
900 * Default values.
901 */
902 const char *pszDevice = NULL;
903 const char *pszDeviceSecondary = NULL;
904 PSERIALTESTDESC paTests = NULL;
905
906 RTGETOPTUNION ValueUnion;
907 RTGETOPTSTATE GetState;
908 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
909 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
910 {
911 switch (rc)
912 {
913 case 'h':
914 serialTestUsage(g_pStdOut);
915 return RTEXITCODE_SUCCESS;
916 case 'v':
917 g_cVerbosity++;
918 break;
919 case 'd':
920 pszDevice = ValueUnion.psz;
921 break;
922 case 'l':
923 pszDeviceSecondary = ValueUnion.psz;
924 break;
925 case 'b':
926 g_SerialPortCfg.uBaudRate = ValueUnion.u32;
927 break;
928 case 'p':
929 if (!RTStrICmp(ValueUnion.psz, "none"))
930 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_NONE;
931 else if (!RTStrICmp(ValueUnion.psz, "even"))
932 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_EVEN;
933 else if (!RTStrICmp(ValueUnion.psz, "odd"))
934 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_ODD;
935 else if (!RTStrICmp(ValueUnion.psz, "mark"))
936 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_MARK;
937 else if (!RTStrICmp(ValueUnion.psz, "space"))
938 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_SPACE;
939 else
940 {
941 RTPrintf("Unknown parity \"%s\" given\n", ValueUnion.psz);
942 return RTEXITCODE_FAILURE;
943 }
944 break;
945 case 'c':
946 if (ValueUnion.u32 == 5)
947 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
948 else if (ValueUnion.u32 == 6)
949 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
950 else if (ValueUnion.u32 == 7)
951 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
952 else if (ValueUnion.u32 == 8)
953 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
954 else
955 {
956 RTPrintf("Unknown data bitcount \"%u\" given\n", ValueUnion.u32);
957 return RTEXITCODE_FAILURE;
958 }
959 break;
960 case 's':
961 if (!RTStrICmp(ValueUnion.psz, "1"))
962 g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
963 else if (!RTStrICmp(ValueUnion.psz, "1.5"))
964 g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
965 else if (!RTStrICmp(ValueUnion.psz, "2"))
966 g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
967 else
968 {
969 RTPrintf("Unknown stop bitcount \"%s\" given\n", ValueUnion.psz);
970 return RTEXITCODE_FAILURE;
971 }
972 break;
973 case 'm':
974 if (!RTStrICmp(ValueUnion.psz, "loopback"))
975 g_enmMode = SERIALTESTMODE_LOOPBACK;
976 else if (!RTStrICmp(ValueUnion.psz, "secondary"))
977 g_enmMode = SERIALTESTMODE_SECONDARY;
978 else if (!RTStrICmp(ValueUnion.psz, "external"))
979 g_enmMode = SERIALTESTMODE_EXTERNAL;
980 else
981 {
982 RTPrintf("Unknown serial test mode \"%s\" given\n", ValueUnion.psz);
983 return RTEXITCODE_FAILURE;
984 }
985 break;
986 case 't':
987 paTests = serialTestSelectFromCmdLine(ValueUnion.psz);
988 if (!paTests)
989 return RTEXITCODE_FAILURE;
990 break;
991 case 'x':
992 g_cbTx = ValueUnion.u32;
993 break;
994 case 'a':
995 g_fAbortOnError = true;
996 break;
997 default:
998 return RTGetOptPrintError(rc, &ValueUnion);
999 }
1000 }
1001
1002 if (g_enmMode == SERIALTESTMODE_SECONDARY && !pszDeviceSecondary)
1003 {
1004 RTPrintf("Mode set to secondary device but no secondary device given\n");
1005 return RTEXITCODE_FAILURE;
1006 }
1007
1008 if (!paTests)
1009 {
1010 /* Select all. */
1011 paTests = (PSERIALTESTDESC)RTMemAllocZ((RT_ELEMENTS(g_aSerialTests) + 1) * sizeof(SERIALTESTDESC));
1012 if (RT_UNLIKELY(!paTests))
1013 {
1014 RTPrintf("Failed to allocate memory for test descriptors\n");
1015 return RTEXITCODE_FAILURE;
1016 }
1017 memcpy(paTests, &g_aSerialTests[0], RT_ELEMENTS(g_aSerialTests) * sizeof(SERIALTESTDESC));
1018 }
1019
1020 rc = RTRandAdvCreateParkMiller(&g_hRand);
1021 if (RT_FAILURE(rc))
1022 {
1023 RTPrintf("Failed to create random number generator: %Rrc\n", rc);
1024 return RTEXITCODE_FAILURE;
1025 }
1026
1027 rc = RTRandAdvSeed(g_hRand, UINT64_C(0x123456789abcdef));
1028 AssertRC(rc);
1029
1030 /*
1031 * Start testing.
1032 */
1033 RTTestBanner(g_hTest);
1034
1035 if (pszDevice)
1036 {
1037 uint32_t fFlags = RTSERIALPORT_OPEN_F_READ
1038 | RTSERIALPORT_OPEN_F_WRITE
1039 | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING;
1040
1041 RTTestSub(g_hTest, "Opening device");
1042 rc = RTSerialPortOpen(&g_hSerialPort, pszDevice, fFlags);
1043 if (RT_SUCCESS(rc))
1044 {
1045 if (g_enmMode == SERIALTESTMODE_SECONDARY)
1046 {
1047 RTTestSub(g_hTest, "Opening secondary device");
1048 rc = RTSerialPortOpen(&g_hSerialPortSecondary, pszDeviceSecondary, fFlags);
1049 if (RT_FAILURE(rc))
1050 serialTestFailed(g_hTest, "Opening secondary device \"%s\" failed with %Rrc\n", pszDevice, rc);
1051 }
1052
1053 if (RT_SUCCESS(rc))
1054 {
1055 RTTestSub(g_hTest, "Setting serial port configuration");
1056
1057 rc = RTSerialPortCfgSet(g_hSerialPort, &g_SerialPortCfg ,NULL);
1058 if (RT_SUCCESS(rc))
1059 {
1060 if (g_enmMode == SERIALTESTMODE_SECONDARY)
1061 {
1062 RTTestSub(g_hTest, "Setting serial port configuration for secondary device");
1063 rc = RTSerialPortCfgSet(g_hSerialPortSecondary, &g_SerialPortCfg, NULL);
1064 if (RT_FAILURE(rc))
1065 serialTestFailed(g_hTest, "Setting configuration of secondary device \"%s\" failed with %Rrc\n", pszDevice, rc);
1066 }
1067
1068 if (RT_SUCCESS(rc))
1069 {
1070 SERIALTEST Test;
1071 PSERIALTESTDESC pTest = &paTests[0];
1072
1073 Test.hTest = g_hTest;
1074 Test.hSerialPort = g_hSerialPort;
1075 Test.pSerialCfg = &g_SerialPortCfg;
1076
1077 while (pTest->pszId)
1078 {
1079 RTTestSub(g_hTest, pTest->pszDesc);
1080 rc = pTest->pfnRun(&Test);
1081 if ( RT_FAILURE(rc)
1082 || RTTestErrorCount(g_hTest) > 0)
1083 serialTestFailed(g_hTest, "Running test \"%s\" failed (%Rrc, cErrors=%u)\n",
1084 pTest->pszId, rc, RTTestErrorCount(g_hTest));
1085
1086 RTTestSubDone(g_hTest);
1087 pTest++;
1088 }
1089 }
1090 }
1091 else
1092 serialTestFailed(g_hTest, "Setting configuration of device \"%s\" failed with %Rrc\n", pszDevice, rc);
1093
1094 RTSerialPortClose(g_hSerialPort);
1095 }
1096 }
1097 else
1098 serialTestFailed(g_hTest, "Opening device \"%s\" failed with %Rrc\n", pszDevice, rc);
1099 }
1100 else
1101 serialTestFailed(g_hTest, "No device given on command line\n");
1102
1103 RTRandAdvDestroy(g_hRand);
1104 RTMemFree(paTests);
1105 RTEXITCODE rcExit = RTTestSummaryAndDestroy(g_hTest);
1106 return rcExit;
1107}
1108
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