xref: /haiku/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIRequest.cpp (revision 66d6afec5a89464880e7793ad1b791cbee1efd8d)
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