IOATAController.cpp [plain text]
#include <IOKit/assert.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOTypes.h>
#include <IOKit/IOTimerEventSource.h>
#include <IOKit/storage/IOStorageProtocolCharacteristics.h>
#include "IOATATypes.h"
#include "IOATAController.h"
#include "IOATACommand.h"
#include "IOATADevice.h"
#include "IOATABusInfo.h"
#include "IOATADevConfig.h"
#include "IOATABusCommand.h"
#include "ATATimerEventSource.h"
#include <libkern/OSByteOrder.h>
#include <libkern/OSAtomic.h>
#define kStatusDelayTime 5
#define kStatusDelayLoopMS 1000 / kStatusDelayTime
#define _doubleBufferDesc reserved->_doubleBufferDesc
#ifdef DLOG
#undef DLOG
#endif
#ifdef ATA_DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#pragma mark -IOService Overrides -
#define super IOService
OSDefineMetaClass( IOATAController, IOService )
OSDefineAbstractStructors( IOATAController, IOService )
OSMetaClassDefineReservedUnused(IOATAController, 0);
OSMetaClassDefineReservedUnused(IOATAController, 1);
OSMetaClassDefineReservedUnused(IOATAController, 2);
OSMetaClassDefineReservedUnused(IOATAController, 3);
OSMetaClassDefineReservedUnused(IOATAController, 4);
OSMetaClassDefineReservedUnused(IOATAController, 5);
OSMetaClassDefineReservedUnused(IOATAController, 6);
OSMetaClassDefineReservedUnused(IOATAController, 7);
OSMetaClassDefineReservedUnused(IOATAController, 8);
OSMetaClassDefineReservedUnused(IOATAController, 9);
OSMetaClassDefineReservedUnused(IOATAController, 10);
OSMetaClassDefineReservedUnused(IOATAController, 11);
OSMetaClassDefineReservedUnused(IOATAController, 12);
OSMetaClassDefineReservedUnused(IOATAController, 13);
OSMetaClassDefineReservedUnused(IOATAController, 14);
OSMetaClassDefineReservedUnused(IOATAController, 15);
OSMetaClassDefineReservedUnused(IOATAController, 16);
OSMetaClassDefineReservedUnused(IOATAController, 17);
OSMetaClassDefineReservedUnused(IOATAController, 18);
OSMetaClassDefineReservedUnused(IOATAController, 19);
OSMetaClassDefineReservedUnused(IOATAController, 20);
bool
IOATAController::init(OSDictionary * properties)
{
DLOG("IOATAController::init() starting\n");
_workLoop = 0;
_cmdGate = 0;
_provider = 0;
_timer = 0;
queue_init( &_commandQueue );
if (super::init(properties) == false)
{
DLOG("IOATAController: super::init() failed\n");
return false;
}
DLOG("IOATAController::init() done\n");
return true;
}
IOService*
IOATAController::probe(IOService* provider, SInt32* score)
{
return this;
}
bool
IOATAController::start(IOService *provider)
{
OSObject * prop;
DLOG("IOATAController::start() begin\n");
_provider = provider;
_busState = IOATAController::kBusFree;
_currentCommand = 0L;
_selectedUnit = kATAInvalidDeviceID;
_queueState = IOATAController::kQueueOpen;
if (!super::start(_provider))
{
DLOG("IOATAController: super::start() failed\n");
return false;
}
prop = getProperty ( kIOPropertyPhysicalInterconnectTypeKey, gIOServicePlane );
if ( prop == NULL )
{
setProperty ( kIOPropertyPhysicalInterconnectTypeKey, kIOPropertyPhysicalInterconnectTypeATA);
}
prop = getProperty ( kIOPropertyPhysicalInterconnectLocationKey, gIOServicePlane );
if ( prop == NULL )
{
setProperty ( kIOPropertyPhysicalInterconnectLocationKey, kIOPropertyInternalKey);
}
reserved = ( ExpansionData * ) IOMalloc ( sizeof ( ExpansionData ) );
if ( !reserved )
return false;
bzero ( reserved, sizeof ( ExpansionData ) );
if( !configureTFPointers() )
{
DLOG("IOATA TF Pointers failed\n");
return false;
}
if( !scanForDrives() )
{
DLOG("IOATA scan for drives failed\n");
return false;
}
if(! allocateDoubleBuffer() )
{
DLOG("IOATA doubleBuffer alloc failed\n");
return false;
}
_workLoop = getWorkLoop();
if (!_workLoop)
{
DLOG("IOATA: IOWorkLoop allocation failed\n");
return false;
}
_workLoop->retain();
_timer = ATATimerEventSource::ataTimerEventSource(this,
(ATATimerEventSource::Action) timeoutOccured);
if (!_timer || _workLoop->addEventSource(_timer))
{
DLOG("IOATA: ATATImerEventSource allocation failed\n");
return false;
}
_cmdGate = IOCommandGate::commandGate(this);
if (!_cmdGate || _workLoop->addEventSource(_cmdGate))
{
DLOG("IOATAController: IOCommandGate failed\n");
return false;
}
registerService();
DLOG("IOATAController::start() done\n");
return true;
}
void
IOATAController::free()
{
if ( _workLoop )
{
if ( _cmdGate )
{
_workLoop->removeEventSource(_cmdGate);
_cmdGate = NULL;
}
if ( _timer )
{
_workLoop->removeEventSource(_timer);
_timer = NULL;
}
_workLoop->release();
}
if ( reserved )
{
if ( _doubleBufferDesc )
{
_doubleBufferDesc->complete();
_doubleBufferDesc->release();
_doubleBuffer.bufferSize = 0;
_doubleBuffer.logicalBuffer = 0;
_doubleBuffer.physicalBuffer = 0;
_doubleBufferDesc = NULL;
}
}
if ( reserved )
{
IOFree ( reserved, sizeof ( ExpansionData ) );
reserved = NULL;
}
super::free();
}
#pragma mark -initialization-
bool
IOATAController::allocateDoubleBuffer( void )
{
DLOG("IOATAController::allocateDoubleBuffer() started\n");
_doubleBufferDesc = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
kernel_task,
kIODirectionInOut | kIOMemoryPhysicallyContiguous,
(kATADefaultSectorSize * 8),
0xFFFFF000UL );
if ( !_doubleBufferDesc )
{
IOLog("%s: double buffer allocation failed\n", getName());
return false;
}
_doubleBufferDesc->prepare();
_doubleBuffer.logicalBuffer = (IOLogicalAddress)_doubleBufferDesc->getBytesNoCopy();
_doubleBuffer.physicalBuffer = _doubleBufferDesc->getPhysicalAddress();
_doubleBuffer.bufferSize = kATADefaultSectorSize * 8;
DLOG("IOATAController::allocateDoubleBuffer() done\n");
return true;
}
bool
IOATAController::configureTFPointers(void)
{
DLOG("IOATA: configureTFPointers. must provide implementation.\n");
return false;
}
#pragma mark -client interface-
IOReturn
IOATAController::selectConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
DLOG("IOATA: sublcass must implement selectConfig.\n");
return kATAModeNotSupported;
}
IOReturn
IOATAController::getConfig( IOATADevConfig* configOut, UInt32 unitNumber)
{
DLOG("IOATA: sublcass must implement getConfig.\n");
return kATAModeNotSupported;
}
IOReturn
IOATAController::provideBusInfo( IOATABusInfo* infoOut)
{
DLOG(" IOATA: sublcass must implement provideBusInfo\n");
return -1;
}
IOReturn
IOATAController::executeCommand(IOATADevice* nub, IOATABusCommand* command)
{
if( !command || !nub )
return -1;
IOReturn resultCode = kATANoErr;
command->setCommandInUse();
_cmdGate->runAction( (IOCommandGate::Action)
&IOATAController::executeCommandAction,
(void *) command, (void *) &resultCode, 0, 0);
return resultCode;
}
#pragma mark -workloop entry-
void
IOATAController::executeCommandAction(OSObject * owner,
void * arg0,
void * arg1,
void * ,
void * )
{
IOATAController* self = (IOATAController*) owner;
IOATABusCommand* command = (IOATABusCommand*) arg0;
IOReturn* result = (IOReturn*) arg1;
assert(command && result);
*result = self->handleCommand( (void*)command );
}
IOReturn
IOATAController::handleCommand( void* param0,
void* param1,
void* param2,
void* param3 )
{
IOATABusCommand* command = (IOATABusCommand*) param0;
if( command == 0L )
{
DLOG("IOATAController::handleCmd nill ptr\n");
return -1;
}
command->state = kATAInitial;
enqueueCommand( command );
dispatchNext();
return kATANoErr;
}
IOReturn
IOATAController::enqueueCommand( IOATABusCommand* command)
{
if( command->getFlags() & mATAFlagImmediate )
{
queue_enter_first( &_commandQueue,
command,
IOATABusCommand*,
queueChain );
} else {
queue_enter( &_commandQueue,
command,
IOATABusCommand*,
queueChain );
}
return kATANoErr;
}
IOATABusCommand*
IOATAController::dequeueFirstCommand( void )
{
IOATABusCommand* cmdPtr = 0L;
if( !queue_empty( &_commandQueue ) )
{
queue_remove_first( &_commandQueue,
cmdPtr,
IOATABusCommand*,
queueChain );
}
return cmdPtr;
}
bool
IOATAController::busCanDispatch( void )
{
if( _busState == IOATAController::kBusFree
&& _queueState == IOATAController::kQueueOpen )
{
return true;
}
if( _busState == IOATAController::kBusFree
&& _queueState == IOATAController::kQueueLocked
&& _immediateGate == kImmediateOK
&& !queue_empty( &_commandQueue ) )
{
DLOG("IOATA Qfrozen check for immediate\n");
IOATABusCommand* cmdPtr = (IOATABusCommand*) queue_first( & _commandQueue ) ;
if( cmdPtr != 0
&& (cmdPtr->getFlags() & mATAFlagImmediate) )
{
DLOG("IOTA q-head is immediate\n");
return true;
}
}
return false;
}
IOReturn
IOATAController::dispatchNext( void )
{
IOReturn result = kATANoErr;
DLOG("IOATAController::dispatchNext start\n");
if( !busCanDispatch() )
return result;
_busState = IOATAController::kBusBusy;
_currentCommand = dequeueFirstCommand();
if( _currentCommand == 0L )
{
DLOG("IOATAController::dispatchNext queue empty\n");
_busState = IOATAController::kBusFree;
return kATAQueueEmpty;
}
_currentCommand->state = IOATAController::kATAStarted;
if( _queueState == IOATAController::kQueueLocked
&& _immediateGate == kImmediateOK
&& _currentCommand->getFlags() & mATAFlagImmediate)
{
_currentCommand->setFlags(_currentCommand->getFlags() | mATAFlagUseNoIRQ );
}
if( _currentCommand->getTaskFilePtr()->ataTFCommand == 0x08 )
{
_currentCommand->setOpcode(kATAFnBusReset);
}
switch( _currentCommand->getOpcode() )
{
case kATAFnExecIO:
case kATAPIFnExecIO:
result = handleExecIO();
break;
case kATAFnRegAccess:
result = handleRegAccess();
break;
case kATAFnBusReset:
result = handleBusReset();
break;
case kATAFnQFlush:
result = handleQueueFlush();
break;
default:
_currentCommand->setResult( kATAUnknownOpcode );
result = kATAUnknownOpcode;
_currentCommand->state = IOATAController::kATAComplete;
completeIO(kATAUnknownOpcode);
break;
}
DLOG("IOATAController::dispatchNext done return = %ld\n", (long int)result);
return result;
}
void
IOATAController::executeEventCallouts( ataEventCode event, ataUnitID unit )
{
IOATABusCommand* currCommand = _currentCommand;
UInt32 currQueueState = _queueState;
UInt32 currBusState = _busState;
UInt32 currImmed = _immediateGate;
_queueState = kQueueLocked;
_busState = kBusFree;
_immediateGate = kImmediateOK;
_currentCommand = 0;
for( int i = 0; i < 2; i++)
{
if( (_nub[i] != 0L )
&& ( (unit == kATAInvalidDeviceID) || (unit == i) ) )
{
_nub[i]->notifyEvent( event );
}
}
_queueState = currQueueState;
_busState = currBusState;
_immediateGate = currImmed;
_currentCommand = currCommand;
}
void
IOATAController::completeIO( IOReturn commandResult )
{
DLOG("IOATAController::completeIO start = %ld\n",(long int)commandResult);
IOATABusCommand* finishedCmd = _currentCommand;
if( finishedCmd == 0L )
return;
_currentCommand = 0L;
finishedCmd->state = IOATAController::kATADone;
stopTimer();
stopDMA();
_busState = IOATAController::kBusFree;
finishedCmd->setResult(commandResult);
finishedCmd->executeCallback();
dispatchNext();
DLOG("IOATAController::completeIO done\n");
}
#pragma mark -timers-
IOReturn
IOATAController::startTimer( UInt32 inMS)
{
IOReturn err = kATANoErr;
_timer->disable();
_timer->cancelTimeout();
_timer->enable();
err = _timer->setTimeoutMS( inMS );
if( err )
{
DLOG("IOATAController::startTimer failed\n");
}
return err;
}
void
IOATAController::stopTimer(void)
{
_timer->disable();
_timer->cancelTimeout();
}
void
IOATAController::timeoutOccured(OSObject *owner, IOTimerEventSource *sender)
{
IOATAController* self = (IOATAController*) owner;
self->handleTimeout();
}
bool
IOATAController::checkTimeout( void )
{
return _timer->hasTimedOut();
}
void
IOATAController::handleTimeout( void )
{
if( _currentCommand != 0L )
{
if( (_currentCommand->getFlags() & mATAFlagUseDMA ) == mATAFlagUseDMA )
{
stopDMA();
}
_currentCommand->setResult( kATATimeoutErr );
_currentCommand->state = IOATAController::kATAComplete;
asyncStatus();
completeIO(kATATimeoutErr);
} else {
dispatchNext();
}
}
#pragma mark -Workloop handlers-
IOReturn
IOATAController::handleDeviceInterrupt(void)
{
volatile UInt8 status = 0x00;
if( !_currentCommand )
{
DLOG("IOATA Device Int no command active\n");
return kATADevIntNoCmd;
}
status = *_tfStatusCmdReg;
OSSynchronizeIO();
return asyncIO();
}
IOReturn
IOATAController::handleExecIO( void )
{
IOReturn err = kATANoErr;
err = selectDevice( _currentCommand->getUnit() );
if( err )
{
IOLog("IOATAController device blocking bus.\n");
_currentCommand->state = IOATAController::kATAComplete;
if( _currentCommand->getFlags() & mATAFlagUseNoIRQ )
{
completeIO( kIOReturnOffline );
return kIOReturnOffline;
}
startTimer( 1000 ); return kATANoErr; }
startTimer( _currentCommand->getTimeoutMS() );
_currentCommand->state = IOATAController::kATAStarted;
if( _currentCommand->getFlags() & mATAFlagUseNoIRQ )
{
err = synchronousIO();
} else {
err = asyncIO();
}
return err;
}
IOReturn
IOATAController::handleRegAccess( void )
{
IOReturn err = kATANoErr;
err = selectDevice( _currentCommand->getUnit() );
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
completeIO( err );
return err;
}
bool isWrite = (_currentCommand->getFlags() & mATAFlagIOWrite) ? true : false;
err = registerAccess( isWrite );
_currentCommand->state = IOATAController::kATAComplete;
completeIO( err );
return err;
}
IOReturn
IOATAController::handleBusReset(void)
{
bool isATAPIReset = ((_currentCommand->getFlags() & mATAFlagProtocolATAPI) != 0);
bool doATAPI[2];
IOReturn err = kATANoErr;
UInt8 index;
UInt8 statCheck;
DLOG("IOATA bus reset start.\n");
doATAPI[0] = doATAPI[1] = false;
if(isATAPIReset)
{
doATAPI[_currentCommand->getUnit()] = true;
}else{
doATAPI[0] = doATAPI[1] = true;
}
for(index=0;index<2;index++)
{
if( doATAPI[index] && _devInfo[index].type == kATAPIDeviceType)
{
OSSynchronizeIO();
*_tfSDHReg = mATASectorSize + (index << 4);
OSSynchronizeIO();
statCheck = *_tfAltSDevCReg;
err = softResetBus(true);
}
}
if(isATAPIReset)
{
executeEventCallouts( kATAPIResetEvent, _currentCommand->getUnit() );
}
if(!isATAPIReset)
{
err = softResetBus();
executeEventCallouts( kATAResetEvent, kATAInvalidDeviceID );
}
_currentCommand->state = IOATAController::kATAComplete;
DLOG("IOATA bus reset done.\n");
completeIO( err );
return err;
}
IOReturn
IOATAController::handleQueueFlush( void )
{
return kATANoErr;
}
IOReturn
IOATAController::asyncIO(void)
{
IOReturn err = kATANoErr;
if( (_currentCommand->getFlags() & mATAFlagProtocolATAPI) == mATAFlagProtocolATAPI
&& _currentCommand->getPacketSize() > 0)
{
_currentCommand->state = determineATAPIState();
}
switch( _currentCommand->state )
{
case kATAStarted : err = asyncCommand();
DLOG("ATAController:: command sent: err = %ld state= %lx\n", (long int) err, (int) _currentCommand->state);
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
asyncStatus();
break;
}
if( _currentCommand->state != IOATAController::kATAPICmd
|| _devInfo[ _currentCommand->getUnit() ].packetSend == kATAPIIRQPacket )
{
DLOG("ATAController:: pending IRQ for packet\n");
break;
}
case kATAPICmd: DLOG("ATAController:: issue packet\n");
err = writePacket();
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
asyncStatus();
break;
}
if( (_currentCommand->getFlags() & (mATAFlagIORead | mATAFlagIOWrite ) )
&& ((_currentCommand->getFlags() & mATAFlagUseDMA ) != mATAFlagUseDMA ) )
{
_currentCommand->state = IOATAController::kATADataTx;
} else {
_currentCommand->state = IOATAController::kATAStatus;
}
break;
case kATADataTx: err = asyncData();
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
asyncStatus();
break;
}
if(_currentCommand->state == kATADataTx
|| ( (_currentCommand->getFlags() & (mATAFlagProtocolATAPI | mATAFlagIOWrite | mATAFlagUseDMA) ) == mATAFlagIOWrite ) )
{
break;
}
if( (_currentCommand->getFlags() & mATAFlagProtocolATAPI) == mATAFlagProtocolATAPI
&& _currentCommand->getPacketSize() > 0)
{
break;
}
case kATAStatus: err = asyncStatus();
_currentCommand->state = IOATAController::kATAComplete;
break;
default:
DLOG("IOATA AsyncIO state broken\n");
err = kATAErrUnknownType;
_currentCommand->state = IOATAController::kATAComplete;
break;
}
if( _currentCommand->state == IOATAController::kATAComplete )
{
completeIO(err);
}
return err;
}
IOReturn
IOATAController::synchronousIO(void)
{
IOReturn err = kATANoErr;
OSSynchronizeIO();
*_tfAltSDevCReg = 0x02;
err = asyncCommand();
DLOG("ATAController::synchronous command sent: err = %ld state= %lx\n", (long int) err, (int) _currentCommand->state);
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
} else {
for( UInt32 i = 0; i< 3000; i++)
{
if( waitForU8Status( mATABusy, 0x00 ) )
break;
IOSleep(10); }
}
if( _currentCommand->state == IOATAController::kATAPICmd )
{
DLOG("ATAController::synchronous issue packet\n");
err = writePacket();
if( err == kATANoErr )
{
if( (_currentCommand->getFlags() & (mATAFlagIORead | mATAFlagIOWrite ) )
&& ((_currentCommand->getFlags() & mATAFlagUseDMA ) != mATAFlagUseDMA ) )
{
_currentCommand->state = IOATAController::kATADataTx;
} else {
_currentCommand->state = IOATAController::kATAStatus;
}
} else {
_currentCommand->state = IOATAController::kATAComplete;
}
}
if( _currentCommand->state == IOATAController::kATADataTx )
{
while( _currentCommand->state == IOATAController::kATADataTx )
{
err = asyncData();
if( err )
{
_currentCommand->state = IOATAController::kATAComplete;
break;
}
}
if( (_currentCommand->getFlags() & mATAFlagProtocolATAPI) == mATAFlagProtocolATAPI
&& _currentCommand->getPacketSize() > 0)
{
waitForU8Status( mATABusy, 0x00 );
}
}
if( _currentCommand->state == IOATAController::kATAStatus )
{
err = asyncStatus();
_currentCommand->state = IOATAController::kATAComplete;
}
volatile UInt8 finalStatus = *_tfStatusCmdReg;
OSSynchronizeIO();
finalStatus++;
if( _currentCommand->state == IOATAController::kATAComplete )
{
completeIO(err);
}
OSSynchronizeIO();
*_tfAltSDevCReg = 0x00;
return err;
}
IOReturn
IOATAController::asyncStatus(void)
{
IOReturn err = kATANoErr;
UInt8 status = *_tfAltSDevCReg;
OSSynchronizeIO();
UInt8 error = 0x00;
if( status & mATAError )
{
error = *_tfFeatureReg;
OSSynchronizeIO();
err = kATADeviceError;
if( _currentCommand->getFlags() & (mATAFlagTFAccess | mATAFlagTFAccessResult) )
{
registerAccess( false );
}
} else if( _currentCommand->getFlags() & mATAFlagTFAccessResult ) {
registerAccess( false );
}
_currentCommand->setEndResult( status, error);
return err;
}
IOReturn
IOATAController::asyncCommand(void)
{
IOReturn err = kATANoErr;
if( (_currentCommand->getFlags() & mATAFlagUseDMA ) == mATAFlagUseDMA )
{
err = startDMA();
}
if( err )
{
stopDMA();
return err;
}
DLOG("ATAController: command flags = %lx , packet size = %d\n",_currentCommand->getFlags(), _currentCommand->getPacketSize() );
err = issueCommand();
if( err )
return err;
if( (_currentCommand->getFlags() & mATAFlagProtocolATAPI) == mATAFlagProtocolATAPI
&& _currentCommand->getPacketSize() > 0)
{
_currentCommand->state = IOATAController::kATAPICmd;
return err;
}
if( (_currentCommand->getFlags() & mATAFlagUseDMA ) == mATAFlagUseDMA )
{
_currentCommand->state = IOATAController::kATAStatus;
return err;
}
if( (_currentCommand->getFlags()
& (mATAFlagIOWrite | mATAFlagUseDMA | mATAFlagProtocolATAPI) )
== mATAFlagIOWrite )
{
_currentCommand->state = IOATAController::kATADataTx;
return asyncData();
}
if( (_currentCommand->getFlags() & mATAFlagIORead ) == mATAFlagIORead )
{
_currentCommand->state = IOATAController::kATADataTx;
} else {
_currentCommand->state = IOATAController::kATAStatus;
}
return err;
}
IOReturn
IOATAController::asyncData(void)
{
IOByteCount bytesRemaining = _currentCommand->getByteCount()
- _currentCommand->getActualTransfer();
if(bytesRemaining < 1)
{
_currentCommand->state = kATAStatus;
return kATANoErr;
}
UInt8 status= 0x00;
while ( !checkTimeout() )
{
OSSynchronizeIO();
status = *_tfAltSDevCReg;
status &= (mATABusy | mATADataRequest | mATAError);
if( mATAError == status )
{
_currentCommand->state = kATAStatus;
return kATADeviceError;
break;
}
if( mATADataRequest == status )
{
break;
}
IODelay( 10 );
}
if ( checkTimeout() )
{
_currentCommand->state = kATAStatus;
return kATATimeoutErr;
}
IOMemoryDescriptor* descriptor = _currentCommand->getBuffer();
IOByteCount xfrPosition = _currentCommand->getPosition() +
_currentCommand->getActualTransfer();
IOByteCount thisPass = bytesRemaining;
IOByteCount overrun = 0;
if( thisPass > _currentCommand->getTransferChunkSize() )
{
thisPass = _currentCommand->getTransferChunkSize();
}
if( _currentCommand->getFlags() & mATAFlagProtocolATAPI)
{
thisPass = readATAPIByteCount();
if( thisPass > bytesRemaining )
{
overrun = thisPass - bytesRemaining;
thisPass = bytesRemaining;
}
}
while( thisPass > 0 )
{
IOByteCount bufferBytes = (thisPass > _doubleBuffer.bufferSize )? _doubleBuffer.bufferSize : thisPass;
if( _currentCommand->getFlags() & mATAFlagIORead )
{
txDataIn(_doubleBuffer.logicalBuffer, bufferBytes );
descriptor->writeBytes( xfrPosition, (void*) _doubleBuffer.logicalBuffer, bufferBytes);
} else {
descriptor->readBytes( xfrPosition, (void*) _doubleBuffer.logicalBuffer, bufferBytes );
txDataOut(_doubleBuffer.logicalBuffer, bufferBytes);
}
xfrPosition += bufferBytes;
thisPass -= bufferBytes;
_currentCommand->setActualTransfer(_currentCommand->getActualTransfer() + bufferBytes);
bytesRemaining -= bufferBytes;
}
if(overrun)
{
handleOverrun( overrun );
}
if(bytesRemaining > 1)
{
_currentCommand->state = kATADataTx;
} else {
_currentCommand->state = kATAStatus;
}
return kATANoErr;
}
#pragma mark - Hardware Access -
void
IOATAController::selectIOTiming( ataUnitID unit )
{
}
IOReturn
IOATAController::selectDevice( ataUnitID unit )
{
UInt32 msLoops = _currentCommand->getTimeoutMS()/10;
if( ! ( (kATADevice0DeviceID == unit)
|| (kATADevice1DeviceID == unit ) ) )
{
DLOG( "IOATA: invalid device ID selected\n");
return kATAInvalidDevID;
}
selectIOTiming( unit );
UInt8 preReqMask = (mATABusy | mATADataRequest );
UInt8 preReqCondition = 0x00;
if( unit != _selectedUnit)
{
while( !waitForU8Status( preReqMask, preReqCondition ) )
{
OSSynchronizeIO();
if( msLoops == 0
|| (*_tfStatusCmdReg & mATADataRequest) == mATADataRequest
|| checkTimeout() )
{
DLOG( "IOATA: BUSY or DRQ can't select device. \n");
return kATAErrDevBusy;
}
msLoops--;
IOSleep(10); }
_selectedUnit = kATAInvalidDeviceID;
*_tfSDHReg = ( unit << 4 );
OSSynchronizeIO();
}
if( _devInfo[ unit ].type == kATADeviceType
&& _currentCommand->getOpcode() == kATAFnExecIO
&& _currentCommand->getTaskFilePtr()->ataTFCommand != 0x90
&& _currentCommand->getTaskFilePtr()->ataTFCommand != 0x91 )
{
preReqMask |= mATADriveReady;
preReqCondition |= mATADriveReady;
}
msLoops = 10;
while( !waitForU8Status( (mATABusy ), 0x00 ))
{
OSSynchronizeIO();
if( msLoops == 0
|| (*_tfStatusCmdReg & mATADataRequest) == mATADataRequest
|| checkTimeout() )
{
DLOG( "IOATA: BUSY can't select device. \n");
return kATAErrDevBusy;
}
msLoops--;
IOSleep(10); }
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
_selectedUnit = unit;
return kATANoErr;
}
IOReturn
IOATAController::issueCommand( void )
{
if( _currentCommand == 0 )
{
DLOG("IOATA can't issue nil command\n");
return kATAErrUnknownType;
}
if( _currentCommand->getFlags() & mATAFlag48BitLBA )
{
IOExtendedLBA* extLBA = _currentCommand->getExtendedLBA();
*_tfSDHReg = extLBA->getDevice();
OSSynchronizeIO();
*_tfFeatureReg = (extLBA->getFeatures16() & 0xFF00) >> 8 ;
*_tfSCountReg = (extLBA->getSectorCount16() & 0xFF00) >> 8 ;
*_tfSectorNReg = (extLBA->getLBALow16() & 0xFF00) >> 8 ;
*_tfCylLoReg = (extLBA->getLBAMid16() & 0xFF00) >> 8 ;
*_tfCylHiReg = (extLBA->getLBAHigh16() & 0xFF00) >> 8 ;
OSSynchronizeIO();
*_tfFeatureReg = extLBA->getFeatures16() & 0x00FF;
*_tfSCountReg = extLBA->getSectorCount16() & 0x00FF;
*_tfSectorNReg = extLBA->getLBALow16() & 0x00FF;
*_tfCylLoReg = extLBA->getLBAMid16() & 0x00FF;
*_tfCylHiReg = extLBA->getLBAHigh16() & 0x00FF;
OSSynchronizeIO();
*_tfStatusCmdReg = extLBA->getCommand();
OSSynchronizeIO();
} else {
ataTaskFile* tfRegs = _currentCommand->getTaskFilePtr();
OSSynchronizeIO();
*_tfSDHReg = tfRegs->ataTFSDH;
OSSynchronizeIO();
*_tfFeatureReg = tfRegs->ataTFFeatures;
*_tfSCountReg = tfRegs->ataTFCount;
*_tfSectorNReg = tfRegs->ataTFSector;
*_tfCylLoReg = tfRegs->ataTFCylLo;
*_tfCylHiReg = tfRegs->ataTFCylHigh;
OSSynchronizeIO();
*_tfStatusCmdReg = tfRegs->ataTFCommand;
}
return kATANoErr;
}
IOReturn
IOATAController::writePacket( void )
{
UInt32 packetSize = _currentCommand->getPacketSize();
UInt16* packetData = _currentCommand->getPacketData();
if ( packetSize == 0)
{
return kATANoErr;
}
UInt8 status = 0x00;
UInt32 breakDRQ = 3;
while ( !waitForU8Status( (mATABusy | mATADataRequest), mATADataRequest)
&& !checkTimeout()
&& (breakDRQ != 0) )
{
status = *_tfAltSDevCReg;
status &= (mATABusy | mATAError);
if( mATAError == status )
{
return kATADeviceError;
}
breakDRQ--;
IOSleep( 10 ); }
if ( checkTimeout()
|| breakDRQ == 0)
{
return kATATimeoutErr;
}
UInt32 packetLength = 6;
if( packetSize > 12 )
{
packetLength = 8;
}
for( UInt32 i = 0; i < packetLength; i++)
{
OSSynchronizeIO();
* _tfDataReg = *packetData;
packetData++;
}
return kATANoErr ;
}
IOReturn
IOATAController::softResetBus( bool doATAPI )
{
IOReturn result = kATANoErr;
if (doATAPI)
{
DLOG("IOATA reset command.\n");
*_tfStatusCmdReg = kSOFTRESET;
OSSynchronizeIO();
} else {
*_tfAltSDevCReg = mATADCRReset;
OSSynchronizeIO();
IODelay( 100 );
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
DLOG("IOATA soft reset sequenced\n");
_selectedUnit = kATAInvalidDeviceID;
}
IOSleep(50);
UInt8 readyMask = mATABusy;
UInt8 readyOn = 0x00;
if( (_devInfo[0].type == kATADeviceType)
&& (!doATAPI) )
{
readyMask |= mATADriveReady; readyOn = mATADriveReady; }
bool resetFailed = true;
for( int i = 0; i < 3100; i++)
{
OSSynchronizeIO();
UInt8 status = *_tfStatusCmdReg;
if( ( status & readyMask )== readyOn)
{
resetFailed = false;
break;
}
IOSleep( 10 );
}
if( resetFailed )
{
DLOG("IOATA device failed to reset.\n");
result = kATATimeoutErr;
}
DLOG("IOATA reset complete.\n");
return result;
}
IOReturn
IOATAController::startDMA( void )
{
DLOG("IOATA Bus controllers that offer DMA must provide implementation/n");
return kATAModeNotSupported;
}
IOReturn
IOATAController::stopDMA( void )
{
DLOG("IOATA Bus controllers that offer DMA must provide implementation/n");
return kATAModeNotSupported;
}
bool
IOATAController::waitForU8Status (UInt8 mask, UInt8 value)
{
int i;
for (i=0; i < kStatusDelayLoopMS; i++)
{
OSSynchronizeIO();
if ((*_tfAltSDevCReg & mask) == value)
{
return true;
}
IODelay( kStatusDelayTime );
}
return false; }
bool
IOATAController::ATAPISlaveExists( void )
{
UInt8 scratchByte;
UInt16 scratchWord;
UInt32 dataCounter;
UInt32 loopCounter;
OSSynchronizeIO();
*_tfAltSDevCReg = 0x02;
OSSynchronizeIO();
*_tfStatusCmdReg = 0xA1;
OSSynchronizeIO();
scratchByte = *_tfAltSDevCReg;
OSSynchronizeIO();
scratchByte = *_tfAltSDevCReg;
if( scratchByte == 0x00 )
{
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
return false;
}
for( loopCounter = 0; loopCounter < 10000; loopCounter++)
{
OSSynchronizeIO();
scratchByte = *_tfAltSDevCReg;
if( scratchByte & 0x01 )
break;
if( (scratchByte & 0x58) == 0x58) {
OSSynchronizeIO();
scratchByte = *_tfStatusCmdReg;
for( dataCounter = 0; dataCounter < 256; dataCounter++ )
{
OSSynchronizeIO();
scratchWord = *_tfDataReg;
}
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
return true;
}
IOSleep(10);
}
OSSynchronizeIO();
scratchByte = *_tfStatusCmdReg;
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
return false;
}
UInt32
IOATAController::scanForDrives( void )
{
UInt32 unitsFound = 0;
UInt8 status = 0x00;
UInt32 milsSpent = 0;
for( int loopMils = 0; milsSpent < 3100; loopMils++ )
{
OSSynchronizeIO();
status = *_tfStatusCmdReg;
if( (status & mATABusy) == 0x00 )
break;
IOSleep( 10 );
milsSpent++;
}
if( ! (milsSpent < 3100) )
goto AllDone;
for( int unit = 0; unit < 2; unit++ )
{
for( int loopMils = 0; milsSpent < 3100; loopMils++ )
{
OSSynchronizeIO();
*_tfSDHReg = ( unit << 4 );
IODelay( 10 );
status = *_tfStatusCmdReg;
if( (status & mATABusy) == 0x00 )
{
break;
}
IOSleep( 10 );
milsSpent++;
}
if( ! (milsSpent < 3100) )
goto AllDone;
if ( ( *_tfCylLoReg == 0x14) && ( *_tfCylHiReg == 0xEB) )
{
if( (unit == 1 )
&& ( _devInfo[0].type == kATAPIDeviceType ) )
{
if( ATAPISlaveExists( ) != true )
{
_devInfo[unit].type = kUnknownATADeviceType;
goto AllDone;
}
}
_devInfo[unit].type = kATAPIDeviceType;
_devInfo[unit].packetSend = kATAPIDRQFast; unitsFound++;
} else if ( (*_tfCylLoReg == 0x00) && (*_tfCylHiReg == 0x00) &&
(*_tfSCountReg == 0x01) && (*_tfSectorNReg == 0x01) &&
( (*_tfAltSDevCReg & 0x51) == 0x50) )
{
_devInfo[unit].type = kATADeviceType;
_devInfo[unit].packetSend = kATAPIUnknown;
unitsFound++;
}else{
_devInfo[unit].type = kUnknownATADeviceType;
_devInfo[unit].packetSend = kATAPIUnknown;
}
}
AllDone:
*_tfSDHReg = 0x00;
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
_selectedUnit = kATAInvalidDeviceID;
return unitsFound;
}
IOReturn
IOATAController::txDataIn (IOLogicalAddress buf, IOByteCount length)
{
register UInt16 *buf16 = (UInt16*)buf;
while (length >= 32) {
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
*buf16++ = *_tfDataReg;
length -= 32; OSSynchronizeIO();
}
while (length >= 2)
{
*buf16++ = *_tfDataReg;
OSSynchronizeIO();
length -= 2; }
UInt8* buf8 = (UInt8*)buf16;
if (length) {
*buf8 = *(IOATARegPtr8Cast(_tfDataReg));
OSSynchronizeIO();
length--;
}
return kATANoErr;
}
IOReturn
IOATAController::txDataOut(IOLogicalAddress buf, IOByteCount length)
{
register UInt16 *buf16 = (UInt16*)buf;
while (length >= 32) {
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
*_tfDataReg = *buf16++;
OSSynchronizeIO();
length -= 32;
}
while (length >= 2)
{
*_tfDataReg = *buf16++;
OSSynchronizeIO();
length -= 2;
}
UInt8* buf8 = (UInt8*)buf16;
if (length)
{
*(IOATARegPtr8Cast(_tfDataReg)) = *buf8;
OSSynchronizeIO();
length--;
}
return kATANoErr;
}
IOByteCount
IOATAController::readATAPIByteCount (void)
{
UInt16 ByteCountValue;
ByteCountValue = (*_tfCylHiReg) << 8;
OSSynchronizeIO();
ByteCountValue += *_tfCylLoReg;
OSSynchronizeIO();
return (IOByteCount) ByteCountValue;
}
IOATAController::transState
IOATAController::determineATAPIState(void)
{
IOATAController::transState nextPhase;
const IOATAController::transState NextState[4] = {kATADataTx, kATAPICmd,
kATADataTx, kATAStatus};
if (_currentCommand->state == kATAStarted) return (kATAStarted);
nextPhase = NextState[ *_tfSCountReg & 0x03];
return nextPhase;
}
void
IOATAController::handleOverrun( IOByteCount length)
{
UInt16 dummy = 0;
if( _currentCommand->getFlags() & mATAFlagIORead )
{
while (length >= 2)
{
dummy = *_tfDataReg;
OSSynchronizeIO();
length -= 2;
}
while (length > 0)
{
dummy = *_tfDataReg;
OSSynchronizeIO();
length--;
}
} else {
while (length >= 2)
{
*_tfDataReg = dummy;
OSSynchronizeIO();
length -= 2;
}
while (length > 0)
{
*_tfDataReg = dummy;
OSSynchronizeIO();
length--;
}
}
}
UInt16
IOATAController::readExtRegister( IOATARegPtr8 inRegister )
{
UInt16 result = (*inRegister);
OSSynchronizeIO();
*_tfAltSDevCReg = 0x80;
OSSynchronizeIO();
result |= ((UInt16) (*inRegister)) << 8;
OSSynchronizeIO();
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
return result;
}
void
IOATAController::writeExtRegister( IOATARegPtr8 inRegister, UInt16 inValue )
{
*inRegister = (UInt8)((inValue & 0xFF00) >> 8);
OSSynchronizeIO();
*inRegister = (UInt8) (inValue & 0xFF);
OSSynchronizeIO();
}
IOReturn
IOATAController::registerAccess(bool isWrite)
{
UInt32 RegAccessMask = _currentCommand->getRegMask();
IOReturn err = kATANoErr;
bool isExtLBA = _currentCommand->getFlags() & mATAFlag48BitLBA;
IOExtendedLBA* extLBA = _currentCommand->getExtendedLBA();
if (RegAccessMask & mATAErrFeaturesValid) {
if(isWrite)
{
if(isExtLBA )
{
writeExtRegister(_tfFeatureReg,extLBA->getFeatures16());
} else {
*_tfFeatureReg = _currentCommand->getErrorReg();
}
}else{
if(isExtLBA )
{
extLBA->setFeatures16( readExtRegister(_tfFeatureReg));
} else {
_currentCommand->setFeatures( *_tfFeatureReg) ;
}
}
}
if (RegAccessMask & mATASectorCntValid) {
if(isWrite)
{
if(isExtLBA )
{
writeExtRegister( _tfSCountReg, extLBA->getSectorCount16());
} else {
*_tfSCountReg = _currentCommand->getSectorCount();
}
}else{
if(isExtLBA )
{
extLBA->setSectorCount16( readExtRegister(_tfSCountReg) );
} else {
_currentCommand->setSectorCount( *_tfSCountReg );
}
}
}
if (RegAccessMask & mATASectorNumValid) {
if(isWrite)
{
if(isExtLBA )
{
writeExtRegister( _tfSectorNReg, extLBA->getLBALow16());
} else {
*_tfSectorNReg = _currentCommand->getSectorNumber();
}
}else{
if(isExtLBA )
{
extLBA->setLBALow16(readExtRegister(_tfSectorNReg));
} else {
_currentCommand->setSectorNumber( *_tfSectorNReg );
}
}
}
if (RegAccessMask & mATACylinderLoValid) {
if(isWrite)
{
if(isExtLBA )
{
writeExtRegister( _tfCylLoReg, extLBA->getLBAMid16());
} else {
*_tfCylLoReg = _currentCommand->getCylLo();
}
}else{
if(isExtLBA )
{
extLBA->setLBAMid16(readExtRegister(_tfCylLoReg));
} else {
_currentCommand->setCylLo( *_tfCylLoReg );
}
}
}
if (RegAccessMask & mATACylinderHiValid) {
if(isWrite)
{
if(isExtLBA )
{
writeExtRegister( _tfCylHiReg, extLBA->getLBAHigh16());
} else {
*_tfCylHiReg = _currentCommand->getCylHi();
}
}else{
if(isExtLBA )
{
extLBA->setLBAHigh16(readExtRegister(_tfCylHiReg));
} else {
_currentCommand->setCylHi( *_tfCylHiReg );
}
}
}
if (RegAccessMask & mATASDHValid) {
if(isWrite)
{
*_tfSDHReg = _currentCommand->getDevice_Head();
}else{
_currentCommand->setDevice_Head( *_tfSDHReg );
}
}
if (RegAccessMask & mATAAltSDevCValid) {
if(isWrite)
{
*_tfAltSDevCReg = _currentCommand->getAltStatus();
}else{
_currentCommand->setControl( *_tfAltSDevCReg );
}
}
if (RegAccessMask & mATADataValid) {
if(isWrite)
{
*_tfDataReg = _currentCommand->getDataReg();
}else{
_currentCommand->setDataReg( *_tfDataReg );
}
}
if (RegAccessMask & mATAStatusCmdValid) {
if(isWrite)
{
*_tfStatusCmdReg = _currentCommand->getStatus();
}else{
_currentCommand->setCommand(*_tfStatusCmdReg );
}
}
return err;
}
#pragma mark -misc functions-
void
IOATAController::swapBytes16( UInt8* dataBuffer, IOByteCount length)
{
IOByteCount i;
UInt8 c;
unsigned char* firstBytePtr;
for (i = 0; i < length; i+=2)
{
firstBytePtr = dataBuffer; c = *dataBuffer++; *firstBytePtr = *dataBuffer; *dataBuffer++= c; }
}
UInt16
IOATAController::bitSigToNumeric(UInt16 binary)
{
UInt16 i, integer;
for (i = 0x0080, integer = 7; ((i & binary) == 0 && i != 0) ; i >>= 1, integer-- )
{;}
return (integer);
}