xref: /haiku/src/add-ons/kernel/generic/scsi_periph/io.cpp (revision bddcee2a27042b4d8d6b0142b466f30abc886648)
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 #include <kernel.h>
19 #include <syscall_restart.h>
20 
21 
22 static status_t
23 inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry)
24 {
25 	const scsi_res_inquiry *device_inquiry = NULL;
26 	size_t inquiryLength;
27 
28 	if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
29 			(const void **)&device_inquiry, &inquiryLength, true) != B_OK)
30 		return B_ERROR;
31 
32 	if (IS_USER_ADDRESS(inquiry)) {
33 		if (user_memcpy(&inquiry, device_inquiry,
34 			min_c(inquiryLength, sizeof(scsi_inquiry))) != B_OK) {
35 			return B_BAD_ADDRESS;
36 		}
37 	} else if (is_called_via_syscall()) {
38 		return B_BAD_ADDRESS;
39 	} else {
40 		memcpy(&inquiry, device_inquiry,
41 			min_c(inquiryLength, sizeof(scsi_inquiry)));
42 	}
43 	return B_OK;
44 }
45 
46 
47 static status_t
48 vpd_page_inquiry(scsi_periph_device_info *device, uint8 page, void* data,
49 	uint16 length)
50 {
51 	SHOW_FLOW0(0, "");
52 
53 	scsi_ccb* ccb = device->scsi->alloc_ccb(device->scsi_device);
54 	if (ccb == NULL)
55 		return B_NO_MEMORY;
56 
57 	scsi_cmd_inquiry *cmd = (scsi_cmd_inquiry *)ccb->cdb;
58 	memset(cmd, 0, sizeof(scsi_cmd_inquiry));
59 	cmd->opcode = SCSI_OP_INQUIRY;
60 	cmd->lun = ccb->target_lun;
61 	cmd->evpd = 1;
62 	cmd->page_code = page;
63 	cmd->allocation_length = length;
64 
65 	ccb->flags = SCSI_DIR_IN;
66 	ccb->cdb_length = sizeof(scsi_cmd_inquiry);
67 
68 	ccb->sort = -1;
69 	ccb->timeout = device->std_timeout;
70 
71 	ccb->data = (uint8*)data;
72 	ccb->sg_list = NULL;
73 	ccb->data_length = length;
74 
75 	status_t status = periph_safe_exec(device, ccb);
76 
77 	device->scsi->free_ccb(ccb);
78 
79 	return status;
80 }
81 
82 
83 status_t
84 vpd_page_get(scsi_periph_device_info *device, uint8 page, void* data,
85 	uint16 length)
86 {
87 	SHOW_FLOW0(0, "");
88 
89 	status_t status = vpd_page_inquiry(device, 0, data, length);
90 	if (status != B_OK)
91 		return status; // or B_BAD_VALUE
92 
93 	if (page == 0)
94 		return B_OK;
95 
96 	scsi_page_list *list_data = (scsi_page_list*)data;
97 	int page_length = min_c(list_data->page_length, length -
98 		offsetof(scsi_page_list, pages));
99 	for (int i = 0; i < page_length; i++) {
100 		if (list_data->pages[i] == page)
101 			return vpd_page_inquiry(device, page, data, length);
102 	}
103 
104 	// TODO buffer might be not big enough
105 
106 	return B_BAD_VALUE;
107 }
108 
109 
110 
111 static status_t
112 prevent_allow(scsi_periph_device_info *device, bool prevent)
113 {
114 	scsi_cmd_prevent_allow cmd;
115 
116 	SHOW_FLOW0(0, "");
117 
118 	memset(&cmd, 0, sizeof(cmd));
119 	cmd.opcode = SCSI_OP_PREVENT_ALLOW;
120 	cmd.prevent = prevent;
121 
122 	return periph_simple_exec(device, (uint8 *)&cmd, sizeof(cmd), NULL, 0,
123 		SCSI_DIR_NONE);
124 }
125 
126 
127 /*! Keep this in sync with scsi_raw driver!!! */
128 static status_t
129 raw_command(scsi_periph_device_info *device, raw_device_command *cmd)
130 {
131 	scsi_ccb *request;
132 
133 	SHOW_FLOW0(0, "");
134 
135 	request = device->scsi->alloc_ccb(device->scsi_device);
136 	if (request == NULL)
137 		return B_NO_MEMORY;
138 
139 	request->flags = 0;
140 
141 	if (cmd->flags & B_RAW_DEVICE_DATA_IN)
142 		request->flags |= SCSI_DIR_IN;
143 	else if (cmd->data_length)
144 		request->flags |= SCSI_DIR_OUT;
145 	else
146 		request->flags |= SCSI_DIR_NONE;
147 
148 	request->data = (uint8*)cmd->data;
149 	request->sg_list = NULL;
150 	request->data_length = cmd->data_length;
151 	request->sort = -1;
152 	request->timeout = cmd->timeout;
153 
154 	memcpy(request->cdb, cmd->command, SCSI_MAX_CDB_SIZE);
155 	request->cdb_length = cmd->command_length;
156 
157 	device->scsi->sync_io(request);
158 
159 	// TBD: should we call standard error handler here, or may the
160 	// actions done there (like starting the unit) confuse the application?
161 
162 	cmd->cam_status = request->subsys_status;
163 	cmd->scsi_status = request->device_status;
164 
165 	if ((request->subsys_status & SCSI_AUTOSNS_VALID) != 0 && cmd->sense_data) {
166 		memcpy(cmd->sense_data, request->sense, min_c(cmd->sense_data_length,
167 			(size_t)SCSI_MAX_SENSE_SIZE - request->sense_resid));
168 	}
169 
170 	if ((cmd->flags & B_RAW_DEVICE_REPORT_RESIDUAL) != 0) {
171 		// this is a bit strange, see Be's sample code where I pinched this from;
172 		// normally, residual means "number of unused bytes left"
173 		// but here, we have to return "number of used bytes", which is the opposite
174 		cmd->data_length = cmd->data_length - request->data_resid;
175 		cmd->sense_data_length = SCSI_MAX_SENSE_SIZE - request->sense_resid;
176 	}
177 
178 	device->scsi->free_ccb(request);
179 
180 	return B_OK;
181 }
182 
183 
184 /*! Universal read/write function */
185 static status_t
186 read_write(scsi_periph_device_info *device, scsi_ccb *request,
187 	io_operation *operation, uint64 offset, size_t originalNumBlocks,
188 	physical_entry* vecs, size_t vecCount, bool isWrite,
189 	size_t* _bytesTransferred)
190 {
191 	uint32 blockSize = device->block_size;
192 	size_t numBlocks = originalNumBlocks;
193 	uint32 pos = offset;
194 	err_res res;
195 	int retries = 0;
196 
197 	do {
198 		size_t numBytes;
199 		bool isReadWrite10 = false;
200 
201 		request->flags = isWrite ? SCSI_DIR_OUT : SCSI_DIR_IN;
202 
203 		// io_operations are generated by a DMAResource and thus contain DMA
204 		// safe physical vectors
205 		if (operation != NULL)
206 			request->flags |= SCSI_DMA_SAFE;
207 
208 		// make sure we avoid 10 byte commands if they aren't supported
209 		if (!device->rw10_enabled || device->preferred_ccb_size == 6) {
210 			// restricting transfer is OK - the block manager will
211 			// take care of transferring the rest
212 			if (numBlocks > 0x100)
213 				numBlocks = 0x100;
214 
215 			// no way to break the 21 bit address limit
216 			if (offset > 0x200000)
217 				return B_BAD_VALUE;
218 
219 			// don't allow transfer cross the 24 bit address limit
220 			// (I'm not sure whether this is allowed, but this way we
221 			// are sure to not ask for trouble)
222 			if (offset < 0x100000)
223 				numBlocks = min_c(numBlocks, 0x100000 - pos);
224 		}
225 
226 		numBytes = numBlocks * blockSize;
227 		if (numBlocks != originalNumBlocks)
228 			panic("I/O operation would need to be cut.");
229 
230 		request->data = NULL;
231 		request->sg_list = vecs;
232 		request->data_length = numBytes;
233 		request->sg_count = vecCount;
234 		request->io_operation = operation;
235 		request->sort = pos;
236 		request->timeout = device->std_timeout;
237 		// see whether daemon instructed us to post an ordered command;
238 		// reset flag after read
239 		SHOW_FLOW(3, "flag=%x, next_tag=%x, ordered: %s",
240 			(int)request->flags, (int)device->next_tag_action,
241 			(request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no");
242 
243 		// use shortest commands whenever possible
244 		if (offset + numBlocks < 0x200000LL && numBlocks <= 0x100) {
245 			scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
246 
247 			isReadWrite10 = false;
248 
249 			memset(cmd, 0, sizeof(*cmd));
250 			cmd->opcode = isWrite ? SCSI_OP_WRITE_6 : SCSI_OP_READ_6;
251 			cmd->high_lba = (pos >> 16) & 0x1f;
252 			cmd->mid_lba = (pos >> 8) & 0xff;
253 			cmd->low_lba = pos & 0xff;
254 			cmd->length = numBlocks;
255 
256 			request->cdb_length = sizeof(*cmd);
257 		} else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000) {
258 			scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
259 
260 			isReadWrite10 = true;
261 
262 			memset(cmd, 0, sizeof(*cmd));
263 			cmd->opcode = isWrite ? SCSI_OP_WRITE_10 : SCSI_OP_READ_10;
264 			cmd->relative_address = 0;
265 			cmd->force_unit_access = 0;
266 			cmd->disable_page_out = 0;
267 			cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
268 			cmd->length = B_HOST_TO_BENDIAN_INT16(numBlocks);
269 
270 			request->cdb_length = sizeof(*cmd);
271 		} else if (offset + numBlocks < 0x100000000LL && numBlocks <= 0x10000000) {
272 			scsi_cmd_rw_12 *cmd = (scsi_cmd_rw_12 *)request->cdb;
273 
274 			memset(cmd, 0, sizeof(*cmd));
275 			cmd->opcode = isWrite ? SCSI_OP_WRITE_12 : SCSI_OP_READ_12;
276 			cmd->relative_address = 0;
277 			cmd->force_unit_access = 0;
278 			cmd->disable_page_out = 0;
279 			cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
280 			cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks);
281 
282 			request->cdb_length = sizeof(*cmd);
283 		} else {
284 			scsi_cmd_rw_16 *cmd = (scsi_cmd_rw_16 *)request->cdb;
285 
286 			memset(cmd, 0, sizeof(*cmd));
287 			cmd->opcode = isWrite ? SCSI_OP_WRITE_16 : SCSI_OP_READ_16;
288 			cmd->force_unit_access_non_volatile = 0;
289 			cmd->force_unit_access = 0;
290 			cmd->disable_page_out = 0;
291 			cmd->lba = B_HOST_TO_BENDIAN_INT64(offset);
292 			cmd->length = B_HOST_TO_BENDIAN_INT32(numBlocks);
293 
294 			request->cdb_length = sizeof(*cmd);
295 		}
296 
297 		// TODO: last chance to detect errors that occured during concurrent accesses
298 		//status_t status = handle->pending_error;
299 		//if (status != B_OK)
300 		//	return status;
301 
302 		device->scsi->async_io(request);
303 
304 		acquire_sem(request->completion_sem);
305 
306 		// ask generic peripheral layer what to do now
307 		res = periph_check_error(device, request);
308 
309 		// TODO: bytes might have been transferred even in the error case!
310 		switch (res.action) {
311 			case err_act_ok:
312 				*_bytesTransferred = numBytes - request->data_resid;
313 				break;
314 
315 			case err_act_start:
316 				res = periph_send_start_stop(device, request, 1,
317 					device->removable);
318 				if (res.action == err_act_ok)
319 					res.action = err_act_retry;
320 				break;
321 
322 			case err_act_invalid_req:
323 				// if this was a 10 byte command, the device probably doesn't
324 				// support them, so disable them and retry
325 				if (isReadWrite10) {
326 					atomic_and(&device->rw10_enabled, 0);
327 					res.action = err_act_retry;
328 				} else
329 					res.action = err_act_fail;
330 				break;
331 		}
332 	} while ((res.action == err_act_retry && retries++ < 3)
333 		|| (res.action == err_act_many_retries && retries++ < 30));
334 
335 	// peripheral layer only created "read" error, so we have to
336 	// map them to "write" errors if this was a write request
337 	if (res.error_code == B_DEV_READ_ERROR && isWrite)
338 		return B_DEV_WRITE_ERROR;
339 
340 	return res.error_code;
341 }
342 
343 
344 // #pragma mark - public functions
345 
346 
347 status_t
348 periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer,
349 	size_t length)
350 {
351 	switch (op) {
352 		case B_GET_MEDIA_STATUS:
353 		{
354 			status_t status = B_OK;
355 
356 			if (handle->device->removable)
357 				status = periph_get_media_status(handle);
358 
359 			SHOW_FLOW(2, "%s", strerror(status));
360 
361 			if (IS_USER_ADDRESS(buffer)) {
362 				if (user_memcpy(buffer, &status, sizeof(status_t)) != B_OK)
363 					return B_BAD_ADDRESS;
364 			} else if (is_called_via_syscall()) {
365 				return B_BAD_ADDRESS;
366 			} else
367 				*(status_t *)buffer = status;
368 			return B_OK;
369 		}
370 
371 		case B_GET_DEVICE_NAME:
372 		{
373 			// TODO: this should be written as an attribute to the node
374 			// Try driver further up first
375 			if (handle->device->scsi->ioctl != NULL) {
376 				status_t status = handle->device->scsi->ioctl(
377 					handle->device->scsi_device, op, buffer, length);
378 				if (status == B_OK)
379 					return B_OK;
380 			}
381 
382 			// If that fails, get SCSI vendor/product
383 			const char* vendor;
384 			if (gDeviceManager->get_attr_string(handle->device->node,
385 					SCSI_DEVICE_VENDOR_ITEM, &vendor, true) == B_OK) {
386 				char name[B_FILE_NAME_LENGTH];
387 				strlcpy(name, vendor, sizeof(name));
388 
389 				const char* product;
390 				if (gDeviceManager->get_attr_string(handle->device->node,
391 						SCSI_DEVICE_PRODUCT_ITEM, &product, true) == B_OK) {
392 					strlcat(name, " ", sizeof(name));
393 					strlcat(name, product, sizeof(name));
394 				}
395 
396 				return user_strlcpy((char*)buffer, name, length) >= 0
397 					? B_OK : B_BAD_ADDRESS;
398 			}
399 			return B_ERROR;
400 		}
401 
402 		case B_SCSI_INQUIRY:
403 			return inquiry(handle->device, (scsi_inquiry *)buffer);
404 
405 		case B_SCSI_PREVENT_ALLOW:
406 			bool result;
407 			if (IS_USER_ADDRESS(buffer)) {
408 				if (user_memcpy(&result, buffer, sizeof(result)) != B_OK)
409 					return B_BAD_ADDRESS;
410 			} else if (is_called_via_syscall()) {
411 				return B_BAD_ADDRESS;
412 			} else
413 				result = *(bool*)buffer;
414 
415 			return prevent_allow(handle->device, result);
416 
417 		case B_RAW_DEVICE_COMMAND:
418 			return raw_command(handle->device, (raw_device_command*)buffer);
419 
420 		default:
421 			if (handle->device->scsi->ioctl != NULL) {
422 				return handle->device->scsi->ioctl(handle->device->scsi_device,
423 					op, buffer, length);
424 			}
425 
426 			SHOW_ERROR(4, "Unknown ioctl: %x", op);
427 			return B_DEV_INVALID_IOCTL;
428 	}
429 }
430 
431 
432 /*!	Kernel daemon - once in a minute, it sets a flag so that the next command
433 	is executed ordered; this way, we avoid starvation of SCSI commands inside
434 	the SCSI queuing system - the ordered command waits for all previous
435 	commands and thus no command can starve longer then a minute
436 */
437 void
438 periph_sync_queue_daemon(void *arg, int iteration)
439 {
440 	scsi_periph_device_info *device = (scsi_periph_device_info *)arg;
441 
442 	SHOW_FLOW0(3, "Setting ordered flag for next R/W access");
443 	atomic_or(&device->next_tag_action, SCSI_ORDERED_QTAG);
444 }
445 
446 
447 status_t
448 periph_read_write(scsi_periph_device_info *device, scsi_ccb *request,
449 	uint64 offset, size_t numBlocks, physical_entry* vecs, size_t vecCount,
450 	bool isWrite, size_t* _bytesTransferred)
451 {
452 	return read_write(device, request, NULL, offset, numBlocks, vecs, vecCount,
453 		isWrite, _bytesTransferred);
454 }
455 
456 
457 status_t
458 periph_io(scsi_periph_device_info *device, io_operation *operation,
459 	size_t* _bytesTransferred)
460 {
461 	const uint32 blockSize = device->block_size;
462 	if (blockSize == 0)
463 		return B_BAD_VALUE;
464 
465 	// don't test rw10_enabled restrictions - this flag may get changed
466 	scsi_ccb *request = device->scsi->alloc_ccb(device->scsi_device);
467 	if (request == NULL)
468 		return B_NO_MEMORY;
469 
470 	status_t status = read_write(device, request, operation,
471 		operation->Offset() / blockSize, operation->Length() / blockSize,
472 		(physical_entry *)operation->Vecs(), operation->VecCount(),
473 		operation->IsWrite(), _bytesTransferred);
474 
475 	device->scsi->free_ccb(request);
476 	return status;
477 }
478 
479