VirtualBox

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

Last change on this file since 28800 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.9 KB
Line 
1/* $Id: DrvChar.cpp 28800 2010-04-27 08:22:32Z vboxsync $ */
2/** @file
3 * Driver that adapts PDMISTREAM into PDMICHARCONNECTOR / PDMICHARPORT.
4 *
5 * Converts synchronous calls (PDMICHARCONNECTOR::pfnWrite, PDMISTREAM::pfnRead)
6 * into asynchronous ones.
7 */
8
9/*
10 * Copyright (C) 2006-2010 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*******************************************************************************
23* Header Files *
24*******************************************************************************/
25#define LOG_GROUP LOG_GROUP_DRV_CHAR
26#include <VBox/pdmdrv.h>
27#include <iprt/asm.h>
28#include <iprt/assert.h>
29#include <iprt/stream.h>
30#include <iprt/semaphore.h>
31#include <iprt/uuid.h>
32
33#include "Builtins.h"
34
35
36/*******************************************************************************
37* Defined Constants And Macros *
38*******************************************************************************/
39/** Size of the send fifo queue (in bytes) */
40#define CHAR_MAX_SEND_QUEUE 0x80
41#define CHAR_MAX_SEND_QUEUE_MASK 0x7f
42
43/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVCHAR. */
44#define PDMICHAR_2_DRVCHAR(pInterface) RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector)
45
46
47/*******************************************************************************
48* Structures and Typedefs *
49*******************************************************************************/
50/**
51 * Char driver instance data.
52 *
53 * @implements PDMICHARCONNECTOR
54 */
55typedef struct DRVCHAR
56{
57 /** Pointer to the driver instance structure. */
58 PPDMDRVINS pDrvIns;
59 /** Pointer to the char port interface of the driver/device above us. */
60 PPDMICHARPORT pDrvCharPort;
61 /** Pointer to the stream interface of the driver below us. */
62 PPDMISTREAM pDrvStream;
63 /** Our char interface. */
64 PDMICHARCONNECTOR ICharConnector;
65 /** Flag to notify the receive thread it should terminate. */
66 volatile bool fShutdown;
67 /** Receive thread ID. */
68 RTTHREAD ReceiveThread;
69 /** Send thread ID. */
70 RTTHREAD SendThread;
71 /** Send event semephore */
72 RTSEMEVENT SendSem;
73
74 /** Internal send FIFO queue */
75 uint8_t aSendQueue[CHAR_MAX_SEND_QUEUE];
76 uint32_t volatile iSendQueueHead;
77 uint32_t iSendQueueTail;
78 uint32_t volatile cEntries;
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, PDMICHARCONNECTOR, &pThis->ICharConnector);
101 return NULL;
102}
103
104
105/* -=-=-=-=- ICharConnector -=-=-=-=- */
106
107/** @copydoc PDMICHARCONNECTOR::pfnWrite */
108static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
109{
110 PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface);
111 const char *pbBuffer = (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 iOld = pThis->iSendQueueHead;
118 uint32_t iNew = (iOld + 1) & CHAR_MAX_SEND_QUEUE_MASK;
119
120 pThis->aSendQueue[iOld] = pbBuffer[i];
121
122 STAM_COUNTER_INC(&pThis->StatBytesWritten);
123 ASMAtomicXchgU32(&pThis->iSendQueueHead, iNew);
124 ASMAtomicIncU32(&pThis->cEntries);
125 if (pThis->cEntries > CHAR_MAX_SEND_QUEUE_MASK / 2)
126 pThis->pDrvCharPort->pfnNotifyBufferFull(pThis->pDrvCharPort, true /*fFull*/);
127 }
128 RTSemEventSignal(pThis->SendSem);
129 return VINF_SUCCESS;
130}
131
132/** @copydoc PDMICHARCONNECTOR::pfnSetParameters */
133static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
134{
135 /*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
136
137 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
138 return VINF_SUCCESS;
139}
140
141
142/* -=-=-=-=- receive thread -=-=-=-=- */
143
144/**
145 * Send thread loop - pushes data down thru the driver chain.
146 *
147 * @returns 0 on success.
148 * @param ThreadSelf Thread handle to this thread.
149 * @param pvUser User argument.
150 */
151static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
152{
153 PDRVCHAR pThis = (PDRVCHAR)pvUser;
154
155 while (!pThis->fShutdown)
156 {
157 int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
158 if (RT_FAILURE(rc))
159 break;
160
161 /*
162 * Write the character to the attached stream (if present).
163 */
164 if ( pThis->fShutdown
165 || !pThis->pDrvStream)
166 break;
167
168 while ( pThis->iSendQueueTail != pThis->iSendQueueHead
169 && !pThis->fShutdown)
170 {
171 size_t cbProcessed = 1;
172
173 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->aSendQueue[pThis->iSendQueueTail], &cbProcessed);
174 pThis->pDrvCharPort->pfnNotifyBufferFull(pThis->pDrvCharPort, false /*fFull*/);
175 if (RT_SUCCESS(rc))
176 {
177 Assert(cbProcessed);
178 pThis->iSendQueueTail++;
179 pThis->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
180 ASMAtomicDecU32(&pThis->cEntries);
181 }
182 else if (rc == VERR_TIMEOUT)
183 {
184 /* Normal case, just means that the stream didn't accept a new
185 * character before the timeout elapsed. Just retry. */
186 rc = VINF_SUCCESS;
187 }
188 else
189 {
190 LogFlow(("Write failed with %Rrc; skipping\n", rc));
191 break;
192 }
193 }
194 }
195
196 return VINF_SUCCESS;
197}
198
199/* -=-=-=-=- receive thread -=-=-=-=- */
200
201/**
202 * Receive thread loop.
203 *
204 * @returns 0 on success.
205 * @param ThreadSelf Thread handle to this thread.
206 * @param pvUser User argument.
207 */
208static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
209{
210 PDRVCHAR pThis = (PDRVCHAR)pvUser;
211 char abBuffer[256];
212 char *pbRemaining = abBuffer;
213 size_t cbRemaining = 0;
214 int rc;
215
216 while (!pThis->fShutdown)
217 {
218 if (!cbRemaining)
219 {
220 /* Get block of data from stream driver. */
221 if (pThis->pDrvStream)
222 {
223 pbRemaining = abBuffer;
224 cbRemaining = sizeof(abBuffer);
225 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, abBuffer, &cbRemaining);
226 if (RT_FAILURE(rc))
227 {
228 LogFlow(("Read failed with %Rrc\n", rc));
229 break;
230 }
231 }
232 else
233 RTThreadSleep(100);
234 }
235 else
236 {
237 /* Send data to guest. */
238 size_t cbProcessed = cbRemaining;
239 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbRemaining, &cbProcessed);
240 if (RT_SUCCESS(rc))
241 {
242 Assert(cbProcessed);
243 pbRemaining += cbProcessed;
244 cbRemaining -= cbProcessed;
245 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
246 }
247 else if (rc == VERR_TIMEOUT)
248 {
249 /* Normal case, just means that the guest didn't accept a new
250 * character before the timeout elapsed. Just retry. */
251 rc = VINF_SUCCESS;
252 }
253 else
254 {
255 LogFlow(("NotifyRead failed with %Rrc\n", rc));
256 break;
257 }
258 }
259 }
260
261 return VINF_SUCCESS;
262}
263
264/**
265 * Set the modem lines.
266 *
267 * @returns VBox status code
268 * @param pInterface Pointer to the interface structure.
269 * @param RequestToSend Set to true if this control line should be made active.
270 * @param DataTerminalReady Set to true if this control line should be made active.
271 */
272static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
273{
274 /* Nothing to do here. */
275 return VINF_SUCCESS;
276}
277
278/**
279 * Sets the TD line into break condition.
280 *
281 * @returns VBox status code.
282 * @param pInterface Pointer to the interface structure containing the called function pointer.
283 * @param fBreak Set to true to let the device send a break false to put into normal operation.
284 * @thread Any thread.
285 */
286static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
287{
288 /* Nothing to do here. */
289 return VINF_SUCCESS;
290}
291
292/* -=-=-=-=- driver interface -=-=-=-=- */
293
294/**
295 * Destruct a char driver instance.
296 *
297 * Most VM resources are freed by the VM. This callback is provided so that
298 * any non-VM resources can be freed correctly.
299 *
300 * @param pDrvIns The driver instance data.
301 */
302static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
303{
304 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
305 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
306 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
307
308 /*
309 * Tell the threads to shut down.
310 */
311 pThis->fShutdown = true;
312 if (pThis->SendSem != NIL_RTSEMEVENT)
313 {
314 RTSemEventSignal(pThis->SendSem);
315 RTSemEventDestroy(pThis->SendSem);
316 pThis->SendSem = NIL_RTSEMEVENT;
317 }
318
319 /*
320 * Wait for the threads.
321 * ASSUMES that PDM destroys the driver chain from the the bottom and up.
322 */
323 if (pThis->ReceiveThread != NIL_RTTHREAD)
324 {
325 int rc = RTThreadWait(pThis->ReceiveThread, 30000, NULL);
326 if (RT_SUCCESS(rc))
327 pThis->ReceiveThread = NIL_RTTHREAD;
328 else
329 LogRel(("Char%d: receive thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
330 }
331
332 if (pThis->SendThread != NIL_RTTHREAD)
333 {
334 int rc = RTThreadWait(pThis->SendThread, 30000, NULL);
335 if (RT_SUCCESS(rc))
336 pThis->SendThread = NIL_RTTHREAD;
337 else
338 LogRel(("Char%d: send thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
339 }
340}
341
342
343/**
344 * Construct a char driver instance.
345 *
346 * @copydoc FNPDMDRVCONSTRUCT
347 */
348static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
349{
350 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
351 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
352 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
353
354 /*
355 * Init basic data members and interfaces.
356 */
357 pThis->fShutdown = false;
358 pThis->ReceiveThread = NIL_RTTHREAD;
359 pThis->SendThread = NIL_RTTHREAD;
360 pThis->SendSem = NIL_RTSEMEVENT;
361 /* IBase. */
362 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
363 /* ICharConnector. */
364 pThis->ICharConnector.pfnWrite = drvCharWrite;
365 pThis->ICharConnector.pfnSetParameters = drvCharSetParameters;
366 pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines;
367 pThis->ICharConnector.pfnSetBreak = drvCharSetBreak;
368
369 /*
370 * Get the ICharPort interface of the above driver/device.
371 */
372 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
373 if (!pThis->pDrvCharPort)
374 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
375
376 /*
377 * Attach driver below and query its stream interface.
378 */
379 PPDMIBASE pBase;
380 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
381 if (RT_FAILURE(rc))
382 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
383 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
384 if (!pThis->pDrvStream)
385 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
386
387 /*
388 * Don't start the receive thread if the driver doesn't support reading
389 */
390 if (pThis->pDrvStream->pfnRead)
391 {
392 rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0,
393 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
394 if (RT_FAILURE(rc))
395 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
396 }
397
398 rc = RTSemEventCreate(&pThis->SendSem);
399 AssertRCReturn(rc, rc);
400
401 rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0,
402 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
403 if (RT_FAILURE(rc))
404 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
405
406
407 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
408 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
409
410 return VINF_SUCCESS;
411}
412
413
414/**
415 * Char driver registration record.
416 */
417const PDMDRVREG g_DrvChar =
418{
419 /* u32Version */
420 PDM_DRVREG_VERSION,
421 /* szName */
422 "Char",
423 /* szRCMod */
424 "",
425 /* szR0Mod */
426 "",
427 /* pszDescription */
428 "Generic char driver.",
429 /* fFlags */
430 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
431 /* fClass. */
432 PDM_DRVREG_CLASS_CHAR,
433 /* cMaxInstances */
434 ~0,
435 /* cbInstance */
436 sizeof(DRVCHAR),
437 /* pfnConstruct */
438 drvCharConstruct,
439 /* pfnDestruct */
440 drvCharDestruct,
441 /* pfnRelocate */
442 NULL,
443 /* pfnIOCtl */
444 NULL,
445 /* pfnPowerOn */
446 NULL,
447 /* pfnReset */
448 NULL,
449 /* pfnSuspend */
450 NULL,
451 /* pfnResume */
452 NULL,
453 /* pfnAttach */
454 NULL,
455 /* pfnDetach */
456 NULL,
457 /* pfnPowerOff */
458 NULL,
459 /* pfnSoftReset */
460 NULL,
461 /* u32EndVersion */
462 PDM_DRVREG_VERSION
463};
464
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