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