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