VirtualBox

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

Last change on this file since 6217 was 6217, checked in by vboxsync, 17 years ago

serial: don't overwrite VM errors from the driver

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 KB
Line 
1/** @file
2 *
3 * VBox stream I/O devices:
4 * Generic char driver
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20
21/*******************************************************************************
22* Header Files *
23*******************************************************************************/
24#define LOG_GROUP LOG_GROUP_DRV_CHAR
25#include <VBox/pdmdrv.h>
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/stream.h>
29#include <iprt/semaphore.h>
30
31#include "Builtins.h"
32
33
34/** Size of the send fifo queue (in bytes) */
35#define CHAR_MAX_SEND_QUEUE 0x80
36#define CHAR_MAX_SEND_QUEUE_MASK 0x7f
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41
42/**
43 * Char driver instance data.
44 */
45typedef struct DRVCHAR
46{
47 /** Pointer to the driver instance structure. */
48 PPDMDRVINS pDrvIns;
49 /** Pointer to the char port interface of the driver/device above us. */
50 PPDMICHARPORT pDrvCharPort;
51 /** Pointer to the stream interface of the driver below us. */
52 PPDMISTREAM pDrvStream;
53 /** Our char interface. */
54 PDMICHAR IChar;
55 /** Flag to notify the receive thread it should terminate. */
56 volatile bool fShutdown;
57 /** Receive thread ID. */
58 RTTHREAD ReceiveThread;
59 /** Send thread ID. */
60 RTTHREAD SendThread;
61 /** Send event semephore */
62 RTSEMEVENT SendSem;
63
64 /** Internal send FIFO queue */
65 uint8_t aSendQueue[CHAR_MAX_SEND_QUEUE];
66 uint32_t iSendQueueHead;
67 uint32_t iSendQueueTail;
68
69 /** Read/write statistics */
70 STAMCOUNTER StatBytesRead;
71 STAMCOUNTER StatBytesWritten;
72} DRVCHAR, *PDRVCHAR;
73
74
75/** Converts a pointer to DRVCHAR::IChar to a PDRVCHAR. */
76#define PDMICHAR_2_DRVCHAR(pInterface) ( (PDRVCHAR)((uintptr_t)pInterface - RT_OFFSETOF(DRVCHAR, IChar)) )
77
78
79/* -=-=-=-=- IBase -=-=-=-=- */
80
81/**
82 * Queries an interface to the driver.
83 *
84 * @returns Pointer to interface.
85 * @returns NULL if the interface was not supported by the driver.
86 * @param pInterface Pointer to this interface structure.
87 * @param enmInterface The requested interface identification.
88 */
89static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
90{
91 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
92 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
93 switch (enmInterface)
94 {
95 case PDMINTERFACE_BASE:
96 return &pDrvIns->IBase;
97 case PDMINTERFACE_CHAR:
98 return &pData->IChar;
99 default:
100 return NULL;
101 }
102}
103
104
105/* -=-=-=-=- IChar -=-=-=-=- */
106
107/** @copydoc PDMICHAR::pfnWrite */
108static DECLCALLBACK(int) drvCharWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
109{
110 PDRVCHAR pData = PDMICHAR_2_DRVCHAR(pInterface);
111 const char *pBuffer = (const char *)pvBuf;
112
113 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
114
115 for (uint32_t i=0;i<cbWrite;i++)
116 {
117 uint32_t idx = pData->iSendQueueHead;
118
119 pData->aSendQueue[idx] = pBuffer[i];
120 idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
121
122 STAM_COUNTER_INC(&pData->StatBytesWritten);
123 ASMAtomicXchgU32(&pData->iSendQueueHead, idx);
124 }
125 RTSemEventSignal(pData->SendSem);
126 return VINF_SUCCESS;
127}
128
129/** @copydoc PDMICHAR::pfnSetParameters */
130static DECLCALLBACK(int) drvCharSetParameters(PPDMICHAR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
131{
132 /*PDRVCHAR pData = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
133
134 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
135 return VINF_SUCCESS;
136}
137
138
139/* -=-=-=-=- receive thread -=-=-=-=- */
140
141/**
142 * Send thread loop.
143 *
144 * @returns 0 on success.
145 * @param ThreadSelf Thread handle to this thread.
146 * @param pvUser User argument.
147 */
148static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
149{
150 PDRVCHAR pData = (PDRVCHAR)pvUser;
151
152 for(;;)
153 {
154 int rc = RTSemEventWait(pData->SendSem, RT_INDEFINITE_WAIT);
155 if (VBOX_FAILURE(rc))
156 break;
157
158 /*
159 * Write the character to the attached stream (if present).
160 */
161 if ( !pData->fShutdown
162 && pData->pDrvStream)
163 {
164 while (pData->iSendQueueTail != pData->iSendQueueHead)
165 {
166 size_t cbProcessed = 1;
167
168 rc = pData->pDrvStream->pfnWrite(pData->pDrvStream, &pData->aSendQueue[pData->iSendQueueTail], &cbProcessed);
169 if (VBOX_SUCCESS(rc))
170 {
171 Assert(cbProcessed);
172 pData->iSendQueueTail++;
173 pData->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
174 }
175 else if (rc == VERR_TIMEOUT)
176 {
177 /* Normal case, just means that the stream didn't accept a new
178 * character before the timeout elapsed. Just retry. */
179 rc = VINF_SUCCESS;
180 }
181 else
182 {
183 LogFlow(("Write failed with %Vrc; skipping\n", rc));
184 break;
185 }
186 }
187 }
188 else
189 break;
190 }
191
192 pData->SendThread = NIL_RTTHREAD;
193
194 return VINF_SUCCESS;
195}
196
197/* -=-=-=-=- receive thread -=-=-=-=- */
198
199/**
200 * Receive thread loop.
201 *
202 * @returns 0 on success.
203 * @param ThreadSelf Thread handle to this thread.
204 * @param pvUser User argument.
205 */
206static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
207{
208 PDRVCHAR pData = (PDRVCHAR)pvUser;
209 char aBuffer[256], *pBuffer;
210 size_t cbRemaining, cbProcessed;
211 int rc;
212
213 cbRemaining = 0;
214 pBuffer = aBuffer;
215 while (!pData->fShutdown)
216 {
217 if (!cbRemaining)
218 {
219 /* Get block of data from stream driver. */
220 if (pData->pDrvStream)
221 {
222 cbRemaining = sizeof(aBuffer);
223 rc = pData->pDrvStream->pfnRead(pData->pDrvStream, aBuffer, &cbRemaining);
224 if (VBOX_FAILURE(rc))
225 {
226 LogFlow(("Read failed with %Vrc\n", rc));
227 break;
228 }
229 }
230 else
231 {
232 cbRemaining = 0;
233 RTThreadSleep(100);
234 }
235 pBuffer = aBuffer;
236 }
237 else
238 {
239 /* Send data to guest. */
240 cbProcessed = cbRemaining;
241 rc = pData->pDrvCharPort->pfnNotifyRead(pData->pDrvCharPort, pBuffer, &cbProcessed);
242 if (VBOX_SUCCESS(rc))
243 {
244 Assert(cbProcessed);
245 pBuffer += cbProcessed;
246 cbRemaining -= cbProcessed;
247 STAM_COUNTER_ADD(&pData->StatBytesRead, cbProcessed);
248 }
249 else if (rc == VERR_TIMEOUT)
250 {
251 /* Normal case, just means that the guest didn't accept a new
252 * character before the timeout elapsed. Just retry. */
253 rc = VINF_SUCCESS;
254 }
255 else
256 {
257 LogFlow(("NotifyRead failed with %Vrc\n", rc));
258 break;
259 }
260 }
261 }
262
263 pData->ReceiveThread = NIL_RTTHREAD;
264
265 return VINF_SUCCESS;
266}
267
268/**
269 * Set the modem lines.
270 *
271 * @returns VBox status code
272 * @param pInterface Pointer to the interface structure.
273 * @param RequestToSend Set to true if this control line should be made active.
274 * @param DataTerminalReady Set to true if this control line should be made active.
275 */
276static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHAR pInterface, bool RequestToSend, bool DataTerminalReady)
277{
278 /* Nothing to do here. */
279 return VINF_SUCCESS;
280}
281
282/* -=-=-=-=- driver interface -=-=-=-=- */
283
284/**
285 * Construct a char driver instance.
286 *
287 * @returns VBox status.
288 * @param pDrvIns The driver instance data.
289 * If the registration structure is needed,
290 * pDrvIns->pDrvReg points to it.
291 * @param pCfgHandle Configuration node handle for the driver. Use this to
292 * obtain the configuration of the driver instance. It's
293 * also found in pDrvIns->pCfgHandle as it's expected to
294 * be used frequently in this function.
295 */
296static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
297{
298 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
299 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
300
301 /*
302 * Init basic data members and interfaces.
303 */
304 pData->ReceiveThread = NIL_RTTHREAD;
305 pData->fShutdown = false;
306 /* IBase. */
307 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
308 /* IChar. */
309 pData->IChar.pfnWrite = drvCharWrite;
310 pData->IChar.pfnSetParameters = drvCharSetParameters;
311 pData->IChar.pfnSetModemLines = drvCharSetModemLines;
312
313 /*
314 * Get the ICharPort interface of the above driver/device.
315 */
316 pData->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
317 if (!pData->pDrvCharPort)
318 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
319
320 /*
321 * Attach driver below and query its stream interface.
322 */
323 PPDMIBASE pBase;
324 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
325 if (VBOX_FAILURE(rc))
326 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
327 pData->pDrvStream = (PPDMISTREAM)pBase->pfnQueryInterface(pBase, PDMINTERFACE_STREAM);
328 if (!pData->pDrvStream)
329 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
330
331 rc = RTThreadCreate(&pData->ReceiveThread, drvCharReceiveLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char Receive");
332 if (VBOX_FAILURE(rc))
333 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
334
335 rc = RTSemEventCreate(&pData->SendSem);
336 AssertRC(rc);
337
338 rc = RTThreadCreate(&pData->SendThread, drvCharSendLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char Send");
339 if (VBOX_FAILURE(rc))
340 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
341
342
343 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
344 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
345
346 return VINF_SUCCESS;
347}
348
349
350/**
351 * Destruct a char driver instance.
352 *
353 * Most VM resources are freed by the VM. This callback is provided so that
354 * any non-VM resources can be freed correctly.
355 *
356 * @param pDrvIns The driver instance data.
357 */
358static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
359{
360 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
361
362 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
363
364 pData->fShutdown = true;
365 if (pData->ReceiveThread)
366 {
367 RTThreadWait(pData->ReceiveThread, 1000, NULL);
368 if (pData->ReceiveThread != NIL_RTTHREAD)
369 LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
370 }
371
372 /* Empty the send queue */
373 pData->iSendQueueTail = pData->iSendQueueHead = 0;
374
375 RTSemEventSignal(pData->SendSem);
376 RTSemEventDestroy(pData->SendSem);
377 pData->SendSem = NIL_RTSEMEVENT;
378
379 if (pData->SendThread)
380 {
381 RTThreadWait(pData->SendThread, 1000, NULL);
382 if (pData->SendThread != NIL_RTTHREAD)
383 LogRel(("Char%d: send thread did not terminate\n", pDrvIns->iInstance));
384 }
385}
386
387/**
388 * Char driver registration record.
389 */
390const PDMDRVREG g_DrvChar =
391{
392 /* u32Version */
393 PDM_DRVREG_VERSION,
394 /* szDriverName */
395 "Char",
396 /* pszDescription */
397 "Generic char driver.",
398 /* fFlags */
399 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
400 /* fClass. */
401 PDM_DRVREG_CLASS_CHAR,
402 /* cMaxInstances */
403 ~0,
404 /* cbInstance */
405 sizeof(DRVCHAR),
406 /* pfnConstruct */
407 drvCharConstruct,
408 /* pfnDestruct */
409 drvCharDestruct,
410 /* pfnIOCtl */
411 NULL,
412 /* pfnPowerOn */
413 NULL,
414 /* pfnReset */
415 NULL,
416 /* pfnSuspend */
417 NULL,
418 /* pfnResume */
419 NULL,
420 /* pfnDetach */
421 NULL,
422 /** pfnPowerOff */
423 NULL
424};
425
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