VirtualBox

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

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

DrvChar.cpp: cosmetics

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