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 10*3aeed660SJérôme Duval #include <strings.h> 1182fda49eSJérôme Duval 1282fda49eSJérôme Duval 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 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 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 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 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 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 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 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 12982fda49eSJérôme Duval mutex_unlock(&fLock); 13082fda49eSJérôme Duval 13182fda49eSJérôme Duval if (resubmit) 13282fda49eSJérôme Duval gSCSI->resubmit(fCCB); 13382fda49eSJérôme Duval else 13482fda49eSJérôme Duval gSCSI->finished(fCCB, 1); 13582fda49eSJérôme Duval 13682fda49eSJérôme Duval TRACE("VirtioSCSIRequest::Finish() done\n"); 13782fda49eSJérôme Duval 13882fda49eSJérôme Duval return B_OK; 13982fda49eSJérôme Duval } 14082fda49eSJérôme Duval 14182fda49eSJérôme Duval 14282fda49eSJérôme Duval void 14382fda49eSJérôme Duval VirtioSCSIRequest::RequestSense() 14482fda49eSJérôme Duval { 14582fda49eSJérôme Duval CALLED(); 14682fda49eSJérôme Duval // Copy sense data from last request into data buffer of current request. 14782fda49eSJérôme Duval // The sense data of last request is still present in the current request, 14882fda49eSJérôme Duval // as it isn't cleared on SCSI_OP_REQUEST_SENSE. 14982fda49eSJérôme Duval scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb; 15082fda49eSJérôme Duval copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense, 15182fda49eSJérôme Duval fResponse->sense_len, false); 15282fda49eSJérôme Duval 15382fda49eSJérôme Duval fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len, 15482fda49eSJérôme Duval command->allocation_length), fCCB->data_length); 15582fda49eSJérôme Duval fResponse->sense_len = 0; 15682fda49eSJérôme Duval } 15782fda49eSJérôme Duval 15882fda49eSJérôme Duval 15982fda49eSJérôme Duval void 16082fda49eSJérôme Duval VirtioSCSIRequest::FillRequest(uint32 inCount, uint32 outCount, 16182fda49eSJérôme Duval physical_entry *entries) 16282fda49eSJérôme Duval { 16382fda49eSJérôme Duval CALLED(); 16482fda49eSJérôme Duval fRequest->task_attr = VIRTIO_SCSI_S_SIMPLE; 16582fda49eSJérôme Duval fRequest->tag = (addr_t)fCCB; 16682fda49eSJérôme Duval fRequest->lun[0] = 1; 16782fda49eSJérôme Duval fRequest->lun[1] = fCCB->target_id; 168426c95e5SJérôme Duval // we don't support lun >= 256 169426c95e5SJérôme Duval fRequest->lun[2] = 0x40; 170426c95e5SJérôme Duval fRequest->lun[3] = fCCB->target_lun & 0xff; 17182fda49eSJérôme Duval 17282fda49eSJérôme Duval memcpy(fRequest->cdb, fCCB->cdb, min_c(fCCB->cdb_length, 173426c95e5SJérôme Duval min_c(sizeof(fRequest->cdb), sizeof(fCCB->cdb)))); 17482fda49eSJérôme Duval 17582fda49eSJérôme Duval get_memory_map(fBuffer, sizeof(struct virtio_scsi_cmd_req) 17682fda49eSJérôme Duval + sizeof(struct virtio_scsi_cmd_resp), &entries[0], 1); 17782fda49eSJérôme Duval entries[0].size = sizeof(struct virtio_scsi_cmd_req); 17882fda49eSJérôme Duval if (outCount > 1) { 17982fda49eSJérôme Duval memcpy(entries + 1, fCCB->sg_list, fCCB->sg_count 18082fda49eSJérôme Duval * sizeof(physical_entry)); 18182fda49eSJérôme Duval } 18282fda49eSJérôme Duval 18382fda49eSJérôme Duval entries[outCount].address = entries[0].address 18482fda49eSJérôme Duval + sizeof(struct virtio_scsi_cmd_req); 18582fda49eSJérôme Duval entries[outCount].size = sizeof(struct virtio_scsi_cmd_resp); 18682fda49eSJérôme Duval 18782fda49eSJérôme Duval if (inCount > 1) { 18882fda49eSJérôme Duval memcpy(entries + outCount + 1, fCCB->sg_list, fCCB->sg_count 18982fda49eSJérôme Duval * sizeof(physical_entry)); 19082fda49eSJérôme Duval } 19182fda49eSJérôme Duval } 19282fda49eSJérôme Duval 19382fda49eSJérôme Duval 19482fda49eSJérôme Duval uchar 19582fda49eSJérôme Duval VirtioSCSIRequest::_ResponseStatus() 19682fda49eSJérôme Duval { 19782fda49eSJérôme Duval uchar status; 19882fda49eSJérôme Duval 19982fda49eSJérôme Duval switch (fResponse->response) { 20082fda49eSJérôme Duval case VIRTIO_SCSI_S_OK: 20182fda49eSJérôme Duval status = SCSI_REQ_CMP; 20282fda49eSJérôme Duval break; 20382fda49eSJérôme Duval case VIRTIO_SCSI_S_OVERRUN: 20482fda49eSJérôme Duval status = SCSI_DATA_RUN_ERR; 20582fda49eSJérôme Duval break; 20682fda49eSJérôme Duval case VIRTIO_SCSI_S_ABORTED: 20782fda49eSJérôme Duval status = SCSI_REQ_ABORTED; 20882fda49eSJérôme Duval break; 20982fda49eSJérôme Duval case VIRTIO_SCSI_S_BAD_TARGET: 21082fda49eSJérôme Duval status = SCSI_TID_INVALID; 21182fda49eSJérôme Duval break; 21282fda49eSJérôme Duval case VIRTIO_SCSI_S_RESET: 21382fda49eSJérôme Duval status = SCSI_SCSI_BUS_RESET; 21482fda49eSJérôme Duval break; 21582fda49eSJérôme Duval case VIRTIO_SCSI_S_BUSY: 21682fda49eSJérôme Duval status = SCSI_SCSI_BUSY; 21782fda49eSJérôme Duval break; 21882fda49eSJérôme Duval case VIRTIO_SCSI_S_TRANSPORT_FAILURE: 21982fda49eSJérôme Duval case VIRTIO_SCSI_S_TARGET_FAILURE: 22082fda49eSJérôme Duval case VIRTIO_SCSI_S_NEXUS_FAILURE: 22182fda49eSJérôme Duval status = SCSI_NO_NEXUS; 22282fda49eSJérôme Duval break; 22382fda49eSJérôme Duval default: /* VIRTIO_SCSI_S_FAILURE */ 22482fda49eSJérôme Duval status = SCSI_REQ_CMP_ERR; 22582fda49eSJérôme Duval break; 22682fda49eSJérôme Duval } 22782fda49eSJérôme Duval 22882fda49eSJérôme Duval return status; 22982fda49eSJérôme Duval } 230