diff --git a/headers/private/drivers/ata_types.h b/headers/private/drivers/ata_types.h index bdb41b8d17..b855d4e6a9 100644 --- a/headers/private/drivers/ata_types.h +++ b/headers/private/drivers/ata_types.h @@ -215,7 +215,7 @@ enum { // packet and dma queued result ATA_MASK_ERROR = 0x01, - ATA_MASK_IREASON = 0x02, + ATA_MASK_INTERRUPT_REASON = 0x02, ATA_MASK_DEVICE_HEAD = 0x20, ATA_MASK_COMMAND = 0x40, @@ -243,7 +243,7 @@ enum { ATA_STATUS_DWF = 0x20, // reserved ATA_STATUS_DMA = 0x20, // reserved ATA_STATUS_DMA_READY = 0x20, // packet: DMA ready - ATA_STATUS_DISK_FAILURE = 0x20, // packet: disk failure + ATA_STATUS_DEVICE_FAULT = 0x20, // device fault ATA_STATUS_DEVICE_READY = 0x40, // device ready ATA_STATUS_BUSY = 0x80 // busy }; diff --git a/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp index 28862db836..bba1194f0f 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp @@ -116,41 +116,34 @@ ATAChannel::SetBus(scsi_bus bus) status_t ATAChannel::ScanBus() { - bool devicePresent[fDeviceCount]; + // check if there is anything at all + if (AltStatus() == 0xff) { + TRACE_ALWAYS("illegal status value, assuming no devices connected\n"); + return B_OK; + } + uint32 deviceSignature[fDeviceCount]; - status_t result = Reset(devicePresent, deviceSignature); + status_t result = Reset(deviceSignature); if (result != B_OK) { TRACE_ERROR("resetting the channel failed\n"); return result; } for (uint8 i = 0; i < fDeviceCount; i++) { - if (!devicePresent[i]) - continue; - ATADevice *device = NULL; if (deviceSignature[i] == ATA_SIGNATURE_ATAPI) device = new(std::nothrow) ATAPIDevice(this, i); else device = new(std::nothrow) ATADevice(this, i); - if (device == NULL) + if (device == NULL) { + TRACE_ERROR("out of memory allocating device\n"); return B_NO_MEMORY; + } TRACE("trying ATA%s device %u\n", device->IsATAPI() ? "PI" : "", i); - bool identified = device->Identify() == B_OK; - if (!identified && !device->IsATAPI()) { - // retry as atapi - delete device; - device = new(std::nothrow) ATAPIDevice(this, i); - if (device == NULL) - return B_NO_MEMORY; - - identified = device->Identify() == B_OK; - } - - if (!identified) { + if (device->Identify() != B_OK) { delete device; continue; } @@ -265,9 +258,8 @@ ATAChannel::SelectDevice(uint8 device) return B_BAD_INDEX; ata_task_file taskFile; - taskFile.chs.head = 0; - taskFile.chs.mode = ATA_MODE_LBA; - taskFile.chs.device = device; + taskFile.lba.mode = ATA_MODE_LBA; + taskFile.lba.device = device; _WriteRegs(&taskFile, ATA_MASK_DEVICE_HEAD); _FlushAndWait(1); @@ -286,38 +278,10 @@ ATAChannel::SelectDevice(uint8 device) } -bool -ATAChannel::IsDevicePresent(uint8 device) -{ - if (SelectDevice(device) != B_OK) - return false; - - ata_task_file taskFile; - taskFile.chs.device = device; - taskFile.chs.mode = ATA_MODE_LBA; - taskFile.chs.command = ATA_COMMAND_NOP; - _WriteRegs(&taskFile, ATA_MASK_DEVICE_HEAD); - - _FlushAndWait(10); - - _ReadRegs(&taskFile, ATA_MASK_STATUS | ATA_MASK_ERROR); - TRACE("status: 0x%02x; error: 0x%02x\n", taskFile.read.status, - taskFile.read.error); - return (taskFile.read.status & 0xf8) != 0xf8 - && taskFile.read.status != 0xa5; -} - - status_t -ATAChannel::Reset(bool *presence, uint32 *signatures) +ATAChannel::Reset(uint32 *signatures) { - TRACE_FUNCTION("%p, %p\n", presence, signatures); - - bool devicePresent[fDeviceCount]; - for (uint8 i = 0; i < fDeviceCount; i++) { - devicePresent[i] = IsDevicePresent(i); - TRACE("device %d: %s present\n", i, devicePresent[i] ? "might be" : "is not"); - } + TRACE_FUNCTION("\n"); SelectDevice(0); @@ -339,14 +303,11 @@ ATAChannel::Reset(bool *presence, uint32 *signatures) _FlushAndWait(150 * 1000); for (uint8 i = 0; i < fDeviceCount; i++) { - if (presence != NULL) - presence[i] = devicePresent[i]; - - if (!devicePresent[i]) - continue; - SelectDevice(i); + // ensure interrupts are disabled for this device + _WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS); + // wait up to 31 seconds for busy to clear if (Wait(0, ATA_STATUS_BUSY, 0, 31 * 1000 * 1000) != B_OK) { TRACE_ERROR("reset timeout\n"); @@ -390,6 +351,9 @@ ATAChannel::Wait(uint8 setBits, uint8 clearedBits, uint32 flags, bigtime_t startTime = system_time(); _FlushAndWait(1); + TRACE("waiting for set bits 0x%02x and cleared bits 0x%02x\n", + setBits, clearedBits); + while (true) { uint8 status = AltStatus(); if ((flags & ATA_CHECK_ERROR_BIT) != 0 @@ -398,9 +362,9 @@ ATAChannel::Wait(uint8 setBits, uint8 clearedBits, uint32 flags, return B_ERROR; } - if ((flags & ATA_CHECK_DISK_FAILURE) != 0 - && (status & ATA_STATUS_DISK_FAILURE) != 0) { - TRACE("disk failure bit set while waiting\n"); + if ((flags & ATA_CHECK_DEVICE_FAULT) != 0 + && (status & ATA_STATUS_DEVICE_FAULT) != 0) { + TRACE("device fault bit set while waiting\n"); return B_ERROR; } @@ -412,7 +376,7 @@ ATAChannel::Wait(uint8 setBits, uint8 clearedBits, uint32 flags, } bigtime_t elapsedTime = system_time() - startTime; - //TRACE("wait status after %lld: %u\n", elapsedTime, status); + TRACE("wait status after %lld: 0x%02x\n", elapsedTime, status); if (elapsedTime > timeout) return B_TIMED_OUT; @@ -459,6 +423,9 @@ ATAChannel::PrepareWaitingForInterrupt() InterruptsSpinLocker locker(fInterruptLock); fExpectsInterrupt = true; fInterruptCondition.Add(&fInterruptConditionEntry); + + // enable interrupts + _WriteControl(0); } @@ -473,6 +440,9 @@ ATAChannel::WaitForInterrupt(bigtime_t timeout) fExpectsInterrupt = false; locker.Unlock(); + // disable interrupts + _WriteControl(ATA_DEVICE_CONTROL_DISABLE_INTS); + if (result != B_OK) { TRACE_ERROR("timeout waiting for interrupt\n"); return B_TIMED_OUT; @@ -485,10 +455,6 @@ ATAChannel::WaitForInterrupt(bigtime_t timeout) status_t ATAChannel::SendRequest(ATARequest *request, uint32 flags) { - // disable interrupts for PIO transfers, enable them for DMA - _WriteControl((flags & ATA_DMA_TRANSFER) != 0 ? 0 - : ATA_DEVICE_CONTROL_DISABLE_INTS); - ATADevice *device = request->Device(); if (device->Select() != B_OK || WaitForIdle() != B_OK) { // resetting the device here will discard current configuration, @@ -553,8 +519,8 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask) } uint8 checkFlags = ATA_STATUS_ERROR; - if (flags & ATA_CHECK_DISK_FAILURE) - checkFlags |= ATA_STATUS_DISK_FAILURE; + if (flags & ATA_CHECK_DEVICE_FAULT) + checkFlags |= ATA_STATUS_DEVICE_FAULT; if ((taskFile->read.status & checkFlags) == 0) return B_OK; @@ -646,13 +612,6 @@ ATAChannel::ExecutePIOTransfer(ATARequest *request) status_t result = 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, currentLength); @@ -670,9 +629,16 @@ ATAChannel::ExecutePIOTransfer(ATARequest *request) *bytesLeft -= currentLength; - // wait 1 pio cycle - if (*bytesLeft > 0) - AltStatus(); + if (*bytesLeft > 0) { + // wait for next block to be ready + if (Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, + ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, + timeout) != B_OK) { + TRACE_ERROR("timeout waiting for device to request data\n"); + result = B_TIMED_OUT; + break; + } + } } if (result == B_OK && WaitDataRequest(false) != B_OK) { diff --git a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp index ac6047e83a..b1f2dd0229 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp @@ -436,9 +436,11 @@ ATADevice::Configure() if (fUseLBA) { fTotalSectors = fInfoBlock.LBA_total_sectors; fTaskFile.lba.mode = ATA_MODE_LBA; + fTaskFile.lba.device = fIndex; } else { fTotalSectors = chsCapacity; fTaskFile.chs.mode = ATA_MODE_CHS; + fTaskFile.chs.device = fIndex; } fUse48Bits = fInfoBlock._48_bit_addresses_supported; @@ -485,7 +487,7 @@ ATADevice::Identify() } if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, - ATA_CHECK_ERROR_BIT | ATA_CHECK_DISK_FAILURE, IsATAPI() + ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, IsATAPI() ? 20 * 1000 * 1000 : 500 * 1000) != B_OK) { TRACE_ERROR("timeout waiting for identify request\n"); return B_TIMED_OUT; @@ -525,13 +527,8 @@ ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address, return B_ERROR; } - uint32 flags = 0; - if (!IsATAPI()) - flags |= ATA_DEVICE_READY_REQUIRED; - if (request->UseDMA()) - flags |= ATA_DMA_TRANSFER; - - status_t result = fChannel->SendRequest(request, flags); + status_t result = fChannel->SendRequest(request, IsATAPI() + ? 0 : ATA_DEVICE_READY_REQUIRED); if (result != B_OK) { TRACE_ERROR("failed to send transfer request\n"); if (request->UseDMA()) @@ -539,6 +536,13 @@ ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address, return result; } + if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, 0, ATA_CHECK_ERROR_BIT + | ATA_CHECK_DEVICE_FAULT, request->Timeout()) != B_OK) { + TRACE_ERROR("timeout waiting for device to request data\n"); + request->SetStatus(SCSI_CMD_TIMEOUT); + return B_TIMED_OUT; + } + if (request->UseDMA()) { fChannel->PrepareWaitingForInterrupt(); fChannel->StartDMA(); @@ -591,6 +595,8 @@ ATADevice::_FillTaskFile(ATARequest *request, uint64 address) }; uint32 sectorCount = *request->BytesLeft() / ATA_BLOCK_SIZE; + TRACE("about to transfer %lu sectors\n", sectorCount); + if (fUseLBA) { if (fUse48Bits && (address + sectorCount > 0xfffffff || sectorCount > 0x100)) { diff --git a/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp b/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp index 060b4a5329..eda3c92a71 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp @@ -136,7 +136,7 @@ ata_sim_reset_bus(scsi_sim_cookie cookie) if (channel->Bus() == NULL) return SCSI_NO_HBA; - channel->Reset(NULL, NULL); + channel->Reset(NULL); return SCSI_REQ_CMP; } diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp index 4cb90acaa9..3869672656 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATAPIDevice.cpp @@ -49,8 +49,7 @@ ATAPIDevice::SendPacket(ATARequest *request) return B_ERROR; } - status_t result = fChannel->SendRequest(request, request->UseDMA() - ? ATA_DMA_TRANSFER : 0); + status_t result = fChannel->SendRequest(request, 0); if (result != B_OK) { TRACE_ERROR("failed to send packet request\n"); if (request->UseDMA()) @@ -60,7 +59,7 @@ ATAPIDevice::SendPacket(ATARequest *request) // wait for device to get ready for packet transmission if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, - ATA_CHECK_ERROR_BIT | ATA_CHECK_DISK_FAILURE, 100 * 1000) != B_OK) { + ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, 100 * 1000) != B_OK) { TRACE_ERROR("timeout waiting for data request\n"); if (request->UseDMA()) fChannel->FinishDMA(); @@ -70,7 +69,7 @@ ATAPIDevice::SendPacket(ATARequest *request) } // make sure device really asks for command packet - fRegisterMask = ATA_MASK_IREASON; + fRegisterMask = ATA_MASK_INTERRUPT_REASON; fChannel->ReadRegs(this); if (!fTaskFile.packet_res.cmd_or_data @@ -96,6 +95,14 @@ ATAPIDevice::SendPacket(ATARequest *request) return B_ERROR; } + if (!request->HasData()) { + result = fChannel->FinishRequest(request, ATA_WAIT_FINISH + | ATA_CHECK_DEVICE_FAULT, ATA_ERROR_ALL); + if (result != B_OK) + TRACE_ERROR("device indicates error after non-data command\n"); + return result; + } + if (request->UseDMA()) { fChannel->PrepareWaitingForInterrupt(); fChannel->StartDMA(); @@ -107,7 +114,7 @@ ATAPIDevice::SendPacket(ATARequest *request) return B_TIMED_OUT; } - result = fChannel->FinishRequest(request, ATA_CHECK_DISK_FAILURE, + result = fChannel->FinishRequest(request, ATA_CHECK_DEVICE_FAULT, ATA_ERROR_ALL); if (result != B_OK) { TRACE_ERROR("device indicates transfer error after dma\n"); @@ -133,33 +140,47 @@ ATAPIDevice::SendPacket(ATARequest *request) return B_ERROR; } - // PIO data transfer if (fChannel->Wait(ATA_STATUS_DATA_REQUEST, ATA_STATUS_BUSY, - ATA_CHECK_ERROR_BIT | ATA_CHECK_DISK_FAILURE, + ATA_CHECK_ERROR_BIT | ATA_CHECK_DEVICE_FAULT, 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); + // PIO data transfer + while (true) { + fRegisterMask = ATA_MASK_INTERRUPT_REASON | 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; + 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); + + if (fChannel->Wait(0, ATA_STATUS_BUSY, 0, request->Timeout()) != B_OK) { + TRACE_ERROR("timeout waiting for device to finish transfer\n"); + request->SetStatus(SCSI_CMD_TIMEOUT); + return B_TIMED_OUT; + } + + if ((fChannel->AltStatus() & ATA_STATUS_DATA_REQUEST) == 0) { + // transfer complete + TRACE("pio transfer complete\n"); + break; + } } - 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); + | ATA_CHECK_DEVICE_FAULT, ATA_ERROR_ALL); if (result != B_OK) TRACE_ERROR("device indicates transfer error after pio\n"); @@ -210,7 +231,7 @@ ATAPIDevice::_FillTaskFilePacket(ATARequest *request) { scsi_ccb *ccb = request->CCB(); fRegisterMask = ATA_MASK_FEATURES | ATA_MASK_BYTE_COUNT; - fTaskFile.packet.dma = request->UseDMA(); + fTaskFile.packet.dma = request->UseDMA() ? 1 : 0; fTaskFile.packet.ovl = 0; fTaskFile.packet.byte_count_0_7 = ccb->data_length & 0xff; fTaskFile.packet.byte_count_8_15 = ccb->data_length >> 8; diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h index 88308f4978..081dd9fc10 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h +++ b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h @@ -29,7 +29,9 @@ #define ATA_MAX_DMA_FAILURES 3 #define ATA_STANDARD_TIMEOUT 10 * 1000 * 1000 #define ATA_RELEASE_TIMEOUT 10 * 1000 * 1000 +#define ATA_SIGNATURE_ATA 0x00000101 #define ATA_SIGNATURE_ATAPI 0xeb140101 +#define ATA_SIGNATURE_SATA 0xc33c0101 #define ATA_SIM_MODULE_NAME "bus_managers/ata/sim/driver_v1" #define ATA_CHANNEL_ID_GENERATOR "ata/channel_id" #define ATA_CHANNEL_ID_ITEM "ata/channel_id" @@ -41,7 +43,7 @@ enum { ATA_CHECK_ERROR_BIT = 0x08, ATA_WAIT_FINISH = 0x10, ATA_WAIT_ANY_BIT = 0x20, - ATA_CHECK_DISK_FAILURE = 0x40 + ATA_CHECK_DEVICE_FAULT = 0x40 }; @@ -75,9 +77,8 @@ public: // ATA stuff status_t SelectDevice(uint8 index); - bool IsDevicePresent(uint8 index); - status_t Reset(bool *presence, uint32 *signatures); + status_t Reset(uint32 *signatures); bool UseDMA() { return fUseDMA; }; @@ -268,6 +269,8 @@ public: void SetBytesLeft(uint32 bytesLeft); size_t * BytesLeft() { return &fBytesLeft; }; + bool HasData() { return fCCB->data_length > 0; }; + status_t Finish(bool resubmit); // SCSI stuff