VirtualBox

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

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

PDM: s/szDriverName/szName/g - PDMDRVREG.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: DrvChar.cpp 26166 2010-02-02 19:54:23Z 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::ICharConnector to a PDRVCHAR. */
46#define PDMICHAR_2_DRVCHAR(pInterface) RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector)
47
48
49/*******************************************************************************
50* Structures and Typedefs *
51*******************************************************************************/
52/**
53 * Char driver instance data.
54 *
55 * @implements PDMICHARCONNECTOR
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 PDMICHARCONNECTOR ICharConnector;
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 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
102 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
103 return NULL;
104}
105
106
107/* -=-=-=-=- ICharConnector -=-=-=-=- */
108
109/** @copydoc PDMICHARCONNECTOR::pfnWrite */
110static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
111{
112 PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface);
113 const char *pBuffer = (const char *)pvBuf;
114
115 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
116
117 for (uint32_t i=0;i<cbWrite;i++)
118 {
119 uint32_t idx = pThis->iSendQueueHead;
120
121 pThis->aSendQueue[idx] = pBuffer[i];
122 idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
123
124 STAM_COUNTER_INC(&pThis->StatBytesWritten);
125 ASMAtomicXchgU32(&pThis->iSendQueueHead, idx);
126 }
127 RTSemEventSignal(pThis->SendSem);
128 return VINF_SUCCESS;
129}
130
131/** @copydoc PDMICHARCONNECTOR::pfnSetParameters */
132static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
133{
134 /*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
135
136 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
137 return VINF_SUCCESS;
138}
139
140
141/* -=-=-=-=- receive thread -=-=-=-=- */
142
143/**
144 * Send thread loop.
145 *
146 * @returns 0 on success.
147 * @param ThreadSelf Thread handle to this thread.
148 * @param pvUser User argument.
149 */
150static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
151{
152 PDRVCHAR pThis = (PDRVCHAR)pvUser;
153
154 for(;;)
155 {
156 int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
157 if (RT_FAILURE(rc))
158 break;
159
160 /*
161 * Write the character to the attached stream (if present).
162 */
163 if ( !pThis->fShutdown
164 && pThis->pDrvStream)
165 {
166 while (pThis->iSendQueueTail != pThis->iSendQueueHead)
167 {
168 size_t cbProcessed = 1;
169
170 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->aSendQueue[pThis->iSendQueueTail], &cbProcessed);
171 if (RT_SUCCESS(rc))
172 {
173 Assert(cbProcessed);
174 pThis->iSendQueueTail++;
175 pThis->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
176 }
177 else if (rc == VERR_TIMEOUT)
178 {
179 /* Normal case, just means that the stream didn't accept a new
180 * character before the timeout elapsed. Just retry. */
181 rc = VINF_SUCCESS;
182 }
183 else
184 {
185 LogFlow(("Write failed with %Rrc; skipping\n", rc));
186 break;
187 }
188 }
189 }
190 else
191 break;
192 }
193
194 pThis->SendThread = NIL_RTTHREAD;
195
196 return VINF_SUCCESS;
197}
198
199/* -=-=-=-=- receive thread -=-=-=-=- */
200
201/**
202 * Receive thread loop.
203 *
204 * @returns 0 on success.
205 * @param ThreadSelf Thread handle to this thread.
206 * @param pvUser User argument.
207 */
208static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
209{
210 PDRVCHAR pThis = (PDRVCHAR)pvUser;
211 char aBuffer[256], *pBuffer;
212 size_t cbRemaining, cbProcessed;
213 int rc;
214
215 cbRemaining = 0;
216 pBuffer = aBuffer;
217 while (!pThis->fShutdown)
218 {
219 if (!cbRemaining)
220 {
221 /* Get block of data from stream driver. */
222 if (pThis->pDrvStream)
223 {
224 cbRemaining = sizeof(aBuffer);
225 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, aBuffer, &cbRemaining);
226 if (RT_FAILURE(rc))
227 {
228 LogFlow(("Read failed with %Rrc\n", rc));
229 break;
230 }
231 }
232 else
233 {
234 cbRemaining = 0;
235 RTThreadSleep(100);
236 }
237 pBuffer = aBuffer;
238 }
239 else
240 {
241 /* Send data to guest. */
242 cbProcessed = cbRemaining;
243 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pBuffer, &cbProcessed);
244 if (RT_SUCCESS(rc))
245 {
246 Assert(cbProcessed);
247 pBuffer += cbProcessed;
248 cbRemaining -= cbProcessed;
249 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
250 }
251 else if (rc == VERR_TIMEOUT)
252 {
253 /* Normal case, just means that the guest didn't accept a new
254 * character before the timeout elapsed. Just retry. */
255 rc = VINF_SUCCESS;
256 }
257 else
258 {
259 LogFlow(("NotifyRead failed with %Rrc\n", rc));
260 break;
261 }
262 }
263 }
264
265 pThis->ReceiveThread = NIL_RTTHREAD;
266
267 return VINF_SUCCESS;
268}
269
270/**
271 * Set the modem lines.
272 *
273 * @returns VBox status code
274 * @param pInterface Pointer to the interface structure.
275 * @param RequestToSend Set to true if this control line should be made active.
276 * @param DataTerminalReady Set to true if this control line should be made active.
277 */
278static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
279{
280 /* Nothing to do here. */
281 return VINF_SUCCESS;
282}
283
284/**
285 * Sets the TD line into break condition.
286 *
287 * @returns VBox status code.
288 * @param pInterface Pointer to the interface structure containing the called function pointer.
289 * @param fBreak Set to true to let the device send a break false to put into normal operation.
290 * @thread Any thread.
291 */
292static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
293{
294 /* Nothing to do here. */
295 return VINF_SUCCESS;
296}
297
298/* -=-=-=-=- driver interface -=-=-=-=- */
299
300/**
301 * Destruct a char driver instance.
302 *
303 * Most VM resources are freed by the VM. This callback is provided so that
304 * any non-VM resources can be freed correctly.
305 *
306 * @param pDrvIns The driver instance data.
307 */
308static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
309{
310 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
311 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
312 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
313
314 pThis->fShutdown = true;
315 if (pThis->ReceiveThread)
316 {
317 RTThreadWait(pThis->ReceiveThread, 1000, NULL);
318 if (pThis->ReceiveThread != NIL_RTTHREAD)
319 LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
320 }
321
322 /* Empty the send queue */
323 pThis->iSendQueueTail = pThis->iSendQueueHead = 0;
324
325 RTSemEventSignal(pThis->SendSem);
326 RTSemEventDestroy(pThis->SendSem);
327 pThis->SendSem = NIL_RTSEMEVENT;
328
329 if (pThis->SendThread)
330 {
331 RTThreadWait(pThis->SendThread, 1000, NULL);
332 if (pThis->SendThread != NIL_RTTHREAD)
333 LogRel(("Char%d: send thread did not terminate\n", pDrvIns->iInstance));
334 }
335}
336
337
338/**
339 * Construct a char driver instance.
340 *
341 * @copydoc FNPDMDRVCONSTRUCT
342 */
343static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle, uint32_t fFlags)
344{
345 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
346 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
347 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
348
349 /*
350 * Init basic data members and interfaces.
351 */
352 pThis->ReceiveThread = NIL_RTTHREAD;
353 pThis->fShutdown = false;
354 /* IBase. */
355 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
356 /* ICharConnector. */
357 pThis->ICharConnector.pfnWrite = drvCharWrite;
358 pThis->ICharConnector.pfnSetParameters = drvCharSetParameters;
359 pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines;
360 pThis->ICharConnector.pfnSetBreak = drvCharSetBreak;
361
362 /*
363 * Get the ICharPort interface of the above driver/device.
364 */
365 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
366 if (!pThis->pDrvCharPort)
367 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
368
369 /*
370 * Attach driver below and query its stream interface.
371 */
372 PPDMIBASE pBase;
373 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
374 if (RT_FAILURE(rc))
375 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
376 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
377 if (!pThis->pDrvStream)
378 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
379
380 /*
381 * Don't start the receive thread if the driver doesn't support reading
382 */
383 if (pThis->pDrvStream->pfnRead)
384 {
385 rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
386 if (RT_FAILURE(rc))
387 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
388 }
389
390 rc = RTSemEventCreate(&pThis->SendSem);
391 AssertRC(rc);
392
393 rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
394 if (RT_FAILURE(rc))
395 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
396
397
398 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
399 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
400
401 return VINF_SUCCESS;
402}
403
404
405/**
406 * Char driver registration record.
407 */
408const PDMDRVREG g_DrvChar =
409{
410 /* u32Version */
411 PDM_DRVREG_VERSION,
412 /* szName */
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