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
VirtioSCSIRequest(bool hasLock)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
~VirtioSCSIRequest()38 VirtioSCSIRequest::~VirtioSCSIRequest()
39 {
40 if (fHasLock)
41 mutex_destroy(&fLock);
42
43 free(fBuffer);
44 }
45
46
47 void
SetStatus(uint8 status)48 VirtioSCSIRequest::SetStatus(uint8 status)
49 {
50 fStatus = status;
51 }
52
53
54 void
SetTimeout(bigtime_t timeout)55 VirtioSCSIRequest::SetTimeout(bigtime_t timeout)
56 {
57 fTimeout = timeout;
58 }
59
60
61 void
SetIsWrite(bool isWrite)62 VirtioSCSIRequest::SetIsWrite(bool isWrite)
63 {
64 fIsWrite = isWrite;
65 }
66
67
68 void
SetBytesLeft(uint32 bytesLeft)69 VirtioSCSIRequest::SetBytesLeft(uint32 bytesLeft)
70 {
71 fBytesLeft = bytesLeft;
72 }
73
74
75 status_t
Start(scsi_ccb * ccb)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
Finish(bool resubmit)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
Abort()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
RequestSense()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
FillRequest(uint32 inCount,uint32 outCount,physical_entry * entries)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
_ResponseStatus()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