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