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

1 by Gianfranco Costamagna
Import upstream version 4.3.36-dfsg
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
};