~ubuntu-branches/ubuntu/trusty/virtualbox-lts-xenial/trusty-updates

« back to all changes in this revision

Viewing changes to src/VBox/Devices/Serial/DrvChar.cpp

  • Committer: Package Import Robot
  • Author(s): Gianfranco Costamagna
  • Date: 2016-02-23 14:28:26 UTC
  • Revision ID: package-import@ubuntu.com-20160223142826-bdu69el2z6wa2a44
Tags: upstream-4.3.36-dfsg
ImportĀ upstreamĀ versionĀ 4.3.36-dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: DrvChar.cpp $ */
 
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-2012 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/stream.h>
 
33
#include <iprt/semaphore.h>
 
34
#include <iprt/uuid.h>
 
35
 
 
36
#include "VBoxDD.h"
 
37
 
 
38
 
 
39
/*******************************************************************************
 
40
*   Defined Constants And Macros                                               *
 
41
*******************************************************************************/
 
42
/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVCHAR. */
 
43
#define PDMICHAR_2_DRVCHAR(pInterface)  RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector)
 
44
 
 
45
 
 
46
/*******************************************************************************
 
47
*   Structures and Typedefs                                                    *
 
48
*******************************************************************************/
 
49
/**
 
50
 * Char driver instance data.
 
51
 *
 
52
 * @implements PDMICHARCONNECTOR
 
53
 */
 
54
typedef struct DRVCHAR
 
55
{
 
56
    /** Pointer to the driver instance structure. */
 
57
    PPDMDRVINS                  pDrvIns;
 
58
    /** Pointer to the char port interface of the driver/device above us. */
 
59
    PPDMICHARPORT               pDrvCharPort;
 
60
    /** Pointer to the stream interface of the driver below us. */
 
61
    PPDMISTREAM                 pDrvStream;
 
62
    /** Our char interface. */
 
63
    PDMICHARCONNECTOR           ICharConnector;
 
64
    /** Flag to notify the receive thread it should terminate. */
 
65
    volatile bool               fShutdown;
 
66
    /** Receive thread ID. */
 
67
    RTTHREAD                    ReceiveThread;
 
68
    /** Send thread ID. */
 
69
    RTTHREAD                    SendThread;
 
70
    /** Send event semaphore */
 
71
    RTSEMEVENT                  SendSem;
 
72
 
 
73
    /** Internal send FIFO queue */
 
74
    uint8_t volatile            u8SendByte;
 
75
    bool volatile               fSending;
 
76
    uint8_t                     Alignment[2];
 
77
 
 
78
    /** Read/write statistics */
 
79
    STAMCOUNTER                 StatBytesRead;
 
80
    STAMCOUNTER                 StatBytesWritten;
 
81
} DRVCHAR, *PDRVCHAR;
 
82
AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
 
83
 
 
84
 
 
85
 
 
86
 
 
87
/* -=-=-=-=- IBase -=-=-=-=- */
 
88
 
 
89
/**
 
90
 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
 
91
 */
 
92
static 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, PDMICHARCONNECTOR, &pThis->ICharConnector);
 
99
    return NULL;
 
100
}
 
101
 
 
102
 
 
103
/* -=-=-=-=- ICharConnector -=-=-=-=- */
 
104
 
 
105
/** @copydoc PDMICHARCONNECTOR::pfnWrite */
 
106
static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
 
107
{
 
108
    PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface);
 
109
    const char *pbBuffer = (const char *)pvBuf;
 
110
 
 
111
    LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
 
112
 
 
113
    for (uint32_t i = 0; i < cbWrite; i++)
 
114
    {
 
115
        if (ASMAtomicXchgBool(&pThis->fSending, true))
 
116
            return VERR_BUFFER_OVERFLOW;
 
117
 
 
118
        pThis->u8SendByte = pbBuffer[i];
 
119
        RTSemEventSignal(pThis->SendSem);
 
120
        STAM_COUNTER_INC(&pThis->StatBytesWritten);
 
121
    }
 
122
    return VINF_SUCCESS;
 
123
}
 
124
 
 
125
/** @copydoc PDMICHARCONNECTOR::pfnSetParameters */
 
126
static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
 
127
{
 
128
    /*PDRVCHAR pThis = PDMICHAR_2_DRVCHAR(pInterface); - unused*/
 
129
 
 
130
    LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
 
131
    return VINF_SUCCESS;
 
132
}
 
133
 
 
134
 
 
135
/* -=-=-=-=- receive thread -=-=-=-=- */
 
136
 
 
137
/**
 
138
 * Send thread loop - pushes data down thru the driver chain.
 
139
 *
 
140
 * @returns 0 on success.
 
141
 * @param   ThreadSelf  Thread handle to this thread.
 
142
 * @param   pvUser      User argument.
 
143
 */
 
144
static DECLCALLBACK(int) drvCharSendLoop(RTTHREAD ThreadSelf, void *pvUser)
 
145
{
 
146
    PDRVCHAR pThis = (PDRVCHAR)pvUser;
 
147
 
 
148
    int rc = VINF_SUCCESS;
 
149
    while (!pThis->fShutdown)
 
150
    {
 
151
        RTMSINTERVAL cMillies = (rc == VERR_TIMEOUT) ? 50 : RT_INDEFINITE_WAIT;
 
152
        rc = RTSemEventWait(pThis->SendSem, cMillies);
 
153
        if (    RT_FAILURE(rc)
 
154
             && rc != VERR_TIMEOUT)
 
155
            break;
 
156
 
 
157
        /*
 
158
         * Write the character to the attached stream (if present).
 
159
         */
 
160
        if (    pThis->fShutdown
 
161
            ||  !pThis->pDrvStream)
 
162
            break;
 
163
 
 
164
        size_t cbProcessed = 1;
 
165
        uint8_t ch = pThis->u8SendByte;
 
166
        rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed);
 
167
        if (RT_SUCCESS(rc))
 
168
        {
 
169
            ASMAtomicXchgBool(&pThis->fSending, false);
 
170
            Assert(cbProcessed == 1);
 
171
        }
 
172
        else if (rc == VERR_TIMEOUT)
 
173
        {
 
174
            /* Normal case, just means that the stream didn't accept a new
 
175
             * character before the timeout elapsed. Just retry. */
 
176
 
 
177
            /* do not change the rc status here, otherwise the (rc == VERR_TIMEOUT) branch
 
178
             * in the wait above will never get executed */
 
179
            /* rc = VINF_SUCCESS; */
 
180
        }
 
181
        else
 
182
        {
 
183
            LogRel(("Write failed with %Rrc; skipping\n", rc));
 
184
            break;
 
185
        }
 
186
    }
 
187
 
 
188
    return VINF_SUCCESS;
 
189
}
 
190
 
 
191
/* -=-=-=-=- receive thread -=-=-=-=- */
 
192
 
 
193
/**
 
194
 * Receive thread loop.
 
195
 *
 
196
 * @returns 0 on success.
 
197
 * @param   ThreadSelf  Thread handle to this thread.
 
198
 * @param   pvUser      User argument.
 
199
 */
 
200
static DECLCALLBACK(int) drvCharReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
 
201
{
 
202
    PDRVCHAR    pThis = (PDRVCHAR)pvUser;
 
203
    char        abBuffer[256];
 
204
    char       *pbRemaining = abBuffer;
 
205
    size_t      cbRemaining = 0;
 
206
    int         rc;
 
207
 
 
208
    while (!pThis->fShutdown)
 
209
    {
 
210
        if (!cbRemaining)
 
211
        {
 
212
            /* Get block of data from stream driver. */
 
213
            if (pThis->pDrvStream)
 
214
            {
 
215
                pbRemaining = abBuffer;
 
216
                cbRemaining = sizeof(abBuffer);
 
217
                rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, abBuffer, &cbRemaining);
 
218
                if (RT_FAILURE(rc))
 
219
                {
 
220
                    LogFlow(("Read failed with %Rrc\n", rc));
 
221
                    break;
 
222
                }
 
223
            }
 
224
            else
 
225
                RTThreadSleep(100);
 
226
        }
 
227
        else
 
228
        {
 
229
            /* Send data to guest. */
 
230
            size_t cbProcessed = cbRemaining;
 
231
            rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbRemaining, &cbProcessed);
 
232
            if (RT_SUCCESS(rc))
 
233
            {
 
234
                Assert(cbProcessed);
 
235
                pbRemaining += cbProcessed;
 
236
                cbRemaining -= cbProcessed;
 
237
                STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
 
238
            }
 
239
            else if (rc == VERR_TIMEOUT)
 
240
            {
 
241
                /* Normal case, just means that the guest didn't accept a new
 
242
                 * character before the timeout elapsed. Just retry. */
 
243
                rc = VINF_SUCCESS;
 
244
            }
 
245
            else
 
246
            {
 
247
                LogFlow(("NotifyRead failed with %Rrc\n", rc));
 
248
                break;
 
249
            }
 
250
        }
 
251
    }
 
252
 
 
253
    return VINF_SUCCESS;
 
254
}
 
255
 
 
256
/**
 
257
 * Set the modem lines.
 
258
 *
 
259
 * @returns VBox status code
 
260
 * @param pInterface        Pointer to the interface structure.
 
261
 * @param RequestToSend     Set to true if this control line should be made active.
 
262
 * @param DataTerminalReady Set to true if this control line should be made active.
 
263
 */
 
264
static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady)
 
265
{
 
266
    /* Nothing to do here. */
 
267
    return VINF_SUCCESS;
 
268
}
 
269
 
 
270
/**
 
271
 * Sets the TD line into break condition.
 
272
 *
 
273
 * @returns VBox status code.
 
274
 * @param   pInterface  Pointer to the interface structure containing the called function pointer.
 
275
 * @param   fBreak      Set to true to let the device send a break false to put into normal operation.
 
276
 * @thread  Any thread.
 
277
 */
 
278
static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
 
279
{
 
280
    /* Nothing to do here. */
 
281
    return VINF_SUCCESS;
 
282
}
 
283
 
 
284
/* -=-=-=-=- driver interface -=-=-=-=- */
 
285
 
 
286
/**
 
287
 * Destruct a char driver instance.
 
288
 *
 
289
 * Most VM resources are freed by the VM. This callback is provided so that
 
290
 * any non-VM resources can be freed correctly.
 
291
 *
 
292
 * @param   pDrvIns     The driver instance data.
 
293
 */
 
294
static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
 
295
{
 
296
    PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
 
297
    LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
 
298
    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
 
299
 
 
300
    /*
 
301
     * Tell the threads to shut down.
 
302
     */
 
303
    pThis->fShutdown = true;
 
304
    if (pThis->SendSem != NIL_RTSEMEVENT)
 
305
    {
 
306
        RTSemEventSignal(pThis->SendSem);
 
307
        pThis->SendSem = NIL_RTSEMEVENT;
 
308
    }
 
309
 
 
310
    /*
 
311
     * Wait for the threads.
 
312
     * ASSUMES that PDM destroys the driver chain from the bottom and up.
 
313
     */
 
314
    if (pThis->ReceiveThread != NIL_RTTHREAD)
 
315
    {
 
316
        int rc = RTThreadWait(pThis->ReceiveThread, 30000, NULL);
 
317
        if (RT_SUCCESS(rc))
 
318
            pThis->ReceiveThread = NIL_RTTHREAD;
 
319
        else
 
320
            LogRel(("Char%d: receive thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
 
321
    }
 
322
 
 
323
    if (pThis->SendThread != NIL_RTTHREAD)
 
324
    {
 
325
        int rc = RTThreadWait(pThis->SendThread, 30000, NULL);
 
326
        if (RT_SUCCESS(rc))
 
327
            pThis->SendThread = NIL_RTTHREAD;
 
328
        else
 
329
            LogRel(("Char%d: send thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
 
330
    }
 
331
 
 
332
    if (pThis->SendSem != NIL_RTSEMEVENT)
 
333
    {
 
334
        RTSemEventDestroy(pThis->SendSem);
 
335
        pThis->SendSem = NIL_RTSEMEVENT;
 
336
    }
 
337
}
 
338
 
 
339
 
 
340
/**
 
341
 * Construct a char driver instance.
 
342
 *
 
343
 * @copydoc FNPDMDRVCONSTRUCT
 
344
 */
 
345
static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
 
346
{
 
347
    PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
 
348
    LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
 
349
    PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
 
350
 
 
351
    /*
 
352
     * Init basic data members and interfaces.
 
353
     */
 
354
    pThis->fShutdown                        = false;
 
355
    pThis->ReceiveThread                    = NIL_RTTHREAD;
 
356
    pThis->SendThread                       = NIL_RTTHREAD;
 
357
    pThis->SendSem                          = NIL_RTSEMEVENT;
 
358
    /* IBase. */
 
359
    pDrvIns->IBase.pfnQueryInterface        = drvCharQueryInterface;
 
360
    /* ICharConnector. */
 
361
    pThis->ICharConnector.pfnWrite          = drvCharWrite;
 
362
    pThis->ICharConnector.pfnSetParameters  = drvCharSetParameters;
 
363
    pThis->ICharConnector.pfnSetModemLines  = drvCharSetModemLines;
 
364
    pThis->ICharConnector.pfnSetBreak       = drvCharSetBreak;
 
365
 
 
366
    /*
 
367
     * Get the ICharPort interface of the above driver/device.
 
368
     */
 
369
    pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
 
370
    if (!pThis->pDrvCharPort)
 
371
        return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
 
372
 
 
373
    /*
 
374
     * Attach driver below and query its stream interface.
 
375
     */
 
376
    PPDMIBASE pBase;
 
377
    int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
 
378
    if (RT_FAILURE(rc))
 
379
        return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
 
380
    pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
 
381
    if (!pThis->pDrvStream)
 
382
        return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
 
383
 
 
384
    /*
 
385
     * Don't start the receive thread if the driver doesn't support reading
 
386
     */
 
387
    if (pThis->pDrvStream->pfnRead)
 
388
    {
 
389
        rc = RTThreadCreate(&pThis->ReceiveThread, drvCharReceiveLoop, (void *)pThis, 0,
 
390
                            RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharRecv");
 
391
        if (RT_FAILURE(rc))
 
392
            return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
 
393
    }
 
394
 
 
395
    rc = RTSemEventCreate(&pThis->SendSem);
 
396
    AssertRCReturn(rc, rc);
 
397
 
 
398
    rc = RTThreadCreate(&pThis->SendThread, drvCharSendLoop, (void *)pThis, 0,
 
399
                        RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "CharSend");
 
400
    if (RT_FAILURE(rc))
 
401
        return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
 
402
 
 
403
 
 
404
    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten,    STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written",         "/Devices/Char%d/Written", pDrvIns->iInstance);
 
405
    PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead,       STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read",            "/Devices/Char%d/Read", pDrvIns->iInstance);
 
406
 
 
407
    return VINF_SUCCESS;
 
408
}
 
409
 
 
410
 
 
411
/**
 
412
 * Char driver registration record.
 
413
 */
 
414
const PDMDRVREG g_DrvChar =
 
415
{
 
416
    /* u32Version */
 
417
    PDM_DRVREG_VERSION,
 
418
    /* szName */
 
419
    "Char",
 
420
    /* szRCMod */
 
421
    "",
 
422
    /* szR0Mod */
 
423
    "",
 
424
    /* pszDescription */
 
425
    "Generic char driver.",
 
426
    /* fFlags */
 
427
    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
 
428
    /* fClass. */
 
429
    PDM_DRVREG_CLASS_CHAR,
 
430
    /* cMaxInstances */
 
431
    ~0U,
 
432
    /* cbInstance */
 
433
    sizeof(DRVCHAR),
 
434
    /* pfnConstruct */
 
435
    drvCharConstruct,
 
436
    /* pfnDestruct */
 
437
    drvCharDestruct,
 
438
    /* pfnRelocate */
 
439
    NULL,
 
440
    /* pfnIOCtl */
 
441
    NULL,
 
442
    /* pfnPowerOn */
 
443
    NULL,
 
444
    /* pfnReset */
 
445
    NULL,
 
446
    /* pfnSuspend */
 
447
    NULL,
 
448
    /* pfnResume */
 
449
    NULL,
 
450
    /* pfnAttach */
 
451
    NULL,
 
452
    /* pfnDetach */
 
453
    NULL,
 
454
    /* pfnPowerOff */
 
455
    NULL,
 
456
    /* pfnSoftReset */
 
457
    NULL,
 
458
    /* u32EndVersion */
 
459
    PDM_DRVREG_VERSION
 
460
};