1/*
2 * Copyright 2009, Michael Lotz, mmlr@mlotz.ch.
3 * Copyright 2008, Marcus Overhagen.
4 * Copyright 2004-2008, Axel D��rfler, axeld@pinc-software.de.
5 * Copyright 2002-2003, Thomas Kurschel.
6 *
7 * Distributed under the terms of the MIT License.
8 */
9
10#include "ATAPrivate.h"
11
12ATAPIDevice::ATAPIDevice(ATAChannel *channel, uint8 index)
13	:	ATADevice(channel, index)
14{
15}
16
17
18ATAPIDevice::~ATAPIDevice()
19{
20}
21
22
23status_t
24ATAPIDevice::SendPacket(ATARequest *request)
25{
26	TRACE_FUNCTION("%p\n", request);
27
28	// only READ/WRITE commands can use DMA
29	// (the device may support it always, but IDE controllers don't
30	// report how much data is transmitted, and this information is
31	// crucial for the SCSI protocol)
32	// special offer: let READ_CD commands use DMA too
33	uint8 command = fPacket[0];
34	request->SetUseDMA(UseDMA() && request->CCB()->sg_list != NULL
35		&& (command == SCSI_OP_READ_6 || command == SCSI_OP_WRITE_6
36		|| command == SCSI_OP_READ_10 || command == SCSI_OP_WRITE_10
37		|| command == SCSI_OP_READ_12 || command == SCSI_OP_WRITE_12
38		|| command == SCSI_OP_READ_CD)
39		&& fChannel->PrepareDMA(request) == B_OK);
40	TRACE("using dma: %s\n", request->UseDMA() ? "yes" : "no");
41
42	if (!request->UseDMA())
43		request->PrepareSGInfo();
44
45	if (_FillTaskFilePacket(request) != B_OK) {
46		TRACE_ERROR("failed to setup transfer request\n");
47		if (request->UseDMA())
48			fChannel->FinishDMA();
49		return B_ERROR;
50	}
51
52	status_t result = fChannel->SendRequest(request, 0);
53	if (result != B_OK) {
54		TRACE_ERROR("failed to send packet request\n");
55		if (request->UseDMA())
56			fChannel->FinishDMA();
57		return result;
58	}
59
60	// wait for device to get ready for packet transmission
61	if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
62		ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, 100 * 1000) != B_OK) {
63		TRACE_ERROR("timeout waiting for data request\n");
64		if (request->UseDMA())
65			fChannel->FinishDMA();
66
67		request->SetStatus(SCSI_SEQUENCE_FAIL);
68		return B_TIMED_OUT;
69	}
70
71	// make sure device really asks for command packet
72	fRegisterMask = ATA_MASK_INTERRUPT_REASON;
73	fChannel->ReadRegs(this);
74
75	if (!fTaskFile.packet_res.cmd_or_data
76		|| fTaskFile.packet_res.input_or_output) {
77		TRACE_ERROR("device doesn't ask for packet\n");
78		if (request->UseDMA())
79			fChannel->FinishDMA();
80
81		request->SetStatus(SCSI_SEQUENCE_FAIL);
82		return B_ERROR;
83	}
84
85	// some old drives need a delay before submitting the packet
86	spin(10);
87
88	// write packet
89	if (fChannel->WritePIO(fPacket, sizeof(fPacket)) != B_OK) {
90		TRACE_ERROR("failed to write packet\n");
91		if (request->UseDMA())
92			fChannel->FinishDMA();
93
94		request->SetStatus(SCSI_HBA_ERR);
95		return B_ERROR;
96	}
97
98	if (!request->HasData())
99		return _FinishRequest(request, ATA_WAIT_FINISH);
100
101	if (request->UseDMA()) {
102		fChannel->PrepareWaitingForInterrupt();
103		fChannel->StartDMA();
104
105		result = fChannel->WaitForInterrupt(request->Timeout());
106		status_t dmaResult = fChannel->FinishDMA();
107		if (result != B_OK) {
108			request->SetStatus(SCSI_CMD_TIMEOUT);
109			return B_TIMED_OUT;
110		}
111
112		result = _FinishRequest(request, ATA_WAIT_FINISH);
113		if (result != B_OK) {
114			TRACE_ERROR("device indicates transfer error after dma\n");
115			return result;
116		}
117
118		// for ATAPI it's ok for the device to send too much
119		if (dmaResult == B_OK || dmaResult == B_DEV_DATA_OVERRUN) {
120			fDMAFailures = 0;
121			request->CCB()->data_resid = 0;
122			return B_OK;
123		}
124
125		TRACE_ERROR("dma transfer failed\n");
126		request->SetSense(SCSIS_KEY_HARDWARE_ERROR,
127			SCSIS_ASC_LUN_COM_FAILURE);
128		fDMAFailures++;
129		if (fDMAFailures >= ATA_MAX_DMA_FAILURES) {
130			TRACE_ALWAYS("disabling DMA after %u failures\n", fDMAFailures);
131			fUseDMA = false;
132		}
133
134		return B_ERROR;
135	}
136
137	result = fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
138		ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, request->Timeout());
139	if (result != B_OK) {
140		if (result == B_TIMED_OUT) {
141			TRACE_ERROR("timeout waiting for device to request data\n");
142			request->SetStatus(SCSI_CMD_TIMEOUT);
143			return B_TIMED_OUT;
144		} else
145			return _FinishRequest(request, 0);
146	}
147
148	// PIO data transfer
149	while (true) {
150		fRegisterMask = ATA_MASK_INTERRUPT_REASON | ATA_MASK_BYTE_COUNT;
151		fChannel->ReadRegs(this);
152
153		if (fTaskFile.packet_res.cmd_or_data) {
154			TRACE_ERROR("device expecting command instead of data\n");
155			request->SetStatus(SCSI_SEQUENCE_FAIL);
156			return B_ERROR;
157		}
158
159		size_t length = fTaskFile.packet_res.byte_count_0_7
160			| ((size_t)fTaskFile.packet_res.byte_count_8_15 << 8);
161		TRACE("about to transfer %lu bytes\n", length);
162
163		request->SetBytesLeft(length);
164		fChannel->ExecutePIOTransfer(request);
165
166		result = fChannel->Wait(0, ATA_STATUS_BUSY, 0, request->Timeout());
167		if (result != B_OK) {
168			if (result == B_TIMED_OUT) {
169				TRACE_ERROR("timeout waiting for device to finish transfer\n");
170				request->SetStatus(SCSI_CMD_TIMEOUT);
171				return B_TIMED_OUT;
172			} else
173				return _FinishRequest(request, 0);
174		}
175
176		if ((fChannel->AltStatus() & ATA_STATUS_DATA_REQUEST) == 0) {
177			// transfer complete
178			TRACE("pio transfer complete\n");
179			break;
180		}
181	}
182
183	return _FinishRequest(request, ATA_WAIT_FINISH);
184}
185
186
187status_t
188ATAPIDevice::ExecuteIO(ATARequest *request)
189{
190	scsi_ccb *ccb = request->CCB();
191	if (ccb->target_lun != 0) {
192		TRACE_ERROR("invalid target lun %d\n", ccb->target_lun);
193		request->SetStatus(SCSI_SEL_TIMEOUT);
194		return B_BAD_INDEX;
195	}
196
197	// ATAPI command packets are 12 bytes long;
198	// if the command is shorter, remaining bytes must be padded with zeros
199	memset(fPacket, 0, sizeof(fPacket));
200	memcpy(fPacket, ccb->cdb, ccb->cdb_length);
201
202	request->SetDevice(this);
203	request->SetIsWrite((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT);
204	return SendPacket(request);
205}
206
207
208status_t
209ATAPIDevice::Configure()
210{
211	if (fInfoBlock.word_0.atapi.atapi_device != ATA_WORD_0_ATAPI_DEVICE) {
212		TRACE_ERROR("infoblock indicates non-atapi device\n");
213		return B_ERROR;
214	}
215
216	fTaskFile.packet.lun = 0;
217
218	status_t result = ConfigureDMA();
219	if (result != B_OK)
220		return result;
221
222	result = DisableCommandQueueing();
223	if (result != B_OK)
224		return result;
225
226	return B_OK;
227}
228
229
230status_t
231ATAPIDevice::_FillTaskFilePacket(ATARequest *request)
232{
233	scsi_ccb *ccb = request->CCB();
234	fRegisterMask = ATA_MASK_FEATURES | ATA_MASK_BYTE_COUNT;
235	fTaskFile.packet.dma = request->UseDMA() ? 1 : 0;
236	fTaskFile.packet.ovl = 0;
237	fTaskFile.packet.byte_count_0_7 = ccb->data_length & 0xff;
238	fTaskFile.packet.byte_count_8_15 = ccb->data_length >> 8;
239	fTaskFile.packet.command = ATA_COMMAND_PACKET;
240	return B_OK;
241}
242
243
244status_t
245ATAPIDevice::_FinishRequest(ATARequest *request, uint32 flags)
246{
247	if (fChannel->FinishRequest(request, flags
248		| ATA_CHECK_DEVICE_FAULT, 0) != B_OK) {
249		// when we get an error from a packet device, we instruct the
250		// scsi layer to do a request sense. but since we don't want to
251		// return an emulated sense coming from ata, we clear our sense
252		// key first so that the next request sense will go to the packet
253		// device directly (as a packet command).
254		request->ClearSense();
255		request->SetStatus(SCSI_REQ_CMP_ERR);
256		request->CCB()->device_status = SCSI_STATUS_CHECK_CONDITION;
257		return B_ERROR;
258	}
259
260	return B_OK;
261}
262