VirtualBox

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

Last change on this file since 55747 was 45061, checked in by vboxsync, 12 years ago

Review of PDM driver destructors making sure that variables they use are correctly initialized in the constructor. Found several RTFileClose(0) cases.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/* $Id: DrvChar.cpp 45061 2013-03-18 14:09:03Z 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 * Note that we don't use a send buffer here to be able to handle
9 * dropping of bytes for xmit at device level.
10 */
11
12/*
13 * Copyright (C) 2006-2012 Oracle Corporation
14 *
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.
22 */
23
24
25/*******************************************************************************
26* Header Files *
27*******************************************************************************/
28#define LOG_GROUP LOG_GROUP_DRV_CHAR
29#include <VBox/vmm/pdmdrv.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/stream.h>
33#include <iprt/semaphore.h>
34#include <iprt/uuid.h>
35
36#include "VBoxDD.h"
37
38
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)
44
45
46/*******************************************************************************
47* Structures and Typedefs *
48*******************************************************************************/
49/**
50 * Char driver instance data.
51 *
52 * @implements PDMICHARCONNECTOR
53 */
54typedef struct DRVCHAR
55{
56 /** Pointer to the driver instance structure. */
57 PPDMDRVINS pDrvIns;
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. */
69 RTTHREAD SendThread;
70 /** Send event semaphore */
71 RTSEMEVENT SendSem;
72
73 /** Internal send FIFO queue */
74 uint8_t volatile u8SendByte;
75 bool volatile fSending;
76 uint8_t Alignment[2];
77
78 /** Read/write statistics */
79 STAMCOUNTER StatBytesRead;
80 STAMCOUNTER StatBytesWritten;
81} DRVCHAR, *PDRVCHAR;
82AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
83
84
85
86
87/* -=-=-=-=- IBase -=-=-=-=- */
88
89/**
90 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
91 */
92static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
93{
94 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
95 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
96
97 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
98 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
99 return NULL;
100}
101
102
103/* -=-=-=-=- ICharConnector -=-=-=-=- */
104
105/** @copydoc PDMICHARCONNECTOR::pfnWrite */
106static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
107{
108 PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface);
109 const char *pbBuffer = (const char *)pvBuf;
110
111 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
112
113 for (uint32_t i = 0; i < cbWrite; i++)
114 {
115 if (ASMAtomicXchgBool(&pThis->fSending, true))
116 return VERR_BUFFER_OVERFLOW;
117
118 pThis->u8SendByte = pbBuffer[i];
119 RTSemEventSignal(pThis->SendSem);
120 STAM_COUNTER_INC(&pThis->StatBytesWritten);
121 }
122 return VINF_SUCCESS;
123}
124
125/** @copydoc PDMICHARCONNECTOR::pfnSetParameters */
126static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
127{
128 /*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
129
130 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
131 return VINF_SUCCESS;
132}
133
134
135/* -=-=-=-=- receive thread -=-=-=-=- */
136
137/**
138 * Send thread loop - pushes data down thru the driver chain.
139 *
140 * @returns 0 on success.
141 * @param ThreadSelf Thread handle to this thread.
142 * @param pvUser User argument.
143 */
144static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
145{
146 PDRVCHAR pThis = (PDRVCHAR)pvUser;
147
148 int rc = VINF_SUCCESS;
149 while (!pThis->fShutdown)
150 {
151 RTMSINTERVAL cMillies = (rc == VERR_TIMEOUT) ? 50 : RT_INDEFINITE_WAIT;
152 rc = RTSemEventWait(pThis->SendSem, cMillies);
153 if ( RT_FAILURE(rc)
154 && rc != VERR_TIMEOUT)
155 break;
156
157 /*
158 * Write the character to the attached stream (if present).
159 */
160 if ( pThis->fShutdown
161 || !pThis->pDrvStream)
162 break;
163
164 size_t cbProcessed = 1;
165 uint8_t ch = pThis->u8SendByte;
166 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed);
167 if (RT_SUCCESS(rc))
168 {
169 ASMAtomicXchgBool(&pThis->fSending, false);
170 Assert(cbProcessed == 1);
171 }
172 else if (rc == VERR_TIMEOUT)
173 {
174 /* Normal case, just means that the stream didn't accept a new
175 * character before the timeout elapsed. Just retry. */
176
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; */
180 }
181 else
182 {
183 LogRel(("Write failed with %Rrc; skipping\n", rc));
184 break;
185 }
186 }
187
188 return VINF_SUCCESS;
189}
190
191/* -=-=-=-=- receive thread -=-=-=-=- */
192
193/**
194 * Receive thread loop.
195 *
196 * @returns 0 on success.
197 * @param ThreadSelf Thread handle to this thread.
198 * @param pvUser User argument.
199 */
200static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
201{
202 PDRVCHAR pThis = (PDRVCHAR)pvUser;
203 char abBuffer[256];
204 char *pbRemaining = abBuffer;
205 size_t cbRemaining = 0;
206 int rc;
207
208 while (!pThis->fShutdown)
209 {
210 if (!cbRemaining)
211 {
212 /* Get block of data from stream driver. */
213 if (pThis->pDrvStream)
214 {
215 pbRemaining = abBuffer;
216 cbRemaining = sizeof(abBuffer);
217 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, abBuffer, &cbRemaining);
218 if (RT_FAILURE(rc))
219 {
220 LogFlow(("Read failed with %Rrc\n", rc));
221 break;
222 }
223 }
224 else
225 RTThreadSleep(100);
226 }
227 else
228 {
229 /* Send data to guest. */
230 size_t cbProcessed = cbRemaining;
231 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbRemaining, &cbProcessed);
232 if (RT_SUCCESS(rc))
233 {
234 Assert(cbProcessed);
235 pbRemaining += cbProcessed;
236 cbRemaining -= cbProcessed;
237 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
238 }
239 else if (rc == VERR_TIMEOUT)
240 {
241 /* Normal case, just means that the guest didn't accept a new
242 * character before the timeout elapsed. Just retry. */
243 rc = VINF_SUCCESS;
244 }
245 else
246 {
247 LogFlow(("NotifyRead failed with %Rrc\n", rc));
248 break;
249 }
250 }
251 }
252
253 return VINF_SUCCESS;
254}
255
256/**
257 * Set the modem lines.
258 *
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.
263 */
264static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
265{
266 /* Nothing to do here. */
267 return VINF_SUCCESS;
268}
269
270/**
271 * Sets the TD line into break condition.
272 *
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.
277 */
278static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
279{
280 /* Nothing to do here. */
281 return VINF_SUCCESS;
282}
283
284/* -=-=-=-=- driver interface -=-=-=-=- */
285
286/**
287 * Destruct a char driver instance.
288 *
289 * Most VM resources are freed by the VM. This callback is provided so that
290 * any non-VM resources can be freed correctly.
291 *
292 * @param pDrvIns The driver instance data.
293 */
294static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
295{
296 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
297 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
298 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
299
300 /*
301 * Tell the threads to shut down.
302 */
303 pThis->fShutdown = true;
304 if (pThis->SendSem != NIL_RTSEMEVENT)
305 {
306 RTSemEventSignal(pThis->SendSem);
307 pThis->SendSem = NIL_RTSEMEVENT;
308 }
309
310 /*
311 * Wait for the threads.
312 * ASSUMES that PDM destroys the driver chain from the bottom and up.
313 */
314 if (pThis->ReceiveThread != NIL_RTTHREAD)
315 {
316 int rc = RTThreadWait(pThis->ReceiveThread, 30000, NULL);
317 if (RT_SUCCESS(rc))
318 pThis->ReceiveThread = NIL_RTTHREAD;
319 else
320 LogRel(("Char%d: receive thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
321 }
322
323 if (pThis->SendThread != NIL_RTTHREAD)
324 {
325 int rc = RTThreadWait(pThis->SendThread, 30000, NULL);
326 if (RT_SUCCESS(rc))
327 pThis->SendThread = NIL_RTTHREAD;
328 else
329 LogRel(("Char%d: send thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
330 }
331
332 if (pThis->SendSem != NIL_RTSEMEVENT)
333 {
334 RTSemEventDestroy(pThis->SendSem);
335 pThis->SendSem = NIL_RTSEMEVENT;
336 }
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 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
348 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
349 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
350
351 /*
352 * Init basic data members and interfaces.
353 */
354 pThis->fShutdown = false;
355 pThis->ReceiveThread = NIL_RTTHREAD;
356 pThis->SendThread = NIL_RTTHREAD;
357 pThis->SendSem = NIL_RTSEMEVENT;
358 /* IBase. */
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;
365
366 /*
367 * Get the ICharPort interface of the above driver/device.
368 */
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);
372
373 /*
374 * Attach driver below and query its stream interface.
375 */
376 PPDMIBASE pBase;
377 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
378 if (RT_FAILURE(rc))
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);
383
384 /*
385 * Don't start the receive thread if the driver doesn't support reading
386 */
387 if (pThis->pDrvStream->pfnRead)
388 {
389 rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0,
390 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
391 if (RT_FAILURE(rc))
392 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
393 }
394
395 rc = RTSemEventCreate(&pThis->SendSem);
396 AssertRCReturn(rc, rc);
397
398 rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0,
399 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
400 if (RT_FAILURE(rc))
401 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
402
403
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);
406
407 return VINF_SUCCESS;
408}
409
410
411/**
412 * Char driver registration record.
413 */
414const PDMDRVREG g_DrvChar =
415{
416 /* u32Version */
417 PDM_DRVREG_VERSION,
418 /* szName */
419 "Char",
420 /* szRCMod */
421 "",
422 /* szR0Mod */
423 "",
424 /* pszDescription */
425 "Generic char driver.",
426 /* fFlags */
427 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
428 /* fClass. */
429 PDM_DRVREG_CLASS_CHAR,
430 /* cMaxInstances */
431 ~0U,
432 /* cbInstance */
433 sizeof(DRVCHAR),
434 /* pfnConstruct */
435 drvCharConstruct,
436 /* pfnDestruct */
437 drvCharDestruct,
438 /* pfnRelocate */
439 NULL,
440 /* pfnIOCtl */
441 NULL,
442 /* pfnPowerOn */
443 NULL,
444 /* pfnReset */
445 NULL,
446 /* pfnSuspend */
447 NULL,
448 /* pfnResume */
449 NULL,
450 /* pfnAttach */
451 NULL,
452 /* pfnDetach */
453 NULL,
454 /* pfnPowerOff */
455 NULL,
456 /* pfnSoftReset */
457 NULL,
458 /* u32EndVersion */
459 PDM_DRVREG_VERSION
460};
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