1/*
2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "ATAPrivate.h"
7
8
9ATARequest::ATARequest(bool hasLock)
10	:
11	fHasLock(hasLock),
12	fDevice(NULL),
13	fTimeout(0),
14	fBytesLeft(0),
15	fIsWrite(false),
16	fUseDMA(false),
17	fCCB(NULL)
18{
19	if (hasLock)
20		mutex_init(&fLock, "ata request");
21
22	ClearSense();
23}
24
25
26ATARequest::~ATARequest()
27{
28	if (fHasLock)
29		mutex_destroy(&fLock);
30}
31
32
33void
34ATARequest::SetStatus(uint8 status)
35{
36	fStatus = status;
37}
38
39
40void
41ATARequest::SetSense(uint8 key, uint16 codeQualifier)
42{
43	fSenseKey = key;
44	fSenseCode = (uint8)(codeQualifier >> 8);
45	fSenseQualifier = (uint8)(codeQualifier & 0xff);
46}
47
48
49void
50ATARequest::ClearSense()
51{
52	fSenseKey = fSenseCode = fSenseQualifier = 0;
53}
54
55
56void
57ATARequest::SetDevice(ATADevice *device)
58{
59	fDevice = device;
60}
61
62
63void
64ATARequest::SetTimeout(bigtime_t timeout)
65{
66	fTimeout = timeout;
67}
68
69
70void
71ATARequest::SetIsWrite(bool isWrite)
72{
73	fIsWrite = isWrite;
74}
75
76
77void
78ATARequest::SetUseDMA(bool useDMA)
79{
80	fUseDMA = useDMA;
81}
82
83
84void
85ATARequest::SetBytesLeft(uint32 bytesLeft)
86{
87	fBytesLeft = bytesLeft;
88}
89
90
91status_t
92ATARequest::Start(scsi_ccb *ccb)
93{
94	if (mutex_trylock(&fLock) != B_OK)
95		return B_BUSY;
96
97	fCCB = ccb;
98	fStatus = SCSI_REQ_CMP;
99	fCCB->device_status = SCSI_STATUS_GOOD;
100	fIsWrite = false;
101	return B_OK;
102}
103
104
105status_t
106ATARequest::Finish(bool resubmit)
107{
108	// when the request completed and has set sense
109    // data, report this to the scsi stack by setting
110    // CHECK CONDITION status
111	if (fStatus == SCSI_REQ_CMP && fSenseKey != 0) {
112		TRACE("setting check condition\n");
113
114		fCCB->subsys_status = SCSI_REQ_CMP_ERR;
115		fCCB->device_status = SCSI_STATUS_CHECK_CONDITION;
116
117		// copy sense data if caller requested it
118		if ((fCCB->flags & SCSI_DIS_AUTOSENSE) == 0) {
119			// we cannot copy sense directly as sense buffer may be too small
120			scsi_sense sense;
121			_FillSense(&sense);
122
123			size_t senseLength = MIN(sizeof(fCCB->sense), sizeof(sense));
124			memcpy(fCCB->sense, &sense, senseLength);
125			fCCB->sense_resid = SCSI_MAX_SENSE_SIZE - senseLength;
126			fCCB->subsys_status |= SCSI_AUTOSNS_VALID;
127			ClearSense();
128		}
129	} else
130		fCCB->subsys_status = fStatus;
131
132	mutex_unlock(&fLock);
133
134	if (resubmit)
135		gSCSIModule->resubmit(fCCB);
136	else
137		gSCSIModule->finished(fCCB, 1);
138
139	return B_OK;
140}
141
142
143void
144ATARequest::RequestSense()
145{
146	// Copy sense data from last request into data buffer of current request.
147	// The sense data of last request is still present in the current request,
148	// as it isn't cleared on SCSI_OP_REQUEST_SENSE.
149	scsi_sense sense;
150	if (fSenseKey != 0)
151		_FillSense(&sense);
152	else
153		memset(&sense, 0, sizeof(sense));
154
155	scsi_cmd_request_sense *command = (scsi_cmd_request_sense *)fCCB->cdb;
156	copy_sg_data(fCCB, 0, command->allocation_length, &sense, sizeof(sense),
157		false);
158
159	fCCB->data_resid = fCCB->data_length - MIN(MIN(sizeof(sense),
160		command->allocation_length), fCCB->data_length);
161	ClearSense();
162}
163
164
165void
166ATARequest::PrepareSGInfo()
167{
168	fSGElementsLeft = fCCB->sg_count;
169	fCurrentSGElement = fCCB->sg_list;
170	fCurrentSGOffset = 0;
171	fHasOddByte = false;
172	fCCB->data_resid = fCCB->data_length;
173}
174
175
176void
177ATARequest::AdvanceSG(uint32 bytes)
178{
179	uint32 bytesLeft = fCurrentSGElement->size - fCurrentSGOffset;
180	if (bytesLeft <= bytes) {
181		fCurrentSGOffset = 0;
182		fCurrentSGElement++;
183		fSGElementsLeft--;
184	} else
185		fCurrentSGOffset += bytes;
186}
187
188
189void
190ATARequest::SetOddByte(uint8 byte)
191{
192	fOddByte = byte;
193	fHasOddByte = true;
194}
195
196
197bool
198ATARequest::GetOddByte(uint8 *byte)
199{
200	if (!fHasOddByte)
201		return false;
202
203	if (byte != NULL)
204		*byte = fOddByte;
205
206	fHasOddByte = false;
207	return true;
208}
209
210
211void
212ATARequest::_FillSense(scsi_sense *sense)
213{
214	memset(sense, 0, sizeof(*sense));
215	sense->error_code = SCSIS_CURR_ERROR;
216	sense->sense_key = fSenseKey;
217	sense->add_sense_length = sizeof(*sense) - 7;
218	sense->asc = fSenseCode;
219	sense->ascq = fSenseQualifier;
220	sense->sense_key_spec.raw.SKSV = 0;	// no additional info
221}
222