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:
parent
c4005c4d29
commit
7997389772
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user