VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvCharNew.cpp@ 73243

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

Devices/Serial: Add missing implementation for character timeout indication when the FIFO is enabled, add 16750 implementation which provides 64byte FIFOs, additional number of bug fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.6 KB
Line 
1/* $Id: DrvCharNew.cpp 73243 2018-07-19 15:08:34Z vboxsync $ */
2/** @file
3 * Driver that adapts PDMISTREAM into PDMISERIALCONNECTOR / PDMISERIALPORT.
4 */
5
6/*
7 * Copyright (C) 2006-2018 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_CHAR
23#include <VBox/vmm/pdmdrv.h>
24#include <VBox/vmm/pdmserialifs.h>
25#include <iprt/asm.h>
26#include <iprt/assert.h>
27#include <iprt/poll.h>
28#include <iprt/stream.h>
29#include <iprt/critsect.h>
30#include <iprt/semaphore.h>
31#include <iprt/uuid.h>
32
33#include "VBoxDD.h"
34
35
36/*********************************************************************************************************************************
37* Defined Constants And Macros *
38*********************************************************************************************************************************/
39
40
41/*********************************************************************************************************************************
42* Structures and Typedefs *
43*********************************************************************************************************************************/
44/**
45 * Char driver instance data.
46 *
47 * @implements PDMISERIALCONNECTOR
48 */
49typedef struct DRVCHAR
50{
51 /** Pointer to the driver instance structure. */
52 PPDMDRVINS pDrvIns;
53 /** Pointer to the char port interface of the driver/device above us. */
54 PPDMISERIALPORT pDrvSerialPort;
55 /** Pointer to the stream interface of the driver below us. */
56 PPDMISTREAM pDrvStream;
57 /** Our serial interface. */
58 PDMISERIALCONNECTOR ISerialConnector;
59 /** Flag to notify the receive thread it should terminate. */
60 volatile bool fShutdown;
61 /** I/O thread. */
62 PPDMTHREAD pThrdIo;
63
64 /** Amount of data available for sending from the device/driver above. */
65 volatile size_t cbAvailWr;
66 /** Small send buffer. */
67 uint8_t abTxBuf[16];
68 /** Amount of data in the buffer. */
69 size_t cbTxUsed;
70
71 /** Receive buffer. */
72 uint8_t abBuffer[256];
73 /** Number of bytes remaining in the receive buffer. */
74 volatile size_t cbRemaining;
75 /** Current position into the read buffer. */
76 uint8_t *pbBuf;
77
78 /** Read/write statistics */
79 STAMCOUNTER StatBytesRead;
80 STAMCOUNTER StatBytesWritten;
81} DRVCHAR, *PDRVCHAR;
82AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
83
84
85
86
87/* -=-=-=-=- IBase -=-=-=-=- */
88
89/**
90 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
91 */
92static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
93{
94 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
95 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
96
97 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
98 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector);
99 return NULL;
100}
101
102
103/* -=-=-=-=- ISerialConnector -=-=-=-=- */
104
105
106/**
107 * @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify}
108 */
109static DECLCALLBACK(int) drvCharDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface, size_t cbAvail)
110{
111 PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector);
112
113 int rc = VINF_SUCCESS;
114 size_t cbAvailOld = ASMAtomicAddZ(&pThis->cbAvailWr, cbAvail);
115 if (!cbAvailOld)
116 rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
117
118 return rc;
119}
120
121
122/**
123 * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr}
124 */
125static DECLCALLBACK(int) drvCharReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf, size_t cbRead, size_t *pcbRead)
126{
127 PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector);
128 int rc = VINF_SUCCESS;
129
130 AssertReturn(pThis->cbRemaining, VERR_INVALID_STATE);
131 size_t cbToRead = RT_MIN(cbRead, pThis->cbRemaining);
132 memcpy(pvBuf, pThis->pbBuf, cbToRead);
133 *pcbRead = cbToRead;
134 size_t cbOld = ASMAtomicSubZ(&pThis->cbRemaining, cbToRead);
135 if (!(cbOld - cbToRead)) /* Kick the I/O thread to fetch new data. */
136 rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
137 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbToRead);
138
139 return rc;
140}
141
142
143/**
144 * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams}
145 */
146static DECLCALLBACK(int) drvCharChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps,
147 PDMSERIALPARITY enmParity, unsigned cDataBits,
148 PDMSERIALSTOPBITS enmStopBits)
149{
150 /* Nothing to do here. */
151 RT_NOREF(pInterface, uBps, enmParity, cDataBits, enmStopBits);
152 return VINF_SUCCESS;
153}
154
155
156/**
157 * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines}
158 */
159static DECLCALLBACK(int) drvCharChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr)
160{
161 /* Nothing to do here. */
162 RT_NOREF(pInterface, fRts, fDtr);
163 return VINF_SUCCESS;
164}
165
166
167/**
168 * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgBrk}
169 */
170static DECLCALLBACK(int) drvCharChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk)
171{
172 /* Nothing to do here. */
173 RT_NOREF(pInterface, fBrk);
174 return VINF_SUCCESS;
175}
176
177
178/**
179 * @callback_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines}
180 */
181static DECLCALLBACK(int) drvCharQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines)
182{
183 /* Nothing to do here. */
184 *pfStsLines = 0;
185 RT_NOREF(pInterface);
186 return VINF_SUCCESS;
187}
188
189
190/* -=-=-=-=- I/O thread -=-=-=-=- */
191
192/**
193 * Send thread loop - pushes data down thru the driver chain.
194 *
195 * @returns VBox status code.
196 * @param pDrvIns The char driver instance.
197 * @param pThread The worker thread.
198 */
199static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
200{
201 RT_NOREF(pDrvIns);
202 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
203
204 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
205 return VINF_SUCCESS;
206
207 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
208 {
209 uint32_t fEvts = 0;
210
211 if ( !pThis->cbRemaining
212 && pThis->pDrvStream->pfnRead)
213 fEvts |= RTPOLL_EVT_READ;
214 if ( pThis->cbAvailWr
215 || pThis->cbTxUsed)
216 fEvts |= RTPOLL_EVT_WRITE;
217
218 uint32_t fEvtsRecv = 0;
219 int rc = pThis->pDrvStream->pfnPoll(pThis->pDrvStream, fEvts, &fEvtsRecv, RT_INDEFINITE_WAIT);
220 if (RT_SUCCESS(rc))
221 {
222 if (fEvtsRecv & RTPOLL_EVT_WRITE)
223 {
224 if (pThis->cbAvailWr)
225 {
226 /* Stuff as much data into the TX buffer as we can. */
227 size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed;
228 size_t cbFetched = 0;
229 rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch,
230 &cbFetched);
231 AssertRC(rc);
232
233 if (cbFetched > 0)
234 {
235 ASMAtomicSubZ(&pThis->cbAvailWr, cbFetched);
236 pThis->cbTxUsed += cbFetched;
237 }
238 else
239 {
240 /* The guest reset the send queue and there is no data available anymore. */
241 pThis->cbAvailWr = 0;
242 }
243 }
244
245 size_t cbProcessed = pThis->cbTxUsed;
246 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->abTxBuf[0], &cbProcessed);
247 if (RT_SUCCESS(rc))
248 {
249 pThis->cbTxUsed -= cbProcessed;
250 if (pThis->cbTxUsed)
251 {
252 /* Move the data in the TX buffer to the front to fill the end again. */
253 memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed);
254 }
255 else
256 pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort);
257 STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed);
258 }
259 else if (rc != VERR_TIMEOUT)
260 {
261 LogRel(("Char#%d: Write failed with %Rrc; skipping\n", pDrvIns->iInstance, rc));
262 break;
263 }
264 }
265
266 if (fEvtsRecv & RTPOLL_EVT_READ)
267 {
268 AssertPtr(pThis->pDrvStream->pfnRead);
269 Assert(!pThis->cbRemaining);
270
271 size_t cbRead = sizeof(pThis->abBuffer);
272 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, &pThis->abBuffer[0], &cbRead);
273 if (RT_FAILURE(rc))
274 {
275 LogFlow(("Read failed with %Rrc\n", rc));
276 break;
277 }
278 pThis->pbBuf = &pThis->abBuffer[0];
279 ASMAtomicWriteZ(&pThis->cbRemaining, cbRead);
280 /* Notify the upper device/driver. */
281 rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead);
282 }
283 }
284 else if (rc != VERR_INTERRUPTED)
285 LogRelMax(10, ("Char#%d: Polling failed with %Rrc\n", pDrvIns->iInstance, rc));
286 }
287
288 return VINF_SUCCESS;
289}
290
291
292/**
293 * Unblock the send worker thread so it can respond to a state change.
294 *
295 * @returns VBox status code.
296 * @param pDrvIns The char driver instance.
297 * @param pThread The worker thread.
298 */
299static DECLCALLBACK(int) drvCharIoLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
300{
301 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
302
303 RT_NOREF(pDrvIns);
304 return pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
305}
306
307
308/* -=-=-=-=- driver interface -=-=-=-=- */
309
310/**
311 * @interface_method_impl{PDMDEVREG,pfnReset}
312 */
313static DECLCALLBACK(void) drvCharReset(PPDMDRVINS pDrvIns)
314{
315 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
316 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
317
318 /* Reset TX and RX buffers. */
319 pThis->cbAvailWr = 0;
320 pThis->cbTxUsed = 0;
321 pThis->cbRemaining = 0;
322}
323
324
325/**
326 * Construct a char driver instance.
327 *
328 * @copydoc FNPDMDRVCONSTRUCT
329 */
330static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
331{
332 RT_NOREF(pCfg);
333 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
334 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
335 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
336
337 /*
338 * Init basic data members and interfaces.
339 */
340 pThis->pDrvIns = pDrvIns;
341 pThis->pThrdIo = NIL_RTTHREAD;
342 /* IBase. */
343 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
344 /* ISerialConnector. */
345 pThis->ISerialConnector.pfnDataAvailWrNotify = drvCharDataAvailWrNotify;
346 pThis->ISerialConnector.pfnReadRdr = drvCharReadRdr;
347 pThis->ISerialConnector.pfnChgParams = drvCharChgParams;
348 pThis->ISerialConnector.pfnChgModemLines = drvCharChgModemLines;
349 pThis->ISerialConnector.pfnChgBrk = drvCharChgBrk;
350 pThis->ISerialConnector.pfnQueryStsLines = drvCharQueryStsLines;
351
352 /*
353 * Get the ISerialPort interface of the above driver/device.
354 */
355 pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT);
356 if (!pThis->pDrvSerialPort)
357 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS,
358 N_("Char#%d has no serial port interface above"), pDrvIns->iInstance);
359
360 /*
361 * Attach driver below and query its stream interface.
362 */
363 PPDMIBASE pBase;
364 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
365 if (RT_FAILURE(rc))
366 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
367 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
368 if (!pThis->pDrvStream)
369 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS,
370 N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
371
372 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdIo, pThis, drvCharIoLoop,
373 drvCharIoLoopWakeup, 0, RTTHREADTYPE_IO, "CharIo");
374 if (RT_FAILURE(rc))
375 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create I/O thread"), pDrvIns->iInstance);
376
377
378 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
379 "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
380 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES,
381 "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
382
383 return VINF_SUCCESS;
384}
385
386
387/**
388 * Char driver registration record.
389 */
390const PDMDRVREG g_DrvChar =
391{
392 /* u32Version */
393 PDM_DRVREG_VERSION,
394 /* szName */
395 "Char",
396 /* szRCMod */
397 "",
398 /* szR0Mod */
399 "",
400 /* pszDescription */
401 "Generic char driver.",
402 /* fFlags */
403 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
404 /* fClass. */
405 PDM_DRVREG_CLASS_CHAR,
406 /* cMaxInstances */
407 ~0U,
408 /* cbInstance */
409 sizeof(DRVCHAR),
410 /* pfnConstruct */
411 drvCharConstruct,
412 /* pfnDestruct */
413 NULL,
414 /* pfnRelocate */
415 NULL,
416 /* pfnIOCtl */
417 NULL,
418 /* pfnPowerOn */
419 NULL,
420 /* pfnReset */
421 drvCharReset,
422 /* pfnSuspend */
423 NULL,
424 /* pfnResume */
425 NULL,
426 /* pfnAttach */
427 NULL,
428 /* pfnDetach */
429 NULL,
430 /* pfnPowerOff */
431 NULL,
432 /* pfnSoftReset */
433 NULL,
434 /* u32EndVersion */
435 PDM_DRVREG_VERSION
436};
Note: See TracBrowser for help on using the repository browser.

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