VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvHostSerial.cpp

Last change on this file was 103425, checked in by vboxsync, 4 months ago

Devices/Serial: Fix some warnings, parfait:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.0 KB
RevLine 
[12348]1/* $Id: DrvHostSerial.cpp 103425 2024-02-19 11:12:14Z vboxsync $ */
[4748]2/** @file
[72073]3 * VBox serial devices: Host serial driver
[4748]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[4748]8 *
[96407]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 * SPDX-License-Identifier: GPL-3.0-only
[4748]26 */
27
28
29
[57358]30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
[4748]33#define LOG_GROUP LOG_GROUP_DRV_HOST_SERIAL
[35346]34#include <VBox/vmm/pdm.h>
[72073]35#include <VBox/vmm/pdmserialifs.h>
[4748]36#include <VBox/err.h>
37
38#include <VBox/log.h>
39#include <iprt/asm.h>
40#include <iprt/assert.h>
[25966]41#include <iprt/file.h>
42#include <iprt/mem.h>
[37596]43#include <iprt/pipe.h>
[4748]44#include <iprt/semaphore.h>
[25966]45#include <iprt/uuid.h>
[70800]46#include <iprt/serialport.h>
[4748]47
[35353]48#include "VBoxDD.h"
[4748]49
50
[57358]51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
[4748]54
55/**
56 * Char driver instance data.
[25966]57 *
[72132]58 * @implements PDMISERIALCONNECTOR
[4748]59 */
60typedef struct DRVHOSTSERIAL
61{
62 /** Pointer to the driver instance structure. */
63 PPDMDRVINS pDrvIns;
[72117]64 /** Pointer to the serial port interface of the driver/device above us. */
65 PPDMISERIALPORT pDrvSerialPort;
66 /** Our serial interface. */
67 PDMISERIALCONNECTOR ISerialConnector;
[70800]68 /** I/O thread. */
69 PPDMTHREAD pIoThrd;
70 /** The serial port handle. */
71 RTSERIALPORT hSerialPort;
[5004]72 /** the device path */
73 char *pszDevicePath;
[83521]74 /** The active config of the serial port. */
75 RTSERIALPORTCFG Cfg;
[5004]76
[73712]77 /** Flag whether data is available from the device/driver above as notified by the driver. */
78 volatile bool fAvailWrExt;
79 /** Internal copy of the flag which gets reset when there is no data anymore. */
80 bool fAvailWrInt;
[72117]81 /** Small send buffer. */
82 uint8_t abTxBuf[16];
83 /** Amount of data in the buffer. */
84 size_t cbTxUsed;
[4993]85
[70800]86 /** The read queue. */
87 uint8_t abReadBuf[256];
[72117]88 /** Current offset to write to next. */
89 volatile uint32_t offWrite;
[70800]90 /** Current offset into the read buffer. */
[72117]91 volatile uint32_t offRead;
[72132]92 /** Current amount of data in the buffer. */
93 volatile size_t cbReadBuf;
[70800]94
[83521]95 /* Flag whether the host device ran into a fatal error condition and I/O is suspended
96 * until the nuext VM suspend/resume cycle where we will try again. */
97 volatile bool fIoFatalErr;
98 /** Event semaphore the I/O thread is waiting on */
99 RTSEMEVENT hSemEvtIoFatalErr;
100
[4748]101 /** Read/write statistics */
102 STAMCOUNTER StatBytesRead;
103 STAMCOUNTER StatBytesWritten;
104} DRVHOSTSERIAL, *PDRVHOSTSERIAL;
105
106
[72117]107/*********************************************************************************************************************************
108* Global Variables *
109*********************************************************************************************************************************/
[4748]110
[72117]111
112/*********************************************************************************************************************************
113* Internal Functions *
114*********************************************************************************************************************************/
115
116
117/**
[82865]118 * Resets the read buffer.
119 *
120 * @returns Number of bytes which were queued in the read buffer before reset.
121 * @param pThis The host serial driver instance.
122 */
123DECLINLINE(size_t) drvHostSerialReadBufReset(PDRVHOSTSERIAL pThis)
124{
125 size_t cbOld = ASMAtomicXchgZ(&pThis->cbReadBuf, 0);
126 ASMAtomicWriteU32(&pThis->offWrite, 0);
127 ASMAtomicWriteU32(&pThis->offRead, 0);
128
129 return cbOld;
130}
131
132
133/**
[72117]134 * Returns number of bytes free in the read buffer and pointer to the start of the free space
135 * in the read buffer.
136 *
137 * @returns Number of bytes free in the buffer.
138 * @param pThis The host serial driver instance.
139 * @param ppv Where to return the pointer if there is still free space.
140 */
141DECLINLINE(size_t) drvHostSerialReadBufGetWrite(PDRVHOSTSERIAL pThis, void **ppv)
142{
143 if (ppv)
[72132]144 *ppv = &pThis->abReadBuf[pThis->offWrite];
[72117]145
[72132]146 size_t cbFree = sizeof(pThis->abReadBuf) - ASMAtomicReadZ(&pThis->cbReadBuf);
147 if (cbFree)
148 cbFree = RT_MIN(cbFree, sizeof(pThis->abReadBuf) - pThis->offWrite);
149
150 return cbFree;
[72117]151}
152
153
154/**
155 * Returns number of bytes used in the read buffer and pointer to the next byte to read.
156 *
157 * @returns Number of bytes free in the buffer.
158 * @param pThis The host serial driver instance.
159 * @param ppv Where to return the pointer to the next data to read.
160 */
161DECLINLINE(size_t) drvHostSerialReadBufGetRead(PDRVHOSTSERIAL pThis, void **ppv)
162{
163 if (ppv)
[72132]164 *ppv = &pThis->abReadBuf[pThis->offRead];
[72117]165
[72132]166 size_t cbUsed = ASMAtomicReadZ(&pThis->cbReadBuf);
167 if (cbUsed)
168 cbUsed = RT_MIN(cbUsed, sizeof(pThis->abReadBuf) - pThis->offRead);
169
170 return cbUsed;
[72117]171}
172
173
174/**
175 * Advances the write position of the read buffer by the given amount of bytes.
176 *
177 * @param pThis The host serial driver instance.
178 * @param cbAdv Number of bytes to advance.
179 */
180DECLINLINE(void) drvHostSerialReadBufWriteAdv(PDRVHOSTSERIAL pThis, size_t cbAdv)
181{
182 uint32_t offWrite = ASMAtomicReadU32(&pThis->offWrite);
183 offWrite = (offWrite + cbAdv) % sizeof(pThis->abReadBuf);
184 ASMAtomicWriteU32(&pThis->offWrite, offWrite);
[72132]185 ASMAtomicAddZ(&pThis->cbReadBuf, cbAdv);
[72117]186}
187
188
189/**
190 * Advances the read position of the read buffer by the given amount of bytes.
191 *
192 * @param pThis The host serial driver instance.
193 * @param cbAdv Number of bytes to advance.
194 */
195DECLINLINE(void) drvHostSerialReadBufReadAdv(PDRVHOSTSERIAL pThis, size_t cbAdv)
196{
197 uint32_t offRead = ASMAtomicReadU32(&pThis->offRead);
198 offRead = (offRead + cbAdv) % sizeof(pThis->abReadBuf);
199 ASMAtomicWriteU32(&pThis->offRead, offRead);
[72132]200 ASMAtomicSubZ(&pThis->cbReadBuf, cbAdv);
[72117]201}
202
203
[83521]204/**
205 * Wakes up the serial port I/O thread.
206 *
207 * @returns VBox status code.
208 * @param pThis The host serial driver instance.
209 */
210static int drvHostSerialWakeupIoThread(PDRVHOSTSERIAL pThis)
211{
212
213 if (RT_UNLIKELY(pThis->fIoFatalErr))
214 return RTSemEventSignal(pThis->hSemEvtIoFatalErr);
215
216 return RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
217}
218
219
[4748]220/* -=-=-=-=- IBase -=-=-=-=- */
221
222/**
[25966]223 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
[4748]224 */
[25966]225static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
[4748]226{
[25966]227 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
228 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
229
[25985]230 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
[72117]231 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector);
[25966]232 return NULL;
[4748]233}
234
235
[72117]236/* -=-=-=-=- ISerialConnector -=-=-=-=- */
[4748]237
[72117]238/** @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify} */
[73712]239static DECLCALLBACK(int) drvHostSerialDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface)
[4748]240{
[72117]241 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
[4748]242
[72117]243 int rc = VINF_SUCCESS;
[73712]244 bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true);
245 if (!fAvailOld)
[83521]246 rc = drvHostSerialWakeupIoThread(pThis);
[4748]247
[72117]248 return rc;
249}
250
251
252/**
253 * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr}
254 */
255static DECLCALLBACK(int) drvHostSerialReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf,
256 size_t cbRead, size_t *pcbRead)
257{
258 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
259 int rc = VINF_SUCCESS;
260 uint8_t *pbDst = (uint8_t *)pvBuf;
261 size_t cbReadAll = 0;
262
263 do
[4748]264 {
[72117]265 void *pvSrc = NULL;
[103425]266 size_t cbReadMax = drvHostSerialReadBufGetRead(pThis, &pvSrc);
267 size_t cbThisRead = RT_MIN(cbReadMax, cbRead);
[72117]268 if (cbThisRead)
269 {
270 memcpy(pbDst, pvSrc, cbThisRead);
271 cbRead -= cbThisRead;
272 pbDst += cbThisRead;
273 cbReadAll += cbThisRead;
274 drvHostSerialReadBufReadAdv(pThis, cbThisRead);
275 }
276 else
277 break;
278 } while (cbRead > 0);
[4748]279
[72117]280 *pcbRead = cbReadAll;
[72132]281 /* Kick the I/O thread if there is nothing to read to recalculate the poll flags. */
282 if (!drvHostSerialReadBufGetRead(pThis, NULL))
[83521]283 rc = drvHostSerialWakeupIoThread(pThis);
[72117]284
285 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbReadAll);
286 return rc;
[4748]287}
288
[70800]289
[72117]290/**
291 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams}
292 */
293static DECLCALLBACK(int) drvHostSerialChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps,
294 PDMSERIALPARITY enmParity, unsigned cDataBits,
295 PDMSERIALSTOPBITS enmStopBits)
[4748]296{
[72117]297 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
[4748]298
[83521]299 pThis->Cfg.uBaudRate = uBps;
[4748]300
[72117]301 switch (enmParity)
[33755]302 {
[72117]303 case PDMSERIALPARITY_EVEN:
[83521]304 pThis->Cfg.enmParity = RTSERIALPORTPARITY_EVEN;
[4748]305 break;
[72117]306 case PDMSERIALPARITY_ODD:
[83521]307 pThis->Cfg.enmParity = RTSERIALPORTPARITY_ODD;
[4748]308 break;
[72117]309 case PDMSERIALPARITY_NONE:
[83521]310 pThis->Cfg.enmParity = RTSERIALPORTPARITY_NONE;
[4748]311 break;
[72117]312 case PDMSERIALPARITY_MARK:
[83521]313 pThis->Cfg.enmParity = RTSERIALPORTPARITY_MARK;
[72117]314 break;
315 case PDMSERIALPARITY_SPACE:
[83521]316 pThis->Cfg.enmParity = RTSERIALPORTPARITY_SPACE;
[72117]317 break;
[4748]318 default:
[72117]319 AssertMsgFailed(("Unsupported parity setting %d\n", enmParity)); /* Should not happen. */
[83521]320 pThis->Cfg.enmParity = RTSERIALPORTPARITY_NONE;
[4748]321 }
322
[33755]323 switch (cDataBits)
324 {
[4748]325 case 5:
[83521]326 pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
[4748]327 break;
328 case 6:
[83521]329 pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
[4748]330 break;
331 case 7:
[83521]332 pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
[4748]333 break;
334 case 8:
[83521]335 pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
[4748]336 break;
337 default:
[70800]338 AssertMsgFailed(("Unsupported data bit count %u\n", cDataBits)); /* Should not happen. */
[83521]339 pThis->Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
[4748]340 }
341
[72117]342 switch (enmStopBits)
343 {
344 case PDMSERIALSTOPBITS_ONE:
[83521]345 pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
[72117]346 break;
347 case PDMSERIALSTOPBITS_ONEPOINTFIVE:
[83521]348 pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
[72117]349 break;
350 case PDMSERIALSTOPBITS_TWO:
[83521]351 pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
[72117]352 break;
353 default:
354 AssertMsgFailed(("Unsupported stop bit count %d\n", enmStopBits)); /* Should not happen. */
[83521]355 pThis->Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
[72117]356 }
357
[83521]358 return RTSerialPortCfgSet(pThis->hSerialPort, &pThis->Cfg, NULL);
[72117]359}
360
361
362/**
363 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines}
364 */
365static DECLCALLBACK(int) drvHostSerialChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr)
366{
367 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
368
369 uint32_t fClear = 0;
370 uint32_t fSet = 0;
371
372 if (fRts)
373 fSet |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
[70800]374 else
[72117]375 fClear |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
[4748]376
[72117]377 if (fDtr)
378 fSet |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
379 else
380 fClear |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
381
382 return RTSerialPortChgStatusLines(pThis->hSerialPort, fClear, fSet);
[4748]383}
384
385
386/**
[72117]387 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgBrk}
388 */
389static DECLCALLBACK(int) drvHostSerialChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk)
390{
391 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
392
393 return RTSerialPortChgBreakCondition(pThis->hSerialPort, fBrk);
394}
395
396
397/**
398 * @interface_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines}
399 */
400static DECLCALLBACK(int) drvHostSerialQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines)
401{
402 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
403
404 return RTSerialPortQueryStatusLines(pThis->hSerialPort, pfStsLines);
405}
406
407
[74446]408/**
409 * @callback_method_impl{PDMISERIALCONNECTOR,pfnQueuesFlush}
410 */
411static DECLCALLBACK(int) drvHostSerialQueuesFlush(PPDMISERIALCONNECTOR pInterface, bool fQueueRecv, bool fQueueXmit)
412{
413 RT_NOREF(fQueueXmit);
414 LogFlowFunc(("pInterface=%#p fQueueRecv=%RTbool fQueueXmit=%RTbool\n", pInterface, fQueueRecv, fQueueXmit));
415 int rc = VINF_SUCCESS;
416 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
417
418 if (fQueueRecv)
[74454]419 {
[82865]420 size_t cbOld = drvHostSerialReadBufReset(pThis);
[74446]421 if (cbOld) /* Kick the I/O thread to fetch new data. */
[83521]422 rc = drvHostSerialWakeupIoThread(pThis);
[74446]423 }
424
425 LogFlowFunc(("-> %Rrc\n", rc));
[103425]426 return rc;
[74446]427}
428
429
[72117]430/* -=-=-=-=- I/O thread -=-=-=-=- */
431
432/**
[83521]433 * The normal I/O loop.
[4748]434 *
[83521]435 * @returns VBox status code.
436 * @param pDrvIns Pointer to the driver instance data.
437 * @param pThis Host serial driver instance data.
438 * @param pThread Thread instance data.
[4748]439 */
[83521]440static int drvHostSerialIoLoopNormal(PPDMDRVINS pDrvIns, PDRVHOSTSERIAL pThis, PPDMTHREAD pThread)
[4748]441{
[83521]442 int rc = VINF_SUCCESS;
443 while ( pThread->enmState == PDMTHREADSTATE_RUNNING
444 && RT_SUCCESS(rc))
[4748]445 {
[70800]446 uint32_t fEvtFlags = RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED | RTSERIALPORT_EVT_F_BREAK_DETECTED;
[12380]447
[73712]448 if (!pThis->fAvailWrInt)
449 pThis->fAvailWrInt = ASMAtomicXchgBool(&pThis->fAvailWrExt, false);
450
[70800]451 /* Wait until there is room again if there is anyting to send. */
[73712]452 if ( pThis->fAvailWrInt
[72117]453 || pThis->cbTxUsed)
[70800]454 fEvtFlags |= RTSERIALPORT_EVT_F_DATA_TX;
[4748]455
[70800]456 /* Try to receive more if there is still room. */
[72117]457 if (drvHostSerialReadBufGetWrite(pThis, NULL) > 0)
[70800]458 fEvtFlags |= RTSERIALPORT_EVT_F_DATA_RX;
[4748]459
[70800]460 uint32_t fEvtsRecv = 0;
[83521]461 rc = RTSerialPortEvtPoll(pThis->hSerialPort, fEvtFlags, &fEvtsRecv, RT_INDEFINITE_WAIT);
[70800]462 if (RT_SUCCESS(rc))
[4748]463 {
[70800]464 if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_TX)
[12347]465 {
[77324]466 if ( pThis->fAvailWrInt
467 && pThis->cbTxUsed < RT_ELEMENTS(pThis->abTxBuf))
[72117]468 {
469 /* Stuff as much data into the TX buffer as we can. */
[73712]470 size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed;
[72117]471 size_t cbFetched = 0;
472 rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch,
473 &cbFetched);
474 AssertRC(rc);
475
[73243]476 if (cbFetched > 0)
[73712]477 pThis->cbTxUsed += cbFetched;
[73243]478 else
479 {
[73712]480 /* There is no data available anymore. */
481 pThis->fAvailWrInt = false;
[73243]482 }
[72117]483 }
484
[73243]485 if (pThis->cbTxUsed)
[12347]486 {
[73243]487 size_t cbProcessed = 0;
488 rc = RTSerialPortWriteNB(pThis->hSerialPort, &pThis->abTxBuf[0], pThis->cbTxUsed, &cbProcessed);
489 if (RT_SUCCESS(rc))
[72117]490 {
[73243]491 pThis->cbTxUsed -= cbProcessed;
[77324]492 if ( pThis->cbTxUsed
493 && cbProcessed)
[73243]494 {
495 /* Move the data in the TX buffer to the front to fill the end again. */
[83521]496 memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed);
497 }
[73243]498 else
499 pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort);
500 STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed);
[72117]501 }
502 else
[73243]503 {
504 LogRelMax(10, ("HostSerial#%d: Sending data failed even though the serial port is marked as writeable (rc=%Rrc)\n",
505 pThis->pDrvIns->iInstance, rc));
506 break;
507 }
[12347]508 }
509 }
510
[70800]511 if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_RX)
[12347]512 {
[72117]513 void *pvDst = NULL;
514 size_t cbToRead = drvHostSerialReadBufGetWrite(pThis, &pvDst);
515 size_t cbRead = 0;
516 rc = RTSerialPortReadNB(pThis->hSerialPort, pvDst, cbToRead, &cbRead);
[82865]517 /*
518 * No data being available while the port is marked as readable can happen
519 * if another thread changed the settings of the port inbetween the poll and
520 * the read call because it can flush all the buffered data (seen on Windows).
521 */
522 if (rc != VINF_TRY_AGAIN)
[40438]523 {
[82865]524 if (RT_SUCCESS(rc))
525 {
526 drvHostSerialReadBufWriteAdv(pThis, cbRead);
527 /* Notify the device/driver above. */
528 rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead);
529 AssertRC(rc);
530 }
531 else
532 LogRelMax(10, ("HostSerial#%d: Reading data failed even though the serial port is marked as readable (rc=%Rrc)\n",
533 pThis->pDrvIns->iInstance, rc));
[40438]534 }
[5004]535 }
[16217]536
[70800]537 if (fEvtsRecv & RTSERIALPORT_EVT_F_BREAK_DETECTED)
[72117]538 pThis->pDrvSerialPort->pfnNotifyBrk(pThis->pDrvSerialPort);
[5004]539
[70800]540 if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
[4928]541 {
[70800]542 /* The status lines have changed. Notify the device. */
543 uint32_t fStsLines = 0;
544 rc = RTSerialPortQueryStatusLines(pThis->hSerialPort, &fStsLines);
545 if (RT_SUCCESS(rc))
[5004]546 {
[70800]547 uint32_t fPdmStsLines = 0;
[6185]548
[70800]549 if (fStsLines & RTSERIALPORT_STS_LINE_DCD)
[72117]550 fPdmStsLines |= PDMISERIALPORT_STS_LINE_DCD;
[70800]551 if (fStsLines & RTSERIALPORT_STS_LINE_RI)
[72117]552 fPdmStsLines |= PDMISERIALPORT_STS_LINE_RI;
[70800]553 if (fStsLines & RTSERIALPORT_STS_LINE_DSR)
[72117]554 fPdmStsLines |= PDMISERIALPORT_STS_LINE_DSR;
[70800]555 if (fStsLines & RTSERIALPORT_STS_LINE_CTS)
[72117]556 fPdmStsLines |= PDMISERIALPORT_STS_LINE_CTS;
[5004]557
[72117]558 rc = pThis->pDrvSerialPort->pfnNotifyStsLinesChanged(pThis->pDrvSerialPort, fPdmStsLines);
[11266]559 if (RT_FAILURE(rc))
[6185]560 {
561 /* Notifying device failed, continue but log it */
[70800]562 LogRelMax(10, ("HostSerial#%d: Notifying device about changed status lines failed with error %Rrc; continuing.\n",
563 pDrvIns->iInstance, rc));
[83521]564 rc = VINF_SUCCESS;
[6185]565 }
566 }
567 else
[83521]568 {
[70800]569 LogRelMax(10, ("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
[83521]570 rc = VINF_SUCCESS;
571 }
[6185]572 }
[4748]573
[70800]574 if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED)
[86467]575 {
576 LogRel(("HostSerial#%d: Status line monitoring failed at a lower level with rc=%Rrc and is disabled\n", pDrvIns->iInstance, rc));
577 rc = VINF_SUCCESS;
578 }
[4748]579 }
[70800]580 else if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
[4748]581 {
[70800]582 /* Getting interrupted or running into a timeout are no error conditions. */
583 rc = VINF_SUCCESS;
[4748]584 }
585 }
586
[83521]587 LogRel(("HostSerial#%d: The underlying host device run into a fatal error condition %Rrc, any data transfer is disabled\n",
588 pDrvIns->iInstance, rc));
589
590 return rc;
591}
592
593
594/**
595 * The error I/O loop.
596 *
597 * @param pThis Host serial driver instance data.
598 * @param pThread Thread instance data.
599 */
600static void drvHostSerialIoLoopError(PDRVHOSTSERIAL pThis, PPDMTHREAD pThread)
601{
602 ASMAtomicXchgBool(&pThis->fIoFatalErr, true);
603
604 PDMDrvHlpVMSetRuntimeError(pThis->pDrvIns, 0 /*fFlags*/, "SerialPortIoError",
605 N_("The host serial port \"%s\" encountered a fatal error and stopped functioning. "
606 "This can be caused by bad cabling or USB to serial converters being unplugged by accident. "
607 "To restart I/O transfers suspend and resume the VM after fixing the underlying issue."),
608 pThis->pszDevicePath);
609
610 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
611 {
612 /*
613 * We have to discard any data which is going to be send (the error
614 * mode resembles the "someone just pulled the plug on the serial port" situation)
615 */
616 RTSemEventWait(pThis->hSemEvtIoFatalErr, RT_INDEFINITE_WAIT);
617
618 if (ASMAtomicXchgBool(&pThis->fAvailWrExt, false))
619 {
620 size_t cbFetched = 0;
621
622 do
623 {
624 /* Stuff as much data into the TX buffer as we can. */
625 uint8_t abDiscard[64];
626 int rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &abDiscard, sizeof(abDiscard),
627 &cbFetched);
628 AssertRC(rc);
629 } while (cbFetched > 0);
630
631 /* Acknowledge the sent data. */
632 pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort);
633
634 /*
635 * Sleep a bit to avoid excessive I/O loop CPU usage, timing is not important in
636 * this mode.
637 */
[91945]638 PDMDrvHlpThreadSleep(pThis->pDrvIns, pThread, 100);
[83521]639 }
640 }
641}
642
643
644/**
645 * I/O thread loop.
646 *
647 * @returns VINF_SUCCESS.
648 * @param pDrvIns PDM driver instance data.
649 * @param pThread The PDM thread data.
650 */
651static DECLCALLBACK(int) drvHostSerialIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
652{
653 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
654
655 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
656 return VINF_SUCCESS;
657
658 int rc = VINF_SUCCESS;
659 if (!pThis->fIoFatalErr)
660 rc = drvHostSerialIoLoopNormal(pDrvIns, pThis, pThread);
661
662 if ( RT_FAILURE(rc)
663 || pThis->fIoFatalErr)
664 drvHostSerialIoLoopError(pThis, pThread);
665
[5004]666 return VINF_SUCCESS;
667}
[4748]668
[5004]669
[6016]670/**
[70800]671 * Unblock the send thread so it can respond to a state change.
[6016]672 *
673 * @returns a VBox status code.
674 * @param pDrvIns The driver instance.
675 * @param pThread The send thread.
676 */
[70800]677static DECLCALLBACK(int) drvHostSerialWakeupIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
[6016]678{
[70800]679 RT_NOREF(pThread);
[11269]680 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
[6185]681
[83521]682 return drvHostSerialWakeupIoThread(pThis);
[70800]683}
[11451]684
685
[4748]686/* -=-=-=-=- driver interface -=-=-=-=- */
687
688/**
[83521]689 * @callback_method_impl{FNPDMDRVRESUME}
690 */
691static DECLCALLBACK(void) drvHostSerialResume(PPDMDRVINS pDrvIns)
692{
693 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
694
695 if (RT_UNLIKELY(pThis->fIoFatalErr))
696 {
697 /* Try to reopen the device and set the old config. */
698 uint32_t fOpenFlags = RTSERIALPORT_OPEN_F_READ
699 | RTSERIALPORT_OPEN_F_WRITE
700 | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING
701 | RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION;
702 int rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
703 if (rc == VERR_NOT_SUPPORTED)
704 {
705 /*
706 * For certain devices (or pseudo terminals) status line monitoring does not work
707 * so try again without it.
708 */
709 fOpenFlags &= ~RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING;
710 rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
711 }
712
713 if (RT_SUCCESS(rc))
714 {
715 /* Set the config which is currently active. */
716 rc = RTSerialPortCfgSet(pThis->hSerialPort, &pThis->Cfg, NULL);
717 if (RT_FAILURE(rc))
718 LogRelMax(10, ("HostSerial#%d: Setting the active serial port config failed with error %Rrc during VM resume; continuing.\n", pDrvIns->iInstance, rc));
719 /* Reset the I/O error flag on success to resume the normal I/O thread loop. */
720 ASMAtomicXchgBool(&pThis->fIoFatalErr, false);
721 }
722 }
723}
724
725
726/**
727 * @callback_method_impl{FNPDMDRVSUSPEND}
728 */
729static DECLCALLBACK(void) drvHostSerialSuspend(PPDMDRVINS pDrvIns)
730{
731 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
732
733 if (RT_UNLIKELY(pThis->fIoFatalErr))
734 {
735 /* Close the device and try reopening it on resume. */
736 if (pThis->hSerialPort != NIL_RTSERIALPORT)
737 {
738 RTSerialPortClose(pThis->hSerialPort);
739 pThis->hSerialPort = NIL_RTSERIALPORT;
740 }
741 }
742}
743
744
745/**
[26001]746 * Destruct a char driver instance.
747 *
748 * Most VM resources are freed by the VM. This callback is provided so that
749 * any non-VM resources can be freed correctly.
750 *
751 * @param pDrvIns The driver instance data.
752 */
753static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
754{
[72117]755 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
[26001]756 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
757 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
758
[70800]759 if (pThis->hSerialPort != NIL_RTSERIALPORT)
[45061]760 {
[70800]761 RTSerialPortClose(pThis->hSerialPort);
762 pThis->hSerialPort = NIL_RTSERIALPORT;
[45061]763 }
[26001]764
[83521]765 if (pThis->hSemEvtIoFatalErr != NIL_RTSEMEVENT)
766 {
767 RTSemEventDestroy(pThis->hSemEvtIoFatalErr);
768 pThis->hSemEvtIoFatalErr = NIL_RTSEMEVENT;
769 }
770
[32678]771 if (pThis->pszDevicePath)
772 {
[91897]773 PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszDevicePath);
[32678]774 pThis->pszDevicePath = NULL;
775 }
[26001]776}
777
[70800]778
[26001]779/**
[4748]780 * Construct a char driver instance.
[23973]781 *
[22277]782 * @copydoc FNPDMDRVCONSTRUCT
[4748]783 */
[64276]784static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
[4748]785{
[64276]786 RT_NOREF1(fFlags);
[26001]787 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
[91868]788 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
789 PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
[4748]790
[91868]791 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
792
[4748]793 /*
794 * Init basic data members and interfaces.
795 */
[72132]796 pThis->pDrvIns = pDrvIns;
[72117]797 pThis->hSerialPort = NIL_RTSERIALPORT;
[73712]798 pThis->fAvailWrExt = false;
799 pThis->fAvailWrInt = false;
[72117]800 pThis->cbTxUsed = 0;
801 pThis->offWrite = 0;
802 pThis->offRead = 0;
[72132]803 pThis->cbReadBuf = 0;
[83521]804 pThis->fIoFatalErr = false;
805 pThis->hSemEvtIoFatalErr = NIL_RTSEMEVENT;
[4748]806 /* IBase. */
[72117]807 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
808 /* ISerialConnector. */
809 pThis->ISerialConnector.pfnDataAvailWrNotify = drvHostSerialDataAvailWrNotify;
810 pThis->ISerialConnector.pfnReadRdr = drvHostSerialReadRdr;
811 pThis->ISerialConnector.pfnChgParams = drvHostSerialChgParams;
812 pThis->ISerialConnector.pfnChgModemLines = drvHostSerialChgModemLines;
813 pThis->ISerialConnector.pfnChgBrk = drvHostSerialChgBrk;
814 pThis->ISerialConnector.pfnQueryStsLines = drvHostSerialQueryStsLines;
[74446]815 pThis->ISerialConnector.pfnQueuesFlush = drvHostSerialQueuesFlush;
[4748]816
817 /*
[91868]818 * Validate the config.
819 */
820 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "DevicePath", "");
821
822 /*
[4748]823 * Query configuration.
824 */
825 /* Device */
[91868]826 int rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
[11266]827 if (RT_FAILURE(rc))
[4748]828 {
[11286]829 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
[4748]830 return rc;
831 }
832
833 /*
834 * Open the device
835 */
[70800]836 uint32_t fOpenFlags = RTSERIALPORT_OPEN_F_READ
837 | RTSERIALPORT_OPEN_F_WRITE
838 | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING
839 | RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION;
840 rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
[72073]841 if (rc == VERR_NOT_SUPPORTED)
842 {
843 /*
844 * For certain devices (or pseudo terminals) status line monitoring does not work
845 * so try again without it.
846 */
847 fOpenFlags &= ~RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING;
848 rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
849 }
850
[11266]851 if (RT_FAILURE(rc))
[5004]852 {
[11284]853 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
[5004]854 switch (rc)
855 {
[4748]856 case VERR_ACCESS_DENIED:
857 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
[22876]858#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
[4748]859 N_("Cannot open host device '%s' for read/write access. Check the permissions "
860 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
861 "of the device group. Make sure that you logout/login after changing "
862 "the group settings of the current user"),
863#else
864 N_("Cannot open host device '%s' for read/write access. Check the permissions "
865 "of that device"),
866#endif
[11269]867 pThis->pszDevicePath, pThis->pszDevicePath);
[4748]868 default:
869 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
870 N_("Failed to open host device '%s'"),
[11269]871 pThis->pszDevicePath);
[4748]872 }
873 }
874
[83521]875 rc = RTSemEventCreate(&pThis->hSemEvtIoFatalErr);
876 if (RT_FAILURE(rc))
877 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d failed to create event semaphore"), pDrvIns->iInstance);
878
[4748]879 /*
[72117]880 * Get the ISerialPort interface of the above driver/device.
[4748]881 */
[72117]882 pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT);
883 if (!pThis->pDrvSerialPort)
884 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no serial port interface above"), pDrvIns->iInstance);
[4748]885
[12325]886 /*
[70800]887 * Create the I/O thread.
[12325]888 */
[70800]889 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pIoThrd, pThis, drvHostSerialIoThread, drvHostSerialWakeupIoThread, 0, RTTHREADTYPE_IO, "SerIo");
[11266]890 if (RT_FAILURE(rc))
[70800]891 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create I/O thread"), pDrvIns->iInstance);
[4748]892
[12325]893 /*
894 * Register release statistics.
895 */
[72073]896 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
897 "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
898 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
899 "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
[4748]900
901 return VINF_SUCCESS;
902}
903
904/**
905 * Char driver registration record.
906 */
907const PDMDRVREG g_DrvHostSerial =
908{
909 /* u32Version */
910 PDM_DRVREG_VERSION,
[26166]911 /* szName */
[4748]912 "Host Serial",
[72073]913 /* szRCMod */
[25893]914 "",
915 /* szR0Mod */
916 "",
[70800]917 /* pszDescription */
[4748]918 "Host serial driver.",
919 /* fFlags */
920 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
921 /* fClass. */
922 PDM_DRVREG_CLASS_CHAR,
923 /* cMaxInstances */
[40282]924 ~0U,
[4748]925 /* cbInstance */
926 sizeof(DRVHOSTSERIAL),
927 /* pfnConstruct */
928 drvHostSerialConstruct,
929 /* pfnDestruct */
930 drvHostSerialDestruct,
[25893]931 /* pfnRelocate */
932 NULL,
[4748]933 /* pfnIOCtl */
934 NULL,
935 /* pfnPowerOn */
936 NULL,
937 /* pfnReset */
938 NULL,
939 /* pfnSuspend */
[83521]940 drvHostSerialSuspend,
[4748]941 /* pfnResume */
[83521]942 drvHostSerialResume,
[22277]943 /* pfnAttach */
944 NULL,
[4748]945 /* pfnDetach */
[23973]946 NULL,
[22277]947 /* pfnPowerOff */
[23973]948 NULL,
[22277]949 /* pfnSoftReset */
[4748]950 NULL,
[22277]951 /* u32EndVersion */
952 PDM_DRVREG_VERSION
[4748]953};
954
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use