~ubuntu-branches/ubuntu/gutsy/virtualbox-ose/gutsy

« back to all changes in this revision

Viewing changes to src/VBox/Devices/Storage/DrvHostDVD.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-09-08 16:44:58 UTC
  • Revision ID: james.westby@ubuntu.com-20070908164458-wao29470vqtr8ksy
Tags: upstream-1.5.0-dfsg2
ImportĀ upstreamĀ versionĀ 1.5.0-dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** @file
 
2
 *
 
3
 * VBox storage devices:
 
4
 * Host DVD block driver
 
5
 */
 
6
 
 
7
/*
 
8
 * Copyright (C) 2006-2007 innotek GmbH
 
9
 *
 
10
 * This file is part of VirtualBox Open Source Edition (OSE), as
 
11
 * available from http://www.virtualbox.org. This file is free software;
 
12
 * you can redistribute it and/or modify it under the terms of the GNU
 
13
 * General Public License as published by the Free Software Foundation,
 
14
 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
 
15
 * distribution. VirtualBox OSE is distributed in the hope that it will
 
16
 * be useful, but WITHOUT ANY WARRANTY of any kind.
 
17
 */
 
18
 
 
19
 
 
20
/*******************************************************************************
 
21
*   Header Files                                                               *
 
22
*******************************************************************************/
 
23
#define LOG_GROUP LOG_GROUP_DRV_HOST_DVD
 
24
#ifdef RT_OS_DARWIN
 
25
# include <mach/mach.h>
 
26
# include <Carbon/Carbon.h>
 
27
# include <IOKit/IOKitLib.h>
 
28
# include <IOKit/IOCFPlugIn.h>
 
29
# include <IOKit/scsi-commands/SCSITaskLib.h>
 
30
# include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
 
31
# include <IOKit/storage/IOStorageDeviceCharacteristics.h>
 
32
# include <mach/mach_error.h>
 
33
# define USE_MEDIA_POLLING
 
34
 
 
35
#elif defined(RT_OS_L4)
 
36
/* nothing (yet). */
 
37
 
 
38
#elif defined RT_OS_LINUX
 
39
# include <sys/ioctl.h>
 
40
/* This is a hack to work around conflicts between these linux kernel headers
 
41
 * and the GLIBC tcpip headers. They have different declarations of the 4
 
42
 * standard byte order functions. */
 
43
# define _LINUX_BYTEORDER_GENERIC_H
 
44
/* This is another hack for not bothering with C++ unfriendly byteswap macros. */
 
45
# define _LINUX_BYTEORDER_SWAB_H
 
46
/* Those macros that are needed are defined in the header below */
 
47
# include "swab.h"
 
48
# include <linux/cdrom.h>
 
49
# include <sys/fcntl.h>
 
50
# include <errno.h>
 
51
# define USE_MEDIA_POLLING
 
52
 
 
53
#elif defined(RT_OS_WINDOWS)
 
54
# include <Windows.h>
 
55
# include <winioctl.h>
 
56
# include <ntddscsi.h>
 
57
# undef USE_MEDIA_POLLING
 
58
 
 
59
#else
 
60
# error "Unsupported Platform."
 
61
#endif
 
62
 
 
63
#include <VBox/pdmdrv.h>
 
64
#include <iprt/assert.h>
 
65
#include <iprt/file.h>
 
66
#include <iprt/string.h>
 
67
#include <iprt/thread.h>
 
68
#include <iprt/critsect.h>
 
69
#include <VBox/scsi.h>
 
70
 
 
71
#include "Builtins.h"
 
72
#include "DrvHostBase.h"
 
73
 
 
74
 
 
75
/* Forward declarations. */
 
76
 
 
77
static DECLCALLBACK(int) drvHostDvdDoLock(PDRVHOSTBASE pThis, bool fLock);
 
78
 
 
79
 
 
80
/** @copydoc PDMIMOUNT::pfnUnmount */
 
81
static DECLCALLBACK(int) drvHostDvdUnmount(PPDMIMOUNT pInterface, bool fForce)
 
82
{
 
83
     PDRVHOSTBASE pThis = PDMIMOUNT_2_DRVHOSTBASE(pInterface);
 
84
     RTCritSectEnter(&pThis->CritSect);
 
85
 
 
86
     /*
 
87
      * Validate state.
 
88
      */
 
89
     int rc = VINF_SUCCESS;
 
90
     if (!pThis->fLocked || fForce)
 
91
     {
 
92
        /* Unlock drive if necessary. */
 
93
        if (pThis->fLocked)
 
94
            drvHostDvdDoLock(pThis, false);
 
95
 
 
96
         /*
 
97
          * Eject the disc.
 
98
          */
 
99
#ifdef RT_OS_DARWIN
 
100
         uint8_t abCmd[16] =
 
101
         {
 
102
             SCSI_START_STOP_UNIT, 0, 0, 0, 2 /*eject+stop*/, 0,
 
103
             0,0,0,0,0,0,0,0,0,0
 
104
         };
 
105
         rc = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
 
106
 
 
107
#elif defined(RT_OS_LINUX)
 
108
         rc = ioctl(pThis->FileDevice, CDROMEJECT, 0);
 
109
         if (rc < 0)
 
110
         {
 
111
             if (errno == EBUSY)
 
112
                 rc = VERR_PDM_MEDIA_LOCKED;
 
113
             else if (errno == ENOSYS)
 
114
                 rc = VERR_NOT_SUPPORTED;
 
115
             else
 
116
                 rc = RTErrConvertFromErrno(errno);
 
117
         }
 
118
 
 
119
#elif defined(RT_OS_WINDOWS)
 
120
         RTFILE FileDevice = pThis->FileDevice;
 
121
         if (FileDevice == NIL_RTFILE) /* obsolete crap */
 
122
             rc = RTFileOpen(&FileDevice, pThis->pszDeviceOpen, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
 
123
         if (VBOX_SUCCESS(rc))
 
124
         {
 
125
             /* do ioctl */
 
126
             DWORD cbReturned;
 
127
             if (DeviceIoControl((HANDLE)FileDevice, IOCTL_STORAGE_EJECT_MEDIA,
 
128
                                 NULL, 0,
 
129
                                 NULL, 0, &cbReturned,
 
130
                                 NULL))
 
131
                 rc = VINF_SUCCESS;
 
132
             else
 
133
                 rc = RTErrConvertFromWin32(GetLastError());
 
134
 
 
135
             /* clean up handle */
 
136
             if (FileDevice != pThis->FileDevice)
 
137
                 RTFileClose(FileDevice);
 
138
         }
 
139
         else
 
140
             AssertMsgFailed(("Failed to open '%s' for ejecting this tray.\n",  rc));
 
141
 
 
142
 
 
143
#else
 
144
         AssertMsgFailed(("Eject is not implemented!\n"));
 
145
         rc = VINF_SUCCESS;
 
146
#endif
 
147
 
 
148
         /*
 
149
          * Media is no longer present.
 
150
          */
 
151
         DRVHostBaseMediaNotPresent(pThis);  /** @todo This isn't thread safe! */
 
152
     }
 
153
     else
 
154
     {
 
155
         Log(("drvHostDvdUnmount: Locked\n"));
 
156
         rc = VERR_PDM_MEDIA_LOCKED;
 
157
     }
 
158
 
 
159
     RTCritSectLeave(&pThis->CritSect);
 
160
     LogFlow(("drvHostDvdUnmount: returns %Vrc\n", rc));
 
161
     return rc;
 
162
}
 
163
 
 
164
 
 
165
/**
 
166
 * Locks or unlocks the drive.
 
167
 *
 
168
 * @returns VBox status code.
 
169
 * @param   pThis       The instance data.
 
170
 * @param   fLock       True if the request is to lock the drive, false if to unlock.
 
171
 */
 
172
static DECLCALLBACK(int) drvHostDvdDoLock(PDRVHOSTBASE pThis, bool fLock)
 
173
{
 
174
#ifdef RT_OS_DARWIN
 
175
    uint8_t abCmd[16] =
 
176
    {
 
177
        SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, fLock, 0,
 
178
        0,0,0,0,0,0,0,0,0,0
 
179
    };
 
180
    int rc = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, NULL, 0, 0);
 
181
 
 
182
#elif defined(RT_OS_LINUX)
 
183
    int rc = ioctl(pThis->FileDevice, CDROM_LOCKDOOR, (int)fLock);
 
184
    if (rc < 0)
 
185
    {
 
186
        if (errno == EBUSY)
 
187
            rc = VERR_ACCESS_DENIED;
 
188
        else if (errno == EDRIVE_CANT_DO_THIS)
 
189
            rc = VERR_NOT_SUPPORTED;
 
190
        else
 
191
            rc = RTErrConvertFromErrno(errno);
 
192
    }
 
193
 
 
194
#elif defined(RT_OS_WINDOWS)
 
195
 
 
196
    PREVENT_MEDIA_REMOVAL PreventMediaRemoval = {fLock};
 
197
    DWORD cbReturned;
 
198
    int rc;
 
199
    if (DeviceIoControl((HANDLE)pThis->FileDevice, IOCTL_STORAGE_MEDIA_REMOVAL,
 
200
                        &PreventMediaRemoval, sizeof(PreventMediaRemoval),
 
201
                        NULL, 0, &cbReturned,
 
202
                        NULL))
 
203
        rc = VINF_SUCCESS;
 
204
    else
 
205
        /** @todo figure out the return codes for already locked. */
 
206
        rc = RTErrConvertFromWin32(GetLastError());
 
207
 
 
208
#else
 
209
    AssertMsgFailed(("Lock/Unlock is not implemented!\n"));
 
210
    int rc = VINF_SUCCESS;
 
211
 
 
212
#endif
 
213
 
 
214
    LogFlow(("drvHostDvdDoLock(, fLock=%RTbool): returns %Vrc\n", fLock, rc));
 
215
    return rc;
 
216
}
 
217
 
 
218
 
 
219
 
 
220
#ifdef RT_OS_LINUX
 
221
/**
 
222
 * Get the media size.
 
223
 *
 
224
 * @returns VBox status code.
 
225
 * @param   pThis   The instance data.
 
226
 * @param   pcb     Where to store the size.
 
227
 */
 
228
static int drvHostDvdGetMediaSize(PDRVHOSTBASE pThis, uint64_t *pcb)
 
229
{
 
230
    /*
 
231
     * Query the media size.
 
232
     */
 
233
    /* Clear the media-changed-since-last-call-thingy just to be on the safe side. */
 
234
    ioctl(pThis->FileDevice, CDROM_MEDIA_CHANGED, CDSL_CURRENT);
 
235
    return RTFileSeek(pThis->FileDevice, 0, RTFILE_SEEK_END, pcb);
 
236
 
 
237
}
 
238
#endif /* RT_OS_LINUX */
 
239
 
 
240
 
 
241
#ifdef USE_MEDIA_POLLING
 
242
/**
 
243
 * Do media change polling.
 
244
 */
 
245
DECLCALLBACK(int) drvHostDvdPoll(PDRVHOSTBASE pThis)
 
246
{
 
247
    /*
 
248
     * Poll for media change.
 
249
     */
 
250
#ifdef RT_OS_DARWIN
 
251
    AssertReturn(pThis->ppScsiTaskDI, VERR_INTERNAL_ERROR);
 
252
 
 
253
    /*
 
254
     * Issue a TEST UNIT READY request.
 
255
     */
 
256
    bool fMediaChanged = false;
 
257
    bool fMediaPresent = false;
 
258
    uint8_t abCmd[16] = { SCSI_TEST_UNIT_READY, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
 
259
    uint8_t abSense[32];
 
260
    int rc2 = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, abSense, sizeof(abSense), 0);
 
261
    if (VBOX_SUCCESS(rc2))
 
262
        fMediaPresent = true;
 
263
    else if (   rc2 == VERR_UNRESOLVED_ERROR
 
264
             && abSense[2] == 6 /* unit attention */
 
265
             && (   (abSense[12] == 0x29 && abSense[13] < 5 /* reset */)
 
266
                 || (abSense[12] == 0x2a && abSense[13] == 0 /* parameters changed */)                        //???
 
267
                 || (abSense[12] == 0x3f && abSense[13] == 0 /* target operating conditions have changed */)  //???
 
268
                 || (abSense[12] == 0x3f && abSense[13] == 2 /* changed operating definition */)              //???
 
269
                 || (abSense[12] == 0x3f && abSense[13] == 3 /* inquery parameters changed */)
 
270
                 || (abSense[12] == 0x3f && abSense[13] == 5 /* device identifier changed */)
 
271
                 )
 
272
            )
 
273
    {
 
274
        fMediaPresent = false;
 
275
        fMediaChanged = true;
 
276
        /** @todo check this media chance stuff on Darwin. */
 
277
    }
 
278
 
 
279
#elif defined(RT_OS_LINUX)
 
280
    bool fMediaPresent = ioctl(pThis->FileDevice, CDROM_DRIVE_STATUS, CDSL_CURRENT) == CDS_DISC_OK;
 
281
 
 
282
#else
 
283
# error "Unsupported platform."
 
284
#endif
 
285
 
 
286
    RTCritSectEnter(&pThis->CritSect);
 
287
 
 
288
    int rc = VINF_SUCCESS;
 
289
    if (pThis->fMediaPresent != fMediaPresent)
 
290
    {
 
291
        LogFlow(("drvHostDvdPoll: %d -> %d\n", pThis->fMediaPresent, fMediaPresent));
 
292
        pThis->fMediaPresent = false;
 
293
        if (fMediaPresent)
 
294
            rc = DRVHostBaseMediaPresent(pThis);
 
295
        else
 
296
            DRVHostBaseMediaNotPresent(pThis);
 
297
    }
 
298
    else if (fMediaPresent)
 
299
    {
 
300
        /*
 
301
         * Poll for media change.
 
302
         */
 
303
#ifdef RT_OS_DARWIN
 
304
        /* taken care of above. */
 
305
#elif defined(RT_OS_LINUX)
 
306
        bool fMediaChanged = ioctl(pThis->FileDevice, CDROM_MEDIA_CHANGED, CDSL_CURRENT) == 1;
 
307
#else
 
308
# error "Unsupported platform."
 
309
#endif
 
310
        if (fMediaChanged)
 
311
        {
 
312
            LogFlow(("drvHostDVDMediaThread: Media changed!\n"));
 
313
            DRVHostBaseMediaNotPresent(pThis);
 
314
            rc = DRVHostBaseMediaPresent(pThis);
 
315
        }
 
316
    }
 
317
 
 
318
    RTCritSectLeave(&pThis->CritSect);
 
319
    return rc;
 
320
}
 
321
#endif /* USE_MEDIA_POLLING */
 
322
 
 
323
 
 
324
/** @copydoc PDMIBLOCK::pfnSendCmd */
 
325
static int drvHostDvdSendCmd(PPDMIBLOCK pInterface, const uint8_t *pbCmd, PDMBLOCKTXDIR enmTxDir, void *pvBuf, size_t *pcbBuf,
 
326
                             uint8_t *pbStat, uint32_t cTimeoutMillies)
 
327
{
 
328
    PDRVHOSTBASE pThis = PDMIBLOCK_2_DRVHOSTBASE(pInterface);
 
329
    int rc;
 
330
    LogFlow(("%s: cmd[0]=%#04x txdir=%d pcbBuf=%d timeout=%d\n", __FUNCTION__, pbCmd[0], enmTxDir, *pcbBuf, cTimeoutMillies));
 
331
 
 
332
#ifdef RT_OS_DARWIN
 
333
    /*
 
334
     * Pass the request on to the internal scsi command interface.
 
335
     * The command seems to be 12 bytes long, the docs a bit copy&pasty on the command length point...
 
336
     */
 
337
    if (enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
 
338
        memset(pvBuf, '\0', *pcbBuf); /* we got read size, but zero it anyway. */
 
339
    uint8_t abSense[32];
 
340
    rc = DRVHostBaseScsiCmd(pThis, pbCmd, 12, PDMBLOCKTXDIR_FROM_DEVICE, pvBuf, pcbBuf, abSense, sizeof(abSense), cTimeoutMillies);
 
341
    if (rc == VERR_UNRESOLVED_ERROR)
 
342
    {
 
343
        *pbStat = abSense[2] & 0x0f;
 
344
        rc = VINF_SUCCESS;
 
345
    }
 
346
 
 
347
#elif defined(RT_OS_L4)
 
348
    /* Not really ported to L4 yet. */
 
349
    rc = VERR_INTERNAL_ERROR;
 
350
 
 
351
#elif defined(RT_OS_LINUX)
 
352
    int direction;
 
353
    struct cdrom_generic_command cgc;
 
354
    request_sense sense;
 
355
 
 
356
    switch (enmTxDir)
 
357
    {
 
358
        case PDMBLOCKTXDIR_NONE:
 
359
            Assert(*pcbBuf == 0);
 
360
            direction = CGC_DATA_NONE;
 
361
            break;
 
362
        case PDMBLOCKTXDIR_FROM_DEVICE:
 
363
            Assert(*pcbBuf != 0);
 
364
            /* Make sure that the buffer is clear for commands reading
 
365
             * data. The actually received data may be shorter than what
 
366
             * we expect, and due to the unreliable feedback about how much
 
367
             * data the ioctl actually transferred, it's impossible to
 
368
             * prevent that. Returning previous buffer contents may cause
 
369
             * security problems inside the guest OS, if users can issue
 
370
             * commands to the CDROM device. */
 
371
            memset(pvBuf, '\0', *pcbBuf);
 
372
            direction = CGC_DATA_READ;
 
373
            break;
 
374
        case PDMBLOCKTXDIR_TO_DEVICE:
 
375
            Assert(*pcbBuf != 0);
 
376
            direction = CGC_DATA_WRITE;
 
377
            break;
 
378
        default:
 
379
            AssertMsgFailed(("enmTxDir invalid!\n"));
 
380
            direction = CGC_DATA_NONE;
 
381
    }
 
382
    memset(&cgc, '\0', sizeof(cgc));
 
383
    memcpy(cgc.cmd, pbCmd, CDROM_PACKET_SIZE);
 
384
    cgc.buffer = (unsigned char *)pvBuf;
 
385
    cgc.buflen = *pcbBuf;
 
386
    cgc.stat = 0;
 
387
    cgc.sense = &sense;
 
388
    cgc.data_direction = direction;
 
389
    cgc.quiet = false;
 
390
    cgc.timeout = cTimeoutMillies;
 
391
    rc = ioctl(pThis->FileDevice, CDROM_SEND_PACKET, &cgc);
 
392
    if (rc < 0)
 
393
    {
 
394
        if (errno == EBUSY)
 
395
            rc = VERR_PDM_MEDIA_LOCKED;
 
396
        else if (errno == ENOSYS)
 
397
            rc = VERR_NOT_SUPPORTED;
 
398
        else
 
399
        {
 
400
            if (rc == VERR_ACCESS_DENIED && cgc.sense->sense_key == SCSI_SENSE_NONE)
 
401
                cgc.sense->sense_key = SCSI_SENSE_ILLEGAL_REQUEST;
 
402
            *pbStat = cgc.sense->sense_key;
 
403
            rc = RTErrConvertFromErrno(errno);
 
404
            Log2(("%s: error status %d, rc=%Vrc\n", __FUNCTION__, cgc.stat, rc));
 
405
        }
 
406
    }
 
407
    Log2(("%s: after ioctl: cgc.buflen=%d txlen=%d\n", __FUNCTION__, cgc.buflen, *pcbBuf));
 
408
    /* The value of cgc.buflen does not reliably reflect the actual amount
 
409
     * of data transferred (for packet commands with little data transfer
 
410
     * it's 0). So just assume that everything worked ok. */
 
411
 
 
412
#elif defined(RT_OS_WINDOWS)
 
413
    int direction;
 
414
    struct _REQ
 
415
    {
 
416
        SCSI_PASS_THROUGH_DIRECT spt;
 
417
        uint8_t aSense[18];
 
418
    } Req;
 
419
    DWORD cbReturned = 0;
 
420
 
 
421
    switch (enmTxDir)
 
422
    {
 
423
        case PDMBLOCKTXDIR_NONE:
 
424
            direction = SCSI_IOCTL_DATA_UNSPECIFIED;
 
425
            break;
 
426
        case PDMBLOCKTXDIR_FROM_DEVICE:
 
427
            Assert(*pcbBuf != 0);
 
428
            /* Make sure that the buffer is clear for commands reading
 
429
             * data. The actually received data may be shorter than what
 
430
             * we expect, and due to the unreliable feedback about how much
 
431
             * data the ioctl actually transferred, it's impossible to
 
432
             * prevent that. Returning previous buffer contents may cause
 
433
             * security problems inside the guest OS, if users can issue
 
434
             * commands to the CDROM device. */
 
435
            memset(pvBuf, '\0', *pcbBuf);
 
436
            direction = SCSI_IOCTL_DATA_IN;
 
437
            break;
 
438
        case PDMBLOCKTXDIR_TO_DEVICE:
 
439
            direction = SCSI_IOCTL_DATA_OUT;
 
440
            break;
 
441
        default:
 
442
            AssertMsgFailed(("enmTxDir invalid!\n"));
 
443
            direction = SCSI_IOCTL_DATA_UNSPECIFIED;
 
444
    }
 
445
    memset(&Req, '\0', sizeof(Req));
 
446
    Req.spt.Length = sizeof(Req.spt);
 
447
    Req.spt.CdbLength = 12;
 
448
    memcpy(Req.spt.Cdb, pbCmd, Req.spt.CdbLength);
 
449
    Req.spt.DataBuffer = pvBuf;
 
450
    Req.spt.DataTransferLength = *pcbBuf;
 
451
    Req.spt.DataIn = direction;
 
452
    Req.spt.TimeOutValue = (cTimeoutMillies + 999) / 1000; /* Convert to seconds */
 
453
    Req.spt.SenseInfoLength = sizeof(Req.aSense);
 
454
    Req.spt.SenseInfoOffset = RT_OFFSETOF(struct _REQ, aSense);
 
455
    if (DeviceIoControl((HANDLE)pThis->FileDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT,
 
456
                        &Req, sizeof(Req), &Req, sizeof(Req), &cbReturned, NULL))
 
457
    {
 
458
        if (cbReturned > RT_OFFSETOF(struct _REQ, aSense))
 
459
            *pbStat = Req.aSense[2] & 0x0f;
 
460
        else
 
461
            *pbStat = 0;
 
462
        /* Windows shares the property of not properly reflecting the actually
 
463
         * transferred data size. See above. Assume that everything worked ok. */
 
464
        rc = VINF_SUCCESS;
 
465
    }
 
466
    else
 
467
        rc = RTErrConvertFromWin32(GetLastError());
 
468
    Log2(("%s: scsistatus=%d bytes returned=%d tlength=%d\n", __FUNCTION__, Req.spt.ScsiStatus, cbReturned, Req.spt.DataTransferLength));
 
469
 
 
470
#else
 
471
# error "Unsupported platform."
 
472
#endif
 
473
    LogFlow(("%s: rc=%Vrc\n", __FUNCTION__, rc));
 
474
    return rc;
 
475
}
 
476
 
 
477
 
 
478
/* -=-=-=-=- driver interface -=-=-=-=- */
 
479
 
 
480
 
 
481
/**
 
482
 * Construct a host dvd drive driver instance.
 
483
 *
 
484
 * @returns VBox status.
 
485
 * @param   pDrvIns     The driver instance data.
 
486
 *                      If the registration structure is needed, pDrvIns->pDrvReg points to it.
 
487
 * @param   pCfgHandle  Configuration node handle for the driver. Use this to obtain the configuration
 
488
 *                      of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
 
489
 *                      iInstance it's expected to be used a bit in this function.
 
490
 */
 
491
static DECLCALLBACK(int) drvHostDvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
 
492
{
 
493
    PDRVHOSTBASE pThis = PDMINS2DATA(pDrvIns, PDRVHOSTBASE);
 
494
    LogFlow(("drvHostDvdConstruct: iInstance=%d\n", pDrvIns->iInstance));
 
495
 
 
496
    /*
 
497
     * Validate configuration.
 
498
     */
 
499
    if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0"))
 
500
        return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
 
501
 
 
502
 
 
503
    /*
 
504
     * Init instance data.
 
505
     */
 
506
    int rc = DRVHostBaseInitData(pDrvIns, pCfgHandle, PDMBLOCKTYPE_DVD);
 
507
    if (VBOX_SUCCESS(rc))
 
508
    {
 
509
        /*
 
510
         * Override stuff.
 
511
         */
 
512
 
 
513
#ifndef RT_OS_L4 /* Passthrough is not supported on L4 yet */
 
514
        bool fPassthrough;
 
515
        rc = CFGMR3QueryBool(pCfgHandle, "Passthrough", &fPassthrough);
 
516
        if (VBOX_SUCCESS(rc) && fPassthrough)
 
517
        {
 
518
            pThis->IBlock.pfnSendCmd = drvHostDvdSendCmd;
 
519
            /* Passthrough requires opening the device in R/W mode. */
 
520
            pThis->fReadOnlyConfig = false;
 
521
        }
 
522
#endif /* !RT_OS_L4 */
 
523
 
 
524
        pThis->IMount.pfnUnmount = drvHostDvdUnmount;
 
525
        pThis->pfnDoLock         = drvHostDvdDoLock;
 
526
#ifdef USE_MEDIA_POLLING
 
527
        if (!fPassthrough)
 
528
            pThis->pfnPoll       = drvHostDvdPoll;
 
529
        else
 
530
            pThis->pfnPoll       = NULL;
 
531
#endif
 
532
#ifdef RT_OS_LINUX
 
533
        pThis->pfnGetMediaSize   = drvHostDvdGetMediaSize;
 
534
#endif
 
535
 
 
536
        /*
 
537
         * 2nd init part.
 
538
         */
 
539
        rc = DRVHostBaseInitFinish(pThis);
 
540
    }
 
541
 
 
542
    if (VBOX_FAILURE(rc))
 
543
    {
 
544
        if (!pThis->fAttachFailError)
 
545
        {
 
546
            /* Suppressing the attach failure error must not affect the normal
 
547
             * DRVHostBaseDestruct, so reset this flag below before leaving. */
 
548
            pThis->fKeepInstance = true;
 
549
            rc = VINF_SUCCESS;
 
550
        }
 
551
        DRVHostBaseDestruct(pDrvIns);
 
552
        pThis->fKeepInstance = false;
 
553
    }
 
554
 
 
555
    LogFlow(("drvHostDvdConstruct: returns %Vrc\n", rc));
 
556
    return rc;
 
557
}
 
558
 
 
559
 
 
560
/**
 
561
 * Block driver registration record.
 
562
 */
 
563
const PDMDRVREG g_DrvHostDVD =
 
564
{
 
565
    /* u32Version */
 
566
    PDM_DRVREG_VERSION,
 
567
    /* szDriverName */
 
568
    "HostDVD",
 
569
    /* pszDescription */
 
570
    "Host DVD Block Driver.",
 
571
    /* fFlags */
 
572
    PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
 
573
    /* fClass. */
 
574
    PDM_DRVREG_CLASS_BLOCK,
 
575
    /* cMaxInstances */
 
576
    ~0,
 
577
    /* cbInstance */
 
578
    sizeof(DRVHOSTBASE),
 
579
    /* pfnConstruct */
 
580
    drvHostDvdConstruct,
 
581
    /* pfnDestruct */
 
582
    DRVHostBaseDestruct,
 
583
    /* pfnIOCtl */
 
584
    NULL,
 
585
    /* pfnPowerOn */
 
586
    NULL,
 
587
    /* pfnReset */
 
588
    NULL,
 
589
    /* pfnSuspend */
 
590
    NULL,
 
591
    /* pfnResume */
 
592
    NULL,
 
593
    /* pfnDetach */
 
594
    NULL
 
595
};
 
596