VirtualBox

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

Last change on this file since 72029 was 72029, checked in by vboxsync, 7 years ago

ValidationKit/tests/serial: Add verbose flag to avoid flooding the log with error messages by default

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette