VirtualBox

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

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

serial: Let the UART device signal if we are not ready to consume more data. Should fix lost characters when working with host hardware devices.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 15.2 KB
 
1/* $Id: DrvChar.cpp 28101 2010-04-08 15:42:36Z 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 cEntries;
84
85 uintptr_t AlignmentPadding;
86 /** Read/write statistics */
87 STAMCOUNTER StatBytesRead;
88 STAMCOUNTER StatBytesWritten;
89} DRVCHAR, *PDRVCHAR;
90AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
91
92
93
94
95/* -=-=-=-=- IBase -=-=-=-=- */
96
97/**
98 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
99 */
100static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
101{
102 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
103 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
104
105 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
106 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
107 return NULL;
108}
109
110
111/* -=-=-=-=- ICharConnector -=-=-=-=- */
112
113/** @copydoc PDMICHARCONNECTOR::pfnWrite */
114static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
115{
116 PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface);
117 const char *pBuffer = (const char *)pvBuf;
118
119 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
120
121 for (uint32_t i=0; i<cbWrite; i++)
122 {
123 uint32_t iOld = pThis->iSendQueueHead;
124 uint32_t iNew = (iOld + 1) & CHAR_MAX_SEND_QUEUE_MASK;
125
126 pThis->aSendQueue[iOld] = pBuffer[i];
127
128 STAM_COUNTER_INC(&pThis->StatBytesWritten);
129 ASMAtomicXchgU32(&pThis->iSendQueueHead, iNew);
130 ASMAtomicIncU32(&pThis->cEntries);
131 if (pThis->cEntries > CHAR_MAX_SEND_QUEUE_MASK / 2)
132 pThis->pDrvCharPort->pfnNotifyBufferFull(pThis->pDrvCharPort, true);
133 }
134 RTSemEventSignal(pThis->SendSem);
135 return VINF_SUCCESS;
136}
137
138/** @copydoc PDMICHARCONNECTOR::pfnSetParameters */
139static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
140{
141 /*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
142
143 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
144 return VINF_SUCCESS;
145}
146
147
148/* -=-=-=-=- receive thread -=-=-=-=- */
149
150/**
151 * Send thread loop - pushes data down thru the driver chain.
152 *
153 * @returns 0 on success.
154 * @param ThreadSelf Thread handle to this thread.
155 * @param pvUser User argument.
156 */
157static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
158{
159 PDRVCHAR pThis = (PDRVCHAR)pvUser;
160
161 while (!pThis->fShutdown)
162 {
163 int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
164 if (RT_FAILURE(rc))
165 break;
166
167 /*
168 * Write the character to the attached stream (if present).
169 */
170 if ( pThis->fShutdown
171 || !pThis->pDrvStream)
172 break;
173
174 while ( pThis->iSendQueueTail != pThis->iSendQueueHead
175 && !pThis->fShutdown)
176 {
177 size_t cbProcessed = 1;
178
179 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->aSendQueue[pThis->iSendQueueTail], &cbProcessed);
180 pThis->pDrvCharPort->pfnNotifyBufferFull(pThis->pDrvCharPort, false);
181 if (RT_SUCCESS(rc))
182 {
183 Assert(cbProcessed);
184 pThis->iSendQueueTail++;
185 pThis->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
186 ASMAtomicDecU32(&pThis->cEntries);
187 }
188 else if (rc == VERR_TIMEOUT)
189 {
190 /* Normal case, just means that the stream didn't accept a new
191 * character before the timeout elapsed. Just retry. */
192 rc = VINF_SUCCESS;
193 }
194 else
195 {
196 LogFlow(("Write failed with %Rrc; skipping\n", rc));
197 break;
198 }
199 }
200 }
201
202 return VINF_SUCCESS;
203}
204
205/* -=-=-=-=- receive thread -=-=-=-=- */
206
207/**
208 * Receive thread loop.
209 *
210 * @returns 0 on success.
211 * @param ThreadSelf Thread handle to this thread.
212 * @param pvUser User argument.
213 */
214static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
215{
216 PDRVCHAR pThis = (PDRVCHAR)pvUser;
217 char aBuffer[256], *pBuffer;
218 size_t cbRemaining, cbProcessed;
219 int rc;
220
221 cbRemaining = 0;
222 pBuffer = aBuffer;
223 while (!pThis->fShutdown)
224 {
225 if (!cbRemaining)
226 {
227 /* Get block of data from stream driver. */
228 if (pThis->pDrvStream)
229 {
230 cbRemaining = sizeof(aBuffer);
231 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, aBuffer, &cbRemaining);
232 if (RT_FAILURE(rc))
233 {
234 LogFlow(("Read failed with %Rrc\n", rc));
235 break;
236 }
237 }
238 else
239 {
240 cbRemaining = 0;
241 RTThreadSleep(100);
242 }
243 pBuffer = aBuffer;
244 }
245 else
246 {
247 /* Send data to guest. */
248 cbProcessed = cbRemaining;
249 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pBuffer, &cbProcessed);
250 if (RT_SUCCESS(rc))
251 {
252 Assert(cbProcessed);
253 pBuffer += cbProcessed;
254 cbRemaining -= cbProcessed;
255 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
256 }
257 else if (rc == VERR_TIMEOUT)
258 {
259 /* Normal case, just means that the guest didn't accept a new
260 * character before the timeout elapsed. Just retry. */
261 rc = VINF_SUCCESS;
262 }
263 else
264 {
265 LogFlow(("NotifyRead failed with %Rrc\n", rc));
266 break;
267 }
268 }
269 }
270
271 return VINF_SUCCESS;
272}
273
274/**
275 * Set the modem lines.
276 *
277 * @returns VBox status code
278 * @param pInterface Pointer to the interface structure.
279 * @param RequestToSend Set to true if this control line should be made active.
280 * @param DataTerminalReady Set to true if this control line should be made active.
281 */
282static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
283{
284 /* Nothing to do here. */
285 return VINF_SUCCESS;
286}
287
288/**
289 * Sets the TD line into break condition.
290 *
291 * @returns VBox status code.
292 * @param pInterface Pointer to the interface structure containing the called function pointer.
293 * @param fBreak Set to true to let the device send a break false to put into normal operation.
294 * @thread Any thread.
295 */
296static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
297{
298 /* Nothing to do here. */
299 return VINF_SUCCESS;
300}
301
302/* -=-=-=-=- driver interface -=-=-=-=- */
303
304/**
305 * Destruct a char driver instance.
306 *
307 * Most VM resources are freed by the VM. This callback is provided so that
308 * any non-VM resources can be freed correctly.
309 *
310 * @param pDrvIns The driver instance data.
311 */
312static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
313{
314 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
315 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
316 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
317
318 /*
319 * Tell the threads to shut down.
320 */
321 pThis->fShutdown = true;
322 if (pThis->SendSem != NIL_RTSEMEVENT)
323 {
324 RTSemEventSignal(pThis->SendSem);
325 RTSemEventDestroy(pThis->SendSem);
326 pThis->SendSem = NIL_RTSEMEVENT;
327 }
328
329 /*
330 * Wait for the threads.
331 * ASSUMES that PDM destroys the driver chain from the the bottom and up.
332 */
333 if (pThis->ReceiveThread != NIL_RTTHREAD)
334 {
335 int rc = RTThreadWait(pThis->ReceiveThread, 30000, NULL);
336 if (RT_SUCCESS(rc))
337 pThis->ReceiveThread = NIL_RTTHREAD;
338 else
339 LogRel(("Char%d: receive thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
340 }
341
342 if (pThis->SendThread != NIL_RTTHREAD)
343 {
344 int rc = RTThreadWait(pThis->SendThread, 30000, NULL);
345 if (RT_SUCCESS(rc))
346 pThis->SendThread = NIL_RTTHREAD;
347 else
348 LogRel(("Char%d: send thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
349 }
350}
351
352
353/**
354 * Construct a char driver instance.
355 *
356 * @copydoc FNPDMDRVCONSTRUCT
357 */
358static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
359{
360 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
361 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
362 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
363
364 /*
365 * Init basic data members and interfaces.
366 */
367 pThis->fShutdown = false;
368 pThis->ReceiveThread = NIL_RTTHREAD;
369 pThis->SendThread = NIL_RTTHREAD;
370 pThis->SendSem = NIL_RTSEMEVENT;
371 /* IBase. */
372 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
373 /* ICharConnector. */
374 pThis->ICharConnector.pfnWrite = drvCharWrite;
375 pThis->ICharConnector.pfnSetParameters = drvCharSetParameters;
376 pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines;
377 pThis->ICharConnector.pfnSetBreak = drvCharSetBreak;
378
379 /*
380 * Get the ICharPort interface of the above driver/device.
381 */
382 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
383 if (!pThis->pDrvCharPort)
384 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
385
386 /*
387 * Attach driver below and query its stream interface.
388 */
389 PPDMIBASE pBase;
390 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
391 if (RT_FAILURE(rc))
392 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
393 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
394 if (!pThis->pDrvStream)
395 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
396
397 /*
398 * Don't start the receive thread if the driver doesn't support reading
399 */
400 if (pThis->pDrvStream->pfnRead)
401 {
402 rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0,
403 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
404 if (RT_FAILURE(rc))
405 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
406 }
407
408 rc = RTSemEventCreate(&pThis->SendSem);
409 AssertRCReturn(rc, rc);
410
411 rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0,
412 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
413 if (RT_FAILURE(rc))
414 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
415
416
417 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
418 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
419
420 return VINF_SUCCESS;
421}
422
423
424/**
425 * Char driver registration record.
426 */
427const PDMDRVREG g_DrvChar =
428{
429 /* u32Version */
430 PDM_DRVREG_VERSION,
431 /* szName */
432 "Char",
433 /* szRCMod */
434 "",
435 /* szR0Mod */
436 "",
437 /* pszDescription */
438 "Generic char driver.",
439 /* fFlags */
440 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
441 /* fClass. */
442 PDM_DRVREG_CLASS_CHAR,
443 /* cMaxInstances */
444 ~0,
445 /* cbInstance */
446 sizeof(DRVCHAR),
447 /* pfnConstruct */
448 drvCharConstruct,
449 /* pfnDestruct */
450 drvCharDestruct,
451 /* pfnRelocate */
452 NULL,
453 /* pfnIOCtl */
454 NULL,
455 /* pfnPowerOn */
456 NULL,
457 /* pfnReset */
458 NULL,
459 /* pfnSuspend */
460 NULL,
461 /* pfnResume */
462 NULL,
463 /* pfnAttach */
464 NULL,
465 /* pfnDetach */
466 NULL,
467 /* pfnPowerOff */
468 NULL,
469 /* pfnSoftReset */
470 NULL,
471 /* u32EndVersion */
472 PDM_DRVREG_VERSION
473};
474
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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