182fda49eSJérôme Duval /*
282fda49eSJérôme Duval * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
382fda49eSJérôme Duval * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
482fda49eSJérôme Duval * Distributed under the terms of the MIT License.
582fda49eSJérôme Duval */
682fda49eSJérôme Duval
782fda49eSJérôme Duval
882fda49eSJérôme Duval #include "VirtioSCSIPrivate.h"
982fda49eSJérôme Duval
103aeed660SJérôme Duval #include <strings.h>
1182fda49eSJérôme Duval
1282fda49eSJérôme Duval
VirtioSCSIRequest(bool hasLock)1382fda49eSJérôme Duval VirtioSCSIRequest::VirtioSCSIRequest(bool hasLock)
1482fda49eSJérôme Duval :
1582fda49eSJérôme Duval fHasLock(hasLock),
16426c95e5SJérôme Duval fStatus(SCSI_REQ_CMP),
1782fda49eSJérôme Duval fTimeout(0),
1882fda49eSJérôme Duval fBytesLeft(0),
1982fda49eSJérôme Duval fIsWrite(false),
2082fda49eSJérôme Duval fCCB(NULL)
2182fda49eSJérôme Duval {
2282fda49eSJérôme Duval if (hasLock)
2382fda49eSJérôme Duval mutex_init(&fLock, "virtio scsi request");
2482fda49eSJérôme Duval
2582fda49eSJérôme Duval fBuffer = malloc(sizeof(struct virtio_scsi_cmd_req)
2682fda49eSJérôme Duval + sizeof(struct virtio_scsi_cmd_resp));
2782fda49eSJérôme Duval bzero(fBuffer, sizeof(struct virtio_scsi_cmd_req)
2882fda49eSJérôme Duval + sizeof(struct virtio_scsi_cmd_resp));
2982fda49eSJérôme Duval
3082fda49eSJérôme Duval fRequest = (struct virtio_scsi_cmd_req *)fBuffer;
3182fda49eSJérôme Duval fResponse = (struct virtio_scsi_cmd_resp *)
3282fda49eSJérôme Duval ((addr_t)fBuffer + sizeof(struct virtio_scsi_cmd_req));
3382fda49eSJérôme Duval
3482fda49eSJérôme Duval fResponse->sense_len = 0;
3582fda49eSJérôme Duval }
3682fda49eSJérôme Duval
3782fda49eSJérôme Duval
~VirtioSCSIRequest()3882fda49eSJérôme Duval VirtioSCSIRequest::~VirtioSCSIRequest()
3982fda49eSJérôme Duval {
4082fda49eSJérôme Duval if (fHasLock)
4182fda49eSJérôme Duval mutex_destroy(&fLock);
4282fda49eSJérôme Duval
4382fda49eSJérôme Duval free(fBuffer);
4482fda49eSJérôme Duval }
4582fda49eSJérôme Duval
4682fda49eSJérôme Duval
4782fda49eSJérôme Duval void
SetStatus(uint8 status)4882fda49eSJérôme Duval VirtioSCSIRequest::SetStatus(uint8 status)
4982fda49eSJérôme Duval {
5082fda49eSJérôme Duval fStatus = status;
5182fda49eSJérôme Duval }
5282fda49eSJérôme Duval
5382fda49eSJérôme Duval
5482fda49eSJérôme Duval void
SetTimeout(bigtime_t timeout)5582fda49eSJérôme Duval VirtioSCSIRequest::SetTimeout(bigtime_t timeout)
5682fda49eSJérôme Duval {
5782fda49eSJérôme Duval fTimeout = timeout;
5882fda49eSJérôme Duval }
5982fda49eSJérôme Duval
6082fda49eSJérôme Duval
6182fda49eSJérôme Duval void
SetIsWrite(bool isWrite)6282fda49eSJérôme Duval VirtioSCSIRequest::SetIsWrite(bool isWrite)
6382fda49eSJérôme Duval {
6482fda49eSJérôme Duval fIsWrite = isWrite;
6582fda49eSJérôme Duval }
6682fda49eSJérôme Duval
6782fda49eSJérôme Duval
6882fda49eSJérôme Duval void
SetBytesLeft(uint32 bytesLeft)6982fda49eSJérôme Duval VirtioSCSIRequest::SetBytesLeft(uint32 bytesLeft)
7082fda49eSJérôme Duval {
7182fda49eSJérôme Duval fBytesLeft = bytesLeft;
7282fda49eSJérôme Duval }
7382fda49eSJérôme Duval
7482fda49eSJérôme Duval
7582fda49eSJérôme Duval status_t
Start(scsi_ccb * ccb)7682fda49eSJérôme Duval VirtioSCSIRequest::Start(scsi_ccb *ccb)
7782fda49eSJérôme Duval {
7882fda49eSJérôme Duval CALLED();
7982fda49eSJérôme Duval if (mutex_trylock(&fLock) != B_OK)
8082fda49eSJérôme Duval return B_BUSY;
8182fda49eSJérôme Duval
8282fda49eSJérôme Duval fCCB = ccb;
8382fda49eSJérôme Duval fStatus = SCSI_REQ_CMP;
8482fda49eSJérôme Duval fCCB->device_status = SCSI_STATUS_GOOD;
8582fda49eSJérôme Duval fIsWrite = false;
8682fda49eSJérôme Duval bzero(fResponse, sizeof(struct virtio_scsi_cmd_resp));
8782fda49eSJérôme Duval
8882fda49eSJérôme Duval TRACE("VirtioSCSIRequest::Start() opcode %x tid %x lun %x\n", ccb->cdb[0],
8982fda49eSJérôme Duval ccb->target_id, ccb->target_lun);
9082fda49eSJérôme Duval
9182fda49eSJérôme Duval return B_OK;
9282fda49eSJérôme Duval }
9382fda49eSJérôme Duval
9482fda49eSJérôme Duval
9582fda49eSJérôme Duval status_t
Finish(bool resubmit)9682fda49eSJérôme Duval VirtioSCSIRequest::Finish(bool resubmit)
9782fda49eSJérôme Duval {
9882fda49eSJérôme Duval CALLED();
9982fda49eSJérôme Duval fStatus = _ResponseStatus();
10082fda49eSJérôme Duval fCCB->data_resid = fResponse->resid;
10182fda49eSJérôme Duval fCCB->subsys_status = fStatus;
10282fda49eSJérôme Duval
10382fda49eSJérôme Duval TRACE("VirtioSCSIRequest::Finish() status 0x%x response 0x%x resid:0x%x"
10482fda49eSJérôme Duval " sense_len:%x\n", fResponse->status, fResponse->response,
10582fda49eSJérôme Duval fResponse->resid, fResponse->sense_len);
10682fda49eSJérôme Duval
10782fda49eSJérôme Duval if (fCCB->cdb[0] == SCSI_OP_INQUIRY) {
10882fda49eSJérôme Duval // when the request is an inquiry, don't do anything
10982fda49eSJérôme Duval } else if (fStatus == SCSI_REQ_CMP && fResponse->status != 0
11082fda49eSJérôme Duval && HasSense()) {
11182fda49eSJérôme Duval // when the request completed and has set sense
11282fda49eSJérôme Duval // data, report this to the scsi stack by setting
11382fda49eSJérôme Duval // CHECK CONDITION status
11482fda49eSJérôme Duval TRACE("setting check condition\n");
11582fda49eSJérôme Duval
11682fda49eSJérôme Duval fCCB->subsys_status = SCSI_REQ_CMP_ERR;
11782fda49eSJérôme Duval fCCB->device_status = SCSI_STATUS_CHECK_CONDITION;
11882fda49eSJérôme Duval
11982fda49eSJérôme Duval // copy sense data if caller requested it
12082fda49eSJérôme Duval if ((fCCB->flags & SCSI_DIS_AUTOSENSE) == 0) {
12182fda49eSJérôme Duval size_t senseLength = min_c(sizeof(fCCB->sense),
12282fda49eSJérôme Duval fResponse->sense_len);
12382fda49eSJérôme Duval memcpy(fCCB->sense, fResponse->sense, senseLength);
12482fda49eSJérôme Duval fCCB->sense_resid = sizeof(fCCB->sense) - senseLength;
12582fda49eSJérôme Duval fCCB->subsys_status |= SCSI_AUTOSNS_VALID;
12682fda49eSJérôme Duval }
12782fda49eSJérôme Duval }
12882fda49eSJérôme Duval
12926c0b5e7SMichael Lotz scsi_ccb *ccb = fCCB;
13082fda49eSJérôme Duval mutex_unlock(&fLock);
13182fda49eSJérôme Duval
13282fda49eSJérôme Duval if (resubmit)
13326c0b5e7SMichael Lotz gSCSI->resubmit(ccb);
13482fda49eSJérôme Duval else
13526c0b5e7SMichael Lotz gSCSI->finished(ccb, 1);
13682fda49eSJérôme Duval
13782fda49eSJérôme Duval TRACE("VirtioSCSIRequest::Finish() done\n");
13882fda49eSJérôme Duval
13982fda49eSJérôme Duval return B_OK;
14082fda49eSJérôme Duval }
14182fda49eSJérôme Duval
14282fda49eSJérôme Duval
14382fda49eSJérôme Duval void
Abort()144*66d6afecSMichael Lotz VirtioSCSIRequest::Abort()
145*66d6afecSMichael Lotz {
146*66d6afecSMichael Lotz scsi_ccb *ccb = fCCB;
147*66d6afecSMichael Lotz mutex_unlock(&fLock);
148*66d6afecSMichael Lotz
149*66d6afecSMichael Lotz ccb->subsys_status = SCSI_REQ_ABORTED;
150*66d6afecSMichael Lotz gSCSI->finished(ccb, 1);
151*66d6afecSMichael Lotz }
152*66d6afecSMichael Lotz
153*66d6afecSMichael Lotz
154*66d6afecSMichael Lotz void
RequestSense()15582fda49eSJérôme Duval VirtioSCSIRequest::RequestSense()
15682fda49eSJérôme Duval {
15782fda49eSJérôme Duval CALLED();
15882fda49eSJérôme Duval // Copy sense data from last request into data buffer of current request.
15982fda49eSJérôme Duval // The sense data of last request is still present in the current request,
16082fda49eSJérôme Duval // as it isn't cleared on SCSI_OP_REQUEST_SENSE.
16182fda49eSJérôme Duval scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb;
16282fda49eSJérôme Duval copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense,
16382fda49eSJérôme Duval fResponse->sense_len, false);
16482fda49eSJérôme Duval
16582fda49eSJérôme Duval fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len,
16682fda49eSJérôme Duval command->allocation_length), fCCB->data_length);
16782fda49eSJérôme Duval fResponse->sense_len = 0;
16882fda49eSJérôme Duval }
16982fda49eSJérôme Duval
17082fda49eSJérôme Duval
17182fda49eSJérôme Duval void
FillRequest(uint32 inCount,uint32 outCount,physical_entry * entries)17282fda49eSJérôme Duval VirtioSCSIRequest::FillRequest(uint32 inCount, uint32 outCount,
17382fda49eSJérôme Duval physical_entry *entries)
17482fda49eSJérôme Duval {
17582fda49eSJérôme Duval CALLED();
17682fda49eSJérôme Duval fRequest->task_attr = VIRTIO_SCSI_S_SIMPLE;
17782fda49eSJérôme Duval fRequest->tag = (addr_t)fCCB;
17882fda49eSJérôme Duval fRequest->lun[0] = 1;
17982fda49eSJérôme Duval fRequest->lun[1] = fCCB->target_id;
180426c95e5SJérôme Duval // we don't support lun >= 256
181426c95e5SJérôme Duval fRequest->lun[2] = 0x40;
182426c95e5SJérôme Duval fRequest->lun[3] = fCCB->target_lun & 0xff;
18382fda49eSJérôme Duval
18482fda49eSJérôme Duval memcpy(fRequest->cdb, fCCB->cdb, min_c(fCCB->cdb_length,
185426c95e5SJérôme Duval min_c(sizeof(fRequest->cdb), sizeof(fCCB->cdb))));
18682fda49eSJérôme Duval
18782fda49eSJérôme Duval get_memory_map(fBuffer, sizeof(struct virtio_scsi_cmd_req)
18882fda49eSJérôme Duval + sizeof(struct virtio_scsi_cmd_resp), &entries[0], 1);
18982fda49eSJérôme Duval entries[0].size = sizeof(struct virtio_scsi_cmd_req);
19082fda49eSJérôme Duval if (outCount > 1) {
19182fda49eSJérôme Duval memcpy(entries + 1, fCCB->sg_list, fCCB->sg_count
19282fda49eSJérôme Duval * sizeof(physical_entry));
19382fda49eSJérôme Duval }
19482fda49eSJérôme Duval
19582fda49eSJérôme Duval entries[outCount].address = entries[0].address
19682fda49eSJérôme Duval + sizeof(struct virtio_scsi_cmd_req);
19782fda49eSJérôme Duval entries[outCount].size = sizeof(struct virtio_scsi_cmd_resp);
19882fda49eSJérôme Duval
19982fda49eSJérôme Duval if (inCount > 1) {
20082fda49eSJérôme Duval memcpy(entries + outCount + 1, fCCB->sg_list, fCCB->sg_count
20182fda49eSJérôme Duval * sizeof(physical_entry));
20282fda49eSJérôme Duval }
20382fda49eSJérôme Duval }
20482fda49eSJérôme Duval
20582fda49eSJérôme Duval
20682fda49eSJérôme Duval uchar
_ResponseStatus()20782fda49eSJérôme Duval VirtioSCSIRequest::_ResponseStatus()
20882fda49eSJérôme Duval {
20982fda49eSJérôme Duval uchar status;
21082fda49eSJérôme Duval
21182fda49eSJérôme Duval switch (fResponse->response) {
21282fda49eSJérôme Duval case VIRTIO_SCSI_S_OK:
21382fda49eSJérôme Duval status = SCSI_REQ_CMP;
21482fda49eSJérôme Duval break;
21582fda49eSJérôme Duval case VIRTIO_SCSI_S_OVERRUN:
21682fda49eSJérôme Duval status = SCSI_DATA_RUN_ERR;
21782fda49eSJérôme Duval break;
21882fda49eSJérôme Duval case VIRTIO_SCSI_S_ABORTED:
21982fda49eSJérôme Duval status = SCSI_REQ_ABORTED;
22082fda49eSJérôme Duval break;
22182fda49eSJérôme Duval case VIRTIO_SCSI_S_BAD_TARGET:
22282fda49eSJérôme Duval status = SCSI_TID_INVALID;
22382fda49eSJérôme Duval break;
22482fda49eSJérôme Duval case VIRTIO_SCSI_S_RESET:
22582fda49eSJérôme Duval status = SCSI_SCSI_BUS_RESET;
22682fda49eSJérôme Duval break;
22782fda49eSJérôme Duval case VIRTIO_SCSI_S_BUSY:
22882fda49eSJérôme Duval status = SCSI_SCSI_BUSY;
22982fda49eSJérôme Duval break;
23082fda49eSJérôme Duval case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
23182fda49eSJérôme Duval case VIRTIO_SCSI_S_TARGET_FAILURE:
23282fda49eSJérôme Duval case VIRTIO_SCSI_S_NEXUS_FAILURE:
23382fda49eSJérôme Duval status = SCSI_NO_NEXUS;
23482fda49eSJérôme Duval break;
23582fda49eSJérôme Duval default: /* VIRTIO_SCSI_S_FAILURE */
23682fda49eSJérôme Duval status = SCSI_REQ_CMP_ERR;
23782fda49eSJérôme Duval break;
23882fda49eSJérôme Duval }
23982fda49eSJérôme Duval
24082fda49eSJérôme Duval return status;
24182fda49eSJérôme Duval }
242