VirtualBox

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

Last change on this file since 91945 was 91945, checked in by vboxsync, 3 years ago

VMM,Devices: Eliminate calls to PDMR3Thread and use the driver hlper callbacks, bugref:10074

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