1 /* 2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de. 3 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "VirtioSCSIPrivate.h" 9 10 #include <string.h> 11 12 13 VirtioSCSIRequest::VirtioSCSIRequest(bool hasLock) 14 : 15 fHasLock(hasLock), 16 fTimeout(0), 17 fBytesLeft(0), 18 fIsWrite(false), 19 fCCB(NULL) 20 { 21 if (hasLock) 22 mutex_init(&fLock, "virtio scsi request"); 23 24 fBuffer = malloc(sizeof(struct virtio_scsi_cmd_req) 25 + sizeof(struct virtio_scsi_cmd_resp)); 26 bzero(fBuffer, sizeof(struct virtio_scsi_cmd_req) 27 + sizeof(struct virtio_scsi_cmd_resp)); 28 29 fRequest = (struct virtio_scsi_cmd_req *)fBuffer; 30 fResponse = (struct virtio_scsi_cmd_resp *) 31 ((addr_t)fBuffer + sizeof(struct virtio_scsi_cmd_req)); 32 33 fResponse->sense_len = 0; 34 } 35 36 37 VirtioSCSIRequest::~VirtioSCSIRequest() 38 { 39 if (fHasLock) 40 mutex_destroy(&fLock); 41 42 free(fBuffer); 43 } 44 45 46 void 47 VirtioSCSIRequest::SetStatus(uint8 status) 48 { 49 fStatus = status; 50 } 51 52 53 void 54 VirtioSCSIRequest::SetTimeout(bigtime_t timeout) 55 { 56 fTimeout = timeout; 57 } 58 59 60 void 61 VirtioSCSIRequest::SetIsWrite(bool isWrite) 62 { 63 fIsWrite = isWrite; 64 } 65 66 67 void 68 VirtioSCSIRequest::SetBytesLeft(uint32 bytesLeft) 69 { 70 fBytesLeft = bytesLeft; 71 } 72 73 74 status_t 75 VirtioSCSIRequest::Start(scsi_ccb *ccb) 76 { 77 CALLED(); 78 if (mutex_trylock(&fLock) != B_OK) 79 return B_BUSY; 80 81 fCCB = ccb; 82 fStatus = SCSI_REQ_CMP; 83 fCCB->device_status = SCSI_STATUS_GOOD; 84 fIsWrite = false; 85 bzero(fResponse, sizeof(struct virtio_scsi_cmd_resp)); 86 87 TRACE("VirtioSCSIRequest::Start() opcode %x tid %x lun %x\n", ccb->cdb[0], 88 ccb->target_id, ccb->target_lun); 89 90 return B_OK; 91 } 92 93 94 status_t 95 VirtioSCSIRequest::Finish(bool resubmit) 96 { 97 CALLED(); 98 fStatus = _ResponseStatus(); 99 fCCB->data_resid = fResponse->resid; 100 fCCB->subsys_status = fStatus; 101 102 TRACE("VirtioSCSIRequest::Finish() status 0x%x response 0x%x resid:0x%x" 103 " sense_len:%x\n", fResponse->status, fResponse->response, 104 fResponse->resid, fResponse->sense_len); 105 106 if (fCCB->cdb[0] == SCSI_OP_INQUIRY) { 107 // when the request is an inquiry, don't do anything 108 } else if (fStatus == SCSI_REQ_CMP && fResponse->status != 0 109 && HasSense()) { 110 // when the request completed and has set sense 111 // data, report this to the scsi stack by setting 112 // CHECK CONDITION status 113 TRACE("setting check condition\n"); 114 115 fCCB->subsys_status = SCSI_REQ_CMP_ERR; 116 fCCB->device_status = SCSI_STATUS_CHECK_CONDITION; 117 118 // copy sense data if caller requested it 119 if ((fCCB->flags & SCSI_DIS_AUTOSENSE) == 0) { 120 size_t senseLength = min_c(sizeof(fCCB->sense), 121 fResponse->sense_len); 122 memcpy(fCCB->sense, fResponse->sense, senseLength); 123 fCCB->sense_resid = sizeof(fCCB->sense) - senseLength; 124 fCCB->subsys_status |= SCSI_AUTOSNS_VALID; 125 } 126 } 127 128 mutex_unlock(&fLock); 129 130 if (resubmit) 131 gSCSI->resubmit(fCCB); 132 else 133 gSCSI->finished(fCCB, 1); 134 135 TRACE("VirtioSCSIRequest::Finish() done\n"); 136 137 return B_OK; 138 } 139 140 141 void 142 VirtioSCSIRequest::RequestSense() 143 { 144 CALLED(); 145 // Copy sense data from last request into data buffer of current request. 146 // The sense data of last request is still present in the current request, 147 // as it isn't cleared on SCSI_OP_REQUEST_SENSE. 148 scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb; 149 copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense, 150 fResponse->sense_len, false); 151 152 fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len, 153 command->allocation_length), fCCB->data_length); 154 fResponse->sense_len = 0; 155 } 156 157 158 void 159 VirtioSCSIRequest::FillRequest(uint32 inCount, uint32 outCount, 160 physical_entry *entries) 161 { 162 CALLED(); 163 fRequest->task_attr = VIRTIO_SCSI_S_SIMPLE; 164 fRequest->tag = (addr_t)fCCB; 165 fRequest->lun[0] = 1; 166 fRequest->lun[1] = fCCB->target_id; 167 fRequest->lun[2] = 0x40 | ((fCCB->target_lun >> 8) & 0x3f); 168 fRequest->lun[3] = (fCCB->target_lun >> 8) & 0xff; 169 170 memcpy(fRequest->cdb, fCCB->cdb, min_c(fCCB->cdb_length, 171 sizeof(fRequest->cdb))); 172 173 get_memory_map(fBuffer, sizeof(struct virtio_scsi_cmd_req) 174 + sizeof(struct virtio_scsi_cmd_resp), &entries[0], 1); 175 entries[0].size = sizeof(struct virtio_scsi_cmd_req); 176 if (outCount > 1) { 177 memcpy(entries + 1, fCCB->sg_list, fCCB->sg_count 178 * sizeof(physical_entry)); 179 } 180 181 entries[outCount].address = entries[0].address 182 + sizeof(struct virtio_scsi_cmd_req); 183 entries[outCount].size = sizeof(struct virtio_scsi_cmd_resp); 184 185 if (inCount > 1) { 186 memcpy(entries + outCount + 1, fCCB->sg_list, fCCB->sg_count 187 * sizeof(physical_entry)); 188 } 189 } 190 191 192 uchar 193 VirtioSCSIRequest::_ResponseStatus() 194 { 195 uchar status; 196 197 switch (fResponse->response) { 198 case VIRTIO_SCSI_S_OK: 199 status = SCSI_REQ_CMP; 200 break; 201 case VIRTIO_SCSI_S_OVERRUN: 202 status = SCSI_DATA_RUN_ERR; 203 break; 204 case VIRTIO_SCSI_S_ABORTED: 205 status = SCSI_REQ_ABORTED; 206 break; 207 case VIRTIO_SCSI_S_BAD_TARGET: 208 status = SCSI_TID_INVALID; 209 break; 210 case VIRTIO_SCSI_S_RESET: 211 status = SCSI_SCSI_BUS_RESET; 212 break; 213 case VIRTIO_SCSI_S_BUSY: 214 status = SCSI_SCSI_BUSY; 215 break; 216 case VIRTIO_SCSI_S_TRANSPORT_FAILURE: 217 case VIRTIO_SCSI_S_TARGET_FAILURE: 218 case VIRTIO_SCSI_S_NEXUS_FAILURE: 219 status = SCSI_NO_NEXUS; 220 break; 221 default: /* VIRTIO_SCSI_S_FAILURE */ 222 status = SCSI_REQ_CMP_ERR; 223 break; 224 } 225 226 return status; 227 } 228 229