VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/TestExecServ/TestExecServiceSerial.cpp@ 71000

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

ValidationKit/TestExecService: Started implementing a serial port based transport backend which can be used if networking is not available

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: TestExecServiceSerial.cpp 71000 2018-02-13 16:45:53Z vboxsync $ */
2/** @file
3 * TestExecServ - Basic Remote Execution Service, Serial port Transport Layer.
4 */
5
6/*
7 * Copyright (C) 2018 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#define LOG_GROUP RTLOGGROUP_DEFAULT
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/err.h>
35#include <iprt/log.h>
36#include <iprt/mem.h>
37#include <iprt/message.h>
38#include <iprt/string.h>
39#include <iprt/serialport.h>
40#include <iprt/thread.h>
41#include <iprt/time.h>
42
43#include "TestExecServiceInternal.h"
44
45
46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
49/** The default baud rate port. */
50#define TXS_SERIAL_DEF_BAUDRATE 115200
51/** The default serial device to use. */
52#if defined(RT_OS_LINUX)
53# define TXS_SERIAL_DEF_DEVICE "/dev/ttyS0"
54#elif defined(RT_OS_WINDOWS)
55# define TXS_SERIAL_DEF_DEVICE "COM1"
56#elif defined(RT_OS_SOLARIS)
57# define TXS_SERIAL_DEF_DEVICE "<todo>"
58#elif defined(RT_OS_FREEBSD)
59# define TXS_SERIAL_DEF_DEVICE "<todo>"
60#elif defined(RT_OS_DARWIN)
61# define TXS_SERIAL_DEF_DEVICE "<todo>"
62#else
63# error "Port me"
64#endif
65
66/*********************************************************************************************************************************
67* Global Variables *
68*********************************************************************************************************************************/
69/** @name Serial Parameters
70 * @{ */
71/** The addresses to bind to. Empty string means any. */
72static uint32_t g_uSerialBaudRate = TXS_SERIAL_DEF_BAUDRATE;
73/** The serial port device to use. */
74static char g_szSerialDevice[256] = TXS_SERIAL_DEF_DEVICE;
75/** @} */
76
77/** The serial port handle. */
78static RTSERIALPORT g_hSerialPort = NIL_RTSERIALPORT;
79/** The size of the stashed data. */
80static size_t g_cbSerialStashed = 0;
81/** The size of the stashed data allocation. */
82static size_t g_cbSerialStashedAlloced = 0;
83/** The stashed data. */
84static uint8_t *g_pbSerialStashed = NULL;
85
86
87
88/**
89 * @interface_method_impl{TXSTRANSPORT,pfnNotifyReboot}
90 */
91static DECLCALLBACK(void) txsSerialNotifyReboot(void)
92{
93 /* nothing to do here */
94}
95
96/**
97 * @interface_method_impl{TXSTRANSPORT,pfnNotifyBye}
98 */
99static DECLCALLBACK(void) txsSerialNotifyBye(void)
100{
101 /* nothing to do here */
102}
103
104/**
105 * @interface_method_impl{TXSTRANSPORT,pfnNotifyHowdy}
106 */
107static DECLCALLBACK(void) txsSerialNotifyHowdy(void)
108{
109 /* nothing to do here */
110}
111
112/**
113 * @interface_method_impl{TXSTRANSPORT,pfnBabble}
114 */
115static DECLCALLBACK(void) txsSerialBabble(PCTXSPKTHDR pPktHdr, RTMSINTERVAL cMsSendTimeout)
116{
117 Assert(g_hSerialPort != NIL_RTSERIALPORT);
118
119 /*
120 * Try send the babble reply.
121 */
122 NOREF(cMsSendTimeout); /** @todo implement the timeout here; non-blocking write + select-on-write. */
123 int rc;
124 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
125 do rc = RTSerialPortWrite(g_hSerialPort, pPktHdr, cbToSend, NULL);
126 while (rc == VERR_INTERRUPTED);
127
128 /*
129 * Disconnect the client.
130 */
131 Log(("txsSerialBabble: RTSerialPortWrite rc=%Rrc\n", rc));
132}
133
134/**
135 * @interface_method_impl{TXSTRANSPORT,pfnSendPkt}
136 */
137static DECLCALLBACK(int) txsSerialSendPkt(PCTXSPKTHDR pPktHdr)
138{
139 Assert(g_hSerialPort != NIL_RTSERIALPORT);
140 Assert(pPktHdr->cb >= sizeof(TXSPKTHDR));
141
142 /*
143 * Write it.
144 */
145 size_t cbToSend = RT_ALIGN_Z(pPktHdr->cb, TXSPKT_ALIGNMENT);
146 int rc = RTSerialPortWrite(g_hSerialPort, pPktHdr, cbToSend, NULL);
147 if ( RT_FAILURE(rc)
148 && rc != VERR_INTERRUPTED)
149 {
150 /* assume fatal connection error. */
151 Log(("RTSerialPortWrite -> %Rrc\n", rc));
152 }
153
154 return rc;
155}
156
157/**
158 * @interface_method_impl{TXSTRANSPORT,pfnRecvPkt}
159 */
160static DECLCALLBACK(int) txsSerialRecvPkt(PPTXSPKTHDR ppPktHdr)
161{
162 Assert(g_hSerialPort != NIL_RTSERIALPORT);
163
164 int rc = VINF_SUCCESS;
165 *ppPktHdr = NULL;
166
167 /*
168 * Read state.
169 */
170 size_t offData = 0;
171 size_t cbData = 0;
172 size_t cbDataAlloced;
173 uint8_t *pbData = NULL;
174
175 /*
176 * Any stashed data?
177 */
178 if (g_cbSerialStashedAlloced)
179 {
180 offData = g_cbSerialStashed;
181 cbDataAlloced = g_cbSerialStashedAlloced;
182 pbData = g_pbSerialStashed;
183
184 g_cbSerialStashed = 0;
185 g_cbSerialStashedAlloced = 0;
186 g_pbSerialStashed = NULL;
187 }
188 else
189 {
190 cbDataAlloced = RT_ALIGN_Z(64, TXSPKT_ALIGNMENT);
191 pbData = (uint8_t *)RTMemAlloc(cbDataAlloced);
192 if (!pbData)
193 return VERR_NO_MEMORY;
194 }
195
196 /*
197 * Read and valid the length.
198 */
199 while (offData < sizeof(uint32_t))
200 {
201 size_t cbRead = sizeof(uint32_t) - offData;
202 rc = RTSerialPortRead(g_hSerialPort, pbData + offData, cbRead, NULL);
203 if (RT_FAILURE(rc))
204 break;
205 offData += cbRead;
206 }
207 if (RT_SUCCESS(rc))
208 {
209 ASMCompilerBarrier(); /* paranoia^3 */
210 cbData = *(uint32_t volatile *)pbData;
211 if (cbData >= sizeof(TXSPKTHDR) && cbData <= TXSPKT_MAX_SIZE)
212 {
213 /*
214 * Align the length and reallocate the return packet it necessary.
215 */
216 cbData = RT_ALIGN_Z(cbData, TXSPKT_ALIGNMENT);
217 if (cbData > cbDataAlloced)
218 {
219 void *pvNew = RTMemRealloc(pbData, cbData);
220 if (pvNew)
221 {
222 pbData = (uint8_t *)pvNew;
223 cbDataAlloced = cbData;
224 }
225 else
226 rc = VERR_NO_MEMORY;
227 }
228 if (RT_SUCCESS(rc))
229 {
230 /*
231 * Read the remainder of the data.
232 */
233 while (offData < cbData)
234 {
235 size_t cbRead = cbData - offData;
236 rc = RTSerialPortRead(g_hSerialPort, pbData + offData, cbRead, NULL);
237 if (RT_FAILURE(rc))
238 break;
239 offData += cbRead;
240 }
241 }
242 }
243 else
244 rc = VERR_NET_PROTOCOL_ERROR;
245 }
246 if (RT_SUCCESS(rc))
247 *ppPktHdr = (PTXSPKTHDR)pbData;
248 else
249 {
250 /*
251 * Deal with errors.
252 */
253 if (rc == VERR_INTERRUPTED)
254 {
255 /* stash it away for the next call. */
256 g_cbSerialStashed = cbData;
257 g_cbSerialStashedAlloced = cbDataAlloced;
258 g_pbSerialStashed = pbData;
259 }
260 else
261 {
262 RTMemFree(pbData);
263
264 /* assume fatal connection error. */
265 Log(("txsSerialRecvPkt: RTSerialPortRead -> %Rrc\n", rc));
266 }
267 }
268
269 return rc;
270}
271
272/**
273 * @interface_method_impl{TXSTRANSPORT,pfnPollIn}
274 */
275static DECLCALLBACK(bool) txsSerialPollIn(void)
276{
277 Assert(g_hSerialPort != NIL_RTSERIALPORT);
278
279 uint32_t fEvtsRecv = 0;
280 int rc = RTSerialPortEvtPoll(g_hSerialPort, RTSERIALPORT_EVT_F_DATA_RX,
281 &fEvtsRecv, 0/*cMillies*/);
282 return RT_SUCCESS(rc);
283}
284
285/**
286 * @interface_method_impl{TXSTRANSPORT,pfnTerm}
287 */
288static DECLCALLBACK(void) txsSerialTerm(void)
289{
290 if (g_hSerialPort != NIL_RTSERIALPORT)
291 RTSerialPortClose(g_hSerialPort);
292
293 /* Clean up stashing. */
294 if (g_pbSerialStashed)
295 RTMemFree(g_pbSerialStashed);
296 g_pbSerialStashed = NULL;
297 g_cbSerialStashed = 0;
298 g_cbSerialStashedAlloced = 0;
299
300 Log(("txsSerialTerm: done\n"));
301}
302
303/**
304 * @interface_method_impl{TXSTRANSPORT,pfnInit}
305 */
306static DECLCALLBACK(int) txsSerialInit(void)
307{
308 uint32_t fOpenFlags = RTSERIALPORT_OPEN_F_READ | RTSERIALPORT_OPEN_F_WRITE;
309 int rc = RTSerialPortOpen(&g_hSerialPort, &g_szSerialDevice[0], fOpenFlags);
310 if (RT_SUCCESS(rc))
311 {
312 RTSERIALPORTCFG SerPortCfg;
313
314 SerPortCfg.uBaudRate = g_uSerialBaudRate;
315 SerPortCfg.enmParity = RTSERIALPORTPARITY_NONE;
316 SerPortCfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
317 SerPortCfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
318 rc = RTSerialPortCfgSet(g_hSerialPort, &SerPortCfg, NULL);
319 if (RT_FAILURE(rc))
320 {
321 RTMsgError("RTSerialPortCfgSet() failed: %Rrc\n", rc);
322 RTSerialPortClose(g_hSerialPort);
323 g_hSerialPort = NIL_RTSERIALPORT;
324 }
325 }
326 else
327 RTMsgError("RTSerialPortOpen(, %s, %#x) failed: %Rrc\n",
328 g_szSerialDevice, fOpenFlags, rc);
329
330 return rc;
331}
332
333/** Options */
334enum TXSSERIALOPT
335{
336 TXSSERIALOPT_BAUDRATE = 1000,
337 TXSSERIALOPT_DEVICE
338};
339
340/**
341 * @interface_method_impl{TXSTRANSPORT,pfnOption}
342 */
343static DECLCALLBACK(int) txsSerialOption(int ch, PCRTGETOPTUNION pVal)
344{
345 int rc;
346
347 switch (ch)
348 {
349 case TXSSERIALOPT_DEVICE:
350 rc = RTStrCopy(g_szSerialDevice, sizeof(g_szSerialDevice), pVal->psz);
351 if (RT_FAILURE(rc))
352 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Serial port device path is too long (%Rrc)", rc);
353 if (!g_szSerialDevice[0])
354 strcpy(g_szSerialDevice, TXS_SERIAL_DEF_DEVICE);
355 return VINF_SUCCESS;
356 case TXSSERIALOPT_BAUDRATE:
357 g_uSerialBaudRate = pVal->u32 == 0 ? TXS_SERIAL_DEF_BAUDRATE : pVal->u32;
358 return VINF_SUCCESS;
359 }
360 return VERR_TRY_AGAIN;
361}
362
363/**
364 * @interface_method_impl{TXSTRANSPORT,pfnUsage}
365 */
366DECLCALLBACK(void) txsSerialUsage(PRTSTREAM pStream)
367{
368 RTStrmPrintf(pStream,
369 " --serial-device <device>\n"
370 " Selects the serial port to use.\n"
371 " Default: %s\n"
372 " --serial-baudrate <baudrate>\n"
373 " Selects the baudrate to set the serial port to.\n"
374 " Default: %u\n"
375 , TXS_SERIAL_DEF_DEVICE, TXS_SERIAL_DEF_BAUDRATE);
376}
377
378/** Command line options for the serial transport layer. */
379static const RTGETOPTDEF g_SerialOpts[] =
380{
381 { "--serial-device", TXSSERIALOPT_DEVICE, RTGETOPT_REQ_STRING },
382 { "--serial-baudrate", TXSSERIALOPT_BAUDRATE, RTGETOPT_REQ_UINT32 }
383};
384
385/** Serial port transport layer. */
386const TXSTRANSPORT g_SerialTransport =
387{
388 /* .szName = */ "serial",
389 /* .pszDesc = */ "Serial",
390 /* .cOpts = */ &g_SerialOpts[0],
391 /* .paOpts = */ RT_ELEMENTS(g_SerialOpts),
392 /* .pfnUsage = */ txsSerialUsage,
393 /* .pfnOption = */ txsSerialOption,
394 /* .pfnInit = */ txsSerialInit,
395 /* .pfnTerm = */ txsSerialTerm,
396 /* .pfnPollIn = */ txsSerialPollIn,
397 /* .pfnPollSetAdd = */ NULL,
398 /* .pfnRecvPkt = */ txsSerialRecvPkt,
399 /* .pfnSendPkt = */ txsSerialSendPkt,
400 /* .pfnBabble = */ txsSerialBabble,
401 /* .pfnNotifyHowdy = */ txsSerialNotifyHowdy,
402 /* .pfnNotifyBye = */ txsSerialNotifyBye,
403 /* .pfnNotifyReboot = */ txsSerialNotifyReboot,
404 /* .u32EndMarker = */ UINT32_C(0x12345678)
405};
406
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