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; }