VirtualBox

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

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