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; }
/** * 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; }
/** * 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; }