VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvChar.cpp@ 73992

Last change on this file since 73992 was 73766, checked in by vboxsync, 6 years ago

Serial: Remove the old serial code and switch over to the new one by default now that it is mostly stable (Any remaining bugs will get fixed)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1/* $Id: DrvChar.cpp 73766 2018-08-19 16:55:11Z vboxsync $ */
2/** @file
3 * Driver that adapts PDMISTREAM into PDMISERIALCONNECTOR / PDMISERIALPORT.
4 */
5
6/*
7 * Copyright (C) 2006-2018 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* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_CHAR
23#include <VBox/vmm/pdmdrv.h>
24#include <VBox/vmm/pdmserialifs.h>
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/poll.h>
28#include <iprt/stream.h>
29#include <iprt/critsect.h>
30#include <iprt/semaphore.h>
31#include <iprt/uuid.h>
32
33#include "VBoxDD.h"
34
35
36/*********************************************************************************************************************************
37* Defined Constants And Macros *
38*********************************************************************************************************************************/
39
40
41/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44/**
45 * Char driver instance data.
46 *
47 * @implements PDMISERIALCONNECTOR
48 */
49typedef struct DRVCHAR
50{
51 /** Pointer to the driver instance structure. */
52 PPDMDRVINS pDrvIns;
53 /** Pointer to the char port interface of the driver/device above us. */
54 PPDMISERIALPORT pDrvSerialPort;
55 /** Pointer to the stream interface of the driver below us. */
56 PPDMISTREAM pDrvStream;
57 /** Our serial interface. */
58 PDMISERIALCONNECTOR ISerialConnector;
59 /** Flag to notify the receive thread it should terminate. */
60 volatile bool fShutdown;
61 /** Flag whether data is available from the device/driver above as notified by the driver. */
62 volatile bool fAvailWrExt;
63 /** Internal copy of the flag which gets reset when there is no data anymore. */
64 bool fAvailWrInt;
65 /** I/O thread. */
66 PPDMTHREAD pThrdIo;
67
68 /** Small send buffer. */
69 uint8_t abTxBuf[16];
70 /** Amount of data in the buffer. */
71 size_t cbTxUsed;
72
73 /** Receive buffer. */
74 uint8_t abBuffer[256];
75 /** Number of bytes remaining in the receive buffer. */
76 volatile size_t cbRemaining;
77 /** Current position into the read buffer. */
78 uint8_t *pbBuf;
79
80 /** Read/write statistics */
81 STAMCOUNTER StatBytesRead;
82 STAMCOUNTER StatBytesWritten;
83} DRVCHAR, *PDRVCHAR;
84AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
85
86
87
88
89/* -=-=-=-=- IBase -=-=-=-=- */
90
91/**
92 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
93 */
94static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
95{
96 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
97 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
98
99 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
100 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector);
101 return NULL;
102}
103
104
105/* -=-=-=-=- ISerialConnector -=-=-=-=- */
106
107
108/**
109 * @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify}
110 */
111static DECLCALLBACK(int) drvCharDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface)
112{
113 LogFlowFunc(("pInterface=%#p\n", pInterface));
114 PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector);
115
116 int rc = VINF_SUCCESS;
117 bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true);
118 if (!fAvailOld)
119 rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
120
121 return rc;
122}
123
124
125/**
126 * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr}
127 */
128static DECLCALLBACK(int) drvCharReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf, size_t cbRead, size_t *pcbRead)
129{
130 LogFlowFunc(("pInterface=%#p pvBuf=%#p cbRead=%zu pcbRead=%#p\n", pInterface, pvBuf, cbRead, pcbRead));
131 PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector);
132 int rc = VINF_SUCCESS;
133
134 AssertReturn(pThis->cbRemaining, VERR_INVALID_STATE);
135 size_t cbToRead = RT_MIN(cbRead, pThis->cbRemaining);
136 memcpy(pvBuf, pThis->pbBuf, cbToRead);
137
138 pThis->pbBuf += cbToRead;
139 *pcbRead = cbToRead;
140 size_t cbOld = ASMAtomicSubZ(&pThis->cbRemaining, cbToRead);
141 if (!(cbOld - cbToRead)) /* Kick the I/O thread to fetch new data. */
142 rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
143 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbToRead);
144
145 LogFlowFunc(("-> %Rrc\n", rc));
146 return rc;
147}
148
149
150/**
151 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams}
152 */
153static DECLCALLBACK(int) drvCharChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps,
154 PDMSERIALPARITY enmParity, unsigned cDataBits,
155 PDMSERIALSTOPBITS enmStopBits)
156{
157 /* Nothing to do here. */
158 RT_NOREF(pInterface, uBps, enmParity, cDataBits, enmStopBits);
159 return VINF_SUCCESS;
160}
161
162
163/**
164 * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines}
165 */
166static DECLCALLBACK(int) drvCharChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr)
167{
168 /* Nothing to do here. */
169 RT_NOREF(pInterface, fRts, fDtr);
170 return VINF_SUCCESS;
171}
172
173
174/**
175 * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgBrk}
176 */
177static DECLCALLBACK(int) drvCharChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk)
178{
179 /* Nothing to do here. */
180 RT_NOREF(pInterface, fBrk);
181 return VINF_SUCCESS;
182}
183
184
185/**
186 * @callback_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines}
187 */
188static DECLCALLBACK(int) drvCharQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines)
189{
190 /* Nothing to do here. */
191 *pfStsLines = 0;
192 RT_NOREF(pInterface);
193 return VINF_SUCCESS;
194}
195
196
197/* -=-=-=-=- I/O thread -=-=-=-=- */
198
199/**
200 * Send thread loop - pushes data down thru the driver chain.
201 *
202 * @returns VBox status code.
203 * @param pDrvIns The char driver instance.
204 * @param pThread The worker thread.
205 */
206static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
207{
208 RT_NOREF(pDrvIns);
209 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
210
211 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
212 return VINF_SUCCESS;
213
214 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
215 {
216 uint32_t fEvts = 0;
217
218 if (!pThis->fAvailWrInt)
219 pThis->fAvailWrInt = ASMAtomicXchgBool(&pThis->fAvailWrExt, false);
220
221 if ( !pThis->cbRemaining
222 && pThis->pDrvStream->pfnRead)
223 fEvts |= RTPOLL_EVT_READ;
224 if ( pThis->fAvailWrInt
225 || pThis->cbTxUsed)
226 fEvts |= RTPOLL_EVT_WRITE;
227
228 uint32_t fEvtsRecv = 0;
229 int rc = pThis->pDrvStream->pfnPoll(pThis->pDrvStream, fEvts, &fEvtsRecv, RT_INDEFINITE_WAIT);
230 if (RT_SUCCESS(rc))
231 {
232 if (fEvtsRecv & RTPOLL_EVT_WRITE)
233 {
234 if (pThis->fAvailWrInt)
235 {
236 /* Stuff as much data into the TX buffer as we can. */
237 size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed;
238 size_t cbFetched = 0;
239 rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch,
240 &cbFetched);
241 AssertRC(rc);
242
243 if (cbFetched > 0)
244 pThis->cbTxUsed += cbFetched;
245 else
246 {
247 /* There is no data available anymore. */
248 pThis->fAvailWrInt = false;
249 }
250 }
251
252 if (pThis->cbTxUsed)
253 {
254 size_t cbProcessed = pThis->cbTxUsed;
255 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->abTxBuf[0], &cbProcessed);
256 if (RT_SUCCESS(rc))
257 {
258 pThis->cbTxUsed -= cbProcessed;
259 if (pThis->cbTxUsed)
260 {
261 /* Move the data in the TX buffer to the front to fill the end again. */
262 memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed);
263 }
264 else
265 pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort);
266 STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed);
267 }
268 else if (rc != VERR_TIMEOUT)
269 {
270 LogRel(("Char#%d: Write failed with %Rrc; skipping\n", pDrvIns->iInstance, rc));
271 break;
272 }
273 }
274 }
275
276 if (fEvtsRecv & RTPOLL_EVT_READ)
277 {
278 AssertPtr(pThis->pDrvStream->pfnRead);
279 Assert(!pThis->cbRemaining);
280
281 size_t cbRead = sizeof(pThis->abBuffer);
282 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, &pThis->abBuffer[0], &cbRead);
283 if (RT_FAILURE(rc))
284 {
285 LogFlow(("Read failed with %Rrc\n", rc));
286 break;
287 }
288
289 if (cbRead)
290 {
291 pThis->pbBuf = &pThis->abBuffer[0];
292 ASMAtomicWriteZ(&pThis->cbRemaining, cbRead);
293 /* Notify the upper device/driver. */
294 rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead);
295 }
296 }
297 }
298 else if (rc != VERR_INTERRUPTED)
299 LogRelMax(10, ("Char#%d: Polling failed with %Rrc\n", pDrvIns->iInstance, rc));
300 }
301
302 return VINF_SUCCESS;
303}
304
305
306/**
307 * Unblock the send worker thread so it can respond to a state change.
308 *
309 * @returns VBox status code.
310 * @param pDrvIns The char driver instance.
311 * @param pThread The worker thread.
312 */
313static DECLCALLBACK(int) drvCharIoLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
314{
315 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
316
317 RT_NOREF(pDrvIns);
318 return pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
319}
320
321
322/* -=-=-=-=- driver interface -=-=-=-=- */
323
324/**
325 * @interface_method_impl{PDMDEVREG,pfnReset}
326 */
327static DECLCALLBACK(void) drvCharReset(PPDMDRVINS pDrvIns)
328{
329 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
330 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
331
332 /* Reset TX and RX buffers. */
333 pThis->fAvailWrExt = false;
334 pThis->fAvailWrInt = false;
335 pThis->cbTxUsed = 0;
336 pThis->cbRemaining = 0;
337}
338
339
340/**
341 * Construct a char driver instance.
342 *
343 * @copydoc FNPDMDRVCONSTRUCT
344 */
345static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
346{
347 RT_NOREF(pCfg);
348 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
349 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
350 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
351
352 /*
353 * Init basic data members and interfaces.
354 */
355 pThis->pDrvIns = pDrvIns;
356 pThis->pThrdIo = NIL_RTTHREAD;
357 /* IBase. */
358 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
359 /* ISerialConnector. */
360 pThis->ISerialConnector.pfnDataAvailWrNotify = drvCharDataAvailWrNotify;
361 pThis->ISerialConnector.pfnReadRdr = drvCharReadRdr;
362 pThis->ISerialConnector.pfnChgParams = drvCharChgParams;
363 pThis->ISerialConnector.pfnChgModemLines = drvCharChgModemLines;
364 pThis->ISerialConnector.pfnChgBrk = drvCharChgBrk;
365 pThis->ISerialConnector.pfnQueryStsLines = drvCharQueryStsLines;
366
367 /*
368 * Get the ISerialPort interface of the above driver/device.
369 */
370 pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT);
371 if (!pThis->pDrvSerialPort)
372 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS,
373 N_("Char#%d has no serial port interface above"), pDrvIns->iInstance);
374
375 /*
376 * Attach driver below and query its stream interface.
377 */
378 PPDMIBASE pBase;
379 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
380 if (RT_FAILURE(rc))
381 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
382 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
383 if (!pThis->pDrvStream)
384 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS,
385 N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
386
387 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdIo, pThis, drvCharIoLoop,
388 drvCharIoLoopWakeup, 0, RTTHREADTYPE_IO, "CharIo");
389 if (RT_FAILURE(rc))
390 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create I/O thread"), pDrvIns->iInstance);
391
392
393 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
394 "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
395 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
396 "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
397
398 return VINF_SUCCESS;
399}
400
401
402/**
403 * Char driver registration record.
404 */
405const PDMDRVREG g_DrvChar =
406{
407 /* u32Version */
408 PDM_DRVREG_VERSION,
409 /* szName */
410 "Char",
411 /* szRCMod */
412 "",
413 /* szR0Mod */
414 "",
415 /* pszDescription */
416 "Generic char driver.",
417 /* fFlags */
418 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
419 /* fClass. */
420 PDM_DRVREG_CLASS_CHAR,
421 /* cMaxInstances */
422 ~0U,
423 /* cbInstance */
424 sizeof(DRVCHAR),
425 /* pfnConstruct */
426 drvCharConstruct,
427 /* pfnDestruct */
428 NULL,
429 /* pfnRelocate */
430 NULL,
431 /* pfnIOCtl */
432 NULL,
433 /* pfnPowerOn */
434 NULL,
435 /* pfnReset */
436 drvCharReset,
437 /* pfnSuspend */
438 NULL,
439 /* pfnResume */
440 NULL,
441 /* pfnAttach */
442 NULL,
443 /* pfnDetach */
444 NULL,
445 /* pfnPowerOff */
446 NULL,
447 /* pfnSoftReset */
448 NULL,
449 /* u32EndVersion */
450 PDM_DRVREG_VERSION
451};
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