1/*
2 * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. 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//!	General SCSI emulation routines
9
10
11#include "ide_internal.h"
12#include "ide_sim.h"
13
14#include <vm/vm.h>
15
16#include <string.h>
17
18
19/*! Emulate REQUEST SENSE */
20void
21ide_request_sense(ide_device_info *device, ide_qrequest *qrequest)
22{
23	scsi_ccb *request = qrequest->request;
24	scsi_cmd_request_sense *cmd = (scsi_cmd_request_sense *)request->cdb;
25	scsi_sense sense;
26	uint32 transferSize;
27
28	// cannot use finish_checksense here, as data is not copied into autosense buffer
29	// but into normal data buffer, SCSI result is GOOD and CAM status is REQ_CMP
30
31	if (device->combined_sense)
32		create_sense(device, &sense);
33	else
34		memset(&sense, 0, sizeof(sense));
35
36	copy_sg_data(request, 0, cmd->allocation_length, &sense, sizeof(sense), false);
37
38	// reset sense information on read
39	device->combined_sense = 0;
40
41	transferSize = min_c(sizeof(sense), cmd->allocation_length);
42	transferSize = min_c(transferSize, request->data_length);
43
44	request->data_resid = request->data_length - transferSize;
45
46	// normally, all flags are set to "success", but for Request Sense
47	// this would have overwritten the sense we want to read
48	device->subsys_status = SCSI_REQ_CMP;
49	request->device_status = SCSI_STATUS_GOOD;
50}
51
52
53/*!	Copy data between request data and buffer
54	request			- request to copy data from/to
55	offset			- offset of data in request
56	allocation_length- limit of request's data buffer according to CDB
57	buffer			- data to copy data from/to
58	size			- number of bytes to copy
59	to_buffer		- true: copy from request to buffer
60					  false: copy from buffer to request
61	return: true, if data of request was large enough
62*/
63bool
64copy_sg_data(scsi_ccb *request, uint offset, uint allocationLength,
65	void *buffer, int size, bool toBuffer)
66{
67	const physical_entry *sgList = request->sg_list;
68	int sgCount = request->sg_count;
69	int requestSize;
70
71	SHOW_FLOW(3, "offset=%u, req_size_limit=%d, size=%d, sg_list=%p, sg_cnt=%d, %s buffer",
72		offset, allocationLength, size, sgList, sgCount, toBuffer ? "to" : "from");
73
74	// skip unused S/G entries
75	while (sgCount > 0 && offset >= sgList->size) {
76		offset -= sgList->size;
77		++sgList;
78		--sgCount;
79	}
80
81	if (sgCount == 0)
82		return 0;
83
84	// remaining bytes we are allowed to copy from/to request
85	requestSize = min_c(allocationLength, request->data_length) - offset;
86
87	// copy one S/G entry at a time
88	for (; size > 0 && requestSize > 0 && sgCount > 0; ++sgList, --sgCount) {
89		size_t bytes;
90
91		bytes = min_c(size, requestSize);
92		bytes = min_c(bytes, sgList->size);
93
94		SHOW_FLOW(4, "buffer=%p, virt_addr=%p, bytes=%d, to_buffer=%d",
95			buffer, (void *)(sgList->address + offset), (int)bytes, toBuffer);
96
97		if (toBuffer) {
98			vm_memcpy_from_physical(buffer, sgList->address + offset, bytes,
99				false);
100		} else {
101			vm_memcpy_to_physical(sgList->address + offset, buffer, bytes,
102				false);
103		}
104
105		buffer = (char *)buffer + bytes;
106		size -= bytes;
107		offset = 0;
108	}
109
110	return size == 0;
111}
112