Implement DMA transfers in the new ATA bus_manager.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30069 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2009-04-09 15:24:40 +00:00
parent c4005c4d29
commit 7997389772
4 changed files with 128 additions and 52 deletions

View File

@ -14,6 +14,7 @@ ATAChannel::ATAChannel(device_node *node)
fChannelID(0),
fController(NULL),
fCookie(NULL),
fExpectsDMATransfer(false),
fStatus(B_NO_INIT),
fSCSIBus(NULL),
fDeviceCount(0),
@ -21,7 +22,9 @@ ATAChannel::ATAChannel(device_node *node)
fUseDMA(true),
fRequest(NULL)
{
mutex_init(&fLock, "ata channel");
mutex_init(&fExecutionLock, "ata io execution");
B_INITIALIZE_SPINLOCK(&fDMATransferLock);
fDMATransferCondition.Init(this, "ata dma transfer");
gDeviceManager->get_attr_uint32(node, ATA_CHANNEL_ID_ITEM, &fChannelID,
true);
@ -80,12 +83,14 @@ ATAChannel::ATAChannel(device_node *node)
fStatus = gDeviceManager->get_driver(parent,
(driver_module_info **)&fController, &fCookie);
gDeviceManager->put_node(parent);
fController->set_channel(fCookie, this);
}
ATAChannel::~ATAChannel()
{
mutex_lock(&fLock);
mutex_lock(&fExecutionLock);
if (fDevices) {
for (uint8 i = 0; i < fDeviceCount; i++)
@ -94,7 +99,7 @@ ATAChannel::~ATAChannel()
}
delete fRequest;
mutex_destroy(&fLock);
mutex_destroy(&fExecutionLock);
}
@ -217,10 +222,10 @@ status_t
ATAChannel::ExecuteIO(scsi_ccb *ccb)
{
TRACE_FUNCTION("%p\n", ccb);
if (mutex_trylock(&fLock) != B_OK)
if (mutex_trylock(&fExecutionLock) != B_OK)
return B_BUSY;
MutexLocker _(&fLock, true);
MutexLocker _(&fExecutionLock, true);
fRequest->SetCCB(ccb);
if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE) {
@ -445,6 +450,10 @@ ATAChannel::WaitForIdle()
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,
@ -479,7 +488,7 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
// wait for the device to finish current command (device no longer busy)
status_t result = Wait(0, ATA_STATUS_BUSY, 0, request->Timeout());
if (result != B_OK) {
TRACE_ERROR("timeout\n");
TRACE_ERROR("timeout waiting for request finish\n");
request->SetStatus(SCSI_CMD_TIMEOUT);
return result;
}
@ -564,12 +573,45 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask)
}
status_t
ATAChannel::PrepareDMA(ATARequest *request)
{
scsi_ccb *ccb = request->CCB();
return fController->prepare_dma(fCookie, ccb->sg_list, ccb->sg_count,
request->IsWrite());
}
status_t
ATAChannel::FinishDMA()
{
return fController->finish_dma(fCookie);
}
status_t
ATAChannel::ExecuteDMATransfer(ATARequest *request)
{
TRACE_ERROR("dma transfers unimplemented\n");
request->SetStatus(SCSI_SEQUENCE_FAIL);
return B_ERROR;
InterruptsSpinLocker locker(fDMATransferLock);
fExpectsDMATransfer = true;
ConditionVariableEntry entry;
fDMATransferCondition.Add(&entry);
locker.Unlock();
fController->start_dma(fCookie);
bigtime_t timeout = system_time() + request->Timeout();
status_t waitResult = entry.Wait(B_ABSOLUTE_TIMEOUT, timeout);
locker.Lock();
fExpectsDMATransfer = false;
locker.Unlock();
if (waitResult != B_OK) {
TRACE_ERROR("timeout waiting for DMA transfer\n");
return B_TIMED_OUT;
}
return B_OK;
}
@ -612,14 +654,6 @@ ATAChannel::ExecutePIOTransfer(ATARequest *request)
result = B_ERROR;
}
if (result == B_OK) {
result = FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
}
if (result != B_OK)
request->SetStatus(SCSI_SEQUENCE_FAIL);
return result;
}
@ -632,6 +666,24 @@ ATAChannel::ReadPIO(uint8 *buffer, size_t length)
}
void
ATAChannel::Interrupt(uint8 status)
{
SpinLocker locker(fDMATransferLock);
if (!fExpectsDMATransfer) {
TRACE_ERROR("interrupt when not expecting transfer\n");
return;
}
if ((status & ATA_STATUS_BUSY) != 0) {
TRACE_ERROR(("interrupt while device is busy\n"));
return;
}
fDMATransferCondition.NotifyAll();
}
status_t
ATAChannel::_ReadRegs(ata_task_file *taskFile, ata_reg_mask mask)
{

View File

@ -17,6 +17,7 @@ ATADevice::ATADevice(ATAChannel *channel, uint8 index)
fUse48Bits(false),
fUseDMA(channel->UseDMA()),
fDMAMode(0),
fDMAFailures(0),
fTotalSectors(0),
fRegisterMask(0)
{
@ -513,18 +514,15 @@ status_t
ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
uint32 sectorCount)
{
request->SetUseDMA(fUseDMA && _PrepareDMA(request) == B_OK);
if (!request->UseDMA()) {
status_t result = _PreparePIO(request);
if (result != B_OK) {
TRACE_ERROR("failed to prepare pio transfer\n");
return result;
}
}
request->SetUseDMA(fUseDMA && fChannel->PrepareDMA(request) == B_OK);
if (!request->UseDMA())
request->PrepareSGInfo();
request->SetBlocksLeft(sectorCount);
if (_FillTaskFile(request, address) != B_OK) {
TRACE_ERROR("failed to setup transfer request\n");
if (request->UseDMA())
fChannel->FinishDMA();
return B_ERROR;
}
@ -537,13 +535,39 @@ ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address,
status_t result = fChannel->SendRequest(request, flags);
if (result != B_OK) {
TRACE_ERROR("failed to send transfer request\n");
if (request->UseDMA())
fChannel->FinishDMA();
return result;
}
if (request->UseDMA())
return fChannel->ExecuteDMATransfer(request);
if (request->UseDMA()) {
result = fChannel->ExecuteDMATransfer(request);
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 {
if (fChannel->ExecutePIOTransfer(request) != B_OK)
request->SetStatus(SCSI_SEQUENCE_FAIL);
}
return fChannel->ExecutePIOTransfer(request);
return fChannel->FinishRequest(request, ATA_WAIT_FINISH
| ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED);
}
@ -621,18 +645,3 @@ ATADevice::_FillTaskFile(ATARequest *request, uint64 address)
return B_OK;
}
status_t
ATADevice::_PrepareDMA(ATARequest *request)
{
return B_ERROR;
}
status_t
ATADevice::_PreparePIO(ATARequest *request)
{
request->PrepareSGInfo();
return B_OK;
}

View File

@ -205,7 +205,13 @@ ata_channel_added(device_node *parent)
status_t
ata_interrupt_handler(void *cookie, uint8 status)
{
return B_UNHANDLED_INTERRUPT;
ATAChannel *channel = (ATAChannel *)cookie;
channel->Interrupt(status);
// the controller driver already checks if its interrupt status indicates
// that the interrupt was for this device, so we are only here if that's
// the case.
return B_HANDLED_INTERRUPT;
}

View File

@ -25,6 +25,7 @@
#include "ATATracing.h"
#include "ata_device_infoblock.h"
#define ATA_MAX_DMA_FAILURES 3
#define ATA_STANDARD_TIMEOUT 10 * 1000 * 1000
#define ATA_RELEASE_TIMEOUT 10 * 1000 * 1000
#define ATA_SIGNATURE_ATAPI 0xeb140101
@ -35,10 +36,10 @@
enum {
ATA_DEVICE_READY_REQUIRED = 0x01,
ATA_IS_WRITE = 0x02,
ATA_DMA_TRANSFER = 0x03,
ATA_CHECK_ERROR_BIT = 0x04,
ATA_WAIT_FINISH = 0x08,
ATA_WAIT_ANY_BIT = 0x10
ATA_DMA_TRANSFER = 0x04,
ATA_CHECK_ERROR_BIT = 0x08,
ATA_WAIT_FINISH = 0x10,
ATA_WAIT_ANY_BIT = 0x20
};
@ -91,11 +92,16 @@ public:
uint32 flags, uint8 errorMask);
// data transfers
status_t PrepareDMA(ATARequest *request);
status_t FinishDMA();
status_t ExecuteDMATransfer(ATARequest *request);
status_t ExecutePIOTransfer(ATARequest *request);
status_t ReadPIO(uint8 *buffer, size_t length);
void Interrupt(uint8 status);
private:
status_t _ReadRegs(ata_task_file *taskFile,
ata_reg_mask mask);
@ -125,7 +131,12 @@ private:
uint32 fChannelID;
ata_controller_interface * fController;
void * fCookie;
mutex fLock;
mutex fExecutionLock;
spinlock fDMATransferLock;
ConditionVariable fDMATransferCondition;
bool fExpectsDMATransfer;
status_t fStatus;
scsi_bus fSCSIBus;
uint8 fDeviceCount;
@ -178,9 +189,6 @@ private:
status_t _FillTaskFile(ATARequest *request,
uint64 address);
status_t _PrepareDMA(ATARequest *request);
status_t _PreparePIO(ATARequest *request);
const char * _DebugContext() { return fDebugContext; };
ATAChannel * fChannel;
@ -189,6 +197,7 @@ private:
bool fUse48Bits;
bool fUseDMA;
uint8 fDMAMode;
uint8 fDMAFailures;
uint64 fTotalSectors;
ata_device_infoblock fInfoBlock;
ata_task_file fTaskFile;