VirtualBox

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

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

warning

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.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/asm.h>
34#include <iprt/assert.h>
35#include <iprt/stream.h>
36#include <iprt/semaphore.h>
37
38#include "Builtins.h"
39
40
41/** Size of the send fifo queue (in bytes) */
42#define CHAR_MAX_SEND_QUEUE 0x80
43#define CHAR_MAX_SEND_QUEUE_MASK 0x7f
44
45/*******************************************************************************
46* Structures and Typedefs *
47*******************************************************************************/
48
49/**
50 * Char driver instance data.
51 */
52typedef struct DRVCHAR
53{
54 /** Pointer to the driver instance structure. */
55 PPDMDRVINS pDrvIns;
56 /** Pointer to the char port interface of the driver/device above us. */
57 PPDMICHARPORT pDrvCharPort;
58 /** Pointer to the stream interface of the driver below us. */
59 PPDMISTREAM pDrvStream;
60 /** Our char interface. */
61 PDMICHAR IChar;
62 /** Flag to notify the receive thread it should terminate. */
63 volatile bool fShutdown;
64 /** Receive thread ID. */
65 RTTHREAD ReceiveThread;
66 /** Send thread ID. */
67 RTTHREAD SendThread;
68 /** Send event semephore */
69 RTSEMEVENT SendSem;
70
71 /** Internal send FIFO queue */
72 uint8_t aSendQueue[CHAR_MAX_SEND_QUEUE];
73 uint32_t iSendQueueHead;
74 uint32_t iSendQueueTail;
75
76 /** Read/write statistics */
77 STAMCOUNTER StatBytesRead;
78 STAMCOUNTER StatBytesWritten;
79} DRVCHAR, *PDRVCHAR;
80
81
82/** Converts a pointer to DRVCHAR::IChar to a PDRVCHAR. */
83#define PDMICHAR_2_DRVCHAR(pInterface) ( (PDRVCHAR)((uintptr_t)pInterface - RT_OFFSETOF(DRVCHAR, IChar)) )
84
85
86/* -=-=-=-=- IBase -=-=-=-=- */
87
88/**
89 * Queries an interface to the driver.
90 *
91 * @returns Pointer to interface.
92 * @returns NULL if the interface was not supported by the driver.
93 * @param pInterface Pointer to this interface structure.
94 * @param enmInterface The requested interface identification.
95 */
96static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
97{
98 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
99 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
100 switch (enmInterface)
101 {
102 case PDMINTERFACE_BASE:
103 return &pDrvIns->IBase;
104 case PDMINTERFACE_CHAR:
105 return &pData->IChar;
106 default:
107 return NULL;
108 }
109}
110
111
112/* -=-=-=-=- IChar -=-=-=-=- */
113
114/** @copydoc PDMICHAR::pfnWrite */
115static DECLCALLBACK(int) drvCharWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
116{
117 PDRVCHAR pData = PDMICHAR_2_DRVCHAR(pInterface);
118 const char *pBuffer = (const char *)pvBuf;
119
120 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
121
122 for (uint32_t i=0;i<cbWrite;i++)
123 {
124 uint32_t idx = pData->iSendQueueHead;
125
126 pData->aSendQueue[idx] = pBuffer[i];
127 idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
128
129 STAM_COUNTER_INC(&pData->StatBytesWritten);
130 ASMAtomicXchgU32(&pData->iSendQueueHead, idx);
131 }
132 RTSemEventSignal(pData->SendSem);
133 return VINF_SUCCESS;
134}
135
136
137/* -=-=-=-=- receive thread -=-=-=-=- */
138
139/**
140 * Send thread loop.
141 *
142 * @returns 0 on success.
143 * @param ThreadSelf Thread handle to this thread.
144 * @param pvUser User argument.
145 */
146static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
147{
148 PDRVCHAR pData = (PDRVCHAR)pvUser;
149
150 for(;;)
151 {
152 int rc = RTSemEventWait(pData->SendSem, RT_INDEFINITE_WAIT);
153 if (VBOX_FAILURE(rc))
154 break;
155
156 /*
157 * Write the character to the attached stream (if present).
158 */
159 if ( !pData->fShutdown
160 && pData->pDrvStream)
161 {
162 while (pData->iSendQueueTail != pData->iSendQueueHead)
163 {
164 size_t cbProcessed = 1;
165
166 rc = pData->pDrvStream->pfnWrite(pData->pDrvStream, &pData->aSendQueue[pData->iSendQueueTail], &cbProcessed);
167 if (VBOX_SUCCESS(rc))
168 {
169 Assert(cbProcessed);
170 pData->iSendQueueTail++;
171 pData->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
172 }
173 else if (rc == VERR_TIMEOUT)
174 {
175 /* Normal case, just means that the stream didn't accept a new
176 * character before the timeout elapsed. Just retry. */
177 rc = VINF_SUCCESS;
178 }
179 else
180 {
181 LogFlow(("Write failed with %Vrc; skipping\n", rc));
182 break;
183 }
184 }
185 }
186 else
187 break;
188 }
189
190 pData->SendThread = NIL_RTTHREAD;
191
192 return VINF_SUCCESS;
193}
194
195
196/* -=-=-=-=- receive thread -=-=-=-=- */
197
198/**
199 * Receive thread loop.
200 *
201 * @returns 0 on success.
202 * @param ThreadSelf Thread handle to this thread.
203 * @param pvUser User argument.
204 */
205static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
206{
207 PDRVCHAR pData = (PDRVCHAR)pvUser;
208 char aBuffer[256], *pBuffer;
209 size_t cbRemaining, cbProcessed;
210 int rc;
211
212 cbRemaining = 0;
213 pBuffer = aBuffer;
214 while (!pData->fShutdown)
215 {
216 if (!cbRemaining)
217 {
218 /* Get block of data from stream driver. */
219 if (pData->pDrvStream)
220 {
221 cbRemaining = sizeof(aBuffer);
222 rc = pData->pDrvStream->pfnRead(pData->pDrvStream, aBuffer, &cbRemaining);
223 if (VBOX_FAILURE(rc))
224 {
225 LogFlow(("Read failed with %Vrc\n", rc));
226 break;
227 }
228 }
229 else
230 {
231 cbRemaining = 0;
232 RTThreadSleep(100);
233 }
234 pBuffer = aBuffer;
235 }
236 else
237 {
238 /* Send data to guest. */
239 cbProcessed = cbRemaining;
240 rc = pData->pDrvCharPort->pfnNotifyRead(pData->pDrvCharPort, pBuffer, &cbProcessed);
241 if (VBOX_SUCCESS(rc))
242 {
243 Assert(cbProcessed);
244 pBuffer += cbProcessed;
245 cbRemaining -= cbProcessed;
246 STAM_COUNTER_ADD(&pData->StatBytesRead, cbProcessed);
247 }
248 else if (rc == VERR_TIMEOUT)
249 {
250 /* Normal case, just means that the guest didn't accept a new
251 * character before the timeout elapsed. Just retry. */
252 rc = VINF_SUCCESS;
253 }
254 else
255 {
256 LogFlow(("NotifyRead failed with %Vrc\n", rc));
257 break;
258 }
259 }
260 }
261
262 pData->ReceiveThread = NIL_RTTHREAD;
263
264 return VINF_SUCCESS;
265}
266
267
268/* -=-=-=-=- driver interface -=-=-=-=- */
269
270/**
271 * Construct a char driver instance.
272 *
273 * @returns VBox status.
274 * @param pDrvIns The driver instance data.
275 * If the registration structure is needed,
276 * pDrvIns->pDrvReg points to it.
277 * @param pCfgHandle Configuration node handle for the driver. Use this to
278 * obtain the configuration of the driver instance. It's
279 * also found in pDrvIns->pCfgHandle as it's expected to
280 * be used frequently in this function.
281 */
282static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
283{
284 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
285 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
286
287 /*
288 * Init basic data members and interfaces.
289 */
290 pData->ReceiveThread = NIL_RTTHREAD;
291 pData->fShutdown = false;
292 /* IBase. */
293 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
294 /* IChar. */
295 pData->IChar.pfnWrite = drvCharWrite;
296
297
298 /*
299 * Get the ICharPort interface of the above driver/device.
300 */
301 pData->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
302 if (!pData->pDrvCharPort)
303 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
304
305 /*
306 * Attach driver below and query its stream interface.
307 */
308 PPDMIBASE pBase;
309 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBase);
310 if (VBOX_FAILURE(rc))
311 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d failed to attach driver below"), pDrvIns->iInstance);
312 pData->pDrvStream = (PPDMISTREAM)pBase->pfnQueryInterface(pBase, PDMINTERFACE_STREAM);
313 if (!pData->pDrvStream)
314 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
315
316 rc = RTThreadCreate(&pData->ReceiveThread, drvCharReceiveLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char Receive");
317 if (VBOX_FAILURE(rc))
318 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
319
320 rc = RTSemEventCreate(&pData->SendSem);
321 AssertRC(rc);
322
323 rc = RTThreadCreate(&pData->SendThread, drvCharSendLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char Send");
324 if (VBOX_FAILURE(rc))
325 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
326
327
328 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
329 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
330
331 return VINF_SUCCESS;
332}
333
334
335/**
336 * Destruct a char driver instance.
337 *
338 * Most VM resources are freed by the VM. This callback is provided so that
339 * any non-VM resources can be freed correctly.
340 *
341 * @param pDrvIns The driver instance data.
342 */
343static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
344{
345 PDRVCHAR pData = PDMINS2DATA(pDrvIns, PDRVCHAR);
346
347 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
348
349 pData->fShutdown = true;
350 if (pData->ReceiveThread)
351 {
352 RTThreadWait(pData->ReceiveThread, 1000, NULL);
353 if (pData->ReceiveThread != NIL_RTTHREAD)
354 LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
355 }
356
357 /* Empty the send queue */
358 pData->iSendQueueTail = pData->iSendQueueHead = 0;
359
360 RTSemEventSignal(pData->SendSem);
361 RTSemEventDestroy(pData->SendSem);
362 pData->SendSem = NIL_RTSEMEVENT;
363
364 if (pData->SendThread)
365 {
366 RTThreadWait(pData->SendThread, 1000, NULL);
367 if (pData->SendThread != NIL_RTTHREAD)
368 LogRel(("Char%d: send thread did not terminate\n", pDrvIns->iInstance));
369 }
370}
371
372/**
373 * Char driver registration record.
374 */
375const PDMDRVREG g_DrvChar =
376{
377 /* u32Version */
378 PDM_DRVREG_VERSION,
379 /* szDriverName */
380 "Char",
381 /* pszDescription */
382 "Generic char driver.",
383 /* fFlags */
384 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
385 /* fClass. */
386 PDM_DRVREG_CLASS_CHAR,
387 /* cMaxInstances */
388 ~0,
389 /* cbInstance */
390 sizeof(DRVCHAR),
391 /* pfnConstruct */
392 drvCharConstruct,
393 /* pfnDestruct */
394 drvCharDestruct,
395 /* pfnIOCtl */
396 NULL,
397 /* pfnPowerOn */
398 NULL,
399 /* pfnReset */
400 NULL,
401 /* pfnSuspend */
402 NULL,
403 /* pfnResume */
404 NULL,
405 /* pfnDetach */
406 NULL,
407 /** pfnPowerOff */
408 NULL
409};
410
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