VirtualBox

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

Last change on this file since 69901 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.2 KB
Line 
1/* $Id: DrvChar.cpp 69500 2017-10-28 15:14:05Z 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 * Note that we don't use a send buffer here to be able to handle
9 * dropping of bytes for xmit at device level.
10 */
11
12/*
13 * Copyright (C) 2006-2017 Oracle Corporation
14 *
15 * This file is part of VirtualBox Open Source Edition (OSE), as
16 * available from http://www.virtualbox.org. This file is free software;
17 * you can redistribute it and/or modify it under the terms of the GNU
18 * General Public License (GPL) as published by the Free Software
19 * Foundation, in version 2 as it comes in the "COPYING" file of the
20 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22 */
23
24
25/*********************************************************************************************************************************
26* Header Files *
27*********************************************************************************************************************************/
28#define LOG_GROUP LOG_GROUP_DRV_CHAR
29#include <VBox/vmm/pdmdrv.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/poll.h>
33#include <iprt/stream.h>
34#include <iprt/semaphore.h>
35#include <iprt/uuid.h>
36
37#include "VBoxDD.h"
38
39
40/*********************************************************************************************************************************
41* Defined Constants And Macros *
42*********************************************************************************************************************************/
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48/**
49 * Char driver instance data.
50 *
51 * @implements PDMICHARCONNECTOR
52 */
53typedef struct DRVCHAR
54{
55 /** Pointer to the driver instance structure. */
56 PPDMDRVINS pDrvIns;
57 /** Pointer to the char port interface of the driver/device above us. */
58 PPDMICHARPORT pDrvCharPort;
59 /** Pointer to the stream interface of the driver below us. */
60 PPDMISTREAM pDrvStream;
61 /** Our char interface. */
62 PDMICHARCONNECTOR ICharConnector;
63 /** Flag to notify the receive thread it should terminate. */
64 volatile bool fShutdown;
65 /** I/O thread. */
66 PPDMTHREAD pThrdIo;
67 /** Thread to relay read data to the device above without
68 * blocking send operations.
69 * @todo: This has to go but needs changes in the interface
70 * between device and driver.
71 */
72 PPDMTHREAD pThrdRead;
73 /** Event semaphore for the read relay thread. */
74 RTSEMEVENT hEvtSemRead;
75
76 /** Internal send FIFO queue */
77 uint8_t volatile u8SendByte;
78 bool volatile fSending;
79 uint8_t Alignment[2];
80
81 /** Receive buffer. */
82 uint8_t abBuffer[256];
83 /** Number of bytes remaining in the receive buffer. */
84 volatile size_t cbRemaining;
85 /** Current position into the read buffer. */
86 uint8_t *pbBuf;
87
88 /** Read/write statistics */
89 STAMCOUNTER StatBytesRead;
90 STAMCOUNTER StatBytesWritten;
91} DRVCHAR, *PDRVCHAR;
92AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
93
94
95
96
97/* -=-=-=-=- IBase -=-=-=-=- */
98
99/**
100 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
101 */
102static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
103{
104 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
105 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
106
107 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
108 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
109 return NULL;
110}
111
112
113/* -=-=-=-=- ICharConnector -=-=-=-=- */
114
115/**
116 * @interface_method_impl{PDMICHARCONNECTOR,pfnWrite}
117 */
118static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
119{
120 PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector);
121 const char *pbBuffer = (const char *)pvBuf;
122
123 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
124
125 for (uint32_t i = 0; i < cbWrite; i++)
126 {
127 if (ASMAtomicXchgBool(&pThis->fSending, true))
128 return VERR_BUFFER_OVERFLOW;
129
130 pThis->u8SendByte = pbBuffer[i];
131 pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
132 STAM_COUNTER_INC(&pThis->StatBytesWritten);
133 }
134 return VINF_SUCCESS;
135}
136
137
138/**
139 * @interface_method_impl{PDMICHARCONNECTOR,pfnSetParameters}
140 */
141static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity,
142 unsigned cDataBits, unsigned cStopBits)
143{
144 RT_NOREF(pInterface, Bps, chParity, cDataBits, cStopBits);
145
146 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
147 return VINF_SUCCESS;
148}
149
150
151/* -=-=-=-=- I/O thread -=-=-=-=- */
152
153/**
154 * Send thread loop - pushes data down thru the driver chain.
155 *
156 * @returns VBox status code.
157 * @param pDrvIns The char driver instance.
158 * @param pThread The worker thread.
159 */
160static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
161{
162 RT_NOREF(pDrvIns);
163 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
164
165 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
166 return VINF_SUCCESS;
167
168 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
169 {
170 uint32_t fEvts = 0;
171
172 if ( !pThis->cbRemaining
173 && pThis->pDrvStream->pfnRead)
174 fEvts |= RTPOLL_EVT_READ;
175 if (pThis->fSending)
176 fEvts |= RTPOLL_EVT_WRITE;
177
178 uint32_t fEvtsRecv = 0;
179 int rc = pThis->pDrvStream->pfnPoll(pThis->pDrvStream, fEvts, &fEvtsRecv, RT_INDEFINITE_WAIT);
180 if (RT_SUCCESS(rc))
181 {
182 if (fEvtsRecv & RTPOLL_EVT_WRITE)
183 {
184 Assert(pThis->fSending);
185
186 size_t cbProcessed = 1;
187 uint8_t ch = pThis->u8SendByte;
188 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed);
189 if (RT_SUCCESS(rc))
190 {
191 ASMAtomicXchgBool(&pThis->fSending, false);
192 Assert(cbProcessed == 1);
193 }
194 else if (rc == VERR_TIMEOUT)
195 {
196 /* Normal case, just means that the stream didn't accept a new
197 * character before the timeout elapsed. Just retry. */
198
199 /* do not change the rc status here, otherwise the (rc == VERR_TIMEOUT) branch
200 * in the wait above will never get executed */
201 /* rc = VINF_SUCCESS; */
202 }
203 else
204 {
205 LogRel(("Write failed with %Rrc; skipping\n", rc));
206 break;
207 }
208 }
209
210 if (fEvtsRecv & RTPOLL_EVT_READ)
211 {
212 AssertPtr(pThis->pDrvStream->pfnRead);
213 Assert(!pThis->cbRemaining);
214
215 size_t cbRead = sizeof(pThis->abBuffer);
216 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, &pThis->abBuffer[0], &cbRead);
217 if (RT_FAILURE(rc))
218 {
219 LogFlow(("Read failed with %Rrc\n", rc));
220 break;
221 }
222 pThis->pbBuf = &pThis->abBuffer[0];
223 ASMAtomicWriteZ(&pThis->cbRemaining, cbRead);
224 RTSemEventSignal(pThis->hEvtSemRead); /* Wakeup relay thread to continue. */
225 }
226 }
227 else if (rc != VERR_INTERRUPTED)
228 LogRelMax(10, ("Char#%d: Polling failed with %Rrc\n", pDrvIns->iInstance, rc));
229 }
230
231 return VINF_SUCCESS;
232}
233
234
235/**
236 * Unblock the send worker thread so it can respond to a state change.
237 *
238 * @returns VBox status code.
239 * @param pDrvIns The char driver instance.
240 * @param pThread The worker thread.
241 */
242static DECLCALLBACK(int) drvCharIoLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
243{
244 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
245
246 RT_NOREF(pDrvIns);
247 return pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
248}
249
250
251static DECLCALLBACK(int) drvCharReadRelayLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
252{
253 RT_NOREF(pDrvIns);
254 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
255
256 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
257 return VINF_SUCCESS;
258
259 int rc = VINF_SUCCESS;
260 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
261 {
262 size_t cbRem = ASMAtomicReadZ(&pThis->cbRemaining);
263
264 /* Block as long as there is nothing to relay. */
265 if (!pThis->cbRemaining)
266 rc = RTSemEventWait(pThis->hEvtSemRead, RT_INDEFINITE_WAIT);
267
268 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
269 break;
270
271 cbRem = ASMAtomicReadZ(&pThis->cbRemaining);
272 if (cbRem)
273 {
274 /* Send data to guest. */
275 size_t cbProcessed = cbRem;
276 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pThis->pbBuf, &cbProcessed);
277 if (RT_SUCCESS(rc))
278 {
279 Assert(cbProcessed);
280 pThis->pbBuf += cbProcessed;
281
282 /* Wake up the I/o thread so it can read new data to process. */
283 cbRem = ASMAtomicSubZ(&pThis->cbRemaining, cbProcessed);
284 if (cbRem == cbProcessed)
285 pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
286 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
287 }
288 else if (rc == VERR_TIMEOUT)
289 {
290 /* Normal case, just means that the guest didn't accept a new
291 * character before the timeout elapsed. Just retry. */
292 rc = VINF_SUCCESS;
293 }
294 else
295 {
296 LogFlow(("NotifyRead failed with %Rrc\n", rc));
297 break;
298 }
299 }
300 }
301
302 return VINF_SUCCESS;
303}
304
305
306/**
307 * Unblock the read relay worker thread so it can respond to a state change.
308 *
309 * @returns VBox status code.
310 * @param pDrvIns The char driver instance.
311 * @param pThread The worker thread.
312 */
313static DECLCALLBACK(int) drvCharReadRelayLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
314{
315 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
316
317 RT_NOREF(pDrvIns);
318 return RTSemEventSignal(pThis->hEvtSemRead);
319}
320
321
322/**
323 * @callback_method_impl{PDMICHARCONNECTOR,pfnSetModemLines}
324 */
325static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool fRequestToSend, bool fDataTerminalReady)
326{
327 /* Nothing to do here. */
328 RT_NOREF(pInterface, fRequestToSend, fDataTerminalReady);
329 return VINF_SUCCESS;
330}
331
332
333/**
334 * @callback_method_impl{PDMICHARCONNECTOR,pfnSetBreak}
335 */
336static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
337{
338 /* Nothing to do here. */
339 RT_NOREF(pInterface, fBreak);
340 return VINF_SUCCESS;
341}
342
343
344/* -=-=-=-=- driver interface -=-=-=-=- */
345
346/**
347 * Destruct a char driver instance.
348 *
349 * Most VM resources are freed by the VM. This callback is provided so that
350 * any non-VM resources can be freed correctly.
351 *
352 * @param pDrvIns The driver instance data.
353 */
354static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
355{
356 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
357 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
358 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
359
360 if (pThis->hEvtSemRead != NIL_RTSEMEVENT)
361 {
362 RTSemEventDestroy(pThis->hEvtSemRead);
363 pThis->hEvtSemRead = NIL_RTSEMEVENT;
364 }
365}
366
367
368/**
369 * Construct a char driver instance.
370 *
371 * @copydoc FNPDMDRVCONSTRUCT
372 */
373static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
374{
375 RT_NOREF(pCfg);
376 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
377 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
378 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
379
380 /*
381 * Init basic data members and interfaces.
382 */
383 pThis->pDrvIns = pDrvIns;
384 pThis->pThrdIo = NIL_RTTHREAD;
385 pThis->pThrdRead = NIL_RTTHREAD;
386 pThis->hEvtSemRead = NIL_RTSEMEVENT;
387 /* IBase. */
388 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
389 /* ICharConnector. */
390 pThis->ICharConnector.pfnWrite = drvCharWrite;
391 pThis->ICharConnector.pfnSetParameters = drvCharSetParameters;
392 pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines;
393 pThis->ICharConnector.pfnSetBreak = drvCharSetBreak;
394
395 /*
396 * Get the ICharPort interface of the above driver/device.
397 */
398 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
399 if (!pThis->pDrvCharPort)
400 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
401
402 /*
403 * Attach driver below and query its stream interface.
404 */
405 PPDMIBASE pBase;
406 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
407 if (RT_FAILURE(rc))
408 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
409 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
410 if (!pThis->pDrvStream)
411 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
412
413 /* Don't start the receive relay thread if reading is not supported. */
414 if (pThis->pDrvStream->pfnRead)
415 {
416 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdRead, pThis, drvCharReadRelayLoop,
417 drvCharReadRelayLoopWakeup, 0, RTTHREADTYPE_IO, "CharReadRel");
418 if (RT_FAILURE(rc))
419 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create read relay thread"), pDrvIns->iInstance);
420
421 rc = RTSemEventCreate(&pThis->hEvtSemRead);
422 AssertRCReturn(rc, rc);
423 }
424
425 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdIo, pThis, drvCharIoLoop,
426 drvCharIoLoopWakeup, 0, RTTHREADTYPE_IO, "CharIo");
427 if (RT_FAILURE(rc))
428 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
429
430
431 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
432 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
433
434 return VINF_SUCCESS;
435}
436
437
438/**
439 * Char driver registration record.
440 */
441const PDMDRVREG g_DrvChar =
442{
443 /* u32Version */
444 PDM_DRVREG_VERSION,
445 /* szName */
446 "Char",
447 /* szRCMod */
448 "",
449 /* szR0Mod */
450 "",
451 /* pszDescription */
452 "Generic char driver.",
453 /* fFlags */
454 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
455 /* fClass. */
456 PDM_DRVREG_CLASS_CHAR,
457 /* cMaxInstances */
458 ~0U,
459 /* cbInstance */
460 sizeof(DRVCHAR),
461 /* pfnConstruct */
462 drvCharConstruct,
463 /* pfnDestruct */
464 drvCharDestruct,
465 /* pfnRelocate */
466 NULL,
467 /* pfnIOCtl */
468 NULL,
469 /* pfnPowerOn */
470 NULL,
471 /* pfnReset */
472 NULL,
473 /* pfnSuspend */
474 NULL,
475 /* pfnResume */
476 NULL,
477 /* pfnAttach */
478 NULL,
479 /* pfnDetach */
480 NULL,
481 /* pfnPowerOff */
482 NULL,
483 /* pfnSoftReset */
484 NULL,
485 /* u32EndVersion */
486 PDM_DRVREG_VERSION
487};
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