VirtualBox

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

Last change on this file since 100144 was 99739, checked in by vboxsync, 19 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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