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