VirtualBox

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

Last change on this file since 79812 was 77324, checked in by vboxsync, 6 years ago

Devices/Serial: Don't try to process data if the either the buffer is full or there is nothing to process

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.4 KB
Line 
1/* $Id: DrvHostSerial.cpp 77324 2019-02-14 21:23:14Z vboxsync $ */
2/** @file
3 * VBox serial devices: Host serial driver
4 */
5
6/*
7 * Copyright (C) 2006-2019 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
65 /** Flag whether data is available from the device/driver above as notified by the driver. */
66 volatile bool fAvailWrExt;
67 /** Internal copy of the flag which gets reset when there is no data anymore. */
68 bool fAvailWrInt;
69 /** Small send buffer. */
70 uint8_t abTxBuf[16];
71 /** Amount of data in the buffer. */
72 size_t cbTxUsed;
73
74 /** The read queue. */
75 uint8_t abReadBuf[256];
76 /** Current offset to write to next. */
77 volatile uint32_t offWrite;
78 /** Current offset into the read buffer. */
79 volatile uint32_t offRead;
80 /** Current amount of data in the buffer. */
81 volatile size_t cbReadBuf;
82
83 /** Read/write statistics */
84 STAMCOUNTER StatBytesRead;
85 STAMCOUNTER StatBytesWritten;
86} DRVHOSTSERIAL, *PDRVHOSTSERIAL;
87
88
89/*********************************************************************************************************************************
90* Global Variables *
91*********************************************************************************************************************************/
92
93
94/*********************************************************************************************************************************
95* Internal Functions *
96*********************************************************************************************************************************/
97
98
99/**
100 * Returns number of bytes free in the read buffer and pointer to the start of the free space
101 * in the read buffer.
102 *
103 * @returns Number of bytes free in the buffer.
104 * @param pThis The host serial driver instance.
105 * @param ppv Where to return the pointer if there is still free space.
106 */
107DECLINLINE(size_t) drvHostSerialReadBufGetWrite(PDRVHOSTSERIAL pThis, void **ppv)
108{
109 if (ppv)
110 *ppv = &pThis->abReadBuf[pThis->offWrite];
111
112 size_t cbFree = sizeof(pThis->abReadBuf) - ASMAtomicReadZ(&pThis->cbReadBuf);
113 if (cbFree)
114 cbFree = RT_MIN(cbFree, sizeof(pThis->abReadBuf) - pThis->offWrite);
115
116 return cbFree;
117}
118
119
120/**
121 * Returns number of bytes used in the read buffer and pointer to the next byte to read.
122 *
123 * @returns Number of bytes free in the buffer.
124 * @param pThis The host serial driver instance.
125 * @param ppv Where to return the pointer to the next data to read.
126 */
127DECLINLINE(size_t) drvHostSerialReadBufGetRead(PDRVHOSTSERIAL pThis, void **ppv)
128{
129 if (ppv)
130 *ppv = &pThis->abReadBuf[pThis->offRead];
131
132 size_t cbUsed = ASMAtomicReadZ(&pThis->cbReadBuf);
133 if (cbUsed)
134 cbUsed = RT_MIN(cbUsed, sizeof(pThis->abReadBuf) - pThis->offRead);
135
136 return cbUsed;
137}
138
139
140/**
141 * Advances the write position of the read buffer by the given amount of bytes.
142 *
143 * @returns nothing.
144 * @param pThis The host serial driver instance.
145 * @param cbAdv Number of bytes to advance.
146 */
147DECLINLINE(void) drvHostSerialReadBufWriteAdv(PDRVHOSTSERIAL pThis, size_t cbAdv)
148{
149 uint32_t offWrite = ASMAtomicReadU32(&pThis->offWrite);
150 offWrite = (offWrite + cbAdv) % sizeof(pThis->abReadBuf);
151 ASMAtomicWriteU32(&pThis->offWrite, offWrite);
152 ASMAtomicAddZ(&pThis->cbReadBuf, cbAdv);
153}
154
155
156/**
157 * Advances the read position of the read buffer by the given amount of bytes.
158 *
159 * @returns nothing.
160 * @param pThis The host serial driver instance.
161 * @param cbAdv Number of bytes to advance.
162 */
163DECLINLINE(void) drvHostSerialReadBufReadAdv(PDRVHOSTSERIAL pThis, size_t cbAdv)
164{
165 uint32_t offRead = ASMAtomicReadU32(&pThis->offRead);
166 offRead = (offRead + cbAdv) % sizeof(pThis->abReadBuf);
167 ASMAtomicWriteU32(&pThis->offRead, offRead);
168 ASMAtomicSubZ(&pThis->cbReadBuf, cbAdv);
169}
170
171
172/* -=-=-=-=- IBase -=-=-=-=- */
173
174/**
175 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
176 */
177static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, const char *pszIID)
178{
179 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
180 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
181
182 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
183 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector);
184 return NULL;
185}
186
187
188/* -=-=-=-=- ISerialConnector -=-=-=-=- */
189
190/** @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify} */
191static DECLCALLBACK(int) drvHostSerialDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface)
192{
193 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
194
195 int rc = VINF_SUCCESS;
196 bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true);
197 if (!fAvailOld)
198 rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
199
200 return rc;
201}
202
203
204/**
205 * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr}
206 */
207static DECLCALLBACK(int) drvHostSerialReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf,
208 size_t cbRead, size_t *pcbRead)
209{
210 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
211 int rc = VINF_SUCCESS;
212 uint8_t *pbDst = (uint8_t *)pvBuf;
213 size_t cbReadAll = 0;
214
215 do
216 {
217 void *pvSrc = NULL;
218 size_t cbThisRead = RT_MIN(drvHostSerialReadBufGetRead(pThis, &pvSrc), cbRead);
219 if (cbThisRead)
220 {
221 memcpy(pbDst, pvSrc, cbThisRead);
222 cbRead -= cbThisRead;
223 pbDst += cbThisRead;
224 cbReadAll += cbThisRead;
225 drvHostSerialReadBufReadAdv(pThis, cbThisRead);
226 }
227 else
228 break;
229 } while (cbRead > 0);
230
231 *pcbRead = cbReadAll;
232 /* Kick the I/O thread if there is nothing to read to recalculate the poll flags. */
233 if (!drvHostSerialReadBufGetRead(pThis, NULL))
234 rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
235
236 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbReadAll);
237 return rc;
238}
239
240
241/**
242 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams}
243 */
244static DECLCALLBACK(int) drvHostSerialChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps,
245 PDMSERIALPARITY enmParity, unsigned cDataBits,
246 PDMSERIALSTOPBITS enmStopBits)
247{
248 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
249 RTSERIALPORTCFG Cfg;
250
251 Cfg.uBaudRate = uBps;
252
253 switch (enmParity)
254 {
255 case PDMSERIALPARITY_EVEN:
256 Cfg.enmParity = RTSERIALPORTPARITY_EVEN;
257 break;
258 case PDMSERIALPARITY_ODD:
259 Cfg.enmParity = RTSERIALPORTPARITY_ODD;
260 break;
261 case PDMSERIALPARITY_NONE:
262 Cfg.enmParity = RTSERIALPORTPARITY_NONE;
263 break;
264 case PDMSERIALPARITY_MARK:
265 Cfg.enmParity = RTSERIALPORTPARITY_MARK;
266 break;
267 case PDMSERIALPARITY_SPACE:
268 Cfg.enmParity = RTSERIALPORTPARITY_SPACE;
269 break;
270 default:
271 AssertMsgFailed(("Unsupported parity setting %d\n", enmParity)); /* Should not happen. */
272 Cfg.enmParity = RTSERIALPORTPARITY_NONE;
273 }
274
275 switch (cDataBits)
276 {
277 case 5:
278 Cfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
279 break;
280 case 6:
281 Cfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
282 break;
283 case 7:
284 Cfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
285 break;
286 case 8:
287 Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
288 break;
289 default:
290 AssertMsgFailed(("Unsupported data bit count %u\n", cDataBits)); /* Should not happen. */
291 Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
292 }
293
294 switch (enmStopBits)
295 {
296 case PDMSERIALSTOPBITS_ONE:
297 Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
298 break;
299 case PDMSERIALSTOPBITS_ONEPOINTFIVE:
300 Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
301 break;
302 case PDMSERIALSTOPBITS_TWO:
303 Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
304 break;
305 default:
306 AssertMsgFailed(("Unsupported stop bit count %d\n", enmStopBits)); /* Should not happen. */
307 Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
308 }
309
310 return RTSerialPortCfgSet(pThis->hSerialPort, &Cfg, NULL);
311}
312
313
314/**
315 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines}
316 */
317static DECLCALLBACK(int) drvHostSerialChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr)
318{
319 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
320
321 uint32_t fClear = 0;
322 uint32_t fSet = 0;
323
324 if (fRts)
325 fSet |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
326 else
327 fClear |= RTSERIALPORT_CHG_STS_LINES_F_RTS;
328
329 if (fDtr)
330 fSet |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
331 else
332 fClear |= RTSERIALPORT_CHG_STS_LINES_F_DTR;
333
334 return RTSerialPortChgStatusLines(pThis->hSerialPort, fClear, fSet);
335}
336
337
338/**
339 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgBrk}
340 */
341static DECLCALLBACK(int) drvHostSerialChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk)
342{
343 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
344
345 return RTSerialPortChgBreakCondition(pThis->hSerialPort, fBrk);
346}
347
348
349/**
350 * @interface_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines}
351 */
352static DECLCALLBACK(int) drvHostSerialQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines)
353{
354 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
355
356 return RTSerialPortQueryStatusLines(pThis->hSerialPort, pfStsLines);
357}
358
359
360/**
361 * @callback_method_impl{PDMISERIALCONNECTOR,pfnQueuesFlush}
362 */
363static DECLCALLBACK(int) drvHostSerialQueuesFlush(PPDMISERIALCONNECTOR pInterface, bool fQueueRecv, bool fQueueXmit)
364{
365 RT_NOREF(fQueueXmit);
366 LogFlowFunc(("pInterface=%#p fQueueRecv=%RTbool fQueueXmit=%RTbool\n", pInterface, fQueueRecv, fQueueXmit));
367 int rc = VINF_SUCCESS;
368 PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector);
369
370 if (fQueueRecv)
371 {
372 size_t cbOld = ASMAtomicXchgZ(&pThis->cbReadBuf, 0);
373 if (cbOld) /* Kick the I/O thread to fetch new data. */
374 rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
375 }
376
377 LogFlowFunc(("-> %Rrc\n", rc));
378 return VINF_SUCCESS;
379}
380
381
382/* -=-=-=-=- I/O thread -=-=-=-=- */
383
384/**
385 * I/O thread loop.
386 *
387 * @returns VINF_SUCCESS.
388 * @param pDrvIns PDM driver instance data.
389 * @param pThread The PDM thread data.
390 */
391static DECLCALLBACK(int) drvHostSerialIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
392{
393 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
394
395 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
396 return VINF_SUCCESS;
397
398 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
399 {
400 uint32_t fEvtFlags = RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED | RTSERIALPORT_EVT_F_BREAK_DETECTED;
401
402 if (!pThis->fAvailWrInt)
403 pThis->fAvailWrInt = ASMAtomicXchgBool(&pThis->fAvailWrExt, false);
404
405 /* Wait until there is room again if there is anyting to send. */
406 if ( pThis->fAvailWrInt
407 || pThis->cbTxUsed)
408 fEvtFlags |= RTSERIALPORT_EVT_F_DATA_TX;
409
410 /* Try to receive more if there is still room. */
411 if (drvHostSerialReadBufGetWrite(pThis, NULL) > 0)
412 fEvtFlags |= RTSERIALPORT_EVT_F_DATA_RX;
413
414 uint32_t fEvtsRecv = 0;
415 int rc = RTSerialPortEvtPoll(pThis->hSerialPort, fEvtFlags, &fEvtsRecv, RT_INDEFINITE_WAIT);
416 if (RT_SUCCESS(rc))
417 {
418 if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_TX)
419 {
420 if ( pThis->fAvailWrInt
421 && pThis->cbTxUsed < RT_ELEMENTS(pThis->abTxBuf))
422 {
423 /* Stuff as much data into the TX buffer as we can. */
424 size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed;
425 size_t cbFetched = 0;
426 rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch,
427 &cbFetched);
428 AssertRC(rc);
429
430 if (cbFetched > 0)
431 pThis->cbTxUsed += cbFetched;
432 else
433 {
434 /* There is no data available anymore. */
435 pThis->fAvailWrInt = false;
436 }
437 }
438
439 if (pThis->cbTxUsed)
440 {
441 size_t cbProcessed = 0;
442 rc = RTSerialPortWriteNB(pThis->hSerialPort, &pThis->abTxBuf[0], pThis->cbTxUsed, &cbProcessed);
443 if (RT_SUCCESS(rc))
444 {
445 pThis->cbTxUsed -= cbProcessed;
446 if ( pThis->cbTxUsed
447 && cbProcessed)
448 {
449 /* Move the data in the TX buffer to the front to fill the end again. */
450 memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed); }
451 else
452 pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort);
453 STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed);
454 }
455 else
456 {
457 LogRelMax(10, ("HostSerial#%d: Sending data failed even though the serial port is marked as writeable (rc=%Rrc)\n",
458 pThis->pDrvIns->iInstance, rc));
459 break;
460 }
461 }
462 }
463
464 if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_RX)
465 {
466 void *pvDst = NULL;
467 size_t cbToRead = drvHostSerialReadBufGetWrite(pThis, &pvDst);
468 size_t cbRead = 0;
469 rc = RTSerialPortReadNB(pThis->hSerialPort, pvDst, cbToRead, &cbRead);
470 if (RT_SUCCESS(rc))
471 {
472 drvHostSerialReadBufWriteAdv(pThis, cbRead);
473 /* Notify the device/driver above. */
474 rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead);
475 AssertRC(rc);
476 }
477 else
478 LogRelMax(10, ("HostSerial#%d: Reading data failed even though the serial port is marked as readable (rc=%Rrc)\n",
479 pThis->pDrvIns->iInstance, rc));
480 }
481
482 if (fEvtsRecv & RTSERIALPORT_EVT_F_BREAK_DETECTED)
483 pThis->pDrvSerialPort->pfnNotifyBrk(pThis->pDrvSerialPort);
484
485 if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
486 {
487 /* The status lines have changed. Notify the device. */
488 uint32_t fStsLines = 0;
489 rc = RTSerialPortQueryStatusLines(pThis->hSerialPort, &fStsLines);
490 if (RT_SUCCESS(rc))
491 {
492 uint32_t fPdmStsLines = 0;
493
494 if (fStsLines & RTSERIALPORT_STS_LINE_DCD)
495 fPdmStsLines |= PDMISERIALPORT_STS_LINE_DCD;
496 if (fStsLines & RTSERIALPORT_STS_LINE_RI)
497 fPdmStsLines |= PDMISERIALPORT_STS_LINE_RI;
498 if (fStsLines & RTSERIALPORT_STS_LINE_DSR)
499 fPdmStsLines |= PDMISERIALPORT_STS_LINE_DSR;
500 if (fStsLines & RTSERIALPORT_STS_LINE_CTS)
501 fPdmStsLines |= PDMISERIALPORT_STS_LINE_CTS;
502
503 rc = pThis->pDrvSerialPort->pfnNotifyStsLinesChanged(pThis->pDrvSerialPort, fPdmStsLines);
504 if (RT_FAILURE(rc))
505 {
506 /* Notifying device failed, continue but log it */
507 LogRelMax(10, ("HostSerial#%d: Notifying device about changed status lines failed with error %Rrc; continuing.\n",
508 pDrvIns->iInstance, rc));
509 }
510 }
511 else
512 LogRelMax(10, ("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
513 }
514
515 if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED)
516 LogRel(("HostSerial#%d: Status line monitoring failed at a lower level and is disabled\n", pDrvIns->iInstance));
517 }
518 else if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
519 {
520 /* Getting interrupted or running into a timeout are no error conditions. */
521 rc = VINF_SUCCESS;
522 }
523 }
524
525 return VINF_SUCCESS;
526}
527
528
529/**
530 * Unblock the send thread so it can respond to a state change.
531 *
532 * @returns a VBox status code.
533 * @param pDrvIns The driver instance.
534 * @param pThread The send thread.
535 */
536static DECLCALLBACK(int) drvHostSerialWakeupIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
537{
538 RT_NOREF(pThread);
539 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
540
541 return RTSerialPortEvtPollInterrupt(pThis->hSerialPort);
542}
543
544
545/* -=-=-=-=- driver interface -=-=-=-=- */
546
547/**
548 * Destruct a char driver instance.
549 *
550 * Most VM resources are freed by the VM. This callback is provided so that
551 * any non-VM resources can be freed correctly.
552 *
553 * @param pDrvIns The driver instance data.
554 */
555static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
556{
557 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
558 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
559 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
560
561 if (pThis->hSerialPort != NIL_RTSERIALPORT)
562 {
563 RTSerialPortClose(pThis->hSerialPort);
564 pThis->hSerialPort = NIL_RTSERIALPORT;
565 }
566
567 if (pThis->pszDevicePath)
568 {
569 MMR3HeapFree(pThis->pszDevicePath);
570 pThis->pszDevicePath = NULL;
571 }
572}
573
574
575/**
576 * Construct a char driver instance.
577 *
578 * @copydoc FNPDMDRVCONSTRUCT
579 */
580static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
581{
582 RT_NOREF1(fFlags);
583 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
584 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
585 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
586
587 /*
588 * Init basic data members and interfaces.
589 */
590 pThis->pDrvIns = pDrvIns;
591 pThis->hSerialPort = NIL_RTSERIALPORT;
592 pThis->fAvailWrExt = false;
593 pThis->fAvailWrInt = false;
594 pThis->cbTxUsed = 0;
595 pThis->offWrite = 0;
596 pThis->offRead = 0;
597 pThis->cbReadBuf = 0;
598 /* IBase. */
599 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
600 /* ISerialConnector. */
601 pThis->ISerialConnector.pfnDataAvailWrNotify = drvHostSerialDataAvailWrNotify;
602 pThis->ISerialConnector.pfnReadRdr = drvHostSerialReadRdr;
603 pThis->ISerialConnector.pfnChgParams = drvHostSerialChgParams;
604 pThis->ISerialConnector.pfnChgModemLines = drvHostSerialChgModemLines;
605 pThis->ISerialConnector.pfnChgBrk = drvHostSerialChgBrk;
606 pThis->ISerialConnector.pfnQueryStsLines = drvHostSerialQueryStsLines;
607 pThis->ISerialConnector.pfnQueuesFlush = drvHostSerialQueuesFlush;
608
609 /*
610 * Query configuration.
611 */
612 /* Device */
613 int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
614 if (RT_FAILURE(rc))
615 {
616 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
617 return rc;
618 }
619
620 /*
621 * Open the device
622 */
623 uint32_t fOpenFlags = RTSERIALPORT_OPEN_F_READ
624 | RTSERIALPORT_OPEN_F_WRITE
625 | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING
626 | RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION;
627 rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
628 if (rc == VERR_NOT_SUPPORTED)
629 {
630 /*
631 * For certain devices (or pseudo terminals) status line monitoring does not work
632 * so try again without it.
633 */
634 fOpenFlags &= ~RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING;
635 rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags);
636 }
637
638 if (RT_FAILURE(rc))
639 {
640 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
641 switch (rc)
642 {
643 case VERR_ACCESS_DENIED:
644 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
645#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
646 N_("Cannot open host device '%s' for read/write access. Check the permissions "
647 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
648 "of the device group. Make sure that you logout/login after changing "
649 "the group settings of the current user"),
650#else
651 N_("Cannot open host device '%s' for read/write access. Check the permissions "
652 "of that device"),
653#endif
654 pThis->pszDevicePath, pThis->pszDevicePath);
655 default:
656 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
657 N_("Failed to open host device '%s'"),
658 pThis->pszDevicePath);
659 }
660 }
661
662 /*
663 * Get the ISerialPort interface of the above driver/device.
664 */
665 pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT);
666 if (!pThis->pDrvSerialPort)
667 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no serial port interface above"), pDrvIns->iInstance);
668
669 /*
670 * Create the I/O thread.
671 */
672 rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pIoThrd, pThis, drvHostSerialIoThread, drvHostSerialWakeupIoThread, 0, RTTHREADTYPE_IO, "SerIo");
673 if (RT_FAILURE(rc))
674 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create I/O thread"), pDrvIns->iInstance);
675
676 /*
677 * Register release statistics.
678 */
679 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
680 "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
681 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
682 "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
683
684 return VINF_SUCCESS;
685}
686
687/**
688 * Char driver registration record.
689 */
690const PDMDRVREG g_DrvHostSerial =
691{
692 /* u32Version */
693 PDM_DRVREG_VERSION,
694 /* szName */
695 "Host Serial",
696 /* szRCMod */
697 "",
698 /* szR0Mod */
699 "",
700 /* pszDescription */
701 "Host serial driver.",
702 /* fFlags */
703 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
704 /* fClass. */
705 PDM_DRVREG_CLASS_CHAR,
706 /* cMaxInstances */
707 ~0U,
708 /* cbInstance */
709 sizeof(DRVHOSTSERIAL),
710 /* pfnConstruct */
711 drvHostSerialConstruct,
712 /* pfnDestruct */
713 drvHostSerialDestruct,
714 /* pfnRelocate */
715 NULL,
716 /* pfnIOCtl */
717 NULL,
718 /* pfnPowerOn */
719 NULL,
720 /* pfnReset */
721 NULL,
722 /* pfnSuspend */
723 NULL,
724 /* pfnResume */
725 NULL,
726 /* pfnAttach */
727 NULL,
728 /* pfnDetach */
729 NULL,
730 /* pfnPowerOff */
731 NULL,
732 /* pfnSoftReset */
733 NULL,
734 /* u32EndVersion */
735 PDM_DRVREG_VERSION
736};
737
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