VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.0 KB
Line 
1/* $Id: SerialTest.cpp 98103 2023-01-17 14:15:46Z 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 * @returns nothing.
239 * @param pSerBuf The serial buffer to initialize.
240 * @param cbTx Maximum number of bytes to transmit.
241 */
242static void serialTestTxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbTx)
243{
244 pSerBuf->iCnt = 0;
245 pSerBuf->offBuf = 0;
246 pSerBuf->cbTxRxMax = 0;
247 pSerBuf->cbTxRxLeft = cbTx;
248 RT_ZERO(pSerBuf->abBuf);
249}
250
251
252/**
253 * Initializes a RX buffer.
254 *
255 * @returns nothing.
256 * @param pSerBuf The serial buffer to initialize.
257 * @param cbRx Maximum number of bytes to receive.
258 */
259static void serialTestRxBufInit(PSERIALTESTTXRXBUFCNT pSerBuf, size_t cbRx)
260{
261 pSerBuf->iCnt = 0;
262 pSerBuf->offBuf = 0;
263 pSerBuf->cbTxRxMax = sizeof(pSerBuf->abBuf);
264 pSerBuf->cbTxRxLeft = cbRx;
265 RT_ZERO(pSerBuf->abBuf);
266}
267
268
269/**
270 * Prepares the given TX buffer with data for sending it out.
271 *
272 * @returns nothing.
273 * @param pSerBuf The TX buffer pointer.
274 */
275static void serialTestTxBufPrepare(PSERIALTESTTXRXBUFCNT pSerBuf)
276{
277 /* Move the data to the front to make room at the end to fill. */
278 if (pSerBuf->offBuf)
279 {
280 memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[pSerBuf->offBuf], sizeof(pSerBuf->abBuf) - pSerBuf->offBuf);
281 pSerBuf->offBuf = 0;
282 }
283
284 /* Fill up with data. */
285 uint32_t offData = 0;
286 while (pSerBuf->cbTxRxMax + sizeof(uint32_t) <= sizeof(pSerBuf->abBuf))
287 {
288 pSerBuf->iCnt++;
289 *(uint32_t *)&pSerBuf->abBuf[pSerBuf->offBuf + offData] = pSerBuf->iCnt;
290 pSerBuf->cbTxRxMax += sizeof(uint32_t);
291 offData += sizeof(uint32_t);
292 }
293}
294
295
296/**
297 * Sends a new batch of data from the TX buffer preapring new data if required.
298 *
299 * @returns IPRT status code.
300 * @param hSerialPort The serial port handle to send the data to.
301 * @param pSerBuf The TX buffer pointer.
302 */
303static int serialTestTxBufSend(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
304{
305 int rc = VINF_SUCCESS;
306
307 if (pSerBuf->cbTxRxLeft)
308 {
309 if (!pSerBuf->cbTxRxMax)
310 serialTestTxBufPrepare(pSerBuf);
311
312 size_t cbToWrite = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
313 size_t cbWritten = 0;
314 rc = RTSerialPortWriteNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToWrite, &cbWritten);
315 if (RT_SUCCESS(rc))
316 {
317 pSerBuf->cbTxRxMax -= cbWritten;
318 pSerBuf->offBuf += cbWritten;
319 pSerBuf->cbTxRxLeft -= cbWritten;
320 }
321 }
322
323 return rc;
324}
325
326
327/**
328 * Receives dat from the given serial port into the supplied RX buffer and does some validity checking.
329 *
330 * @returns IPRT status code.
331 * @param hSerialPort The serial port handle to receive data from.
332 * @param pSerBuf The RX buffer pointer.
333 */
334static int serialTestRxBufRecv(RTSERIALPORT hSerialPort, PSERIALTESTTXRXBUFCNT pSerBuf)
335{
336 int rc = VINF_SUCCESS;
337
338 if (pSerBuf->cbTxRxLeft)
339 {
340 size_t cbToRead = RT_MIN(pSerBuf->cbTxRxMax, pSerBuf->cbTxRxLeft);
341 size_t cbRead = 0;
342 rc = RTSerialPortReadNB(hSerialPort, &pSerBuf->abBuf[pSerBuf->offBuf], cbToRead, &cbRead);
343 if (RT_SUCCESS(rc))
344 {
345 pSerBuf->offBuf += cbRead;
346 pSerBuf->cbTxRxMax -= cbRead;
347 pSerBuf->cbTxRxLeft -= cbRead;
348 }
349 }
350
351 return rc;
352}
353
354
355/**
356 * Verifies the data in the given RX buffer for correct transmission.
357 *
358 * @returns Flag whether verification failed.
359 * @param hTest The test handle to report errors to.
360 * @param pSerBuf The RX buffer pointer.
361 * @param iCntTx The current TX counter value the RX buffer should never get ahead of,
362 * UINT32_MAX disables this check.
363 */
364static bool serialTestRxBufVerify(RTTEST hTest, PSERIALTESTTXRXBUFCNT pSerBuf, uint32_t iCntTx)
365{
366 uint32_t offRx = 0;
367 bool fFailed = false;
368
369 while (offRx + sizeof(uint32_t) < pSerBuf->offBuf)
370 {
371 uint32_t u32Val = *(uint32_t *)&pSerBuf->abBuf[offRx];
372 offRx += sizeof(uint32_t);
373
374 if (RT_UNLIKELY(u32Val != ++pSerBuf->iCnt))
375 {
376 fFailed = true;
377 if (g_cVerbosity > 0)
378 serialTestFailed(hTest, "Data corruption/loss detected, expected counter value %u got %u\n",
379 pSerBuf->iCnt, u32Val);
380 }
381 }
382
383 if (RT_UNLIKELY(pSerBuf->iCnt > iCntTx))
384 {
385 fFailed = true;
386 serialTestFailed(hTest, "Overtook the send buffer, expected maximum counter value %u got %u\n",
387 iCntTx, pSerBuf->iCnt);
388 }
389
390 /* Remove processed data from the buffer and move the rest to the front. */
391 if (offRx)
392 {
393 memmove(&pSerBuf->abBuf[0], &pSerBuf->abBuf[offRx], sizeof(pSerBuf->abBuf) - offRx);
394 pSerBuf->offBuf -= offRx;
395 pSerBuf->cbTxRxMax += offRx;
396 }
397
398 return fFailed;
399}
400
401
402DECLINLINE(bool) serialTestRndTrue(void)
403{
404 return RTRandAdvU32Ex(g_hRand, 0, 1) == 1;
405}
406
407
408/**
409 * Runs a simple read/write test.
410 *
411 * @returns IPRT status code.
412 * @param pSerialTest The serial test configuration.
413 */
414static DECLCALLBACK(int) serialTestRunReadWrite(PSERIALTEST pSerialTest)
415{
416 uint64_t tsStart = RTTimeNanoTS();
417 bool fFailed = false;
418 SERIALTESTTXRXBUFCNT SerBufTx;
419 SERIALTESTTXRXBUFCNT SerBufRx;
420
421 serialTestTxBufInit(&SerBufTx, g_cbTx);
422 serialTestRxBufInit(&SerBufRx, g_cbTx);
423
424 int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
425 while ( RT_SUCCESS(rc)
426 && ( SerBufTx.cbTxRxLeft
427 || SerBufRx.cbTxRxLeft))
428 {
429 uint32_t fEvts = 0;
430 uint32_t fEvtsQuery = 0;
431 if (SerBufTx.cbTxRxLeft)
432 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX;
433 if (SerBufRx.cbTxRxLeft)
434 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX;
435
436 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
437 if (RT_FAILURE(rc))
438 break;
439
440 if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
441 {
442 rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx);
443 if (RT_FAILURE(rc))
444 break;
445
446 bool fRes = serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, SerBufTx.iCnt);
447 if (fRes && !fFailed)
448 {
449 fFailed = true;
450 serialTestFailed(pSerialTest->hTest, "Data corruption/loss detected\n");
451 }
452 }
453 if ( RT_SUCCESS(rc)
454 && (fEvts & RTSERIALPORT_EVT_F_DATA_TX))
455 rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
456 }
457
458 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
459 size_t cNsPerByte = tsRuntime / g_cbTx;
460 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
461 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
462
463 return rc;
464}
465
466
467/**
468 * Runs a simple write test without doing any verification.
469 *
470 * @returns IPRT status code.
471 * @param pSerialTest The serial test configuration.
472 */
473static DECLCALLBACK(int) serialTestRunWrite(PSERIALTEST pSerialTest)
474{
475 uint64_t tsStart = RTTimeNanoTS();
476 SERIALTESTTXRXBUFCNT SerBufTx;
477
478 serialTestTxBufInit(&SerBufTx, g_cbTx);
479
480 int rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
481 while ( RT_SUCCESS(rc)
482 && SerBufTx.cbTxRxLeft)
483 {
484 uint32_t fEvts = 0;
485
486 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, RTSERIALPORT_EVT_F_DATA_TX, &fEvts, RT_INDEFINITE_WAIT);
487 if (RT_FAILURE(rc))
488 break;
489
490 if (fEvts & RTSERIALPORT_EVT_F_DATA_TX)
491 rc = serialTestTxBufSend(pSerialTest->hSerialPort, &SerBufTx);
492 }
493
494 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
495 size_t cNsPerByte = tsRuntime / g_cbTx;
496 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
497 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
498
499 return rc;
500}
501
502
503/**
504 * Runs the counterpart to the write test, reading and verifying data.
505 *
506 * @returns IPRT status code.
507 * @param pSerialTest The serial test configuration.
508 */
509static DECLCALLBACK(int) serialTestRunReadVerify(PSERIALTEST pSerialTest)
510{
511 int rc = VINF_SUCCESS;
512 uint64_t tsStart = RTTimeNanoTS();
513 bool fFailed = false;
514 SERIALTESTTXRXBUFCNT SerBufRx;
515
516 serialTestRxBufInit(&SerBufRx, g_cbTx);
517
518 while ( RT_SUCCESS(rc)
519 && SerBufRx.cbTxRxLeft)
520 {
521 uint32_t fEvts = 0;
522 uint32_t fEvtsQuery = RTSERIALPORT_EVT_F_DATA_RX;
523
524 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
525 if (RT_FAILURE(rc))
526 break;
527
528 if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
529 {
530 rc = serialTestRxBufRecv(pSerialTest->hSerialPort, &SerBufRx);
531 if (RT_FAILURE(rc))
532 break;
533
534 bool fRes = serialTestRxBufVerify(pSerialTest->hTest, &SerBufRx, UINT32_MAX);
535 if (fRes && !fFailed)
536 {
537 fFailed = true;
538 serialTestFailed(pSerialTest->hTest, "Data corruption/loss detected\n");
539 }
540 }
541 }
542
543 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
544 size_t cNsPerByte = tsRuntime / g_cbTx;
545 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
546 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
547
548 return rc;
549}
550
551
552/**
553 * Tests setting status lines and getting notified about status line changes.
554 *
555 * @returns IPRT status code.
556 * @param pSerialTest The serial test configuration.
557 */
558static DECLCALLBACK(int) serialTestRunStsLines(PSERIALTEST pSerialTest)
559{
560 int rc = VINF_SUCCESS;
561
562 if (g_enmMode == SERIALTESTMODE_LOOPBACK)
563 {
564 uint32_t fStsLinesQueriedOld = 0;
565
566 rc = RTSerialPortChgStatusLines(pSerialTest->hSerialPort,
567 RTSERIALPORT_CHG_STS_LINES_F_RTS | RTSERIALPORT_CHG_STS_LINES_F_DTR,
568 0);
569 if (RT_SUCCESS(rc))
570 {
571 rc = RTSerialPortQueryStatusLines(pSerialTest->hSerialPort, &fStsLinesQueriedOld);
572 if (RT_SUCCESS(rc))
573 {
574 /* Everything should be clear at this stage. */
575 if (!fStsLinesQueriedOld)
576 {
577 uint32_t fStsLinesSetOld = 0;
578
579 for (uint32_t i = 0; i < SERIALTEST_STS_LINE_TOGGLE_COUNT; i++)
580 {
581 uint32_t fStsLinesSet = 0;
582 uint32_t fStsLinesClear = 0;
583
584 /* Change RTS? */
585 if (serialTestRndTrue())
586 {
587 /* Clear, if set previously otherwise set it. */
588 if (fStsLinesSetOld & RTSERIALPORT_CHG_STS_LINES_F_RTS)
589 fStsLinesClear |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
590 else
591 fStsLinesSet |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
592 }
593
594 /* Change DTR? */
595 if (serialTestRndTrue())
596 {
597 /* Clear, if set previously otherwise set it. */
598 if (fStsLinesSetOld & RTSERIALPORT_CHG_STS_LINES_F_DTR)
599 fStsLinesClear |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
600 else
601 fStsLinesSet |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
602 }
603
604 rc = RTSerialPortChgStatusLines(pSerialTest->hSerialPort, fStsLinesClear, fStsLinesSet);
605 if (RT_FAILURE(rc))
606 {
607 serialTestFailed(g_hTest, "Changing status lines failed with %Rrc on iteration %u (fSet=%#x fClear=%#x)\n",
608 rc, i, fStsLinesSet, fStsLinesClear);
609 break;
610 }
611
612 /* Wait for status line monitor event. */
613 uint32_t fEvtsRecv = 0;
614 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED,
615 &fEvtsRecv, RT_MS_1SEC);
616 if ( RT_FAILURE(rc)
617 && (rc != VERR_TIMEOUT && !fStsLinesSet && !fStsLinesClear))
618 {
619 serialTestFailed(g_hTest, "Waiting for status line change failed with %Rrc on iteration %u\n",
620 rc, i);
621 break;
622 }
623
624 uint32_t fStsLinesQueried = 0;
625 rc = RTSerialPortQueryStatusLines(pSerialTest->hSerialPort, &fStsLinesQueried);
626 if (RT_FAILURE(rc))
627 {
628 serialTestFailed(g_hTest, "Querying status lines failed with %Rrc on iteration %u\n",
629 rc, i);
630 break;
631 }
632
633 /* Compare expected and real result. */
634 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
635 != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_DSR))
636 {
637 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
638 && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR))
639 serialTestFailed(g_hTest, "DSR line got set when it shouldn't be on iteration %u\n", i);
640 else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_DSR)
641 && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
642 serialTestFailed(g_hTest, "DSR line got cleared when it shouldn't be on iteration %u\n", i);
643 }
644 else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
645 || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
646 serialTestFailed(g_hTest, "DSR line didn't change when it should have on iteration %u\n", i);
647
648 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
649 != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_DCD))
650 {
651 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
652 && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR))
653 serialTestFailed(g_hTest, "DCD line got set when it shouldn't be on iteration %u\n", i);
654 else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_DCD)
655 && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
656 serialTestFailed(g_hTest, "DCD line got cleared when it shouldn't be on iteration %u\n", i);
657 }
658 else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
659 || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
660 serialTestFailed(g_hTest, "DCD line didn't change when it should have on iteration %u\n", i);
661
662 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
663 != (fStsLinesQueriedOld & RTSERIALPORT_STS_LINE_CTS))
664 {
665 if ( (fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
666 && !(fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_RTS))
667 serialTestFailed(g_hTest, "CTS line got set when it shouldn't be on iteration %u\n", i);
668 else if ( !(fStsLinesQueried & RTSERIALPORT_STS_LINE_CTS)
669 && !(fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
670 serialTestFailed(g_hTest, "CTS line got cleared when it shouldn't be on iteration %u\n", i);
671 }
672 else if ( (fStsLinesSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)
673 || (fStsLinesClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
674 serialTestFailed(g_hTest, "CTS line didn't change when it should have on iteration %u\n", i);
675
676 if (RTTestErrorCount(g_hTest) > 0)
677 break;
678
679 fStsLinesSetOld |= fStsLinesSet;
680 fStsLinesSetOld &= ~fStsLinesClear;
681 fStsLinesQueriedOld = fStsLinesQueried;
682 }
683 }
684 else
685 serialTestFailed(g_hTest, "Status lines active which should be clear (%#x, but expected %#x)\n",
686 fStsLinesQueriedOld, 0);
687 }
688 else
689 serialTestFailed(g_hTest, "Querying status lines failed with %Rrc\n", rc);
690 }
691 else
692 serialTestFailed(g_hTest, "Clearing status lines failed with %Rrc\n", rc);
693 }
694 else
695 rc = VERR_NOT_IMPLEMENTED;
696
697 return rc;
698}
699
700
701/**
702 * Runs a simple echo service (not a real test on its own).
703 *
704 * @returns IPRT status code.
705 * @param pSerialTest The serial test configuration.
706 */
707static DECLCALLBACK(int) serialTestRunEcho(PSERIALTEST pSerialTest)
708{
709 int rc = VINF_SUCCESS;
710 uint64_t tsStart = RTTimeNanoTS();
711 uint8_t abBuf[_1K];
712 size_t cbLeft = g_cbTx;
713 size_t cbInBuf = 0;
714
715 while ( RT_SUCCESS(rc)
716 && ( cbLeft
717 || cbInBuf))
718 {
719 uint32_t fEvts = 0;
720 uint32_t fEvtsQuery = 0;
721 if (cbInBuf)
722 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_TX;
723 if (cbLeft && cbInBuf < sizeof(abBuf))
724 fEvtsQuery |= RTSERIALPORT_EVT_F_DATA_RX;
725
726 rc = RTSerialPortEvtPoll(pSerialTest->hSerialPort, fEvtsQuery, &fEvts, RT_INDEFINITE_WAIT);
727 if (RT_FAILURE(rc))
728 break;
729
730 if (fEvts & RTSERIALPORT_EVT_F_DATA_RX)
731 {
732 size_t cbThisRead = RT_MIN(cbLeft, sizeof(abBuf) - cbInBuf);
733 size_t cbRead = 0;
734 rc = RTSerialPortReadNB(pSerialTest->hSerialPort, &abBuf[cbInBuf], cbThisRead, &cbRead);
735 if (RT_SUCCESS(rc))
736 {
737 cbInBuf += cbRead;
738 cbLeft -= cbRead;
739 }
740 else if (RT_FAILURE(rc))
741 break;
742 }
743
744 if (fEvts & RTSERIALPORT_EVT_F_DATA_TX)
745 {
746 size_t cbWritten = 0;
747 rc = RTSerialPortWriteNB(pSerialTest->hSerialPort, &abBuf[0], cbInBuf, &cbWritten);
748 if (RT_SUCCESS(rc))
749 {
750 memmove(&abBuf[0], &abBuf[cbWritten], cbInBuf - cbWritten);
751 cbInBuf -= cbWritten;
752 }
753 }
754 }
755
756 uint64_t tsRuntime = RTTimeNanoTS() - tsStart;
757 size_t cNsPerByte = tsRuntime / g_cbTx;
758 uint64_t cbBytesPerSec = RT_NS_1SEC / cNsPerByte;
759 RTTestValue(pSerialTest->hTest, "Throughput", cbBytesPerSec, RTTESTUNIT_BYTES_PER_SEC);
760
761 return rc;
762}
763
764
765/**
766 * Returns an array of test descriptors get from the given string.
767 *
768 * @returns Pointer to the array of test descriptors.
769 * @param pszTests The string containing the tests separated with ':'.
770 */
771static PSERIALTESTDESC serialTestSelectFromCmdLine(const char *pszTests)
772{
773 size_t cTests = 1;
774
775 const char *pszNext = strchr(pszTests, ':');
776 while (pszNext)
777 {
778 pszNext++;
779 cTests++;
780 pszNext = strchr(pszNext, ':');
781 }
782
783 PSERIALTESTDESC paTests = (PSERIALTESTDESC)RTMemAllocZ((cTests + 1) * sizeof(SERIALTESTDESC));
784 if (RT_LIKELY(paTests))
785 {
786 uint32_t iTest = 0;
787
788 pszNext = strchr(pszTests, ':');
789 while (pszNext)
790 {
791 bool fFound = false;
792
793 pszNext++; /* Skip : character. */
794
795 for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
796 {
797 if (!RTStrNICmp(pszTests, g_aSerialTests[i].pszId, pszNext - pszTests - 1))
798 {
799 memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
800 fFound = true;
801 break;
802 }
803 }
804
805 if (RT_UNLIKELY(!fFound))
806 {
807 RTPrintf("Testcase \"%.*s\" not known\n", pszNext - pszTests - 1, pszTests);
808 RTMemFree(paTests);
809 return NULL;
810 }
811
812 pszTests = pszNext;
813 pszNext = strchr(pszTests, ':');
814 }
815
816 /* Fill last descriptor. */
817 bool fFound = false;
818 for (unsigned i = 0; i < RT_ELEMENTS(g_aSerialTests); i++)
819 {
820 if (!RTStrICmp(pszTests, g_aSerialTests[i].pszId))
821 {
822 memcpy(&paTests[iTest], &g_aSerialTests[i], sizeof(SERIALTESTDESC));
823 fFound = true;
824 break;
825 }
826 }
827
828 if (RT_UNLIKELY(!fFound))
829 {
830 RTPrintf("Testcase \"%s\" not known\n", pszTests);
831 RTMemFree(paTests);
832 paTests = NULL;
833 }
834 }
835 else
836 RTPrintf("Failed to allocate test descriptors for %u selected tests\n", cTests);
837
838 return paTests;
839}
840
841
842/**
843 * Shows tool usage text.
844 */
845static void serialTestUsage(PRTSTREAM pStrm)
846{
847 char szExec[RTPATH_MAX];
848 RTStrmPrintf(pStrm, "usage: %s [options]\n",
849 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
850 RTStrmPrintf(pStrm, "\n");
851 RTStrmPrintf(pStrm, "options: \n");
852
853
854 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
855 {
856 const char *pszHelp;
857 switch (g_aCmdOptions[i].iShort)
858 {
859 case 'h':
860 pszHelp = "Displays this help and exit";
861 break;
862 case 'd':
863 pszHelp = "Use the specified serial port device";
864 break;
865 case 'b':
866 pszHelp = "Use the given baudrate";
867 break;
868 case 'p':
869 pszHelp = "Use the given parity, valid modes are: none, even, odd, mark, space";
870 break;
871 case 'c':
872 pszHelp = "Use the given data bitcount, valid are: 5, 6, 7, 8";
873 break;
874 case 's':
875 pszHelp = "Use the given stop bitcount, valid are: 1, 1.5, 2";
876 break;
877 case 'm':
878 pszHelp = "Mode of the serial port, valid are: loopback, secondary, external";
879 break;
880 case 'l':
881 pszHelp = "Use the given serial port device as the secondary device";
882 break;
883 case 't':
884 pszHelp = "The tests to run separated by ':'";
885 break;
886 case 'x':
887 pszHelp = "Number of bytes to transmit during read/write tests";
888 break;
889 default:
890 pszHelp = "Option undocumented";
891 break;
892 }
893 char szOpt[256];
894 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
895 RTStrmPrintf(pStrm, " %-30s%s\n", szOpt, pszHelp);
896 }
897}
898
899
900int main(int argc, char *argv[])
901{
902 /*
903 * Init IPRT and globals.
904 */
905 int rc = RTTestInitAndCreate("SerialTest", &g_hTest);
906 if (rc)
907 return rc;
908
909 /*
910 * Default values.
911 */
912 const char *pszDevice = NULL;
913 const char *pszDeviceSecondary = NULL;
914 PSERIALTESTDESC paTests = NULL;
915
916 RTGETOPTUNION ValueUnion;
917 RTGETOPTSTATE GetState;
918 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
919 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
920 {
921 switch (rc)
922 {
923 case 'h':
924 serialTestUsage(g_pStdOut);
925 return RTEXITCODE_SUCCESS;
926 case 'v':
927 g_cVerbosity++;
928 break;
929 case 'd':
930 pszDevice = ValueUnion.psz;
931 break;
932 case 'l':
933 pszDeviceSecondary = ValueUnion.psz;
934 break;
935 case 'b':
936 g_SerialPortCfg.uBaudRate = ValueUnion.u32;
937 break;
938 case 'p':
939 if (!RTStrICmp(ValueUnion.psz, "none"))
940 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_NONE;
941 else if (!RTStrICmp(ValueUnion.psz, "even"))
942 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_EVEN;
943 else if (!RTStrICmp(ValueUnion.psz, "odd"))
944 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_ODD;
945 else if (!RTStrICmp(ValueUnion.psz, "mark"))
946 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_MARK;
947 else if (!RTStrICmp(ValueUnion.psz, "space"))
948 g_SerialPortCfg.enmParity = RTSERIALPORTPARITY_SPACE;
949 else
950 {
951 RTPrintf("Unknown parity \"%s\" given\n", ValueUnion.psz);
952 return RTEXITCODE_FAILURE;
953 }
954 break;
955 case 'c':
956 if (ValueUnion.u32 == 5)
957 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
958 else if (ValueUnion.u32 == 6)
959 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
960 else if (ValueUnion.u32 == 7)
961 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
962 else if (ValueUnion.u32 == 8)
963 g_SerialPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
964 else
965 {
966 RTPrintf("Unknown data bitcount \"%u\" given\n", ValueUnion.u32);
967 return RTEXITCODE_FAILURE;
968 }
969 break;
970 case 's':
971 if (!RTStrICmp(ValueUnion.psz, "1"))
972 g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
973 else if (!RTStrICmp(ValueUnion.psz, "1.5"))
974 g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
975 else if (!RTStrICmp(ValueUnion.psz, "2"))
976 g_SerialPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
977 else
978 {
979 RTPrintf("Unknown stop bitcount \"%s\" given\n", ValueUnion.psz);
980 return RTEXITCODE_FAILURE;
981 }
982 break;
983 case 'm':
984 if (!RTStrICmp(ValueUnion.psz, "loopback"))
985 g_enmMode = SERIALTESTMODE_LOOPBACK;
986 else if (!RTStrICmp(ValueUnion.psz, "secondary"))
987 g_enmMode = SERIALTESTMODE_SECONDARY;
988 else if (!RTStrICmp(ValueUnion.psz, "external"))
989 g_enmMode = SERIALTESTMODE_EXTERNAL;
990 else
991 {
992 RTPrintf("Unknown serial test mode \"%s\" given\n", ValueUnion.psz);
993 return RTEXITCODE_FAILURE;
994 }
995 break;
996 case 't':
997 paTests = serialTestSelectFromCmdLine(ValueUnion.psz);
998 if (!paTests)
999 return RTEXITCODE_FAILURE;
1000 break;
1001 case 'x':
1002 g_cbTx = ValueUnion.u32;
1003 break;
1004 case 'a':
1005 g_fAbortOnError = true;
1006 break;
1007 default:
1008 return RTGetOptPrintError(rc, &ValueUnion);
1009 }
1010 }
1011
1012 if (g_enmMode == SERIALTESTMODE_SECONDARY && !pszDeviceSecondary)
1013 {
1014 RTPrintf("Mode set to secondary device but no secondary device given\n");
1015 return RTEXITCODE_FAILURE;
1016 }
1017
1018 if (!paTests)
1019 {
1020 /* Select all. */
1021 paTests = (PSERIALTESTDESC)RTMemAllocZ((RT_ELEMENTS(g_aSerialTests) + 1) * sizeof(SERIALTESTDESC));
1022 if (RT_UNLIKELY(!paTests))
1023 {
1024 RTPrintf("Failed to allocate memory for test descriptors\n");
1025 return RTEXITCODE_FAILURE;
1026 }
1027 memcpy(paTests, &g_aSerialTests[0], RT_ELEMENTS(g_aSerialTests) * sizeof(SERIALTESTDESC));
1028 }
1029
1030 rc = RTRandAdvCreateParkMiller(&g_hRand);
1031 if (RT_FAILURE(rc))
1032 {
1033 RTPrintf("Failed to create random number generator: %Rrc\n", rc);
1034 return RTEXITCODE_FAILURE;
1035 }
1036
1037 rc = RTRandAdvSeed(g_hRand, UINT64_C(0x123456789abcdef));
1038 AssertRC(rc);
1039
1040 /*
1041 * Start testing.
1042 */
1043 RTTestBanner(g_hTest);
1044
1045 if (pszDevice)
1046 {
1047 uint32_t fFlags = RTSERIALPORT_OPEN_F_READ
1048 | RTSERIALPORT_OPEN_F_WRITE
1049 | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING;
1050
1051 RTTestSub(g_hTest, "Opening device");
1052 rc = RTSerialPortOpen(&g_hSerialPort, pszDevice, fFlags);
1053 if (RT_SUCCESS(rc))
1054 {
1055 if (g_enmMode == SERIALTESTMODE_SECONDARY)
1056 {
1057 RTTestSub(g_hTest, "Opening secondary device");
1058 rc = RTSerialPortOpen(&g_hSerialPortSecondary, pszDeviceSecondary, fFlags);
1059 if (RT_FAILURE(rc))
1060 serialTestFailed(g_hTest, "Opening secondary device \"%s\" failed with %Rrc\n", pszDevice, rc);
1061 }
1062
1063 if (RT_SUCCESS(rc))
1064 {
1065 RTTestSub(g_hTest, "Setting serial port configuration");
1066
1067 rc = RTSerialPortCfgSet(g_hSerialPort, &g_SerialPortCfg ,NULL);
1068 if (RT_SUCCESS(rc))
1069 {
1070 if (g_enmMode == SERIALTESTMODE_SECONDARY)
1071 {
1072 RTTestSub(g_hTest, "Setting serial port configuration for secondary device");
1073 rc = RTSerialPortCfgSet(g_hSerialPortSecondary, &g_SerialPortCfg, NULL);
1074 if (RT_FAILURE(rc))
1075 serialTestFailed(g_hTest, "Setting configuration of secondary device \"%s\" failed with %Rrc\n", pszDevice, rc);
1076 }
1077
1078 if (RT_SUCCESS(rc))
1079 {
1080 SERIALTEST Test;
1081 PSERIALTESTDESC pTest = &paTests[0];
1082
1083 Test.hTest = g_hTest;
1084 Test.hSerialPort = g_hSerialPort;
1085 Test.pSerialCfg = &g_SerialPortCfg;
1086
1087 while (pTest->pszId)
1088 {
1089 RTTestSub(g_hTest, pTest->pszDesc);
1090 rc = pTest->pfnRun(&Test);
1091 if ( RT_FAILURE(rc)
1092 || RTTestErrorCount(g_hTest) > 0)
1093 serialTestFailed(g_hTest, "Running test \"%s\" failed (%Rrc, cErrors=%u)\n",
1094 pTest->pszId, rc, RTTestErrorCount(g_hTest));
1095
1096 RTTestSubDone(g_hTest);
1097 pTest++;
1098 }
1099 }
1100 }
1101 else
1102 serialTestFailed(g_hTest, "Setting configuration of device \"%s\" failed with %Rrc\n", pszDevice, rc);
1103
1104 RTSerialPortClose(g_hSerialPort);
1105 }
1106 }
1107 else
1108 serialTestFailed(g_hTest, "Opening device \"%s\" failed with %Rrc\n", pszDevice, rc);
1109 }
1110 else
1111 serialTestFailed(g_hTest, "No device given on command line\n");
1112
1113 RTRandAdvDestroy(g_hRand);
1114 RTMemFree(paTests);
1115 RTEXITCODE rcExit = RTTestSummaryAndDestroy(g_hTest);
1116 return rcExit;
1117}
1118
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