VirtualBox

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

Last change on this file since 25966 was 25966, checked in by vboxsync, 15 years ago

PDMIBASE refactoring; use UUID as interface IDs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: DrvChar.cpp 25966 2010-01-22 11:15:43Z vboxsync $ */
2/** @file
3 * VBox stream I/O devices: Generic char driver
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23
24/*******************************************************************************
25* Header Files *
26*******************************************************************************/
27#define LOG_GROUP LOG_GROUP_DRV_CHAR
28#include <VBox/pdmdrv.h>
29#include <iprt/asm.h>
30#include <iprt/assert.h>
31#include <iprt/stream.h>
32#include <iprt/semaphore.h>
33#include <iprt/uuid.h>
34
35#include "Builtins.h"
36
37
38/*******************************************************************************
39* Defined Constants And Macros *
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/** Converts a pointer to DRVCHAR::IChar to a PDRVCHAR. */
46#define PDMICHAR_2_DRVCHAR(pInterface) ( (PDRVCHAR)((uintptr_t)pInterface - RT_OFFSETOF(DRVCHAR, IChar)) )
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * Char driver instance data.
54 *
55 * @implements PDMICHAR
56 */
57typedef struct DRVCHAR
58{
59 /** Pointer to the driver instance structure. */
60 PPDMDRVINS pDrvIns;
61 /** Pointer to the char port interface of the driver/device above us. */
62 PPDMICHARPORT pDrvCharPort;
63 /** Pointer to the stream interface of the driver below us. */
64 PPDMISTREAM pDrvStream;
65 /** Our char interface. */
66 PDMICHAR IChar;
67 /** Flag to notify the receive thread it should terminate. */
68 volatile bool fShutdown;
69 /** Receive thread ID. */
70 RTTHREAD ReceiveThread;
71 /** Send thread ID. */
72 RTTHREAD SendThread;
73 /** Send event semephore */
74 RTSEMEVENT SendSem;
75
76 /** Internal send FIFO queue */
77 uint8_t aSendQueue[CHAR_MAX_SEND_QUEUE];
78 uint32_t iSendQueueHead;
79 uint32_t iSendQueueTail;
80
81 uintptr_t AlignmentPadding;
82 /** Read/write statistics */
83 STAMCOUNTER StatBytesRead;
84 STAMCOUNTER StatBytesWritten;
85} DRVCHAR, *PDRVCHAR;
86AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
87
88
89
90
91/* -=-=-=-=- IBase -=-=-=-=- */
92
93/**
94 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
95 */
96static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
97{
98 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
99 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
100
101 if (RTUuidCompare2Strs(pszIID, PDMIBASE_IID) == 0)
102 return &pDrvIns->IBase;
103 if (RTUuidCompare2Strs(pszIID, PDMINTERFACE_CHAR) == 0)
104 return &pThis->IChar;
105 return NULL;
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 pThis = 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 = pThis->iSendQueueHead;
122
123 pThis->aSendQueue[idx] = pBuffer[i];
124 idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
125
126 STAM_COUNTER_INC(&pThis->StatBytesWritten);
127 ASMAtomicXchgU32(&pThis->iSendQueueHead, idx);
128 }
129 RTSemEventSignal(pThis->SendSem);
130 return VINF_SUCCESS;
131}
132
133/** @copydoc PDMICHAR::pfnSetParameters */
134static DECLCALLBACK(int) drvCharSetParameters(PPDMICHAR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
135{
136 /*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
137
138 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
139 return VINF_SUCCESS;
140}
141
142
143/* -=-=-=-=- receive thread -=-=-=-=- */
144
145/**
146 * Send thread loop.
147 *
148 * @returns 0 on success.
149 * @param ThreadSelf Thread handle to this thread.
150 * @param pvUser User argument.
151 */
152static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
153{
154 PDRVCHAR pThis = (PDRVCHAR)pvUser;
155
156 for(;;)
157 {
158 int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
159 if (RT_FAILURE(rc))
160 break;
161
162 /*
163 * Write the character to the attached stream (if present).
164 */
165 if ( !pThis->fShutdown
166 && pThis->pDrvStream)
167 {
168 while (pThis->iSendQueueTail != pThis->iSendQueueHead)
169 {
170 size_t cbProcessed = 1;
171
172 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->aSendQueue[pThis->iSendQueueTail], &cbProcessed);
173 if (RT_SUCCESS(rc))
174 {
175 Assert(cbProcessed);
176 pThis->iSendQueueTail++;
177 pThis->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
178 }
179 else if (rc == VERR_TIMEOUT)
180 {
181 /* Normal case, just means that the stream didn't accept a new
182 * character before the timeout elapsed. Just retry. */
183 rc = VINF_SUCCESS;
184 }
185 else
186 {
187 LogFlow(("Write failed with %Rrc; skipping\n", rc));
188 break;
189 }
190 }
191 }
192 else
193 break;
194 }
195
196 pThis->SendThread = NIL_RTTHREAD;
197
198 return VINF_SUCCESS;
199}
200
201/* -=-=-=-=- receive thread -=-=-=-=- */
202
203/**
204 * Receive thread loop.
205 *
206 * @returns 0 on success.
207 * @param ThreadSelf Thread handle to this thread.
208 * @param pvUser User argument.
209 */
210static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
211{
212 PDRVCHAR pThis = (PDRVCHAR)pvUser;
213 char aBuffer[256], *pBuffer;
214 size_t cbRemaining, cbProcessed;
215 int rc;
216
217 cbRemaining = 0;
218 pBuffer = aBuffer;
219 while (!pThis->fShutdown)
220 {
221 if (!cbRemaining)
222 {
223 /* Get block of data from stream driver. */
224 if (pThis->pDrvStream)
225 {
226 cbRemaining = sizeof(aBuffer);
227 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, aBuffer, &cbRemaining);
228 if (RT_FAILURE(rc))
229 {
230 LogFlow(("Read failed with %Rrc\n", rc));
231 break;
232 }
233 }
234 else
235 {
236 cbRemaining = 0;
237 RTThreadSleep(100);
238 }
239 pBuffer = aBuffer;
240 }
241 else
242 {
243 /* Send data to guest. */
244 cbProcessed = cbRemaining;
245 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pBuffer, &cbProcessed);
246 if (RT_SUCCESS(rc))
247 {
248 Assert(cbProcessed);
249 pBuffer += cbProcessed;
250 cbRemaining -= cbProcessed;
251 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
252 }
253 else if (rc == VERR_TIMEOUT)
254 {
255 /* Normal case, just means that the guest didn't accept a new
256 * character before the timeout elapsed. Just retry. */
257 rc = VINF_SUCCESS;
258 }
259 else
260 {
261 LogFlow(("NotifyRead failed with %Rrc\n", rc));
262 break;
263 }
264 }
265 }
266
267 pThis->ReceiveThread = NIL_RTTHREAD;
268
269 return VINF_SUCCESS;
270}
271
272/**
273 * Set the modem lines.
274 *
275 * @returns VBox status code
276 * @param pInterface Pointer to the interface structure.
277 * @param RequestToSend Set to true if this control line should be made active.
278 * @param DataTerminalReady Set to true if this control line should be made active.
279 */
280static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHAR pInterface, bool RequestToSend, bool DataTerminalReady)
281{
282 /* Nothing to do here. */
283 return VINF_SUCCESS;
284}
285
286/**
287 * Sets the TD line into break condition.
288 *
289 * @returns VBox status code.
290 * @param pInterface Pointer to the interface structure containing the called function pointer.
291 * @param fBreak Set to true to let the device send a break false to put into normal operation.
292 * @thread Any thread.
293 */
294static DECLCALLBACK(int) drvCharSetBreak(PPDMICHAR pInterface, bool fBreak)
295{
296 /* Nothing to do here. */
297 return VINF_SUCCESS;
298}
299
300/* -=-=-=-=- driver interface -=-=-=-=- */
301
302/**
303 * Construct a char driver instance.
304 *
305 * @copydoc FNPDMDRVCONSTRUCT
306 */
307static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
308{
309 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
310 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
311
312 /*
313 * Init basic data members and interfaces.
314 */
315 pThis->ReceiveThread = NIL_RTTHREAD;
316 pThis->fShutdown = false;
317 /* IBase. */
318 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
319 /* IChar. */
320 pThis->IChar.pfnWrite = drvCharWrite;
321 pThis->IChar.pfnSetParameters = drvCharSetParameters;
322 pThis->IChar.pfnSetModemLines = drvCharSetModemLines;
323 pThis->IChar.pfnSetBreak = drvCharSetBreak;
324
325 /*
326 * Get the ICharPort interface of the above driver/device.
327 */
328 pThis->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
329 if (!pThis->pDrvCharPort)
330 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
331
332 /*
333 * Attach driver below and query its stream interface.
334 */
335 PPDMIBASE pBase;
336 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
337 if (RT_FAILURE(rc))
338 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
339 pThis->pDrvStream = (PPDMISTREAM)pBase->pfnQueryInterface(pBase, PDMINTERFACE_STREAM);
340 if (!pThis->pDrvStream)
341 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
342
343 /*
344 * Don't start the receive thread if the driver doesn't support reading
345 */
346 if (pThis->pDrvStream->pfnRead)
347 {
348 rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
349 if (RT_FAILURE(rc))
350 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
351 }
352
353 rc = RTSemEventCreate(&pThis->SendSem);
354 AssertRC(rc);
355
356 rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
357 if (RT_FAILURE(rc))
358 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
359
360
361 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
362 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
363
364 return VINF_SUCCESS;
365}
366
367
368/**
369 * Destruct a char driver instance.
370 *
371 * Most VM resources are freed by the VM. This callback is provided so that
372 * any non-VM resources can be freed correctly.
373 *
374 * @param pDrvIns The driver instance data.
375 */
376static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
377{
378 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
379
380 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
381
382 pThis->fShutdown = true;
383 if (pThis->ReceiveThread)
384 {
385 RTThreadWait(pThis->ReceiveThread, 1000, NULL);
386 if (pThis->ReceiveThread != NIL_RTTHREAD)
387 LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
388 }
389
390 /* Empty the send queue */
391 pThis->iSendQueueTail = pThis->iSendQueueHead = 0;
392
393 RTSemEventSignal(pThis->SendSem);
394 RTSemEventDestroy(pThis->SendSem);
395 pThis->SendSem = NIL_RTSEMEVENT;
396
397 if (pThis->SendThread)
398 {
399 RTThreadWait(pThis->SendThread, 1000, NULL);
400 if (pThis->SendThread != NIL_RTTHREAD)
401 LogRel(("Char%d: send thread did not terminate\n", pDrvIns->iInstance));
402 }
403}
404
405/**
406 * Char driver registration record.
407 */
408const PDMDRVREG g_DrvChar =
409{
410 /* u32Version */
411 PDM_DRVREG_VERSION,
412 /* szDriverName */
413 "Char",
414 /* szRCMod */
415 "",
416 /* szR0Mod */
417 "",
418 /* pszDescription */
419 "Generic char driver.",
420 /* fFlags */
421 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
422 /* fClass. */
423 PDM_DRVREG_CLASS_CHAR,
424 /* cMaxInstances */
425 ~0,
426 /* cbInstance */
427 sizeof(DRVCHAR),
428 /* pfnConstruct */
429 drvCharConstruct,
430 /* pfnDestruct */
431 drvCharDestruct,
432 /* pfnRelocate */
433 NULL,
434 /* pfnIOCtl */
435 NULL,
436 /* pfnPowerOn */
437 NULL,
438 /* pfnReset */
439 NULL,
440 /* pfnSuspend */
441 NULL,
442 /* pfnResume */
443 NULL,
444 /* pfnAttach */
445 NULL,
446 /* pfnDetach */
447 NULL,
448 /* pfnPowerOff */
449 NULL,
450 /* pfnSoftReset */
451 NULL,
452 /* u32EndVersion */
453 PDM_DRVREG_VERSION
454};
455
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