VirtualBox

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

Last change on this file since 40754 was 40282, checked in by vboxsync, 12 years ago

*: gcc-4.7: ~0 => ~0U in initializers (warning: narrowing conversion of -1' from int' to `unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing])

  • 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 40282 2012-02-28 21:02:40Z 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-2010 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 }
308
309 /*
310 * Wait for the threads.
311 * ASSUMES that PDM destroys the driver chain from the the bottom and up.
312 */
313 if (pThis->ReceiveThread != NIL_RTTHREAD)
314 {
315 int rc = RTThreadWait(pThis->ReceiveThread, 30000, NULL);
316 if (RT_SUCCESS(rc))
317 pThis->ReceiveThread = NIL_RTTHREAD;
318 else
319 LogRel(("Char%d: receive thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
320 }
321
322 if (pThis->SendThread != NIL_RTTHREAD)
323 {
324 int rc = RTThreadWait(pThis->SendThread, 30000, NULL);
325 if (RT_SUCCESS(rc))
326 pThis->SendThread = NIL_RTTHREAD;
327 else
328 LogRel(("Char%d: send thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
329 }
330
331 if (pThis->SendSem != NIL_RTSEMEVENT)
332 {
333 RTSemEventDestroy(pThis->SendSem);
334 pThis->SendSem = NIL_RTSEMEVENT;
335 }
336}
337
338
339/**
340 * Construct a char driver instance.
341 *
342 * @copydoc FNPDMDRVCONSTRUCT
343 */
344static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
345{
346 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
347 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
348 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
349
350 /*
351 * Init basic data members and interfaces.
352 */
353 pThis->fShutdown = false;
354 pThis->ReceiveThread = NIL_RTTHREAD;
355 pThis->SendThread = NIL_RTTHREAD;
356 pThis->SendSem = NIL_RTSEMEVENT;
357 /* IBase. */
358 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
359 /* ICharConnector. */
360 pThis->ICharConnector.pfnWrite = drvCharWrite;
361 pThis->ICharConnector.pfnSetParameters = drvCharSetParameters;
362 pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines;
363 pThis->ICharConnector.pfnSetBreak = drvCharSetBreak;
364
365 /*
366 * Get the ICharPort interface of the above driver/device.
367 */
368 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
369 if (!pThis->pDrvCharPort)
370 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
371
372 /*
373 * Attach driver below and query its stream interface.
374 */
375 PPDMIBASE pBase;
376 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
377 if (RT_FAILURE(rc))
378 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
379 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
380 if (!pThis->pDrvStream)
381 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
382
383 /*
384 * Don't start the receive thread if the driver doesn't support reading
385 */
386 if (pThis->pDrvStream->pfnRead)
387 {
388 rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0,
389 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
390 if (RT_FAILURE(rc))
391 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
392 }
393
394 rc = RTSemEventCreate(&pThis->SendSem);
395 AssertRCReturn(rc, rc);
396
397 rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0,
398 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
399 if (RT_FAILURE(rc))
400 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
401
402
403 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
404 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
405
406 return VINF_SUCCESS;
407}
408
409
410/**
411 * Char driver registration record.
412 */
413const PDMDRVREG g_DrvChar =
414{
415 /* u32Version */
416 PDM_DRVREG_VERSION,
417 /* szName */
418 "Char",
419 /* szRCMod */
420 "",
421 /* szR0Mod */
422 "",
423 /* pszDescription */
424 "Generic char driver.",
425 /* fFlags */
426 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
427 /* fClass. */
428 PDM_DRVREG_CLASS_CHAR,
429 /* cMaxInstances */
430 ~0U,
431 /* cbInstance */
432 sizeof(DRVCHAR),
433 /* pfnConstruct */
434 drvCharConstruct,
435 /* pfnDestruct */
436 drvCharDestruct,
437 /* pfnRelocate */
438 NULL,
439 /* pfnIOCtl */
440 NULL,
441 /* pfnPowerOn */
442 NULL,
443 /* pfnReset */
444 NULL,
445 /* pfnSuspend */
446 NULL,
447 /* pfnResume */
448 NULL,
449 /* pfnAttach */
450 NULL,
451 /* pfnDetach */
452 NULL,
453 /* pfnPowerOff */
454 NULL,
455 /* pfnSoftReset */
456 NULL,
457 /* u32EndVersion */
458 PDM_DRVREG_VERSION
459};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use