#include "VirtioSCSIPrivate.h"
#include <strings.h>
VirtioSCSIRequest::VirtioSCSIRequest(bool hasLock)
:
fHasLock(hasLock),
fStatus(SCSI_REQ_CMP),
fTimeout(0),
fBytesLeft(0),
fIsWrite(false),
fCCB(NULL)
{
if (hasLock)
mutex_init(&fLock, "virtio scsi request");
fBuffer = malloc(sizeof(struct virtio_scsi_cmd_req)
+ sizeof(struct virtio_scsi_cmd_resp));
bzero(fBuffer, sizeof(struct virtio_scsi_cmd_req)
+ sizeof(struct virtio_scsi_cmd_resp));
fRequest = (struct virtio_scsi_cmd_req *)fBuffer;
fResponse = (struct virtio_scsi_cmd_resp *)
((addr_t)fBuffer + sizeof(struct virtio_scsi_cmd_req));
fResponse->sense_len = 0;
}
VirtioSCSIRequest::~VirtioSCSIRequest()
{
if (fHasLock)
mutex_destroy(&fLock);
free(fBuffer);
}
void
VirtioSCSIRequest::SetStatus(uint8 status)
{
fStatus = status;
}
void
VirtioSCSIRequest::SetTimeout(bigtime_t timeout)
{
fTimeout = timeout;
}
void
VirtioSCSIRequest::SetIsWrite(bool isWrite)
{
fIsWrite = isWrite;
}
void
VirtioSCSIRequest::SetBytesLeft(uint32 bytesLeft)
{
fBytesLeft = bytesLeft;
}
status_t
VirtioSCSIRequest::Start(scsi_ccb *ccb)
{
CALLED();
if (mutex_trylock(&fLock) != B_OK)
return B_BUSY;
fCCB = ccb;
fStatus = SCSI_REQ_CMP;
fCCB->device_status = SCSI_STATUS_GOOD;
fIsWrite = false;
bzero(fResponse, sizeof(struct virtio_scsi_cmd_resp));
TRACE("VirtioSCSIRequest::Start() opcode %x tid %x lun %x\n", ccb->cdb[0],
ccb->target_id, ccb->target_lun);
return B_OK;
}
status_t
VirtioSCSIRequest::Finish(bool resubmit)
{
CALLED();
fStatus = _ResponseStatus();
fCCB->data_resid = fResponse->resid;
fCCB->subsys_status = fStatus;
TRACE("VirtioSCSIRequest::Finish() status 0x%x response 0x%x resid:0x%x"
" sense_len:%x\n", fResponse->status, fResponse->response,
fResponse->resid, fResponse->sense_len);
if (fCCB->cdb[0] == SCSI_OP_INQUIRY) {
} else if (fStatus == SCSI_REQ_CMP && fResponse->status != 0
&& HasSense()) {
TRACE("setting check condition\n");
fCCB->subsys_status = SCSI_REQ_CMP_ERR;
fCCB->device_status = SCSI_STATUS_CHECK_CONDITION;
if ((fCCB->flags & SCSI_DIS_AUTOSENSE) == 0) {
size_t senseLength = min_c(sizeof(fCCB->sense),
fResponse->sense_len);
memcpy(fCCB->sense, fResponse->sense, senseLength);
fCCB->sense_resid = sizeof(fCCB->sense) - senseLength;
fCCB->subsys_status |= SCSI_AUTOSNS_VALID;
}
}
scsi_ccb *ccb = fCCB;
mutex_unlock(&fLock);
if (resubmit)
gSCSI->resubmit(ccb);
else
gSCSI->finished(ccb, 1);
TRACE("VirtioSCSIRequest::Finish() done\n");
return B_OK;
}
void
VirtioSCSIRequest::Abort()
{
scsi_ccb *ccb = fCCB;
mutex_unlock(&fLock);
ccb->subsys_status = SCSI_REQ_ABORTED;
gSCSI->finished(ccb, 1);
}
void
VirtioSCSIRequest::RequestSense()
{
CALLED();
scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb;
copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense,
fResponse->sense_len, false);
fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len,
command->allocation_length), fCCB->data_length);
fResponse->sense_len = 0;
}
void
VirtioSCSIRequest::FillRequest(uint32 inCount, uint32 outCount,
physical_entry *entries)
{
CALLED();
fRequest->task_attr = VIRTIO_SCSI_S_SIMPLE;
fRequest->tag = (addr_t)fCCB;
fRequest->lun[0] = 1;
fRequest->lun[1] = fCCB->target_id;
fRequest->lun[2] = 0x40;
fRequest->lun[3] = fCCB->target_lun & 0xff;
memcpy(fRequest->cdb, fCCB->cdb, min_c(fCCB->cdb_length,
min_c(sizeof(fRequest->cdb), sizeof(fCCB->cdb))));
get_memory_map(fBuffer, sizeof(struct virtio_scsi_cmd_req)
+ sizeof(struct virtio_scsi_cmd_resp), &entries[0], 1);
entries[0].size = sizeof(struct virtio_scsi_cmd_req);
if (outCount > 1) {
memcpy(entries + 1, fCCB->sg_list, fCCB->sg_count
* sizeof(physical_entry));
}
entries[outCount].address = entries[0].address
+ sizeof(struct virtio_scsi_cmd_req);
entries[outCount].size = sizeof(struct virtio_scsi_cmd_resp);
if (inCount > 1) {
memcpy(entries + outCount + 1, fCCB->sg_list, fCCB->sg_count
* sizeof(physical_entry));
}
}
uchar
VirtioSCSIRequest::_ResponseStatus()
{
uchar status;
switch (fResponse->response) {
case VIRTIO_SCSI_S_OK:
status = SCSI_REQ_CMP;
break;
case VIRTIO_SCSI_S_OVERRUN:
status = SCSI_DATA_RUN_ERR;
break;
case VIRTIO_SCSI_S_ABORTED:
status = SCSI_REQ_ABORTED;
break;
case VIRTIO_SCSI_S_BAD_TARGET:
status = SCSI_TID_INVALID;
break;
case VIRTIO_SCSI_S_RESET:
status = SCSI_SCSI_BUS_RESET;
break;
case VIRTIO_SCSI_S_BUSY:
status = SCSI_SCSI_BUSY;
break;
case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
case VIRTIO_SCSI_S_TARGET_FAILURE:
case VIRTIO_SCSI_S_NEXUS_FAILURE:
status = SCSI_NO_NEXUS;
break;
default:
status = SCSI_REQ_CMP_ERR;
break;
}
return status;
}