* Rework ATAPI handling, removing all PIO interrupts. As Marcus pointed out the

complication is not really worth it and it would in any case be unreliable
  in the shared interrupt case (even though you could always check the poll
  condition in the interrupt handler).
* Probably broke ATAPI with those changes though.
* Change blocksLeft to bytesLeft to allow using it for arbitrary ATAPI requests.
* Check some more error conditions and actually let those states get through
  to the SCSI layer.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30109 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2009-04-10 22:29:59 +00:00
parent 8bbb189e27
commit 33d80ba202
5 changed files with 112 additions and 97 deletions

View File

@ -393,8 +393,16 @@ ATAChannel::Wait(uint8 setBits, uint8 clearedBits, uint32 flags,
while (true) {
uint8 status = AltStatus();
if ((flags & ATA_CHECK_ERROR_BIT) != 0
&& (status & ATA_STATUS_ERROR) != 0)
&& (status & ATA_STATUS_ERROR) != 0) {
TRACE("error bit set while waiting\n");
return B_ERROR;
}
if ((flags & ATA_CHECK_DISK_FAILURE) != 0
&& (status & ATA_STATUS_DISK_FAILURE) != 0) {
TRACE("disk failure bit set while waiting\n");
return B_ERROR;
}
if ((status & clearedBits) == 0) {
if ((flags & ATA_WAIT_ANY_BIT) != 0 && (status & setBits) != 0)
@ -426,8 +434,7 @@ status_t
ATAChannel::WaitDataRequest(bool high)
{
return Wait(high ? ATA_STATUS_DATA_REQUEST : 0,
high ? 0 : ATA_STATUS_DATA_REQUEST,
ATA_CHECK_ERROR_BIT, (high ? 10 : 1) * 1000 * 1000);
high ? 0 : ATA_STATUS_DATA_REQUEST, 0, (high ? 10 : 1) * 1000 * 1000);
}
@ -514,7 +521,7 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
{
if (flags & ATA_WAIT_FINISH) {
// wait for the device to finish current command (device no longer busy)
status_t result = Wait(0, ATA_STATUS_BUSY, 0, request->Timeout());
status_t result = Wait(0, ATA_STATUS_BUSY, flags, request->Timeout());
if (result != B_OK) {
TRACE_ERROR("timeout waiting for request finish\n");
request->SetStatus(SCSI_CMD_TIMEOUT);
@ -545,12 +552,19 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
return B_ERROR;
}
if ((taskFile->read.status & ATA_STATUS_ERROR) == 0)
uint8 checkFlags = ATA_STATUS_ERROR;
if (flags & ATA_CHECK_DISK_FAILURE)
checkFlags |= ATA_STATUS_DISK_FAILURE;
if ((taskFile->read.status & checkFlags) == 0)
return B_OK;
request->SetStatus(SCSI_SEQUENCE_FAIL);
TRACE_ERROR("command failed, error bit is set: 0x%02x\n",
taskFile->read.error);
if ((taskFile->read.error & ATA_ERROR_MEDIUM_CHANGED)
!= ATA_ERROR_MEDIUM_CHANGED) {
TRACE_ERROR("command failed, error bit is set: 0x%02x\n",
taskFile->read.error);
}
uint8 error = taskFile->read.error & errorMask;
if (error & ATA_ERROR_INTERFACE_CRC) {
TRACE_ERROR("interface crc error\n");
@ -558,7 +572,7 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
return B_ERROR;
}
if (flags & ATA_IS_WRITE) {
if (request->IsWrite()) {
if (error & ATA_ERROR_WRITE_PROTECTED) {
request->SetSense(SCSIS_KEY_DATA_PROTECT, SCSIS_ASC_WRITE_PROTECTED);
return B_ERROR;
@ -630,32 +644,34 @@ ATAChannel::ExecutePIOTransfer(ATARequest *request)
{
bigtime_t timeout = request->Timeout();
status_t result = B_OK;
uint32 *blocksLeft = request->BlocksLeft();
while (*blocksLeft > 0) {
if (Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, 0, timeout) != B_OK) {
size_t *bytesLeft = request->BytesLeft();
while (*bytesLeft > 0) {
if (Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, ATA_CHECK_ERROR_BIT
| ATA_CHECK_DISK_FAILURE, timeout) != B_OK) {
TRACE_ERROR("timeout waiting for device to request data\n");
result = B_TIMED_OUT;
break;
}
size_t currentLength = MIN(*bytesLeft, ATA_BLOCK_SIZE);
if (request->IsWrite()) {
result = _WritePIOBlock(request, 512);
result = _WritePIOBlock(request, currentLength);
if (result != B_OK) {
TRACE_ERROR("failed to write pio block\n");
break;
}
} else {
result = _ReadPIOBlock(request, 512);
result = _ReadPIOBlock(request, currentLength);
if (result != B_OK) {
TRACE_ERROR("failed to read pio block\n");
break;
}
}
(*blocksLeft)--;
*bytesLeft -= currentLength;
// wait 1 pio cycle
if (*blocksLeft > 0)
if (*bytesLeft > 0)
AltStatus();
}

View File

@ -169,8 +169,7 @@ ATADevice::ReadCapacity(ATARequest *request)
}
scsi_res_read_capacity data;
data.block_size = B_HOST_TO_BENDIAN_INT32(512);
// TODO: 512 bytes fixed block size?
data.block_size = B_HOST_TO_BENDIAN_INT32(ATA_BLOCK_SIZE);
uint32 lastBlock = fTotalSectors - 1;
data.lba = B_HOST_TO_BENDIAN_INT32(lastBlock);
@ -462,7 +461,7 @@ status_t
ATADevice::Identify()
{
snprintf(fDebugContext, sizeof(fDebugContext), "%s %lu-%u",
IsATAPI() ? "pi " : "", fChannel->ChannelID(), fIndex);
IsATAPI() ? "pi" : "", fChannel->ChannelID(), fIndex);
ATARequest request(false);
request.SetDevice(this);
@ -485,8 +484,9 @@ ATADevice::Identify()
return B_TIMED_OUT;
}
if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, 0,
IsATAPI() ? 20 * 1000 * 1000 : 500 * 1000) != B_OK) {
if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
ATA_CHECK_ERROR_BIT | ATA_CHECK_DISK_FAILURE, IsATAPI()
? 20 * 1000 * 1000 : 500 * 1000) != B_OK) {
TRACE_ERROR("timeout waiting for identify request\n");
return B_TIMED_OUT;
}
@ -517,7 +517,7 @@ ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
if (!request->UseDMA())
request->PrepareSGInfo();
request->SetBlocksLeft(sectorCount);
request->SetBytesLeft(sectorCount * ATA_BLOCK_SIZE);
if (_FillTaskFile(request, address) != B_OK) {
TRACE_ERROR("failed to setup transfer request\n");
if (request->UseDMA())
@ -571,7 +571,7 @@ ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
}
return fChannel->FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ALL);
}
@ -590,7 +590,7 @@ ATADevice::_FillTaskFile(ATARequest *request, uint64 address)
{ ATA_COMMAND_READ_DMA, ATA_COMMAND_WRITE_DMA }
};
uint32 sectorCount = *request->BlocksLeft();
uint32 sectorCount = *request->BytesLeft() / ATA_BLOCK_SIZE;
if (fUseLBA) {
if (fUse48Bits
&& (address + sectorCount > 0xfffffff || sectorCount > 0x100)) {

View File

@ -49,10 +49,8 @@ ATAPIDevice::SendPacket(ATARequest *request)
return B_ERROR;
}
if (fInterruptsForPacket)
fChannel->PrepareWaitingForInterrupt();
status_t result = fChannel->SendRequest(request, ATA_DMA_TRANSFER);
status_t result = fChannel->SendRequest(request, request->UseDMA()
? ATA_DMA_TRANSFER : 0);
if (result != B_OK) {
TRACE_ERROR("failed to send packet request\n");
if (request->UseDMA())
@ -61,15 +59,8 @@ ATAPIDevice::SendPacket(ATARequest *request)
}
// wait for device to get ready for packet transmission
bool timedOut = false;
if (fInterruptsForPacket)
timedOut = fChannel->WaitForInterrupt(request->Timeout()) != B_OK;
else {
timedOut = fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, 0,
100 * 1000) != B_OK;
}
if (timedOut) {
if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
ATA_CHECK_ERROR_BIT | ATA_CHECK_DISK_FAILURE, 100 * 1000) != B_OK) {
TRACE_ERROR("timeout waiting for data request\n");
if (request->UseDMA())
fChannel->FinishDMA();
@ -95,8 +86,6 @@ ATAPIDevice::SendPacket(ATARequest *request)
// some old drives need a delay before submitting the packet
spin(10);
fChannel->PrepareWaitingForInterrupt();
// write packet
if (fChannel->WritePIO(fPacket, sizeof(fPacket)) != B_OK) {
TRACE_ERROR("failed to write packet\n");
@ -108,65 +97,73 @@ ATAPIDevice::SendPacket(ATARequest *request)
}
if (request->UseDMA()) {
fChannel->PrepareWaitingForInterrupt();
fChannel->StartDMA();
result = fChannel->WaitForInterrupt(request->Timeout());
status_t dmaResult = fChannel->FinishDMA();
if (result == B_OK && dmaResult == B_OK) {
fDMAFailures = 0;
request->CCB()->data_resid = 0;
} else {
if (dmaResult != B_OK) {
request->SetSense(SCSIS_KEY_HARDWARE_ERROR,
SCSIS_ASC_LUN_COM_FAILURE);
fDMAFailures++;
if (fDMAFailures >= ATA_MAX_DMA_FAILURES) {
TRACE_ALWAYS("disabling DMA after %u failures\n",
fDMAFailures);
fUseDMA = false;
}
} else {
// timeout
request->SetStatus(SCSI_CMD_TIMEOUT);
}
}
} else {
result = fChannel->WaitForInterrupt(request->Timeout());
if (result != B_OK) {
TRACE_ERROR("timeout waiting for device to request data\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
request->SetStatus(SCSI_CMD_TIMEOUT);
return B_TIMED_OUT;
}
while (true) {
uint8 altStatus = fChannel->AltStatus();
if ((altStatus & ATA_STATUS_DATA_REQUEST) == 0)
break;
fRegisterMask = ATA_MASK_ERROR | ATA_MASK_IREASON;
fChannel->ReadRegs(this);
if (fTaskFile.packet_res.cmd_or_data) {
TRACE_ERROR("device expecting command instead of data\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
}
fRegisterMask = ATA_MASK_BYTE_COUNT;
fChannel->ReadRegs(this);
size_t length = fTaskFile.packet_res.byte_count_0_7
| ((size_t)fTaskFile.packet_res.byte_count_8_15 << 8);
TRACE("about to transfer %lu bytes\n", length);
request->SetBlocksLeft((length + 511) / 512);
if (fChannel->ExecutePIOTransfer(request) != B_OK) {
TRACE_ERROR("failed to transfer data\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
}
result = fChannel->FinishRequest(request, ATA_CHECK_DISK_FAILURE,
ATA_ERROR_ALL);
if (result != B_OK) {
TRACE_ERROR("device indicates transfer error after dma\n");
return result;
}
// for ATAPI it's ok for the device to send too much
if (dmaResult == B_OK || dmaResult == B_DEV_DATA_OVERRUN) {
fDMAFailures = 0;
request->CCB()->data_resid = 0;
return B_OK;
}
TRACE_ERROR("dma transfer failed\n");
request->SetSense(SCSIS_KEY_HARDWARE_ERROR,
SCSIS_ASC_LUN_COM_FAILURE);
fDMAFailures++;
if (fDMAFailures >= ATA_MAX_DMA_FAILURES) {
TRACE_ALWAYS("disabling DMA after %u failures\n", fDMAFailures);
fUseDMA = false;
}
return B_ERROR;
}
return fChannel->FinishRequest(request, ATA_WAIT_FINISH, ATA_ERROR_ABORTED);
// PIO data transfer
if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY,
ATA_CHECK_ERROR_BIT | ATA_CHECK_DISK_FAILURE,
request->Timeout()) != B_OK) {
TRACE_ERROR("timeout waiting for device to request data\n");
request->SetStatus(SCSI_CMD_TIMEOUT);
return B_TIMED_OUT;
}
fRegisterMask = ATA_MASK_IREASON | ATA_MASK_BYTE_COUNT;
fChannel->ReadRegs(this);
if (fTaskFile.packet_res.cmd_or_data) {
TRACE_ERROR("device expecting command instead of data\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
}
size_t length = fTaskFile.packet_res.byte_count_0_7
| ((size_t)fTaskFile.packet_res.byte_count_8_15 << 8);
TRACE("about to transfer %lu bytes\n", length);
request->SetBytesLeft(length);
fChannel->ExecutePIOTransfer(request);
result = fChannel->FinishRequest(request, ATA_WAIT_FINISH
| ATA_CHECK_DISK_FAILURE, ATA_ERROR_ALL);
if (result != B_OK)
TRACE_ERROR("device indicates transfer error after pio\n");
return result;
}
@ -199,7 +196,6 @@ ATAPIDevice::Configure()
return B_ERROR;
fTaskFile.packet.lun = 0;
fInterruptsForPacket = fInfoBlock._0.atapi.drq_speed == 1;
status_t result = ConfigureDMA();
if (result != B_OK)

View File

@ -25,6 +25,7 @@
#include "ATATracing.h"
#include "ata_device_infoblock.h"
#define ATA_BLOCK_SIZE 512 /* TODO: retrieve */
#define ATA_MAX_DMA_FAILURES 3
#define ATA_STANDARD_TIMEOUT 10 * 1000 * 1000
#define ATA_RELEASE_TIMEOUT 10 * 1000 * 1000
@ -39,7 +40,8 @@ enum {
ATA_DMA_TRANSFER = 0x04,
ATA_CHECK_ERROR_BIT = 0x08,
ATA_WAIT_FINISH = 0x10,
ATA_WAIT_ANY_BIT = 0x20
ATA_WAIT_ANY_BIT = 0x20,
ATA_CHECK_DISK_FAILURE = 0x40
};
@ -103,6 +105,7 @@ public:
status_t ReadRegs(ATADevice *device);
uint8 AltStatus();
bool IsDMAInterruptSet();
status_t ReadPIO(uint8 *buffer, size_t length);
status_t WritePIO(uint8 *buffer, size_t length);
@ -232,7 +235,6 @@ virtual status_t Configure();
private:
status_t _FillTaskFilePacket(ATARequest *request);
bool fInterruptsForPacket;
uint8 fPacket[12];
};
@ -263,8 +265,8 @@ public:
void SetUseDMA(bool useDMA);
bool UseDMA() { return fUseDMA; };
void SetBlocksLeft(uint32 blocksLeft);
uint32 * BlocksLeft() { return &fBlocksLeft; };
void SetBytesLeft(uint32 bytesLeft);
size_t * BytesLeft() { return &fBytesLeft; };
status_t Finish(bool resubmit);
@ -302,7 +304,7 @@ private:
ATADevice * fDevice;
bigtime_t fTimeout;
uint32 fBlocksLeft;
size_t fBytesLeft;
bool fIsWrite;
bool fUseDMA;
scsi_ccb * fCCB;

View File

@ -9,7 +9,7 @@ ATARequest::ATARequest(bool hasLock)
: fHasLock(hasLock),
fDevice(NULL),
fTimeout(0),
fBlocksLeft(0),
fBytesLeft(0),
fIsWrite(false),
fUseDMA(false),
fCCB(NULL)
@ -80,9 +80,9 @@ ATARequest::SetUseDMA(bool useDMA)
void
ATARequest::SetBlocksLeft(uint32 blocksLeft)
ATARequest::SetBytesLeft(uint32 bytesLeft)
{
fBlocksLeft = blocksLeft;
fBytesLeft = bytesLeft;
}
@ -94,6 +94,7 @@ ATARequest::Start(scsi_ccb *ccb)
fCCB = ccb;
fStatus = SCSI_REQ_CMP;
fIsWrite = false;
return B_OK;
}