xref: /haiku/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp (revision 2222d0559df303a9846a2fad53741f8b20b14d7c)
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 
12 ATAPIDevice::ATAPIDevice(ATAChannel *channel, uint8 index)
13 	:	ATADevice(channel, index)
14 {
15 }
16 
17 
18 ATAPIDevice::~ATAPIDevice()
19 {
20 }
21 
22 
23 status_t
24 ATAPIDevice::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()
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 
187 status_t
188 ATAPIDevice::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 
208 status_t
209 ATAPIDevice::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 
230 status_t
231 ATAPIDevice::_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 
244 status_t
245 ATAPIDevice::_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