xref: /haiku/src/add-ons/kernel/busses/scsi/virtio/VirtioSCSIRequest.cpp (revision 60b39cd7416028e61e3d30bb3ba28bd3526e6001)
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 	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 	mutex_unlock(&fLock);
130 
131 	if (resubmit)
132 		gSCSI->resubmit(fCCB);
133 	else
134 		gSCSI->finished(fCCB, 1);
135 
136 	TRACE("VirtioSCSIRequest::Finish() done\n");
137 
138 	return B_OK;
139 }
140 
141 
142 void
143 VirtioSCSIRequest::RequestSense()
144 {
145 	CALLED();
146 	// Copy sense data from last request into data buffer of current request.
147 	// The sense data of last request is still present in the current request,
148 	// as it isn't cleared on SCSI_OP_REQUEST_SENSE.
149 	scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb;
150 	copy_sg_data(fCCB, 0, command->allocation_length, fResponse->sense,
151 		fResponse->sense_len, false);
152 
153 	fCCB->data_resid = fCCB->data_length - min_c(min_c(fResponse->sense_len,
154 		command->allocation_length), fCCB->data_length);
155 	fResponse->sense_len = 0;
156 }
157 
158 
159 void
160 VirtioSCSIRequest::FillRequest(uint32 inCount, uint32 outCount,
161 	physical_entry *entries)
162 {
163 	CALLED();
164 	fRequest->task_attr = VIRTIO_SCSI_S_SIMPLE;
165 	fRequest->tag = (addr_t)fCCB;
166 	fRequest->lun[0] = 1;
167 	fRequest->lun[1] = fCCB->target_id;
168 	// we don't support lun >= 256
169 	fRequest->lun[2] = 0x40;
170 	fRequest->lun[3] = fCCB->target_lun & 0xff;
171 
172 	memcpy(fRequest->cdb, fCCB->cdb, min_c(fCCB->cdb_length,
173 		min_c(sizeof(fRequest->cdb), sizeof(fCCB->cdb))));
174 
175 	get_memory_map(fBuffer, sizeof(struct virtio_scsi_cmd_req)
176 		+ sizeof(struct virtio_scsi_cmd_resp), &entries[0], 1);
177 	entries[0].size = sizeof(struct virtio_scsi_cmd_req);
178 	if (outCount > 1) {
179 		memcpy(entries + 1, fCCB->sg_list, fCCB->sg_count
180 			* sizeof(physical_entry));
181 	}
182 
183 	entries[outCount].address = entries[0].address
184 		+ sizeof(struct virtio_scsi_cmd_req);
185 	entries[outCount].size = sizeof(struct virtio_scsi_cmd_resp);
186 
187 	if (inCount > 1) {
188 		memcpy(entries + outCount + 1, fCCB->sg_list, fCCB->sg_count
189 			* sizeof(physical_entry));
190 	}
191 }
192 
193 
194 uchar
195 VirtioSCSIRequest::_ResponseStatus()
196 {
197 	uchar status;
198 
199 	switch (fResponse->response) {
200 		case VIRTIO_SCSI_S_OK:
201 			status = SCSI_REQ_CMP;
202 			break;
203 		case VIRTIO_SCSI_S_OVERRUN:
204 			status = SCSI_DATA_RUN_ERR;
205 			break;
206 		case VIRTIO_SCSI_S_ABORTED:
207 			status = SCSI_REQ_ABORTED;
208 			break;
209 		case VIRTIO_SCSI_S_BAD_TARGET:
210 			status = SCSI_TID_INVALID;
211 			break;
212 		case VIRTIO_SCSI_S_RESET:
213 			status = SCSI_SCSI_BUS_RESET;
214 			break;
215 		case VIRTIO_SCSI_S_BUSY:
216 			status = SCSI_SCSI_BUSY;
217 			break;
218 		case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
219 		case VIRTIO_SCSI_S_TARGET_FAILURE:
220 		case VIRTIO_SCSI_S_NEXUS_FAILURE:
221 			status = SCSI_NO_NEXUS;
222 			break;
223 		default: /* VIRTIO_SCSI_S_FAILURE */
224 			status = SCSI_REQ_CMP_ERR;
225 			break;
226 	}
227 
228 	return status;
229 }
230