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.
10 #include "ATAPrivate.h"
12 ATAPIDevice::ATAPIDevice(ATAChannel
*channel
, uint8 index
)
13 : ATADevice(channel
, index
)
18 ATAPIDevice::~ATAPIDevice()
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();
52 status_t result
= fChannel
->SendRequest(request
, 0);
54 TRACE_ERROR("failed to send packet request\n");
55 if (request
->UseDMA())
56 fChannel
->FinishDMA();
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
);
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
);
85 // some old drives need a delay before submitting the 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
);
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
);
112 result
= _FinishRequest(request
, ATA_WAIT_FINISH
);
113 if (result
!= B_OK
) {
114 TRACE_ERROR("device indicates transfer error after dma\n");
118 // for ATAPI it's ok for the device to send too much
119 if (dmaResult
== B_OK
|| dmaResult
== B_DEV_DATA_OVERRUN
) {
121 request
->CCB()->data_resid
= 0;
125 TRACE_ERROR("dma transfer failed\n");
126 request
->SetSense(SCSIS_KEY_HARDWARE_ERROR
,
127 SCSIS_ASC_LUN_COM_FAILURE
);
129 if (fDMAFailures
>= ATA_MAX_DMA_FAILURES
) {
130 TRACE_ALWAYS("disabling DMA after %u failures\n", fDMAFailures
);
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
);
145 return _FinishRequest(request
, 0);
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
);
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
);
173 return _FinishRequest(request
, 0);
176 if ((fChannel
->AltStatus() & ATA_STATUS_DATA_REQUEST
) == 0) {
178 TRACE("pio transfer complete\n");
183 return _FinishRequest(request
, ATA_WAIT_FINISH
);
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
);
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
);
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");
216 fTaskFile
.packet
.lun
= 0;
218 status_t result
= ConfigureDMA();
222 result
= DisableCommandQueueing();
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
;
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
;