VBOXDDU_DECL(int) VSCSIIoReqCompleted(VSCSIIOREQ hVScsiIoReq, int rcIoReq, bool fRedoPossible)
{
    PVSCSIIOREQINT pVScsiIoReq = hVScsiIoReq;
    PVSCSILUNINT pVScsiLun;
    PVSCSIREQINT pVScsiReq;
    int rcReq = SCSI_STATUS_OK;

    AssertPtrReturn(pVScsiIoReq, VERR_INVALID_HANDLE);

    LogFlowFunc(("hVScsiIoReq=%#p rcIoReq=%Rrc\n", hVScsiIoReq, rcIoReq));

    pVScsiLun = pVScsiIoReq->pVScsiLun;
    pVScsiReq = pVScsiIoReq->pVScsiReq;

    AssertMsg(pVScsiLun->IoReq.cReqOutstanding > 0,
              ("Unregistered I/O request completed\n"));

    ASMAtomicDecU32(&pVScsiLun->IoReq.cReqOutstanding);

    /** @todo error reporting */
    if (RT_SUCCESS(rcIoReq))
        rcReq = vscsiReqSenseOkSet(pVScsiReq);
    else if (!fRedoPossible)
    {
        /** @todo Not 100% correct for the write case as the 0x00 ASCQ for write errors
         * is not used for SBC devices. */
        rcReq = vscsiReqSenseErrorSet(pVScsiReq, SCSI_SENSE_MEDIUM_ERROR,
                                      pVScsiIoReq->enmTxDir == VSCSIIOREQTXDIR_READ
                                      ? SCSI_ASC_READ_ERROR
                                      : SCSI_ASC_WRITE_ERROR);
    }
    else
        rcReq = SCSI_STATUS_CHECK_CONDITION;

    /* Free the I/O request */
    RTMemFree(pVScsiIoReq);

    /* Notify completion of the SCSI request. */
    vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, fRedoPossible, rcIoReq);

    return VINF_SUCCESS;
}
Пример #2
0
VBOXDDU_DECL(int) VSCSIDeviceReqEnqueue(VSCSIDEVICE hVScsiDevice, VSCSIREQ hVScsiReq)
{
    PVSCSIDEVICEINT pVScsiDevice = (PVSCSIDEVICEINT)hVScsiDevice;
    PVSCSIREQINT    pVScsiReq    = (PVSCSIREQINT)hVScsiReq;
    int rc = VINF_SUCCESS;

    /* Parameter checks */
    AssertPtrReturn(pVScsiDevice, VERR_INVALID_HANDLE);
    AssertPtrReturn(pVScsiReq, VERR_INVALID_HANDLE);

    /* Check if this request can be handled by us */
    int rcReq;
    bool fProcessed = vscsiDeviceReqProcess(pVScsiDevice, pVScsiReq, &rcReq);
    if (!fProcessed)
    {
        /* Pass to the LUN driver */
        if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
        {
            PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
            rc = pVScsiLun->pVScsiLunDesc->pfnVScsiLunReqProcess(pVScsiLun, pVScsiReq);
        }
        else
        {
            /* LUN not present, report error. */
            vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq,
                                  SCSI_SENSE_ILLEGAL_REQUEST,
                                  SCSI_ASC_LOGICAL_UNIT_DOES_NOT_RESPOND_TO_SELECTION,
                                  0x00);

            vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
                                   SCSI_STATUS_CHECK_CONDITION, false, VINF_SUCCESS);
        }
    }
    else
        vscsiDeviceReqComplete(pVScsiDevice, pVScsiReq,
                               rcReq, false, VINF_SUCCESS);

    return rc;
}
Пример #3
0
/**
 * Process a request common for all device types.
 *
 * @returns Flag whether we could handle the request.
 * @param   pVScsiDevice    The virtual SCSI device instance.
 * @param   pVScsiReq       The SCSi request.
 * @param   prcReq          The final return value if the request was handled.
 */
static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq,
                                  int *prcReq)
{
    bool fProcessed = true;

    switch (pVScsiReq->pbCDB[0])
    {
        case SCSI_INQUIRY:
        {
            if (!vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
            {
                size_t cbData;
                SCSIINQUIRYDATA ScsiInquiryReply;

                memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
                ScsiInquiryReply.cbAdditional = 31;
                ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
                ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
                cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
                *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
            }
            else
                fProcessed = false; /* Let the LUN process the request because it will provide LUN specific data */

            break;
        }
        case SCSI_REPORT_LUNS:
        {
            /*
             * If allocation length is less than 16 bytes SPC compliant devices have
             * to return an error.
             */
            if (vscsiBE2HU32(&pVScsiReq->pbCDB[6]) < 16)
                *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
            else
            {
                size_t cbData;
                uint8_t aReply[16]; /* We report only one LUN. */

                memset(aReply, 0, sizeof(aReply));
                vscsiH2BEU32(&aReply[0], 8); /* List length starts at position 0. */
                cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
                if (cbData < 16)
                    *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
                else
                    *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
            }
            break;
        }
        case SCSI_TEST_UNIT_READY:
        {
            if (pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady)
                *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
            else
                fProcessed = false; /* The LUN will provide details. */
            break;
        }
        case SCSI_REQUEST_SENSE:
        {
            /* Descriptor format sense data is not supported and results in an error. */
            if ((pVScsiReq->pbCDB[1] & 0x1) != 0)
                *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
            else
                *prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq);
            break;
        }
        default:
            fProcessed = false;
    }

    return fProcessed;
}
Пример #4
0
/**
 * Process a request common for all device types.
 *
 * @returns Flag whether we could handle the request.
 * @param   pVScsiDevice    The virtual SCSI device instance.
 * @param   pVScsiReq       The SCSi request.
 * @param   prcReq          The final return value if the request was handled.
 */
static bool vscsiDeviceReqProcess(PVSCSIDEVICEINT pVScsiDevice, PVSCSIREQINT pVScsiReq,
                                  int *prcReq)
{
    bool fProcessed = true;

    switch (pVScsiReq->pbCDB[0])
    {
        case SCSI_INQUIRY:
        {
            if (!vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
            {
                size_t cbData;
                SCSIINQUIRYDATA ScsiInquiryReply;

                vscsiReqSetXferSize(pVScsiReq, vscsiBE2HU16(&pVScsiReq->pbCDB[3]));
                memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
                ScsiInquiryReply.cbAdditional = 31;
                ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
                ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
                cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
                *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
            }
            else
                fProcessed = false; /* Let the LUN process the request because it will provide LUN specific data */

            break;
        }
        case SCSI_REPORT_LUNS:
        {
            /*
             * If allocation length is less than 16 bytes SPC compliant devices have
             * to return an error.
             */
            vscsiReqSetXferSize(pVScsiReq, vscsiBE2HU32(&pVScsiReq->pbCDB[6]));
            if (pVScsiReq->cbXfer < 16)
                *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
            else
            {
                size_t cbData;
                uint8_t aReply[16]; /* We report only one LUN. */

                memset(aReply, 0, sizeof(aReply));
                vscsiH2BEU32(&aReply[0], 8); /* List length starts at position 0. */
                cbData = RTSgBufCopyFromBuf(&pVScsiReq->SgBuf, aReply, sizeof(aReply));
                if (cbData < 16)
                    *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
                else
                    *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
            }
            break;
        }
        case SCSI_TEST_UNIT_READY:
        {
            if (   vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun)
                && pVScsiDevice->papVScsiLun[pVScsiReq->iLun]->fReady)
                *prcReq = vscsiReqSenseOkSet(&pVScsiDevice->VScsiSense, pVScsiReq);
            else
                fProcessed = false; /* The LUN (if present) will provide details. */
            break;
        }
        case SCSI_REQUEST_SENSE:
        {
            vscsiReqSetXferSize(pVScsiReq, pVScsiReq->pbCDB[4]);

            /* Descriptor format sense data is not supported and results in an error. */
            if ((pVScsiReq->pbCDB[1] & 0x1) != 0)
                *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
            else
                *prcReq = vscsiReqSenseCmd(&pVScsiDevice->VScsiSense, pVScsiReq);
            break;
        }
#if 0
        case SCSI_MAINTENANCE_IN:
        {
            if (pVScsiReq->pbCDB[1] == SCSI_MAINTENANCE_IN_REPORT_SUPP_OPC)
            {
                /*
                 * If the LUN is present and has the CDB info set we will execute the command, otherwise
                 * just fail with an illegal request error.
                 */
                if (vscsiDeviceLunIsPresent(pVScsiDevice, pVScsiReq->iLun))
                {
                    PVSCSILUNINT pVScsiLun = pVScsiDevice->papVScsiLun[pVScsiReq->iLun];
                    if (pVScsiLun->pVScsiLunDesc->paSupOpcInfo)
                    {
                        bool fTimeoutDesc = RT_BOOL(pVScsiReq->pbCDB[2] & 0x80);
                        uint8_t u8ReportMode = pVScsiReq->pbCDB[2] & 0x7;
                        uint8_t u8Opc = pVScsiReq->pbCDB[3];
                        uint16_t u16SvcAction = vscsiBE2HU16(&pVScsiReq->pbCDB[4]);
                        uint16_t cbData = vscsiBE2HU16(&pVScsiReq->pbCDB[6]);

                        switch (u8ReportMode)
                        {
                            case 0:
                                *prcReq = vscsiDeviceReportAllSupportedOpc(pVScsiLun, pVScsiReq, fTimeoutDesc, cbData);
                                break;
                            case 1:
                                *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData);
                                break;
                            case 2:
                                *prcReq = vscsiDeviceReportOpc(pVScsiLun, pVScsiReq, u8Opc, fTimeoutDesc, cbData);
                                break;
                            default:
                                *prcReq = vscsiReqSenseErrorSet(&pVScsiDevice->VScsiSense, pVScsiReq,
                                                                SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET, 0x00);
                        }
                    }
                    else
                        *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
                }
                else
                    *prcReq = vscsiLunReqSenseErrorSet(pVScsiLun, pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE, 0x00);
            }
            else
                fProcessed = false; /* Might also be the SEND KEY MMC command. */
        }
#endif
        default:
            fProcessed = false;
    }

    return fProcessed;
}
static int vscsiLunSbcReqProcess(PVSCSILUNINT pVScsiLun, PVSCSIREQINT pVScsiReq)
{
    PVSCSILUNSBC pVScsiLunSbc = (PVSCSILUNSBC)pVScsiLun;
    int rc = VINF_SUCCESS;
    int rcReq = SCSI_STATUS_OK;
    uint64_t uLbaStart = 0;
    uint32_t cSectorTransfer = 0;
    VSCSIIOREQTXDIR enmTxDir = VSCSIIOREQTXDIR_INVALID;

    switch(pVScsiReq->pbCDB[0])
    {
        case SCSI_INQUIRY:
        {
            SCSIINQUIRYDATA ScsiInquiryReply;

            memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));

            ScsiInquiryReply.cbAdditional           = 31;
            ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS;
            ScsiInquiryReply.u3PeripheralQualifier  = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_CONNECTED;
            ScsiInquiryReply.u3AnsiVersion          = 0x05; /* SPC-4 compliant */
            ScsiInquiryReply.fCmdQue                = 1;    /* Command queuing supported. */
            ScsiInquiryReply.fWBus16                = 1;
            vscsiPadStr(ScsiInquiryReply.achVendorId, "VBOX", 8);
            vscsiPadStr(ScsiInquiryReply.achProductId, "HARDDISK", 16);
            vscsiPadStr(ScsiInquiryReply.achProductLevel, "1.0", 4);

            vscsiCopyToIoMemCtx(&pVScsiReq->IoMemCtx, (uint8_t *)&ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
            rcReq = vscsiReqSenseOkSet(pVScsiReq);
            break;
        }
        case SCSI_READ_CAPACITY:
        {
            uint8_t aReply[8];
            memset(aReply, 0, sizeof(aReply));

            /*
             * If sector size exceeds the maximum value that is
             * able to be stored in 4 bytes return 0xffffffff in this field
             */
            if (pVScsiLunSbc->cSectors > UINT32_C(0xffffffff))
                vscsiH2BEU32(aReply, UINT32_C(0xffffffff));
            else
                vscsiH2BEU32(aReply, pVScsiLunSbc->cSectors - 1);
            vscsiH2BEU32(&aReply[4], 512);
            vscsiCopyToIoMemCtx(&pVScsiReq->IoMemCtx, aReply, sizeof(aReply));
            rcReq = vscsiReqSenseOkSet(pVScsiReq);
            break;
        }
        case SCSI_MODE_SENSE_6:
        {
            uint8_t uModePage = pVScsiReq->pbCDB[2] & 0x3f;
            uint8_t aReply[24];
            uint8_t *pu8ReplyPos;

            memset(aReply, 0, sizeof(aReply));
            aReply[0] = 4; /* Reply length 4. */
            aReply[1] = 0; /* Default media type. */
            aReply[2] = RT_BIT(4); /* Caching supported. */
            aReply[3] = 0; /* Block descriptor length. */

            pu8ReplyPos = aReply + 4;

            if ((uModePage == 0x08) || (uModePage == 0x3f))
            {
                memset(pu8ReplyPos, 0, 20);
                *pu8ReplyPos++ = 0x08; /* Page code. */
                *pu8ReplyPos++ = 0x12; /* Size of the page. */
                *pu8ReplyPos++ = 0x4;  /* Write cache enabled. */
            }

            vscsiCopyToIoMemCtx(&pVScsiReq->IoMemCtx, aReply, sizeof(aReply));
            rcReq = vscsiReqSenseOkSet(pVScsiReq);
            break;
        }
        case SCSI_READ_6:
        {
            enmTxDir       = VSCSIIOREQTXDIR_READ;
            uLbaStart      = ((uint64_t)    pVScsiReq->pbCDB[3]
                                        |  (pVScsiReq->pbCDB[2] <<  8)
                                        | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
            cSectorTransfer = pVScsiReq->pbCDB[4];
            break;
        }
        case SCSI_READ_10:
        {
            enmTxDir        = VSCSIIOREQTXDIR_READ;
            uLbaStart       = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
            cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
            break;
        }
        case SCSI_READ_12:
        {
            enmTxDir        = VSCSIIOREQTXDIR_READ;
            uLbaStart       = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
            cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]);
            break;
        }
        case SCSI_READ_16:
        {
            enmTxDir        = VSCSIIOREQTXDIR_READ;
            uLbaStart       = vscsiBE2HU64(&pVScsiReq->pbCDB[2]);
            cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]);
            break;
        }
        case SCSI_WRITE_6:
        {
            enmTxDir        = VSCSIIOREQTXDIR_WRITE;
            uLbaStart       = ((uint64_t)  pVScsiReq->pbCDB[3]
                                        | (pVScsiReq->pbCDB[2] <<  8)
                                        | ((pVScsiReq->pbCDB[1] & 0x1f) << 16));
            cSectorTransfer = pVScsiReq->pbCDB[4];
            break;
        }
        case SCSI_WRITE_10:
        {
            enmTxDir        = VSCSIIOREQTXDIR_WRITE;
            uLbaStart       = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
            cSectorTransfer = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
            break;
        }
        case SCSI_WRITE_12:
        {
            enmTxDir        = VSCSIIOREQTXDIR_WRITE;
            uLbaStart       = vscsiBE2HU32(&pVScsiReq->pbCDB[2]);
            cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[6]);
            break;
        }
        case SCSI_WRITE_16:
        {
            enmTxDir        = VSCSIIOREQTXDIR_WRITE;
            uLbaStart       = vscsiBE2HU64(&pVScsiReq->pbCDB[2]);
            cSectorTransfer = vscsiBE2HU32(&pVScsiReq->pbCDB[10]);
            break;
        }
        case SCSI_SYNCHRONIZE_CACHE:
        {
            break; /* Handled below */
        }
        case SCSI_READ_BUFFER:
        {
            uint8_t uDataMode = pVScsiReq->pbCDB[1] & 0x1f;

            switch (uDataMode)
            {
                case 0x00:
                case 0x01:
                case 0x02:
                case 0x03:
                case 0x0a:
                    break;
                case 0x0b:
                {
                    uint8_t aReply[4];

                    /* We do not implement an echo buffer. */
                    memset(aReply, 0, sizeof(aReply));

                    vscsiCopyToIoMemCtx(&pVScsiReq->IoMemCtx, aReply, sizeof(aReply));
                    rcReq =  vscsiReqSenseOkSet(pVScsiReq);
                    break;
                }
                case 0x1a:
                case 0x1c:
                    break;
                default:
                    AssertMsgFailed(("Invalid data mode\n"));
            }
            break;
        }
        case SCSI_VERIFY_10:
        case SCSI_START_STOP_UNIT:
        {
            rcReq = vscsiReqSenseOkSet(pVScsiReq);
            break;
        }
        case SCSI_LOG_SENSE:
        {
            uint16_t cbMax = vscsiBE2HU16(&pVScsiReq->pbCDB[7]);
            uint8_t uPageCode = pVScsiReq->pbCDB[2] & 0x3f;
            uint8_t uSubPageCode = pVScsiReq->pbCDB[3];

            switch (uPageCode)
            {
                case 0x00:
                {
                    if (uSubPageCode == 0)
                    {
                        uint8_t aReply[4];

                        aReply[0] = 0;
                        aReply[1] = 0;
                        aReply[2] = 0;
                        aReply[3] = 0;
                        vscsiCopyToIoMemCtx(&pVScsiReq->IoMemCtx, aReply, sizeof(aReply));
                        rcReq = vscsiReqSenseOkSet(pVScsiReq);
                        break;
                    }
                }
                default:
                    rcReq = vscsiReqSenseErrorSet(pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET);
            }
            break;
        }
        case SCSI_SERVICE_ACTION_IN_16:
        {
            switch (pVScsiReq->pbCDB[1] & 0x1f)
            {
                case SCSI_SVC_ACTION_IN_READ_CAPACITY_16:
                {
                    uint8_t aReply[32];

                    memset(aReply, 0, sizeof(aReply));
                    vscsiH2BEU64(aReply, pVScsiLunSbc->cSectors - 1);
                    vscsiH2BEU32(&aReply[8], 512);
                    /* Leave the rest 0 */

                    vscsiCopyToIoMemCtx(&pVScsiReq->IoMemCtx, aReply, sizeof(aReply));
                    rcReq = vscsiReqSenseOkSet(pVScsiReq);
                    break;
                }
                default:
                    rcReq = vscsiReqSenseErrorSet(pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_INV_FIELD_IN_CMD_PACKET); /* Don't know if this is correct */
            }
            break;
        }
        default:
            //AssertMsgFailed(("Command %#x [%s] not implemented\n", pRequest->pbCDB[0], SCSICmdText(pRequest->pbCDB[0])));
            rcReq = vscsiReqSenseErrorSet(pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_ILLEGAL_OPCODE);
    }

    if (enmTxDir != VSCSIIOREQTXDIR_INVALID)
    {
        LogFlow(("%s: uLbaStart=%llu cSectorTransfer=%u\n",
                 __FUNCTION__, uLbaStart, cSectorTransfer));

        if (RT_UNLIKELY(uLbaStart + cSectorTransfer > pVScsiLunSbc->cSectors))
        {
            rcReq = vscsiReqSenseErrorSet(pVScsiReq, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_LOGICAL_BLOCK_OOR);
            vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
        }
        else if (!cSectorTransfer)
        {
            /* A 0 transfer length is not an error. */
            rcReq = vscsiReqSenseOkSet(pVScsiReq);
            vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);
        }
        else
        {
            /* Enqueue new I/O request */
            rc = vscsiIoReqTransferEnqueue(pVScsiLun, pVScsiReq, enmTxDir,
                                           uLbaStart * 512, cSectorTransfer * 512);
        }
    }
    else if (pVScsiReq->pbCDB[0] ==  SCSI_SYNCHRONIZE_CACHE)
    {
        /* Enqueue flush */
        rc = vscsiIoReqFlushEnqueue(pVScsiLun, pVScsiReq);
    }
    else /* Request completed */
        vscsiDeviceReqComplete(pVScsiLun->pVScsiDevice, pVScsiReq, rcReq, false, VINF_SUCCESS);

    return rc;
}