BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / bus_managers / ata / ATAPIDevice.cpp
blob84c8df5c57b1bc660eaf12882a33f394a71841fb
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.
7 * Distributed under the terms of the MIT License.
8 */
10 #include "ATAPrivate.h"
12 ATAPIDevice::ATAPIDevice(ATAChannel *channel, uint8 index)
13 : ATADevice(channel, index)
18 ATAPIDevice::~ATAPIDevice()
23 status_t
24 ATAPIDevice::SendPacket(ATARequest *request)
26 TRACE_FUNCTION("%p\n", request);
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");
42 if (!request->UseDMA())
43 request->PrepareSGInfo();
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;
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;
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();
67 request->SetStatus(SCSI_SEQUENCE_FAIL);
68 return B_TIMED_OUT;
71 // make sure device really asks for command packet
72 fRegisterMask = ATA_MASK_INTERRUPT_REASON;
73 fChannel->ReadRegs(this);
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();
81 request->SetStatus(SCSI_SEQUENCE_FAIL);
82 return B_ERROR;
85 // some old drives need a delay before submitting the packet
86 spin(10);
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();
94 request->SetStatus(SCSI_HBA_ERR);
95 return B_ERROR;
98 if (!request->HasData())
99 return _FinishRequest(request, ATA_WAIT_FINISH);
101 if (request->UseDMA()) {
102 fChannel->PrepareWaitingForInterrupt();
103 fChannel->StartDMA();
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;
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;
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;
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;
134 return B_ERROR;
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);
148 // PIO data transfer
149 while (true) {
150 fRegisterMask = ATA_MASK_INTERRUPT_REASON | ATA_MASK_BYTE_COUNT;
151 fChannel->ReadRegs(this);
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;
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);
163 request->SetBytesLeft(length);
164 fChannel->ExecutePIOTransfer(request);
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);
176 if ((fChannel->AltStatus() & ATA_STATUS_DATA_REQUEST) == 0) {
177 // transfer complete
178 TRACE("pio transfer complete\n");
179 break;
183 return _FinishRequest(request, ATA_WAIT_FINISH);
187 status_t
188 ATAPIDevice::ExecuteIO(ATARequest *request)
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;
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);
202 request->SetDevice(this);
203 request->SetIsWrite((ccb->flags & SCSI_DIR_MASK) == SCSI_DIR_OUT);
204 return SendPacket(request);
208 status_t
209 ATAPIDevice::Configure()
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;
216 fTaskFile.packet.lun = 0;
218 status_t result = ConfigureDMA();
219 if (result != B_OK)
220 return result;
222 result = DisableCommandQueueing();
223 if (result != B_OK)
224 return result;
226 return B_OK;
230 status_t
231 ATAPIDevice::_FillTaskFilePacket(ATARequest *request)
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;
244 status_t
245 ATAPIDevice::_FinishRequest(ATARequest *request, uint32 flags)
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;
260 return B_OK;