DrvHostDVD.cpp revision c98fb3e16fcd571a790eab772c0c66173d225205
/** @file
*
* VBox storage devices:
* Host DVD block driver
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License as published by the Free Software Foundation,
* in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
* distribution. VirtualBox OSE is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_HOST_DVD
#ifdef RT_OS_DARWIN
# include <IOKit/IOKitLib.h>
# include <IOKit/IOCFPlugIn.h>
# include <mach/mach_error.h>
# define USE_MEDIA_POLLING
/* nothing (yet). */
#elif defined RT_OS_LINUX
/* This is a hack to work around conflicts between these linux kernel headers
* and the GLIBC tcpip headers. They have different declarations of the 4
* standard byte order functions. */
# define _LINUX_BYTEORDER_GENERIC_H
/* This is another hack for not bothering with C++ unfriendly byteswap macros. */
# define _LINUX_BYTEORDER_SWAB_H
/* Those macros that are needed are defined in the header below */
# include "swab.h"
# include <errno.h>
# define USE_MEDIA_POLLING
#elif defined(RT_OS_WINDOWS)
# include <Windows.h>
# include <winioctl.h>
# include <ntddscsi.h>
#else
# error "Unsupported Platform."
#endif
#include <iprt/critsect.h>
#include "Builtins.h"
#include "DrvHostBase.h"
/* Forward declarations. */
/** @copydoc PDMIMOUNT::pfnUnmount */
{
/*
* Validate state.
*/
int rc = VINF_SUCCESS;
{
/* Unlock drive if necessary. */
drvHostDvdDoLock(pThis, false);
/*
* Eject the disc.
*/
#ifdef RT_OS_DARWIN
{
0,0,0,0,0,0,0,0,0,0
};
#elif defined(RT_OS_LINUX)
if (rc < 0)
{
else
}
#elif defined(RT_OS_WINDOWS)
rc = RTFileOpen(&FileDevice, pThis->pszDeviceOpen, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
if (VBOX_SUCCESS(rc))
{
/* do ioctl */
NULL, 0,
NULL, 0, &cbReturned,
NULL))
rc = VINF_SUCCESS;
else
/* clean up handle */
}
else
#else
AssertMsgFailed(("Eject is not implemented!\n"));
rc = VINF_SUCCESS;
#endif
/*
* Media is no longer present.
*/
}
else
{
Log(("drvHostDvdUnmount: Locked\n"));
}
return rc;
}
/**
* 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.
*/
{
#ifdef RT_OS_DARWIN
{
SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL, 0, 0, 0, fLock, 0,
0,0,0,0,0,0,0,0,0,0
};
#elif defined(RT_OS_LINUX)
if (rc < 0)
{
else if (errno == EDRIVE_CANT_DO_THIS)
else
}
#elif defined(RT_OS_WINDOWS)
int rc;
&PreventMediaRemoval, sizeof(PreventMediaRemoval),
NULL, 0, &cbReturned,
NULL))
rc = VINF_SUCCESS;
else
/** @todo figure out the return codes for already locked. */
#else
AssertMsgFailed(("Lock/Unlock is not implemented!\n"));
int rc = VINF_SUCCESS;
#endif
return rc;
}
#ifdef RT_OS_LINUX
/**
* Get the media size.
*
* @returns VBox status code.
* @param pThis The instance data.
* @param pcb Where to store the size.
*/
{
/*
* Query the media size.
*/
/* Clear the media-changed-since-last-call-thingy just to be on the safe side. */
}
#endif /* RT_OS_LINUX */
#ifdef USE_MEDIA_POLLING
/**
* Do media change polling.
*/
{
/*
* Poll for media change.
*/
#ifdef RT_OS_DARWIN
/*
* Issue a TEST UNIT READY request.
*/
bool fMediaChanged = false;
bool fMediaPresent = false;
int rc2 = DRVHostBaseScsiCmd(pThis, abCmd, 6, PDMBLOCKTXDIR_NONE, NULL, NULL, abSense, sizeof(abSense), 0);
if (VBOX_SUCCESS(rc2))
fMediaPresent = true;
else if ( rc2 == VERR_UNRESOLVED_ERROR
)
)
{
fMediaPresent = false;
fMediaChanged = true;
/** @todo check this media chance stuff on Darwin. */
}
#elif defined(RT_OS_LINUX)
#else
# error "Unsupported platform."
#endif
int rc = VINF_SUCCESS;
{
pThis->fMediaPresent = false;
if (fMediaPresent)
else
}
else if (fMediaPresent)
{
/*
* Poll for media change.
*/
#ifdef RT_OS_DARWIN
/* taken care of above. */
#elif defined(RT_OS_LINUX)
#else
# error "Unsupported platform."
#endif
if (fMediaChanged)
{
LogFlow(("drvHostDVDMediaThread: Media changed!\n"));
}
}
return rc;
}
#endif /* USE_MEDIA_POLLING */
/** @copydoc PDMIBLOCK::pfnSendCmd */
static int drvHostDvdSendCmd(PPDMIBLOCK pInterface, const uint8_t *pbCmd, PDMBLOCKTXDIR enmTxDir, void *pvBuf, size_t *pcbBuf,
{
int rc;
LogFlow(("%s: cmd[0]=%#04x txdir=%d pcbBuf=%d timeout=%d\n", __FUNCTION__, pbCmd[0], enmTxDir, *pcbBuf, cTimeoutMillies));
#ifdef RT_OS_DARWIN
/*
* Pass the request on to the internal scsi command interface.
* The command seems to be 12 bytes long, the docs a bit copy&pasty on the command length point...
*/
if (enmTxDir == PDMBLOCKTXDIR_FROM_DEVICE)
rc = DRVHostBaseScsiCmd(pThis, pbCmd, 12, PDMBLOCKTXDIR_FROM_DEVICE, pvBuf, pcbBuf, abSense, sizeof(abSense), cTimeoutMillies);
if (rc == VERR_UNRESOLVED_ERROR)
{
rc = VINF_SUCCESS;
}
/* Not really ported to L4 yet. */
#elif defined(RT_OS_LINUX)
int direction;
struct cdrom_generic_command cgc;
switch (enmTxDir)
{
case PDMBLOCKTXDIR_NONE:
break;
/* Make sure that the buffer is clear for commands reading
* data. The actually received data may be shorter than what
* we expect, and due to the unreliable feedback about how much
* data the ioctl actually transferred, it's impossible to
* prevent that. Returning previous buffer contents may cause
* security problems inside the guest OS, if users can issue
* commands to the CDROM device. */
break;
case PDMBLOCKTXDIR_TO_DEVICE:
break;
default:
AssertMsgFailed(("enmTxDir invalid!\n"));
}
if (rc < 0)
{
else
{
}
}
/* The value of cgc.buflen does not reliably reflect the actual amount
* of data transferred (for packet commands with little data transfer
* it's 0). So just assume that everything worked ok. */
#elif defined(RT_OS_WINDOWS)
int direction;
struct _REQ
{
} Req;
DWORD cbReturned = 0;
switch (enmTxDir)
{
case PDMBLOCKTXDIR_NONE:
break;
/* Make sure that the buffer is clear for commands reading
* data. The actually received data may be shorter than what
* we expect, and due to the unreliable feedback about how much
* data the ioctl actually transferred, it's impossible to
* prevent that. Returning previous buffer contents may cause
* security problems inside the guest OS, if users can issue
* commands to the CDROM device. */
break;
case PDMBLOCKTXDIR_TO_DEVICE:
break;
default:
AssertMsgFailed(("enmTxDir invalid!\n"));
}
{
else
*pbStat = 0;
/* Windows shares the property of not properly reflecting the actually
* transferred data size. See above. Assume that everything worked ok. */
rc = VINF_SUCCESS;
}
else
Log2(("%s: scsistatus=%d bytes returned=%d tlength=%d\n", __FUNCTION__, Req.spt.ScsiStatus, cbReturned, Req.spt.DataTransferLength));
#else
# error "Unsupported platform."
#endif
return rc;
}
/* -=-=-=-=- driver interface -=-=-=-=- */
/**
* Construct a host dvd drive driver instance.
*
* @returns VBox status.
* @param pDrvIns The driver instance data.
* If the registration structure is needed, pDrvIns->pDrvReg points to it.
* @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
* of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
*/
{
/*
* Validate configuration.
*/
if (!CFGMR3AreValuesValid(pCfgHandle, "Path\0Interval\0Locked\0BIOSVisible\0AttachFailError\0Passthrough\0"))
/*
* Init instance data.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Override stuff.
*/
#ifndef RT_OS_L4 /* Passthrough is not supported on L4 yet */
bool fPassthrough;
{
/* Passthrough requires opening the device in R/W mode. */
pThis->fReadOnlyConfig = false;
}
#endif /* !RT_OS_L4 */
#ifdef USE_MEDIA_POLLING
if (!fPassthrough)
else
#endif
#ifdef RT_OS_LINUX
#endif
/*
* 2nd init part.
*/
}
if (VBOX_FAILURE(rc))
{
if (!pThis->fAttachFailError)
{
/* Suppressing the attach failure error must not affect the normal
* DRVHostBaseDestruct, so reset this flag below before leaving. */
pThis->fKeepInstance = true;
rc = VINF_SUCCESS;
}
pThis->fKeepInstance = false;
}
return rc;
}
/**
* Block driver registration record.
*/
const PDMDRVREG g_DrvHostDVD =
{
/* u32Version */
/* szDriverName */
"HostDVD",
/* pszDescription */
"Host DVD Block Driver.",
/* fFlags */
/* fClass. */
/* cMaxInstances */
~0,
/* cbInstance */
sizeof(DRVHOSTBASE),
/* pfnConstruct */
/* pfnDestruct */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnDetach */
};