summaryrefslogtreecommitdiff
path: root/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp')
-rw-r--r--src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp186
1 files changed, 180 insertions, 6 deletions
diff --git a/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp b/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp
index 6da3833b..10330cdf 100644
--- a/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp
+++ b/src/VBox/Devices/Storage/VSCSI/VSCSILunMmc.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2011 Oracle Corporation
+ * Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -44,6 +44,116 @@ typedef struct VSCSILUNMMC
bool fLocked;
} VSCSILUNMMC, *PVSCSILUNMMC;
+
+DECLINLINE(void) mmcLBA2MSF(uint8_t *pbBuf, uint32_t iLBA)
+{
+ iLBA += 150;
+ pbBuf[0] = (iLBA / 75) / 60;
+ pbBuf[1] = (iLBA / 75) % 60;
+ pbBuf[2] = iLBA % 75;
+}
+
+DECLINLINE(uint32_t) mmcMSF2LBA(const uint8_t *pbBuf)
+{
+ return (pbBuf[0] * 60 + pbBuf[1]) * 75 + pbBuf[2];
+}
+
+
+/* Fabricate normal TOC information. */
+static int mmcReadTOCNormal(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
+{
+ PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
+ uint8_t aReply[32];
+ uint8_t *pbBuf = aReply;
+ uint8_t *q;
+ uint8_t iStartTrack;
+ uint32_t cbSize;
+
+ iStartTrack = pVScsiReq->pbCDB[6];
+ if (iStartTrack > 1 && iStartTrack != 0xaa)
+ {
+ return vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
+ }
+ q = pbBuf + 2;
+ *q++ = 1; /* first session */
+ *q++ = 1; /* last session */
+ if (iStartTrack <= 1)
+ {
+ *q++ = 0; /* reserved */
+ *q++ = 0x14; /* ADR, CONTROL */
+ *q++ = 1; /* track number */
+ *q++ = 0; /* reserved */
+ if (fMSF)
+ {
+ *q++ = 0; /* reserved */
+ mmcLBA2MSF(q, 0);
+ q += 3;
+ }
+ else
+ {
+ /* sector 0 */
+ vscsiH2BEU32(q, 0);
+ q += 4;
+ }
+ }
+ /* lead out track */
+ *q++ = 0; /* reserved */
+ *q++ = 0x14; /* ADR, CONTROL */
+ *q++ = 0xaa; /* track number */
+ *q++ = 0; /* reserved */
+ if (fMSF)
+ {
+ *q++ = 0; /* reserved */
+ mmcLBA2MSF(q, pVScsiLunMmc->cSectors);
+ q += 3;
+ }
+ else
+ {
+ vscsiH2BEU32(q, pVScsiLunMmc->cSectors);
+ q += 4;
+ }
+ cbSize = q - pbBuf;
+ Assert(cbSize <= sizeof(aReply));
+ vscsiH2BEU16(pbBuf, cbSize - 2);
+ if (cbSize < cbMaxTransfer)
+ cbMaxTransfer = cbSize;
+
+ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, cbMaxTransfer);
+
+ return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+}
+
+/* Fabricate session information. */
+static int mmcReadTOCMulti(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq, uint16_t cbMaxTransfer, bool fMSF)
+{
+ PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
+ uint8_t aReply[32];
+ uint8_t *pbBuf = aReply;
+
+ /* multi session: only a single session defined */
+ memset(pbBuf, 0, 12);
+ pbBuf[1] = 0x0a;
+ pbBuf[2] = 0x01; /* first complete session number */
+ pbBuf[3] = 0x01; /* last complete session number */
+ pbBuf[5] = 0x14; /* ADR, CONTROL */
+ pbBuf[6] = 1; /* first track in last complete session */
+
+ if (fMSF)
+ {
+ pbBuf[8] = 0; /* reserved */
+ mmcLBA2MSF(pbBuf + 8, 0);
+ }
+ else
+ {
+ /* sector 0 */
+ vscsiH2BEU32(pbBuf + 8, 0);
+ }
+
+ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, 12);
+
+ return vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+}
+
static int vscsiLunMmcInit(PVSCSILUNINT pVScsiLun)
{
PVSCSILUNMMC pVScsiLunMmc = (PVSCSILUNMMC)pVScsiLun;
@@ -73,9 +183,40 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
uint32_t cSectorTransfer = 0;
int rc = VINF_SUCCESS;
int rcReq = SCSI_STATUS_OK;
+ unsigned uCmd = pVScsiReq->pbCDB[0];
- switch(pVScsiReq->pbCDB[0])
+ /*
+ * GET CONFIGURATION, GET EVENT/STATUS NOTIFICATION, INQUIRY, and REQUEST SENSE commands
+ * operate even when a unit attention condition exists for initiator; every other command
+ * needs to report CHECK CONDITION in that case.
+ */
+ if (!pVScsiLunMmc->Core.fReady && uCmd != SCSI_INQUIRY)
{
+ /*
+ * A note on media changes: As long as a medium is not present, the unit remains in
+ * the 'not ready' state. Technically the unit becomes 'ready' soon after a medium
+ * is inserted; however, we internally keep the 'not ready' state until we've had
+ * a chance to report the UNIT ATTENTION status indicating a media change.
+ */
+ if (pVScsiLunMmc->Core.fMediaPresent)
+ {
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_UNIT_ATTENTION,
+ SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED, 0x00);
+ pVScsiLunMmc->Core.fReady = true;
+ }
+ else
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY,
+ SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
+ }
+ else
+ {
+ switch (uCmd)
+ {
+ case SCSI_TEST_UNIT_READY:
+ Assert(!pVScsiLunMmc->Core.fReady); /* Only should get here if LUN isn't ready. */
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_NOT_READY, SCSI_ASC_MEDIUM_NOT_PRESENT, 0x00);
+ break;
+
case SCSI_INQUIRY:
{
SCSIINQUIRYDATA ScsiInquiryReply;
@@ -120,6 +261,7 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
uint8_t aReply[24];
uint8_t *pu8ReplyPos;
+ bool fValid = false;
memset(aReply, 0, sizeof(aReply));
aReply[0] = 4; /* Reply length 4. */
@@ -135,10 +277,18 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
*pu8ReplyPos++ = 0x08; /* Page code. */
*pu8ReplyPos++ = 0x12; /* Size of the page. */
*pu8ReplyPos++ = 0x4; /* Write cache enabled. */
+ fValid = true;
+ } else if (uModePage == 0) {
+ fValid = true;
}
- RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
- rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+ /* Querying unknown pages must fail. */
+ if (fValid) {
+ RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
+ rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
+ } else {
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
+ }
break;
}
case SCSI_MODE_SELECT_6:
@@ -267,13 +417,37 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
{
pVScsiLunMmc->fLocked = pVScsiReq->pbCDB[4] & 1;
+ vscsiLunMediumSetLock(pVScsiLun, pVScsiLunMmc->fLocked);
rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);
break;
}
+ case SCSI_READ_TOC_PMA_ATIP:
+ {
+ uint8_t format;
+ uint16_t cbMax;
+ bool fMSF;
+
+ format = pVScsiReq->pbCDB[2] & 0x0f;
+ cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
+ fMSF = (pVScsiReq->pbCDB[1] >> 1) & 1;
+ switch (format)
+ {
+ case 0x00:
+ mmcReadTOCNormal(pVScsiLun, pVScsiReq, cbMax, fMSF);
+ break;
+ case 0x01:
+ mmcReadTOCMulti(pVScsiLun, pVScsiReq, cbMax, fMSF);
+ break;
+ default:
+ rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
+ }
+ break;
+ }
default:
- //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
+ //AssertMsgFailed(("Command %#x [%s] not implemented\n", pVScsiReq->pbCDB[0], SCSICmdText(pVScsiReq->pbCDB[0])));
rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
+ }
}
if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
@@ -286,7 +460,7 @@ static int vscsiLunMmcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
rcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR, 0x00);
vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
}
- else if (!cSectorTransfer)
+ else if (!cSectorTransfer)
{
/* A 0 transfer length is not an error. */
rcReq = vscsiLunReqSenseOkSet(pVScsiLun, pVScsiReq);