VirtualBox

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

Last change on this file since 99775 was 99775, checked in by vboxsync, 20 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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