VirtualBox

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

最後變更 在這個檔案從28103是 28103,由 vboxsync 提交於 15 年 前

build fix

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

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