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