xref: /haiku/src/add-ons/kernel/generic/scsi_periph/io.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
1 /*
2  * Copyright 2004-2011, 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 
9 //!	Everything doing the real input/output stuff.
10 
11 
12 #include "scsi_periph_int.h"
13 #include <scsi.h>
14 
15 #include <string.h>
16 #include <stdlib.h>
17 
18 
19 static status_t
20 inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry)
21 {
22 	const scsi_res_inquiry *device_inquiry = NULL;
23 	size_t inquiryLength;
24 
25 	if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
26 			(const void **)&device_inquiry, &inquiryLength, true) != B_OK)
27 		return B_ERROR;
28 
29 	memcpy(inquiry, device_inquiry, min_c(inquiryLength, sizeof(scsi_inquiry)));
30 	return B_OK;
31 }
32 
33 
34 static status_t
35 prevent_allow(scsi_periph_device_info *device, bool prevent)
36 {
37 	scsi_cmd_prevent_allow cmd;
38 
39 	SHOW_FLOW0(0, "");
40 
41 	memset(&cmd, 0, sizeof(cmd));
42 	cmd.opcode = SCSI_OP_PREVENT_ALLOW;
43 	cmd.prevent = prevent;
44 
45 	return periph_simple_exec(device, (uint8 *)&cmd, sizeof(cmd), NULL, 0,
46 		SCSI_DIR_NONE);
47 }
48 
49 
50 /*! Keep this in sync with scsi_raw driver!!! */
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 /*! Universal read/write function */
108 static status_t
109 read_write(scsi_periph_device_info *device, scsi_ccb *request,
110 	io_operation *operation, uint64 offset, size_t originalNumBlocks,
111 	physical_entry* vecs, size_t vecCount, bool isWrite,
112 	size_t* _bytesTransferred)
113 {
114 	uint32 blockSize = device->block_size;
115 	size_t numBlocks = originalNumBlocks;
116 	uint32 pos = offset;
117 	err_res res;
118 	int retries = 0;
119 
120 	do {
121 		size_t numBytes;
122 		bool isReadWrite10 = false;
123 
124 		request->flags = isWrite ? SCSI_DIR_OUT : SCSI_DIR_IN;
125 
126 		// io_operations are generated by a DMAResource and thus contain DMA
127 		// safe physical vectors
128 		if (operation != NULL)
129 			request->flags |= SCSI_DMA_SAFE;
130 
131 		// make sure we avoid 10 byte commands if they aren't supported
132 		if (!device->rw10_enabled || device->preferred_ccb_size == 6) {
133 			// restricting transfer is OK - the block manager will
134 			// take care of transferring the rest
135 			if (numBlocks > 0x100)
136 				numBlocks = 0x100;
137 
138 			// no way to break the 21 bit address limit
139 			if (offset > 0x200000)
140 				return B_BAD_VALUE;
141 
142 			// don't allow transfer cross the 24 bit address limit
143 			// (I'm not sure whether this is allowed, but this way we
144 			// are sure to not ask for trouble)
145 			if (offset < 0x100000)
146 				numBlocks = min_c(numBlocks, 0x100000 - pos);
147 		}
148 
149 		numBytes = numBlocks * blockSize;
150 		if (numBlocks != originalNumBlocks)
151 			panic("I/O operation would need to be cut.");
152 
153 		request->data = NULL;
154 		request->sg_list = vecs;
155 		request->data_length = numBytes;
156 		request->sg_count = vecCount;
157 		request->io_operation = operation;
158 		request->sort = pos;
159 		request->timeout = device->std_timeout;
160 		// see whether daemon instructed us to post an ordered command;
161 		// reset flag after read
162 		SHOW_FLOW(3, "flag=%x, next_tag=%x, ordered: %s",
163 			(int)request->flags, (int)device->next_tag_action,
164 			(request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no");
165 
166 		// use shortest commands whenever possible
167 		if (offset + numBlocks < 0x200000LL && numBlocks <= 0x100) {
168 			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
169 
170 			isReadWrite10 = false;
171 
172 			memset(cmd, 0, sizeof(*cmd));
173 			cmd->opcode = isWrite ? SCSI_OP_WRITE_6 : SCSI_OP_READ_6;
174 			cmd->high_lba = (pos >> 16) & 0x1f;
175 			cmd->mid_lba = (pos >> 8) & 0xff;
176 			cmd->low_lba = pos & 0xff;
177 			cmd->length = numBlocks;
178 
179 			request->cdb_length = sizeof(*cmd);
180 		} else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000) {
181 			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
182 
183 			isReadWrite10 = true;
184 
185 			memset(cmd, 0, sizeof(*cmd));
186 			cmd->opcode = isWrite ? SCSI_OP_WRITE_10 : SCSI_OP_READ_10;
187 			cmd->relative_address = 0;
188 			cmd->force_unit_access = 0;
189 			cmd->disable_page_out = 0;
190 			cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
191 			cmd->length = B_HOST_TO_BENDIAN_INT16(numBlocks);
192 
193 			request->cdb_length = sizeof(*cmd);
194 		} else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000000) {
195 			scsi_cmd_rw_12 *cmd = (scsi_cmd_rw_12 *)request->cdb;
196 
197 			memset(cmd, 0, sizeof(*cmd));
198 			cmd->opcode = isWrite ? SCSI_OP_WRITE_12 : SCSI_OP_READ_12;
199 			cmd->relative_address = 0;
200 			cmd->force_unit_access = 0;
201 			cmd->disable_page_out = 0;
202 			cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
203 			cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks);
204 
205 			request->cdb_length = sizeof(*cmd);
206 		} else {
207 			scsi_cmd_rw_16 *cmd = (scsi_cmd_rw_16 *)request->cdb;
208 
209 			memset(cmd, 0, sizeof(*cmd));
210 			cmd->opcode = isWrite ? SCSI_OP_WRITE_16 : SCSI_OP_READ_16;
211 			cmd->force_unit_access_non_volatile = 0;
212 			cmd->force_unit_access = 0;
213 			cmd->disable_page_out = 0;
214 			cmd->lba = B_HOST_TO_BENDIAN_INT64(offset);
215 			cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks);
216 
217 			request->cdb_length = sizeof(*cmd);
218 		}
219 
220 		// TODO: last chance to detect errors that occured during concurrent accesses
221 		//status_t status = handle->pending_error;
222 		//if (status != B_OK)
223 		//	return status;
224 
225 		device->scsi->async_io(request);
226 
227 		acquire_sem(request->completion_sem);
228 
229 		// ask generic peripheral layer what to do now
230 		res = periph_check_error(device, request);
231 
232 		// TODO: bytes might have been transferred even in the error case!
233 		switch (res.action) {
234 			case err_act_ok:
235 				*_bytesTransferred = numBytes - request->data_resid;
236 				break;
237 
238 			case err_act_start:
239 				res = periph_send_start_stop(device, request, 1,
240 					device->removable);
241 				if (res.action == err_act_ok)
242 					res.action = err_act_retry;
243 				break;
244 
245 			case err_act_invalid_req:
246 				// if this was a 10 byte command, the device probably doesn't
247 				// support them, so disable them and retry
248 				if (isReadWrite10) {
249 					atomic_and(&device->rw10_enabled, 0);
250 					res.action = err_act_retry;
251 				} else
252 					res.action = err_act_fail;
253 				break;
254 		}
255 	} while ((res.action == err_act_retry && retries++ < 3)
256 		|| (res.action == err_act_many_retries && retries++ < 30));
257 
258 	// peripheral layer only created "read" error, so we have to
259 	// map them to "write" errors if this was a write request
260 	if (res.error_code == B_DEV_READ_ERROR && isWrite)
261 		return B_DEV_WRITE_ERROR;
262 
263 	return res.error_code;
264 }
265 
266 
267 // #pragma mark - public functions
268 
269 
270 status_t
271 periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer,
272 	size_t length)
273 {
274 	switch (op) {
275 		case B_GET_MEDIA_STATUS:
276 		{
277 			status_t status = B_OK;
278 
279 			if (handle->device->removable)
280 				status = periph_get_media_status(handle);
281 
282 			SHOW_FLOW(2, "%s", strerror(status));
283 
284 			*(status_t *)buffer = status;
285 			return B_OK;
286 		}
287 
288 		case B_GET_DEVICE_NAME:
289 		{
290 			// TODO: this should be written as an attribute to the node
291 			// Try driver further up first
292 			if (handle->device->scsi->ioctl != NULL) {
293 				status_t status = handle->device->scsi->ioctl(
294 					handle->device->scsi_device, op, buffer, length);
295 				if (status == B_OK)
296 					return B_OK;
297 			}
298 
299 			// If that fails, get SCSI vendor/product
300 			const char* vendor;
301 			if (gDeviceManager->get_attr_string(handle->device->node,
302 					SCSI_DEVICE_VENDOR_ITEM, &vendor, true) == B_OK) {
303 				char name[B_FILE_NAME_LENGTH];
304 				strlcpy(name, vendor, sizeof(name));
305 
306 				const char* product;
307 				if (gDeviceManager->get_attr_string(handle->device->node,
308 						SCSI_DEVICE_PRODUCT_ITEM, &product, true) == B_OK) {
309 					strlcat(name, " ", sizeof(name));
310 					strlcat(name, product, sizeof(name));
311 				}
312 
313 				return user_strlcpy((char*)buffer, name, length) >= 0
314 					? B_OK : B_BAD_ADDRESS;
315 			}
316 			return B_ERROR;
317 		}
318 
319 		case B_SCSI_INQUIRY:
320 			return inquiry(handle->device, (scsi_inquiry *)buffer);
321 
322 		case B_SCSI_PREVENT_ALLOW:
323 			return prevent_allow(handle->device, *(bool *)buffer);
324 
325 		case B_RAW_DEVICE_COMMAND:
326 			return raw_command(handle->device, (raw_device_command*)buffer);
327 
328 		default:
329 			if (handle->device->scsi->ioctl != NULL) {
330 				return handle->device->scsi->ioctl(handle->device->scsi_device,
331 					op, buffer, length);
332 			}
333 
334 			SHOW_ERROR(4, "Unknown ioctl: %x", op);
335 			return B_DEV_INVALID_IOCTL;
336 	}
337 }
338 
339 
340 /*!	Kernel daemon - once in a minute, it sets a flag so that the next command
341 	is executed ordered; this way, we avoid starvation of SCSI commands inside
342 	the SCSI queuing system - the ordered command waits for all previous
343 	commands and thus no command can starve longer then a minute
344 */
345 void
346 periph_sync_queue_daemon(void *arg, int iteration)
347 {
348 	scsi_periph_device_info *device = (scsi_periph_device_info *)arg;
349 
350 	SHOW_FLOW0(3, "Setting ordered flag for next R/W access");
351 	atomic_or(&device->next_tag_action, SCSI_ORDERED_QTAG);
352 }
353 
354 
355 status_t
356 periph_read_write(scsi_periph_device_info *device, scsi_ccb *request,
357 	uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount,
358 	bool isWrite, size_t* _bytesTransferred)
359 {
360 	return read_write(device, request, NULL, offset, numBlocks, vecs, vecCount,
361 		isWrite, _bytesTransferred);
362 }
363 
364 
365 status_t
366 periph_io(scsi_periph_device_info *device, io_operation *operation,
367 	size_t* _bytesTransferred)
368 {
369 	const uint32 blockSize = device->block_size;
370 
371 	// don't test rw10_enabled restrictions - this flag may get changed
372 	scsi_ccb *request = device->scsi->alloc_ccb(device->scsi_device);
373 	if (request == NULL)
374 		return B_NO_MEMORY;
375 
376 	status_t status = read_write(device, request, operation,
377 		operation->Offset() / blockSize, operation->Length() / blockSize,
378 		(physical_entry *)operation->Vecs(), operation->VecCount(),
379 		operation->IsWrite(), _bytesTransferred);
380 
381 	device->scsi->free_ccb(request);
382 	return status;
383 }
384 
385