VirtualBox

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

Last change on this file since 1680 was 1680, checked in by vboxsync, 18 years ago

eol-style

  • Property svn:eol-style set to native
File size: 9.8 KB
Line 
1/** @file
2 *
3 * VBox stream I/O devices:
4 * Generic char driver
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung 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 as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24
25/*******************************************************************************
26* Header Files *
27*******************************************************************************/
28#define LOG_GROUP LOG_GROUP_DRV_CHAR
29#include <VBox/pdm.h>
30#include <VBox/err.h>
31
32#include <VBox/log.h>
33#include <iprt/assert.h>
34#include <iprt/stream.h>
35
36#include "Builtins.h"
37
38
39/*******************************************************************************
40* Structures and Typedefs *
41*******************************************************************************/
42
43/**
44 * Char driver instance data.
45 */
46typedef struct DRVCHAR
47{
48 /** Pointer to the driver instance structure. */
49 PPDMDRVINS pDrvIns;
50 /** Pointer to the char port interface of the driver/device above us. */
51 PPDMICHARPORT pDrvCharPort;
52 /** Pointer to the stream interface of the driver below us. */
53 PPDMISTREAM pDrvStream;
54 /** Our char interface. */
55 PDMICHAR IChar;
56 /** Flag to notify the receive thread it should terminate. */
57 volatile bool fShutdown;
58 /** Receive thread ID. */
59 RTTHREAD ReceiveThread;
60} DRVCHAR, *PDRVCHAR;
61
62
63/** Converts a pointer to DRVCHAR::IChar to a PDRVCHAR. */
64#define PDMICHAR_2_DRVCHAR(pInterface) ( (PDRVCHAR)((uintptr_t)pInterface - RT_OFFSETOF(DRVCHAR, IChar)) )
65
66
67/* -=-=-=-=- IBase -=-=-=-=- */
68
69/**
70 * Queries an interface to the driver.
71 *
72 * @returns Pointer to interface.
73 * @returns NULL if the interface was not supported by the driver.
74 * @param pInterface Pointer to this interface structure.
75 * @param enmInterface The requested interface identification.
76 */
77static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
78{
79 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
80 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
81 switch (enmInterface)
82 {
83 case PDMINTERFACE_BASE:
84 return &pDrvIns->IBase;
85 case PDMINTERFACE_CHAR:
86 return &pData->IChar;
87 default:
88 return NULL;
89 }
90}
91
92
93/* -=-=-=-=- IChar -=-=-=-=- */
94
95/** @copydoc PDMICHAR::pfnWrite */
96static DECLCALLBACK(int) drvCharWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
97{
98 PDRVCHAR pData = PDMICHAR_2_DRVCHAR(pInterface);
99 int rc = VINF_SUCCESS;
100
101 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
102
103 /*
104 * Write the character to the attached stream (if present).
105 */
106 if (pData->pDrvStream)
107 {
108 const char *pBuffer = (const char *)pvBuf;
109 size_t cbProcessed = cbWrite;
110
111 while (cbWrite)
112 {
113 rc = pData->pDrvStream->pfnWrite(pData->pDrvStream, pBuffer, &cbProcessed);
114 if (VBOX_SUCCESS(rc))
115 {
116 Assert(cbProcessed);
117 cbWrite -= cbProcessed;
118 pBuffer += cbProcessed;
119 }
120 else if (rc == VERR_TIMEOUT)
121 {
122 /* Normal case, just means that the stream didn't accept a new
123 * character before the timeout elapsed. Just retry. */
124 rc = VINF_SUCCESS;
125 }
126 else
127 AssertRC(rc);
128 }
129 }
130 else
131 rc = VERR_PDM_NO_ATTACHED_DRIVER;
132
133 LogFlow(("%s: returns rc=%Vrc\n", __FUNCTION__, rc));
134 return rc;
135}
136
137
138/* -=-=-=-=- receive thread -=-=-=-=- */
139
140/**
141 * Receive thread loop.
142 *
143 * @returns 0 on success.
144 * @param ThreadSelf Thread handle to this thread.
145 * @param pvUser User argument.
146 */
147static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
148{
149 PDRVCHAR pData = (PDRVCHAR)pvUser;
150 char aBuffer[256], *pBuffer;
151 size_t cbRemaining, cbProcessed;
152 int rc;
153
154 cbRemaining = 0;
155 pBuffer = aBuffer;
156 while (!pData->fShutdown)
157 {
158 if (!cbRemaining)
159 {
160 /* Get block of data from stream driver. */
161 if (pData->pDrvStream)
162 {
163 cbRemaining = sizeof(aBuffer);
164 rc = pData->pDrvStream->pfnRead(pData->pDrvStream, aBuffer, &cbRemaining);
165 AssertRC(rc);
166 }
167 else
168 {
169 cbRemaining = 0;
170 RTThreadSleep(100);
171 }
172 pBuffer = aBuffer;
173 }
174 else
175 {
176 /* Send data to guest. */
177 cbProcessed = cbRemaining;
178 rc = pData->pDrvCharPort->pfnNotifyRead(pData->pDrvCharPort, pBuffer, &cbProcessed);
179 if (VBOX_SUCCESS(rc))
180 {
181 Assert(cbProcessed);
182 pBuffer += cbProcessed;
183 cbRemaining -= cbProcessed;
184 }
185 else if (rc == VERR_TIMEOUT)
186 {
187 /* Normal case, just means that the guest didn't accept a new
188 * character before the timeout elapsed. Just retry. */
189 rc = VINF_SUCCESS;
190 }
191 else
192 AssertRC(rc);
193 }
194 }
195
196 pData->ReceiveThread = NIL_RTTHREAD;
197
198 return VINF_SUCCESS;
199}
200
201
202/* -=-=-=-=- driver interface -=-=-=-=- */
203
204/**
205 * Construct a char driver instance.
206 *
207 * @returns VBox status.
208 * @param pDrvIns The driver instance data.
209 * If the registration structure is needed,
210 * pDrvIns->pDrvReg points to it.
211 * @param pCfgHandle Configuration node handle for the driver. Use this to
212 * obtain the configuration of the driver instance. It's
213 * also found in pDrvIns->pCfgHandle as it's expected to
214 * be used frequently in this function.
215 */
216static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
217{
218 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
219 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
220
221 /*
222 * Init basic data members and interfaces.
223 */
224 pData->ReceiveThread = NIL_RTTHREAD;
225 pData->fShutdown = false;
226 /* IBase. */
227 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
228 /* IChar. */
229 pData->IChar.pfnWrite = drvCharWrite;
230
231
232 /*
233 * Get the ICharPort interface of the above driver/device.
234 */
235 pData->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
236 if (!pData->pDrvCharPort)
237 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
238
239 /*
240 * Attach driver below and query its stream interface.
241 */
242 PPDMIBASE pBase;
243 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
244 if (VBOX_FAILURE(rc))
245 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d failed to attach driver below"), pDrvIns->iInstance);
246 pData->pDrvStream = (PPDMISTREAM)pBase->pfnQueryInterface(pBase, PDMINTERFACE_STREAM);
247 if (!pData->pDrvStream)
248 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
249
250 rc = RTThreadCreate(&pData->ReceiveThread, drvCharReceiveLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char");
251 if (VBOX_FAILURE(rc))
252 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
253
254 return VINF_SUCCESS;
255}
256
257
258/**
259 * Destruct a char driver instance.
260 *
261 * Most VM resources are freed by the VM. This callback is provided so that
262 * any non-VM resources can be freed correctly.
263 *
264 * @param pDrvIns The driver instance data.
265 */
266static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
267{
268 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
269
270 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
271
272 /** @todo r=bird: use RTThreadWait() and the RTTHREADFLAGS_WAITABLE instead of active waiting like this.
273 * (the api is relatively new, which is why it's not used in all the places it should.) */
274 pData->fShutdown = true;
275 RTThreadWait(pData->ReceiveThread, 1000, NULL);
276 if (pData->ReceiveThread != NIL_RTTHREAD)
277 LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
278}
279
280
281/**
282 * Char driver registration record.
283 */
284const PDMDRVREG g_DrvChar =
285{
286 /* u32Version */
287 PDM_DRVREG_VERSION,
288 /* szDriverName */
289 "Char",
290 /* pszDescription */
291 "Generic char driver.",
292 /* fFlags */
293 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
294 /* fClass. */
295 PDM_DRVREG_CLASS_CHAR,
296 /* cMaxInstances */
297 ~0,
298 /* cbInstance */
299 sizeof(DRVCHAR),
300 /* pfnConstruct */
301 drvCharConstruct,
302 /* pfnDestruct */
303 drvCharDestruct,
304 /* pfnIOCtl */
305 NULL,
306 /* pfnPowerOn */
307 NULL,
308 /* pfnReset */
309 NULL,
310 /* pfnSuspend */
311 NULL,
312 /* pfnResume */
313 NULL,
314 /* pfnDetach */
315 NULL,
316 /** pfnPowerOff */
317 NULL
318};
319
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