VirtualBox

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

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

Use pdmdrv.h and pdmdev.h where appropirate.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette