From 33d80ba202e1cda4c2b97b9888b1cee94bcb74e1 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Fri, 10 Apr 2009 22:29:59 +0000 Subject: [PATCH] * 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 --- .../kernel/bus_managers/ata/ATAChannel.cpp | 48 ++++--- .../kernel/bus_managers/ata/ATADevice.cpp | 16 +-- .../kernel/bus_managers/ata/ATAPIDevice.cpp | 126 +++++++++--------- .../kernel/bus_managers/ata/ATAPrivate.h | 12 +- .../kernel/bus_managers/ata/ATARequest.cpp | 7 +- 5 files changed, 112 insertions(+), 97 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp index a2341a6206..28862db836 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp @@ -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(); } diff --git a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp index bac4258534..ac6047e83a 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp @@ -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)) { diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp index 2549e16439..4cb90acaa9 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp @@ -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) diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h index f5c8778c0d..88308f4978 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h +++ b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h @@ -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; diff --git a/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp b/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp index a6dd37f9ee..58497b3c88 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATARequest.cpp @@ -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; }