xref: /haiku/src/add-ons/kernel/generic/scsi_periph/io.cpp (revision 9760dcae2038d47442f4658c2575844c6cf92c40)
1 /*
2  * Copyright 2004-2008, Haiku, Inc. All Rights Reserved.
3  * Copyright 2002/03, Thomas Kurschel. All rights reserved.
4  *
5  * Distributed under the terms of the MIT License.
6  */
7 
8 //!	Everything doing the real input/output stuff.
9 
10 
11 #include "scsi_periph_int.h"
12 #include <scsi.h>
13 
14 #include <string.h>
15 #include <stdlib.h>
16 
17 
18 static status_t
19 inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry)
20 {
21 	const scsi_res_inquiry *device_inquiry = NULL;
22 	size_t inquiryLength;
23 
24 	if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
25 			(const void **)&device_inquiry, &inquiryLength, true) != B_OK)
26 		return B_ERROR;
27 
28 	memcpy(inquiry, device_inquiry, min_c(inquiryLength, sizeof(scsi_inquiry)));
29 	return B_OK;
30 }
31 
32 
33 static status_t
34 prevent_allow(scsi_periph_device_info *device, bool prevent)
35 {
36 	scsi_cmd_prevent_allow cmd;
37 
38 	SHOW_FLOW0(0, "");
39 
40 	memset(&cmd, 0, sizeof(cmd));
41 	cmd.opcode = SCSI_OP_PREVENT_ALLOW;
42 	cmd.prevent = prevent;
43 
44 	return periph_simple_exec(device, (uint8 *)&cmd, sizeof(cmd), NULL, 0,
45 		SCSI_DIR_NONE);
46 }
47 
48 
49 /** !!!keep this in sync with scsi_raw driver!!! */
50 
51 static status_t
52 raw_command(scsi_periph_device_info *device, raw_device_command *cmd)
53 {
54 	scsi_ccb *request;
55 
56 	SHOW_FLOW0(0, "");
57 
58 	request = device->scsi->alloc_ccb(device->scsi_device);
59 	if (request == NULL)
60 		return B_NO_MEMORY;
61 
62 	request->flags = 0;
63 
64 	if (cmd->flags & B_RAW_DEVICE_DATA_IN)
65 		request->flags |= SCSI_DIR_IN;
66 	else if (cmd->data_length)
67 		request->flags |= SCSI_DIR_OUT;
68 	else
69 		request->flags |= SCSI_DIR_NONE;
70 
71 	request->data = (uint8*)cmd->data;
72 	request->sg_list = NULL;
73 	request->data_length = cmd->data_length;
74 	request->sort = -1;
75 	request->timeout = cmd->timeout;
76 
77 	memcpy(request->cdb, cmd->command, SCSI_MAX_CDB_SIZE);
78 	request->cdb_length = cmd->command_length;
79 
80 	device->scsi->sync_io(request);
81 
82 	// TBD: should we call standard error handler here, or may the
83 	// actions done there (like starting the unit) confuse the application?
84 
85 	cmd->cam_status = request->subsys_status;
86 	cmd->scsi_status = request->device_status;
87 
88 	if ((request->subsys_status & SCSI_AUTOSNS_VALID) != 0 && cmd->sense_data) {
89 		memcpy(cmd->sense_data, request->sense, min_c(cmd->sense_data_length,
90 			(size_t)SCSI_MAX_SENSE_SIZE - request->sense_resid));
91 	}
92 
93 	if ((cmd->flags & B_RAW_DEVICE_REPORT_RESIDUAL) != 0) {
94 		// this is a bit strange, see Be's sample code where I pinched this from;
95 		// normally, residual means "number of unused bytes left"
96 		// but here, we have to return "number of used bytes", which is the opposite
97 		cmd->data_length = cmd->data_length - request->data_resid;
98 		cmd->sense_data_length = SCSI_MAX_SENSE_SIZE - request->sense_resid;
99 	}
100 
101 	device->scsi->free_ccb(request);
102 
103 	return B_OK;
104 }
105 
106 
107 status_t
108 periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer,
109 	size_t length)
110 {
111 	switch (op) {
112 		case B_GET_MEDIA_STATUS: {
113 			status_t res = B_OK;
114 
115 			if (handle->device->removable)
116 				res = periph_get_media_status(handle);
117 
118 			SHOW_FLOW(2, "%s", strerror(res));
119 
120 			*(status_t *)buffer = res;
121 			return B_OK;
122 		}
123 
124 		case B_SCSI_INQUIRY:
125 			return inquiry(handle->device, (scsi_inquiry *)buffer);
126 
127 		case B_SCSI_PREVENT_ALLOW:
128 			return prevent_allow(handle->device, *(bool *)buffer);
129 
130 		case B_RAW_DEVICE_COMMAND:
131 			return raw_command(handle->device, (raw_device_command*)buffer);
132 
133 		default:
134 			if (handle->device->scsi->ioctl != NULL) {
135 				return handle->device->scsi->ioctl(handle->device->scsi_device,
136 					op, buffer, length);
137 			}
138 
139 			SHOW_ERROR(4, "Unknown ioctl: %x", op);
140 			return B_BAD_VALUE;
141 	}
142 }
143 
144 
145 /*!	kernel daemon
146 	once in a minute, it sets a flag so that the next command is executed
147 	ordered; this way, we avoid starvation of SCSI commands inside the
148 	SCSI queuing system - the ordered command waits for all previous
149 	commands and thus no command can starve longer then a minute
150 */
151 void
152 periph_sync_queue_daemon(void *arg, int iteration)
153 {
154 	scsi_periph_device_info *device = (scsi_periph_device_info *)arg;
155 
156 	SHOW_FLOW0(3, "Setting ordered flag for next R/W access");
157 	atomic_or(&device->next_tag_action, SCSI_ORDERED_QTAG);
158 }
159 
160 
161 /*! Universal read/write function */
162 status_t
163 periph_io(scsi_periph_device_info *device, io_operation *operation,
164 	size_t* _bytesTransferred)
165 {
166 	uint32 blockSize = device->block_size;
167 	size_t numBlocks = operation->Length() / blockSize;
168 	uint32 pos = operation->Offset() / blockSize;
169 	scsi_ccb *request;
170 	err_res res;
171 	int retries = 0;
172 	int err;
173 
174 	// don't test rw10_enabled restrictions - this flag may get changed
175 	request = device->scsi->alloc_ccb(device->scsi_device);
176 
177 	if (request == NULL)
178 		return B_NO_MEMORY;
179 
180 	do {
181 		size_t numBytes;
182 		bool is_rw10;
183 
184 		request->flags = operation->IsWrite() ? SCSI_DIR_OUT : SCSI_DIR_IN;
185 
186 		// make sure we avoid 10 byte commands if they aren't supported
187 		if (!device->rw10_enabled || device->preferred_ccb_size == 6) {
188 			// restricting transfer is OK - the block manager will
189 			// take care of transferring the rest
190 			if (numBlocks > 0x100)
191 				numBlocks = 0x100;
192 
193 			// no way to break the 21 bit address limit
194 			if (pos > 0x200000) {
195 				err = B_BAD_VALUE;
196 				goto abort;
197 			}
198 
199 			// don't allow transfer cross the 24 bit address limit
200 			// (I'm not sure whether this is allowed, but this way we
201 			// are sure to not ask for trouble)
202 			if (pos < 0x100000)
203 				numBlocks = min_c(numBlocks, 0x100000 - pos);
204 		}
205 
206 		numBytes = numBlocks * blockSize;
207 		if (numBytes != operation->Length())
208 			panic("I/O operation would need to be cut.");
209 
210 		request->data = NULL;
211 		request->sg_list = (physical_entry*)operation->Vecs();
212 		request->data_length = numBytes;
213 		request->sg_count = operation->VecCount();
214 		request->io_operation = operation;
215 		request->sort = pos;
216 		request->timeout = device->std_timeout;
217 		// see whether daemon instructed us to post an ordered command;
218 		// reset flag after read
219 		SHOW_FLOW( 3, "flag=%x, next_tag=%x, ordered: %s",
220 			(int)request->flags, (int)device->next_tag_action,
221 			(request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no" );
222 
223 		// use shortest commands whenever possible
224 		if (pos + numBlocks < 0x200000 && numBlocks <= 0x100) {
225 			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
226 
227 			is_rw10 = false;
228 
229 			memset(cmd, 0, sizeof(*cmd));
230 			cmd->opcode = operation->IsWrite()
231 				? SCSI_OP_WRITE_6 : SCSI_OP_READ_6;
232 			cmd->high_lba = (pos >> 16) & 0x1f;
233 			cmd->mid_lba = (pos >> 8) & 0xff;
234 			cmd->low_lba = pos & 0xff;
235 			cmd->length = numBlocks;
236 
237 			request->cdb_length = sizeof(*cmd);
238 		} else {
239 			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
240 
241 			is_rw10 = true;
242 
243 			memset(cmd, 0, sizeof(*cmd));
244 			cmd->opcode = operation->IsWrite()
245 				? SCSI_OP_WRITE_10 : SCSI_OP_READ_10;
246 			cmd->relative_address = 0;
247 			cmd->force_unit_access = 0;
248 			cmd->disable_page_out = 0;
249 			cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
250 			cmd->length = B_HOST_TO_BENDIAN_INT16(numBlocks);
251 
252 			request->cdb_length = sizeof(*cmd);
253 		}
254 
255 		// last chance to detect errors that occured during concurrent accesses
256 		err = 0; // TODO: handle->pending_error;
257 
258 		if (err)
259 			goto abort;
260 
261 		device->scsi->async_io(request);
262 
263 		acquire_sem(request->completion_sem);
264 
265 		// ask generic peripheral layer what to do now
266 		res = periph_check_error(device, request);
267 
268 		switch (res.action) {
269 			case err_act_ok:
270 				*_bytesTransferred = numBytes - request->data_resid;
271 				break;
272 
273 			case err_act_start:
274 				res = periph_send_start_stop(device, request, 1,
275 					device->removable);
276 				if (res.action == err_act_ok)
277 					res.action = err_act_retry;
278 				break;
279 
280 			case err_act_invalid_req:
281 				// if this was a 10 byte command, the device probably doesn't
282 				// support them, so disable them and retry
283 				if (is_rw10) {
284 					atomic_and(&device->rw10_enabled, 0);
285 					res.action = err_act_retry;
286 				} else
287 					res.action = err_act_fail;
288 				break;
289 		}
290 	} while((res.action == err_act_retry && retries++ < 3)
291 		|| (res.action == err_act_many_retries && retries++ < 30));
292 
293 	device->scsi->free_ccb(request);
294 
295 	// peripheral layer only created "read" error, so we have to
296 	// map them to "write" errors if this was a write request
297 	if (res.error_code == B_DEV_READ_ERROR && operation->IsWrite())
298 		return B_DEV_WRITE_ERROR;
299 
300 	return res.error_code;
301 
302 abort:
303 	device->scsi->free_ccb(request);
304 	return err;
305 }
306 
307