VirtualBox

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

Last change on this file since 99196 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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