1
/* $Id: DrvChar.cpp $ */
3
* Driver that adapts PDMISTREAM into PDMICHARCONNECTOR / PDMICHARPORT.
5
* Converts synchronous calls (PDMICHARCONNECTOR::pfnWrite, PDMISTREAM::pfnRead)
6
* into asynchronous ones.
8
* Note that we don't use a send buffer here to be able to handle
9
* dropping of bytes for xmit at device level.
13
* Copyright (C) 2006-2012 Oracle Corporation
15
* This file is part of VirtualBox Open Source Edition (OSE), as
16
* available from http://www.virtualbox.org. This file is free software;
17
* you can redistribute it and/or modify it under the terms of the GNU
18
* General Public License (GPL) as published by the Free Software
19
* Foundation, in version 2 as it comes in the "COPYING" file of the
20
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
25
/*******************************************************************************
27
*******************************************************************************/
28
#define LOG_GROUP LOG_GROUP_DRV_CHAR
29
#include <VBox/vmm/pdmdrv.h>
31
#include <iprt/assert.h>
32
#include <iprt/stream.h>
33
#include <iprt/semaphore.h>
34
#include <iprt/uuid.h>
39
/*******************************************************************************
40
* Defined Constants And Macros *
41
*******************************************************************************/
42
/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVCHAR. */
43
#define PDMICHAR_2_DRVCHAR(pInterface) RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector)
46
/*******************************************************************************
47
* Structures and Typedefs *
48
*******************************************************************************/
50
* Char driver instance data.
52
* @implements PDMICHARCONNECTOR
54
typedef struct DRVCHAR
56
/** Pointer to the driver instance structure. */
58
/** Pointer to the char port interface of the driver/device above us. */
59
PPDMICHARPORT pDrvCharPort;
60
/** Pointer to the stream interface of the driver below us. */
61
PPDMISTREAM pDrvStream;
62
/** Our char interface. */
63
PDMICHARCONNECTOR ICharConnector;
64
/** Flag to notify the receive thread it should terminate. */
65
volatile bool fShutdown;
66
/** Receive thread ID. */
67
RTTHREAD ReceiveThread;
68
/** Send thread ID. */
70
/** Send event semaphore */
73
/** Internal send FIFO queue */
74
uint8_t volatile u8SendByte;
75
bool volatile fSending;
78
/** Read/write statistics */
79
STAMCOUNTER StatBytesRead;
80
STAMCOUNTER StatBytesWritten;
82
AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
87
/* -=-=-=-=- IBase -=-=-=-=- */
90
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
92
static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
94
PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
95
PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
97
PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
98
PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
103
/* -=-=-=-=- ICharConnector -=-=-=-=- */
105
/** @copydoc PDMICHARCONNECTOR::pfnWrite */
106
static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
108
PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface);
109
const char *pbBuffer = (const char *)pvBuf;
111
LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
113
for (uint32_t i = 0; i < cbWrite; i++)
115
if (ASMAtomicXchgBool(&pThis->fSending, true))
116
return VERR_BUFFER_OVERFLOW;
118
pThis->u8SendByte = pbBuffer[i];
119
RTSemEventSignal(pThis->SendSem);
120
STAM_COUNTER_INC(&pThis->StatBytesWritten);
125
/** @copydoc PDMICHARCONNECTOR::pfnSetParameters */
126
static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
128
/*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
130
LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
135
/* -=-=-=-=- receive thread -=-=-=-=- */
138
* Send thread loop - pushes data down thru the driver chain.
140
* @returns 0 on success.
141
* @param ThreadSelf Thread handle to this thread.
142
* @param pvUser User argument.
144
static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
146
PDRVCHAR pThis = (PDRVCHAR)pvUser;
148
int rc = VINF_SUCCESS;
149
while (!pThis->fShutdown)
151
RTMSINTERVAL cMillies = (rc == VERR_TIMEOUT) ? 50 : RT_INDEFINITE_WAIT;
152
rc = RTSemEventWait(pThis->SendSem, cMillies);
154
&& rc != VERR_TIMEOUT)
158
* Write the character to the attached stream (if present).
160
if ( pThis->fShutdown
161
|| !pThis->pDrvStream)
164
size_t cbProcessed = 1;
165
uint8_t ch = pThis->u8SendByte;
166
rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed);
169
ASMAtomicXchgBool(&pThis->fSending, false);
170
Assert(cbProcessed == 1);
172
else if (rc == VERR_TIMEOUT)
174
/* Normal case, just means that the stream didn't accept a new
175
* character before the timeout elapsed. Just retry. */
177
/* do not change the rc status here, otherwise the (rc == VERR_TIMEOUT) branch
178
* in the wait above will never get executed */
179
/* rc = VINF_SUCCESS; */
183
LogRel(("Write failed with %Rrc; skipping\n", rc));
191
/* -=-=-=-=- receive thread -=-=-=-=- */
194
* Receive thread loop.
196
* @returns 0 on success.
197
* @param ThreadSelf Thread handle to this thread.
198
* @param pvUser User argument.
200
static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
202
PDRVCHAR pThis = (PDRVCHAR)pvUser;
204
char *pbRemaining = abBuffer;
205
size_t cbRemaining = 0;
208
while (!pThis->fShutdown)
212
/* Get block of data from stream driver. */
213
if (pThis->pDrvStream)
215
pbRemaining = abBuffer;
216
cbRemaining = sizeof(abBuffer);
217
rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, abBuffer, &cbRemaining);
220
LogFlow(("Read failed with %Rrc\n", rc));
229
/* Send data to guest. */
230
size_t cbProcessed = cbRemaining;
231
rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbRemaining, &cbProcessed);
235
pbRemaining += cbProcessed;
236
cbRemaining -= cbProcessed;
237
STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
239
else if (rc == VERR_TIMEOUT)
241
/* Normal case, just means that the guest didn't accept a new
242
* character before the timeout elapsed. Just retry. */
247
LogFlow(("NotifyRead failed with %Rrc\n", rc));
257
* Set the modem lines.
259
* @returns VBox status code
260
* @param pInterface Pointer to the interface structure.
261
* @param RequestToSend Set to true if this control line should be made active.
262
* @param DataTerminalReady Set to true if this control line should be made active.
264
static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
266
/* Nothing to do here. */
271
* Sets the TD line into break condition.
273
* @returns VBox status code.
274
* @param pInterface Pointer to the interface structure containing the called function pointer.
275
* @param fBreak Set to true to let the device send a break false to put into normal operation.
276
* @thread Any thread.
278
static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
280
/* Nothing to do here. */
284
/* -=-=-=-=- driver interface -=-=-=-=- */
287
* Destruct a char driver instance.
289
* Most VM resources are freed by the VM. This callback is provided so that
290
* any non-VM resources can be freed correctly.
292
* @param pDrvIns The driver instance data.
294
static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
296
PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
297
LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
298
PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
301
* Tell the threads to shut down.
303
pThis->fShutdown = true;
304
if (pThis->SendSem != NIL_RTSEMEVENT)
306
RTSemEventSignal(pThis->SendSem);
307
pThis->SendSem = NIL_RTSEMEVENT;
311
* Wait for the threads.
312
* ASSUMES that PDM destroys the driver chain from the bottom and up.
314
if (pThis->ReceiveThread != NIL_RTTHREAD)
316
int rc = RTThreadWait(pThis->ReceiveThread, 30000, NULL);
318
pThis->ReceiveThread = NIL_RTTHREAD;
320
LogRel(("Char%d: receive thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
323
if (pThis->SendThread != NIL_RTTHREAD)
325
int rc = RTThreadWait(pThis->SendThread, 30000, NULL);
327
pThis->SendThread = NIL_RTTHREAD;
329
LogRel(("Char%d: send thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
332
if (pThis->SendSem != NIL_RTSEMEVENT)
334
RTSemEventDestroy(pThis->SendSem);
335
pThis->SendSem = NIL_RTSEMEVENT;
341
* Construct a char driver instance.
343
* @copydoc FNPDMDRVCONSTRUCT
345
static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
347
PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
348
LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
349
PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
352
* Init basic data members and interfaces.
354
pThis->fShutdown = false;
355
pThis->ReceiveThread = NIL_RTTHREAD;
356
pThis->SendThread = NIL_RTTHREAD;
357
pThis->SendSem = NIL_RTSEMEVENT;
359
pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
360
/* ICharConnector. */
361
pThis->ICharConnector.pfnWrite = drvCharWrite;
362
pThis->ICharConnector.pfnSetParameters = drvCharSetParameters;
363
pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines;
364
pThis->ICharConnector.pfnSetBreak = drvCharSetBreak;
367
* Get the ICharPort interface of the above driver/device.
369
pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
370
if (!pThis->pDrvCharPort)
371
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
374
* Attach driver below and query its stream interface.
377
int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
379
return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
380
pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
381
if (!pThis->pDrvStream)
382
return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
385
* Don't start the receive thread if the driver doesn't support reading
387
if (pThis->pDrvStream->pfnRead)
389
rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0,
390
RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
392
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
395
rc = RTSemEventCreate(&pThis->SendSem);
396
AssertRCReturn(rc, rc);
398
rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0,
399
RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
401
return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
404
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
405
PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
412
* Char driver registration record.
414
const PDMDRVREG g_DrvChar =
425
"Generic char driver.",
427
PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
429
PDM_DRVREG_CLASS_CHAR,