summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Storage/DrvHostDVD.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Storage/DrvHostDVD.cpp')
-rw-r--r--src/VBox/Devices/Storage/DrvHostDVD.cpp581
1 files changed, 581 insertions, 0 deletions
diff --git a/src/VBox/Devices/Storage/DrvHostDVD.cpp b/src/VBox/Devices/Storage/DrvHostDVD.cpp
new file mode 100644
index 00000000..a9a58725
--- /dev/null
+++ b/src/VBox/Devices/Storage/DrvHostDVD.cpp
@@ -0,0 +1,581 @@
+/* $Id: DrvHostDVD.cpp $ */
+/** @file
+ * DrvHostDVD - Host DVD block driver.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_HOST_DVD
+#include <iprt/asm.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmstorageifs.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/critsect.h>
+#include <VBox/scsi.h>
+#include <VBox/scsiinline.h>
+
+#include "VBoxDD.h"
+#include "DrvHostBase.h"
+#include "ATAPIPassthrough.h"
+
+/** ATAPI sense info size. */
+#define ATAPI_SENSE_SIZE 64
+/** Size of an ATAPI packet. */
+#define ATAPI_PACKET_SIZE 12
+
+/**
+ * Host DVD driver instance data.
+ */
+typedef struct DRVHOSTDVD
+{
+ /** Base driver data. */
+ DRVHOSTBASE Core;
+ /** The current tracklist of the loaded medium if passthrough is used. */
+ PTRACKLIST pTrackList;
+ /** ATAPI sense data. */
+ uint8_t abATAPISense[ATAPI_SENSE_SIZE];
+ /** Flag whether to overwrite the inquiry data with our emulated settings. */
+ bool fInquiryOverwrite;
+} DRVHOSTDVD;
+/** Pointer to the host DVD driver instance data. */
+typedef DRVHOSTDVD *PDRVHOSTDVD;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+static uint8_t drvHostDvdCmdOK(PDRVHOSTDVD pThis)
+{
+ memset(pThis->abATAPISense, '\0', sizeof(pThis->abATAPISense));
+ pThis->abATAPISense[0] = 0x70;
+ pThis->abATAPISense[7] = 10;
+ return SCSI_STATUS_OK;
+}
+
+static uint8_t drvHostDvdCmdError(PDRVHOSTDVD pThis, const uint8_t *pabATAPISense, size_t cbATAPISense)
+{
+ Log(("%s: sense=%#x (%s) asc=%#x ascq=%#x (%s)\n", __FUNCTION__, pabATAPISense[2] & 0x0f, SCSISenseText(pabATAPISense[2] & 0x0f),
+ pabATAPISense[12], pabATAPISense[13], SCSISenseExtText(pabATAPISense[12], pabATAPISense[13])));
+ memset(pThis->abATAPISense, '\0', sizeof(pThis->abATAPISense));
+ memcpy(pThis->abATAPISense, pabATAPISense, RT_MIN(cbATAPISense, sizeof(pThis->abATAPISense)));
+ return SCSI_STATUS_CHECK_CONDITION;
+}
+
+/** @todo deprecated function - doesn't provide enough info. Replace by direct
+ * calls to drvHostDvdCmdError() with full data. */
+static uint8_t drvHostDvdCmdErrorSimple(PDRVHOSTDVD pThis, uint8_t uATAPISenseKey, uint8_t uATAPIASC)
+{
+ uint8_t abATAPISense[ATAPI_SENSE_SIZE];
+ memset(abATAPISense, '\0', sizeof(abATAPISense));
+ abATAPISense[0] = 0x70 | (1 << 7);
+ abATAPISense[2] = uATAPISenseKey & 0x0f;
+ abATAPISense[7] = 10;
+ abATAPISense[12] = uATAPIASC;
+ return drvHostDvdCmdError(pThis, abATAPISense, sizeof(abATAPISense));
+}
+
+
+/**
+ * Parse the CDB and check whether it can be passed through safely.
+ *
+ * @returns Flag whether to passthrough to the device is considered safe.
+ * @param pThis The host DVD driver instance.
+ * @param pReq The request.
+ * @param pbCdb The CDB to parse.
+ * @param cbCdb Size of the CDB in bytes.
+ * @param cbBuf Size of the guest buffer.
+ * @param penmTxDir Where to store the transfer direction (guest to host or vice versa).
+ * @param pcbXfer Where to store the transfer size encoded in the CDB.
+ * @param pcbSector Where to store the sector size used for the transfer.
+ * @param pu8ScsiSts Where to store the SCSI status code.
+ */
+static bool drvHostDvdParseCdb(PDRVHOSTDVD pThis, PDRVHOSTBASEREQ pReq,
+ const uint8_t *pbCdb, size_t cbCdb, size_t cbBuf,
+ PDMMEDIATXDIR *penmTxDir, size_t *pcbXfer,
+ size_t *pcbSector, uint8_t *pu8ScsiSts)
+{
+ bool fPassthrough = false;
+
+ if ( pbCdb[0] == SCSI_REQUEST_SENSE
+ && (pThis->abATAPISense[2] & 0x0f) != SCSI_SENSE_NONE)
+ {
+ /* Handle the command here and copy sense data over. */
+ void *pvBuf = NULL;
+ int rc = drvHostBaseBufferRetain(&pThis->Core, pReq, cbBuf, false /*fWrite*/, &pvBuf);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pvBuf, &pThis->abATAPISense[0], RT_MIN(sizeof(pThis->abATAPISense), cbBuf));
+ rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbBuf, false /* fWrite */, pvBuf);
+ AssertRC(rc);
+ drvHostDvdCmdOK(pThis);
+ *pu8ScsiSts = SCSI_STATUS_OK;
+ }
+ }
+ else
+ fPassthrough = ATAPIPassthroughParseCdb(pbCdb, cbCdb, cbBuf, pThis->pTrackList,
+ &pThis->abATAPISense[0], sizeof(pThis->abATAPISense),
+ penmTxDir, pcbXfer, pcbSector, pu8ScsiSts);
+
+ return fPassthrough;
+}
+
+
+/**
+ * Locks or unlocks the drive.
+ *
+ * @returns VBox status code.
+ * @param pThis The instance data.
+ * @param fLock True if the request is to lock the drive, false if to unlock.
+ */
+static DECLCALLBACK(int) drvHostDvdDoLock(PDRVHOSTBASE pThis, bool fLock)
+{
+ int rc = drvHostBaseDoLockOs(pThis, fLock);
+
+ LogFlow(("drvHostDvdDoLock(, fLock=%RTbool): returns %Rrc\n", fLock, rc));
+ return rc;
+}
+
+
+/** @interface_method_impl{PDMIMEDIA,pfnSendCmd} */
+static DECLCALLBACK(int) drvHostDvdSendCmd(PPDMIMEDIA pInterface, const uint8_t *pbCdb, size_t cbCdb,
+ PDMMEDIATXDIR enmTxDir, void *pvBuf, uint32_t *pcbBuf,
+ uint8_t *pabSense, size_t cbSense, uint32_t cTimeoutMillies)
+{
+ PDRVHOSTBASE pThis = RT_FROM_MEMBER(pInterface, DRVHOSTBASE, IMedia);
+ int rc;
+ LogFlow(("%s: cmd[0]=%#04x txdir=%d pcbBuf=%d timeout=%d\n", __FUNCTION__, pbCdb[0], enmTxDir, *pcbBuf, cTimeoutMillies));
+
+ RTCritSectEnter(&pThis->CritSect);
+ /* Pass the request on to the internal scsi command interface. */
+ if (enmTxDir == PDMMEDIATXDIR_FROM_DEVICE)
+ memset(pvBuf, '\0', *pcbBuf); /* we got read size, but zero it anyway. */
+ rc = drvHostBaseScsiCmdOs(pThis, pbCdb, cbCdb, enmTxDir, pvBuf, pcbBuf, pabSense, cbSense, cTimeoutMillies);
+ if (rc == VERR_UNRESOLVED_ERROR)
+ /* sense information set */
+ rc = VERR_DEV_IO_ERROR;
+
+ if (pbCdb[0] == SCSI_GET_EVENT_STATUS_NOTIFICATION)
+ {
+ uint8_t *pbBuf = (uint8_t*)pvBuf;
+ Log2(("Event Status Notification class=%#02x supported classes=%#02x\n", pbBuf[2], pbBuf[3]));
+ if (RT_BE2H_U16(*(uint16_t*)pbBuf) >= 6)
+ Log2((" event %#02x %#02x %#02x %#02x\n", pbBuf[4], pbBuf[5], pbBuf[6], pbBuf[7]));
+ }
+ RTCritSectLeave(&pThis->CritSect);
+
+ LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
+ return rc;
+}
+
+
+/** @interface_method_impl{PDMIMEDIAEX,pfnIoReqSendScsiCmd} */
+static DECLCALLBACK(int) drvHostDvdIoReqSendScsiCmd(PPDMIMEDIAEX pInterface, PDMMEDIAEXIOREQ hIoReq,
+ uint32_t uLun, const uint8_t *pbCdb, size_t cbCdb,
+ PDMMEDIAEXIOREQSCSITXDIR enmTxDir, PDMMEDIAEXIOREQSCSITXDIR *penmTxDirRet,
+ size_t cbBuf, uint8_t *pabSense, size_t cbSense, size_t *pcbSenseRet,
+ uint8_t *pu8ScsiSts, uint32_t cTimeoutMillies)
+{
+ RT_NOREF3(uLun, cTimeoutMillies, enmTxDir);
+
+ PDRVHOSTDVD pThis = RT_FROM_MEMBER(pInterface, DRVHOSTDVD, Core.IMediaEx);
+ PDRVHOSTBASEREQ pReq = (PDRVHOSTBASEREQ)hIoReq;
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("%s: pbCdb[0]=%#04x{%s} enmTxDir=%d cbBuf=%zu timeout=%u\n",
+ __FUNCTION__, pbCdb[0], SCSICmdText(pbCdb[0]), enmTxDir, cbBuf, cTimeoutMillies));
+
+ RTCritSectEnter(&pThis->Core.CritSect);
+
+ /*
+ * Parse the command first to fend off any illegal or dangerous commands we don't want the guest
+ * to execute on the host drive.
+ */
+ PDMMEDIATXDIR enmXferDir = PDMMEDIATXDIR_NONE;
+ size_t cbXfer = 0;
+ size_t cbSector = 0;
+ size_t cbScsiCmdBufLimit = drvHostBaseScsiCmdGetBufLimitOs(&pThis->Core);
+ bool fPassthrough = drvHostDvdParseCdb(pThis, pReq, pbCdb, cbCdb, cbBuf,
+ &enmXferDir, &cbXfer, &cbSector, pu8ScsiSts);
+ if (fPassthrough)
+ {
+ void *pvBuf = NULL;
+ size_t cbXferCur = cbXfer;
+
+ pReq->cbReq = cbXfer;
+ pReq->cbResidual = cbXfer;
+
+ if (cbXfer)
+ rc = drvHostBaseBufferRetain(&pThis->Core, pReq, cbXfer, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, &pvBuf);
+
+ if (cbXfer > cbScsiCmdBufLimit)
+ {
+ /* Linux accepts commands with up to 100KB of data, but expects
+ * us to handle commands with up to 128KB of data. The usual
+ * imbalance of powers. */
+ uint8_t aATAPICmd[ATAPI_PACKET_SIZE];
+ uint32_t iATAPILBA, cSectors;
+ uint8_t *pbBuf = (uint8_t *)pvBuf;
+
+ switch (pbCdb[0])
+ {
+ case SCSI_READ_10:
+ case SCSI_WRITE_10:
+ case SCSI_WRITE_AND_VERIFY_10:
+ iATAPILBA = scsiBE2H_U32(pbCdb + 2);
+ cSectors = scsiBE2H_U16(pbCdb + 7);
+ break;
+ case SCSI_READ_12:
+ case SCSI_WRITE_12:
+ iATAPILBA = scsiBE2H_U32(pbCdb + 2);
+ cSectors = scsiBE2H_U32(pbCdb + 6);
+ break;
+ case SCSI_READ_CD:
+ iATAPILBA = scsiBE2H_U32(pbCdb + 2);
+ cSectors = scsiBE2H_U24(pbCdb + 6);
+ break;
+ case SCSI_READ_CD_MSF:
+ iATAPILBA = scsiMSF2LBA(pbCdb + 3);
+ cSectors = scsiMSF2LBA(pbCdb + 6) - iATAPILBA;
+ break;
+ default:
+ AssertMsgFailed(("Don't know how to split command %#04x\n", pbCdb[0]));
+ LogRelMax(10, ("HostDVD#%u: CD-ROM passthrough split error\n", pThis->Core.pDrvIns->iInstance));
+ *pu8ScsiSts = drvHostDvdCmdErrorSimple(pThis, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
+ rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbBuf, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, pvBuf);
+ RTCritSectLeave(&pThis->Core.CritSect);
+ return VINF_SUCCESS;
+ }
+ memcpy(aATAPICmd, pbCdb, RT_MIN(cbCdb, ATAPI_PACKET_SIZE));
+ uint32_t cReqSectors = 0;
+ for (uint32_t i = cSectors; i > 0; i -= cReqSectors)
+ {
+ if (i * cbSector > cbScsiCmdBufLimit)
+ cReqSectors = (uint32_t)(cbScsiCmdBufLimit / cbSector);
+ else
+ cReqSectors = i;
+ uint32_t cbCurrTX = (uint32_t)cbSector * cReqSectors;
+ switch (pbCdb[0])
+ {
+ case SCSI_READ_10:
+ case SCSI_WRITE_10:
+ case SCSI_WRITE_AND_VERIFY_10:
+ scsiH2BE_U32(aATAPICmd + 2, iATAPILBA);
+ scsiH2BE_U16(aATAPICmd + 7, cReqSectors);
+ break;
+ case SCSI_READ_12:
+ case SCSI_WRITE_12:
+ scsiH2BE_U32(aATAPICmd + 2, iATAPILBA);
+ scsiH2BE_U32(aATAPICmd + 6, cReqSectors);
+ break;
+ case SCSI_READ_CD:
+ scsiH2BE_U32(aATAPICmd + 2, iATAPILBA);
+ scsiH2BE_U24(aATAPICmd + 6, cReqSectors);
+ break;
+ case SCSI_READ_CD_MSF:
+ scsiLBA2MSF(aATAPICmd + 3, iATAPILBA);
+ scsiLBA2MSF(aATAPICmd + 6, iATAPILBA + cReqSectors);
+ break;
+ }
+ rc = drvHostBaseScsiCmdOs(&pThis->Core, aATAPICmd, sizeof(aATAPICmd),
+ enmXferDir, pbBuf, &cbCurrTX,
+ &pThis->abATAPISense[0], sizeof(pThis->abATAPISense),
+ cTimeoutMillies /**< @todo timeout */);
+ if (rc != VINF_SUCCESS)
+ break;
+
+ pReq->cbResidual -= cbCurrTX;
+ iATAPILBA += cReqSectors;
+ pbBuf += cbSector * cReqSectors;
+ }
+ }
+ else
+ {
+ uint32_t cbXferTmp = (uint32_t)cbXferCur;
+ rc = drvHostBaseScsiCmdOs(&pThis->Core, pbCdb, cbCdb, enmXferDir, pvBuf, &cbXferTmp,
+ &pThis->abATAPISense[0], sizeof(pThis->abATAPISense), cTimeoutMillies);
+ if (RT_SUCCESS(rc))
+ pReq->cbResidual -= cbXferTmp;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Do post processing for certain commands. */
+ switch (pbCdb[0])
+ {
+ case SCSI_SEND_CUE_SHEET:
+ case SCSI_READ_TOC_PMA_ATIP:
+ {
+ if (!pThis->pTrackList)
+ rc = ATAPIPassthroughTrackListCreateEmpty(&pThis->pTrackList);
+
+ if (RT_SUCCESS(rc))
+ rc = ATAPIPassthroughTrackListUpdate(pThis->pTrackList, pbCdb, pvBuf, cbXfer);
+
+ if (RT_FAILURE(rc))
+ LogRelMax(10, ("HostDVD#%u: Error (%Rrc) while updating the tracklist during %s, burning the disc might fail\n",
+ pThis->Core.pDrvIns->iInstance, rc,
+ pbCdb[0] == SCSI_SEND_CUE_SHEET ? "SEND CUE SHEET" : "READ TOC/PMA/ATIP"));
+ break;
+ }
+ case SCSI_SYNCHRONIZE_CACHE:
+ {
+ if (pThis->pTrackList)
+ ATAPIPassthroughTrackListClear(pThis->pTrackList);
+ break;
+ }
+ }
+
+ if (enmXferDir == PDMMEDIATXDIR_FROM_DEVICE)
+ {
+ Assert(cbXferCur <= cbXfer);
+
+ if ( pbCdb[0] == SCSI_INQUIRY
+ && pThis->fInquiryOverwrite)
+ {
+ const char *pszInqVendorId = "VBOX";
+ const char *pszInqProductId = "CD-ROM";
+ const char *pszInqRevision = "1.0";
+
+ if (pThis->Core.pDrvMediaPort->pfnQueryScsiInqStrings)
+ {
+ rc = pThis->Core.pDrvMediaPort->pfnQueryScsiInqStrings(pThis->Core.pDrvMediaPort, &pszInqVendorId,
+ &pszInqProductId, &pszInqRevision);
+ AssertRC(rc);
+ }
+ /* Make sure that the real drive cannot be identified.
+ * Motivation: changing the VM configuration should be as
+ * invisible as possible to the guest. */
+ if (cbXferCur >= 8 + 8)
+ scsiPadStr((uint8_t *)pvBuf + 8, pszInqVendorId, 8);
+ if (cbXferCur >= 16 + 16)
+ scsiPadStr((uint8_t *)pvBuf + 16, pszInqProductId, 16);
+ if (cbXferCur >= 32 + 4)
+ scsiPadStr((uint8_t *)pvBuf + 32, pszInqRevision, 4);
+ }
+
+ if (cbXferCur)
+ Log3(("ATAPI PT data read (%d): %.*Rhxs\n", cbXferCur, cbXferCur, (uint8_t *)pvBuf));
+ }
+
+ *pu8ScsiSts = drvHostDvdCmdOK(pThis);
+ }
+ else
+ {
+ do
+ {
+ /* don't log superfluous errors */
+ if ( rc == VERR_DEV_IO_ERROR
+ && ( pbCdb[0] == SCSI_TEST_UNIT_READY
+ || pbCdb[0] == SCSI_READ_CAPACITY
+ || pbCdb[0] == SCSI_READ_DVD_STRUCTURE
+ || pbCdb[0] == SCSI_READ_TOC_PMA_ATIP))
+ break;
+ LogRelMax(10, ("HostDVD#%u: CD-ROM passthrough cmd=%#04x sense=%d ASC=%#02x ASCQ=%#02x %Rrc\n",
+ pThis->Core.pDrvIns->iInstance, pbCdb[0], pThis->abATAPISense[2] & 0x0f,
+ pThis->abATAPISense[12], pThis->abATAPISense[13], rc));
+ } while (0);
+ *pu8ScsiSts = SCSI_STATUS_CHECK_CONDITION;
+ rc = VINF_SUCCESS;
+ }
+
+ if (cbXfer)
+ rc = drvHostBaseBufferRelease(&pThis->Core, pReq, cbXfer, enmXferDir == PDMMEDIATXDIR_TO_DEVICE, pvBuf);
+ }
+
+ /*
+ * We handled the command, check the status code and copy over the sense data if
+ * it is CHECK CONDITION.
+ */
+ if ( *pu8ScsiSts == SCSI_STATUS_CHECK_CONDITION
+ && RT_VALID_PTR(pabSense)
+ && cbSense > 0)
+ {
+ size_t cbSenseCpy = RT_MIN(cbSense, sizeof(pThis->abATAPISense));
+
+ memcpy(pabSense, &pThis->abATAPISense[0], cbSenseCpy);
+ if (pcbSenseRet)
+ *pcbSenseRet = cbSenseCpy;
+ }
+
+ if (penmTxDirRet)
+ {
+ switch (enmXferDir)
+ {
+ case PDMMEDIATXDIR_NONE:
+ *penmTxDirRet = PDMMEDIAEXIOREQSCSITXDIR_NONE;
+ break;
+ case PDMMEDIATXDIR_FROM_DEVICE:
+ *penmTxDirRet = PDMMEDIAEXIOREQSCSITXDIR_FROM_DEVICE;
+ break;
+ case PDMMEDIATXDIR_TO_DEVICE:
+ *penmTxDirRet = PDMMEDIAEXIOREQSCSITXDIR_TO_DEVICE;
+ break;
+ default:
+ *penmTxDirRet = PDMMEDIAEXIOREQSCSITXDIR_UNKNOWN;
+ }
+ }
+
+ RTCritSectLeave(&pThis->Core.CritSect);
+
+ LogFlow(("%s: rc=%Rrc\n", __FUNCTION__, rc));
+ return rc;
+}
+
+
+/* -=-=-=-=- driver interface -=-=-=-=- */
+
+
+/** @interface_method_impl{PDMDRVREG,pfnDestruct} */
+static DECLCALLBACK(void) drvHostDvdDestruct(PPDMDRVINS pDrvIns)
+{
+ PDRVHOSTDVD pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDVD);
+
+ if (pThis->pTrackList)
+ {
+ ATAPIPassthroughTrackListDestroy(pThis->pTrackList);
+ pThis->pTrackList = NULL;
+ }
+
+ DRVHostBaseDestruct(pDrvIns);
+}
+
+/**
+ * Construct a host dvd drive driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvHostDvdConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDRVHOSTDVD pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDVD);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ LogFlow(("drvHostDvdConstruct: iInstance=%d\n", pDrvIns->iInstance));
+
+ int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "InquiryOverwrite", &pThis->fInquiryOverwrite, true);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("HostDVD configuration error: failed to read \"InquiryOverwrite\" as boolean"));
+
+ bool fPassthrough;
+ rc = pHlp->pfnCFGMQueryBool(pCfg, "Passthrough", &fPassthrough);
+ if (RT_SUCCESS(rc) && fPassthrough)
+ {
+ pThis->Core.IMedia.pfnSendCmd = drvHostDvdSendCmd;
+ pThis->Core.IMediaEx.pfnIoReqSendScsiCmd = drvHostDvdIoReqSendScsiCmd;
+ /* Passthrough requires opening the device in R/W mode. */
+ pThis->Core.fReadOnlyConfig = false;
+ }
+
+ pThis->Core.pfnDoLock = drvHostDvdDoLock;
+
+ /*
+ * Init instance data.
+ */
+ rc = DRVHostBaseInit(pDrvIns, pCfg, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0InquiryOverwrite\0",
+ PDMMEDIATYPE_DVD);
+ LogFlow(("drvHostDvdConstruct: returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Reset a host dvd drive driver instance.
+ *
+ * @copydoc FNPDMDRVRESET
+ */
+static DECLCALLBACK(void) drvHostDvdReset(PPDMDRVINS pDrvIns)
+{
+ PDRVHOSTDVD pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTDVD);
+
+ if (pThis->pTrackList)
+ {
+ ATAPIPassthroughTrackListDestroy(pThis->pTrackList);
+ pThis->pTrackList = NULL;
+ }
+
+ int rc = drvHostBaseDoLockOs(&pThis->Core, false);
+ RT_NOREF(rc);
+}
+
+
+/**
+ * Block driver registration record.
+ */
+const PDMDRVREG g_DrvHostDVD =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "HostDVD",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "Host DVD Block Driver.",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_BLOCK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVHOSTDVD),
+ /* pfnConstruct */
+ drvHostDvdConstruct,
+ /* pfnDestruct */
+ drvHostDvdDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ drvHostDvdReset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+