#include <IOKit/IOTypes.h>
#include "IOATATypes.h"
#include "IOATAController.h"
#include "IOATADevice.h"
#include "IOATABusInfo.h"
#include "IOATADevConfig.h"
#include <IOKit/IOMemoryCursor.h>
#include <libkern/OSByteOrder.h>
#include <libkern/OSAtomic.h>
#include "IOPCIATA.h"
#include "IOATABusCommand.h"
#ifdef DLOG
#undef DLOG
#endif
#ifdef ATA_DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define ATARecordEventMACRO(type,param,bus,data) (void) (type); (void) (param); (void) (bus); (void) (data)
#pragma mark -IOService Overrides -
#define kATAXferDMADesc 64
#define kATAMaxDMADesc kATAXferDMADesc
#define kMaxATAXfer 512 * 256
#define _prdBuffer reserved->_prdBuffer
#define super IOATAController
OSDefineMetaClass( IOPCIATA, IOATAController )
OSDefineAbstractStructors( IOPCIATA, IOATAController )
OSMetaClassDefineReservedUnused(IOPCIATA, 0);
OSMetaClassDefineReservedUnused(IOPCIATA, 1);
OSMetaClassDefineReservedUnused(IOPCIATA, 2);
OSMetaClassDefineReservedUnused(IOPCIATA, 3);
OSMetaClassDefineReservedUnused(IOPCIATA, 4);
OSMetaClassDefineReservedUnused(IOPCIATA, 5);
OSMetaClassDefineReservedUnused(IOPCIATA, 6);
OSMetaClassDefineReservedUnused(IOPCIATA, 7);
OSMetaClassDefineReservedUnused(IOPCIATA, 8);
OSMetaClassDefineReservedUnused(IOPCIATA, 9);
OSMetaClassDefineReservedUnused(IOPCIATA, 10);
OSMetaClassDefineReservedUnused(IOPCIATA, 11);
OSMetaClassDefineReservedUnused(IOPCIATA, 12);
OSMetaClassDefineReservedUnused(IOPCIATA, 13);
OSMetaClassDefineReservedUnused(IOPCIATA, 14);
OSMetaClassDefineReservedUnused(IOPCIATA, 15);
OSMetaClassDefineReservedUnused(IOPCIATA, 16);
OSMetaClassDefineReservedUnused(IOPCIATA, 17);
OSMetaClassDefineReservedUnused(IOPCIATA, 18);
OSMetaClassDefineReservedUnused(IOPCIATA, 19);
OSMetaClassDefineReservedUnused(IOPCIATA, 20);
bool
IOPCIATA::init(OSDictionary * properties)
{
DLOG("IOPCIATA::init() starting\n");
_bmCommandReg = 0;
_bmStatusReg = 0;
_bmPRDAddresReg = 0;
_prdTable = 0;
_prdTablePhysical = 0;
_DMACursor = 0;
_dmaState = IOPCIATA::kATADMAInactive;
if (super::init(properties) == false)
{
DLOG("IOPCIATA: super::init() failed\n");
return false;
}
DLOG("IOPCIATA::init() done\n");
return true;
}
bool
IOPCIATA::start(IOService *provider)
{
DLOG("IOPCIATA::start() begin\n");
if (!super::start( provider))
{
DLOG("IOPCIATA: super::start() failed\n");
return false;
}
reserved = ( ExpansionData * ) IOMalloc ( sizeof ( ExpansionData ) );
if ( !reserved )
return false;
bzero ( reserved, sizeof ( ExpansionData ) );
if( ! allocDMAChannel() )
{
DLOG("IOPCIATA: allocDMAChannel failed\n");
return false;
}
DLOG("IOPCIATA::start() done\n");
return true;
}
void
IOPCIATA::free()
{
freeDMAChannel();
if ( reserved )
{
IOFree ( reserved, sizeof ( ExpansionData ) );
reserved = NULL;
}
super::free();
}
#pragma mark -initialization-
bool
IOPCIATA::allocDMAChannel(void)
{
if( _bmCommandReg == 0
|| _bmStatusReg == 0
|| _bmPRDAddresReg == 0 )
{
DLOG("IOPCIATA bm regs not initialised.\n");
return false;
}
_prdBuffer = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
kernel_task,
kIODirectionInOut | kIOMemoryPhysicallyContiguous,
sizeof(PRD) * kATAMaxDMADesc,
0xFFFFFFF0UL );
if ( !_prdBuffer )
{
IOLog("%s: PRD buffer allocation failed\n", getName());
return false;
}
_prdBuffer->prepare ( );
_prdTable = (PRD *) _prdBuffer->getBytesNoCopy();
_prdTablePhysical = _prdBuffer->getPhysicalAddress();
_DMACursor = IONaturalMemoryCursor::withSpecification(0x10000,
kMaxATAXfer
);
if( ! _DMACursor )
{
freeDMAChannel();
DLOG("IOPCIATA alloc DMACursor failed\n");
return false;
}
initATADMAChains(_prdTable);
return true;
}
bool
IOPCIATA::freeDMAChannel(void)
{
if( _prdBuffer )
{
stopDMA();
_prdBuffer->complete();
_prdBuffer->release();
_prdBuffer = NULL;
_prdTable = NULL;
_prdTablePhysical = 0;
}
if( _DMACursor )
{
_DMACursor->release();
_DMACursor = NULL;
}
return true;
}
#pragma mark -DMA Interface-
IOReturn
IOPCIATA::startDMA( void )
{
IOReturn err = kATANoErr;
stopDMA();
_dmaState = kATADMAStarting;
err = createChannelCommands();
if( err )
{
DLOG("IOPCIATA error createChannelCmds err = %ld\n", (long int)err);
stopDMA();
return err;
}
activateDMAEngine();
return err;
}
IOReturn
IOPCIATA::stopDMA( void )
{
if(_dmaState != kATADMAInactive)
shutDownATADMA();
_dmaState = kATADMAInactive;
return kATANoErr;
}
#pragma mark -DMA Implementation-
void
IOPCIATA::initATADMAChains (PRD* descPtr)
{
UInt32 i;
for (i = 0; i < kATAMaxDMADesc; i++)
{
descPtr->bufferPtr = 0;
descPtr->byteCount = 1;
descPtr->flags = OSSwapHostToLittleInt16( (UInt16) kLast_PRD);
descPtr++;
}
}
void
IOPCIATA::stopDMAEngine(void)
{
OSSynchronizeIO();
*_bmCommandReg = mBMCmdStop;
}
void
IOPCIATA::activateDMAEngine(void)
{
DLOG("IOPCIATA prd table is at: %lx\n", _prdTablePhysical);
*_bmStatusReg = (UInt8) mBMStatusError | mBMStatusInt | (_currentCommand->getUnit() == 0 ? mBMStatusDrv0 : mBMStatusDrv1) ;
OSSynchronizeIO();
*_bmPRDAddresReg = OSSwapHostToLittleInt32((UInt32) _prdTablePhysical);
OSSynchronizeIO();
UInt8 theCommand = (_currentCommand->getFlags() & mATAFlagIORead) ? mBMCmdStartInput : mBMCmdStartOutput;
DLOG("IOPCIATA: bmCommand is %X\n", theCommand);
*_bmCommandReg = theCommand;
OSSynchronizeIO();
DLOG("IOPCIATA: bmStaus is %X\n", *_bmStatusReg);
}
void
IOPCIATA::shutDownATADMA (void)
{
_dmaState = kATADMAInactive;
stopDMAEngine();
}
void
IOPCIATA::setPRD(UInt8 *bffr, UInt16 count, PRD *tableElement, UInt16 end)
{
DLOG("IOPCIATA set PRD ptr = %lx count = %x flags = %x\n", (long) bffr, count, end);
tableElement->bufferPtr = OSSwapHostToLittleInt32((UInt32)(uintptr_t)bffr);
tableElement->byteCount = OSSwapHostToLittleInt16(count);
tableElement->flags = OSSwapHostToLittleInt16(end);
}
IOReturn
IOPCIATA::createChannelCommands(void)
{
IOMemoryDescriptor* descriptor = _currentCommand->getBuffer();
IOMemoryCursor::PhysicalSegment physSegment;
UInt32 index = 0;
UInt8 *xferDataPtr, *ptr2EndData, *next64KBlock, *starting64KBlock;
UInt32 xferCount, count2Next64KBlock;
if( ! descriptor )
{
DLOG("IOPCIATA nil buffer!\n");
return -1;
}
IOByteCount bytesRemaining = _currentCommand->getByteCount() ;
IOByteCount xfrPosition = _currentCommand->getPosition() ;
IOByteCount transferSize = 0;
while( _DMACursor->getPhysicalSegments(
descriptor,
xfrPosition,
&physSegment,
1,
bytesRemaining, &transferSize) )
{
xferDataPtr = (UInt8*) physSegment.location;
xferCount = physSegment.length;
bytesRemaining -= xferCount;
xfrPosition += xferCount;
starting64KBlock = (UInt8*) ( (uintptr_t) xferDataPtr & 0xffff0000);
ptr2EndData = xferDataPtr + xferCount;
next64KBlock = (starting64KBlock + 0x10000);
while( xferCount > 0 )
{
if (ptr2EndData > next64KBlock)
{
count2Next64KBlock = next64KBlock - xferDataPtr;
if (index < kATAMaxDMADesc)
{
setPRD(xferDataPtr, (UInt16)count2Next64KBlock, &_prdTable[index], kContinue_PRD);
xferDataPtr = next64KBlock;
next64KBlock += 0x10000;
xferCount -= count2Next64KBlock;
index++;
} else {
DLOG("IOPCIATA dma too big, PRD table exhausted A.\n");
_dmaState = kATADMAError;
return -1;
}
} else {
if (index < kATAMaxDMADesc)
{
setPRD(xferDataPtr, (UInt16) xferCount, &_prdTable[index], (bytesRemaining == 0) ? kLast_PRD : kContinue_PRD);
xferCount = 0;
index++;
} else {
DLOG("IOPCIATA dma too big, PRD table exhausted B.\n");
_dmaState = kATADMAError;
return -1;
}
}
}
}
_dmaState = kATADMAStatus;
DLOG("IOPCIATA PRD chain end %ld \n", index);
return kATANoErr;
}
IOReturn
IOPCIATA::handleDeviceInterrupt(void)
{
if( _dmaState == kATADMAStatus )
{
OSSynchronizeIO();
UInt8 bmStatus = *_bmStatusReg;
if( bmStatus & mBMStatusError )
{
_dmaState = kATADMAError;
} else {
_currentCommand->setActualTransfer(_currentCommand->getByteCount());
_dmaState = kATADMAComplete;
}
stopDMA();
}
return super::handleDeviceInterrupt();
}