* 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:
parent
8bbb189e27
commit
33d80ba202
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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)) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user