* Reworked device detection, we'll see how well this works...

* Reworked some of the transfer handling after reading the specs.
* Ensure that the device selection bit is set correctly for all commands.
* Generally disable interrupts and enable them only when expecting a DMA one.
* Renamed disk failure to device fault according to specs.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30248 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2009-04-18 20:58:33 +00:00
parent b43891053d
commit 827f849a34
6 changed files with 109 additions and 113 deletions

View File

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

View File

@ -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) {

View File

@ -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)) {

View File

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

View File

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

View File

@ -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