ApplePCCardATA.cpp [plain text]
#include "ApplePCCardATA.h"
#include <IOKit/ata/ATADeviceNub.h>
#include <IOKit/assert.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOTypes.h>
#include <IOKit/IOMessage.h>
#include <IOKit/ata/IOATATypes.h>
#include <IOKit/ata/IOATAController.h>
#include <IOKit/ata/IOATACommand.h>
#include <IOKit/ata/IOATADevice.h>
#include <IOKit/ata/IOATABusInfo.h>
#include <IOKit/ata/IOATADevConfig.h>
#include <libkern/OSByteOrder.h>
#include <libkern/OSAtomic.h>
#ifdef DLOG
#undef DLOG
#endif
#ifdef ATA_DEBUG
#define DLOG(fmt, args...) IOLog(fmt, ## args)
#else
#define DLOG(fmt, args...)
#endif
#define kCompatibleString "pccard-ata"
#define kATASupportedPIOModes 0x0001 // mode 0 only
#pragma mark -IOService Overrides -
#define super IOATAController
OSDefineMetaClassAndStructors( ApplePCCardATA, IOATAController )
bool
ApplePCCardATA::init(OSDictionary* properties)
{
DLOG("ApplePCCardATA init start\n");
_devIntSrc = 0;
isBusOnline = true;
baseAddressMap = 0;
baseAddressMap2 = 0;
if (super::init(properties) == false)
{
DLOG("ApplePCCardATA: super::init() failed\n");
return false;
}
DLOG("ApplePCCardATA init done\n");
return true;
}
IOService*
ApplePCCardATA::probe(IOService* provider, SInt32* score)
{
OSData *compatibleEntry;
DLOG("ApplePCCardATA starting probe\n");
compatibleEntry = OSDynamicCast( OSData, provider->getProperty( "compatible" ) );
if ( compatibleEntry == 0 )
{
DLOG("ApplePCCardATA failed getting compatible property\n");
return 0;
}
if ( compatibleEntry->isEqualTo( kCompatibleString, sizeof(kCompatibleString)-1 ) == false )
{
DLOG("ApplePCCardATA compatible property doesn't match\n");
return 0;
}
return this;
}
bool
ApplePCCardATA::start(IOService *provider)
{
DLOG("ApplePCCardATA::start() begin\n");
if( ! provider->open(this) )
{
DLOG("ApplePCCardATA provider did not open\n");
return false;
}
ATADeviceNub* newNub=0L;
if (!super::start( provider))
{
DLOG("ApplePCCardATA: super::start() failed\n");
provider->close(this);
return false;
}
if( ! createDeviceInterrupt() )
{
DLOG("ApplePCCardATA: createDeviceInterrupts failed\n");
return false;
}
if( !enableInterrupt(0) )
{
DLOG("ApplePCCardATA: enable ints failed\n");
return false;
}
DLOG("ApplePCCardATA::start() done\n");
for( UInt32 i = 0; i < 2; i++)
{
if( _devInfo[i].type != kUnknownATADeviceType )
{
DLOG("ApplePCCardATA creating nub\n");
newNub = ATADeviceNub::ataDeviceNub( (IOATAController*)this, (ataUnitID) i, _devInfo[i].type );
if( newNub )
{
DLOG("ApplePCCardATA attach nub\n");
newNub->attach(this);
_nub[i] = (IOATADevice*) newNub;
DLOG("ApplePCCardATA register nub\n");
newNub->registerService();
newNub = 0L;
}
}
}
return true;
}
void
ApplePCCardATA::free()
{
if(_devIntSrc)
{
_devIntSrc->release();
_devIntSrc = 0;
}
if( baseAddressMap )
{
baseAddressMap->release();
baseAddressMap = 0;
}
if( baseAddressMap2 )
{
baseAddressMap2->release();
baseAddressMap2 = 0;
}
#ifdef __i386__
#define RELEASE(x) do { if(x) { (x)->release(); (x) = 0; } } while(0)
RELEASE( _tfDataReg );
RELEASE( _tfFeatureReg );
RELEASE( _tfSCountReg );
RELEASE( _tfSectorNReg );
RELEASE( _tfCylLoReg );
RELEASE( _tfCylHiReg );
RELEASE( _tfSDHReg );
RELEASE( _tfStatusCmdReg );
RELEASE( _tfAltSDevCReg );
#endif
super::free();
}
IOWorkLoop*
ApplePCCardATA::getWorkLoop() const
{
DLOG("ApplePCCardATA::getWorkLoop\n");
IOWorkLoop* wl = _workLoop;
if (!wl)
{
wl = IOWorkLoop::workLoop();
if (!wl)
return 0;
}
return wl;
}
#pragma mark -Controller specific-
bool
ApplePCCardATA::configureTFPointers(void)
{
volatile UInt8* baseAddress = 0;
volatile UInt8* alternateAddress = 0;
baseAddressMap = _provider->mapDeviceMemoryWithIndex(0);
if( !baseAddressMap )
{
DLOG("ApplePCCardATA no base map\n");
return false;
}
DLOG("ApplePCCardATA basemap ptr = %lx\n", baseAddressMap );
baseAddress = (volatile UInt8*) baseAddressMap->getVirtualAddress();
if( !baseAddress )
{
DLOG("ApplePCCardATA no base address\n");
return false;
}
baseAddressMap2 = _provider->mapDeviceMemoryWithIndex(1);
if( !baseAddressMap2 )
{
DLOG("ApplePCCardATA no second map\n");
} else {
alternateAddress = (volatile UInt8*) baseAddressMap2->getVirtualAddress();
if( !alternateAddress )
{
DLOG("ApplePCCardATA second window has no valid address address\n");
return false;
}
}
#if defined(__ppc__)
_tfDataReg = (volatile UInt16*) (baseAddress + 0x00);
_tfFeatureReg = baseAddress + 0x01;
_tfSCountReg = baseAddress + 0x2;
_tfSectorNReg = baseAddress + 0x3;
_tfCylLoReg = baseAddress + 0x4;
_tfCylHiReg = baseAddress + 0x5;
_tfSDHReg = baseAddress + 0x6;
_tfStatusCmdReg = baseAddress + 0x7;
if( alternateAddress == 0)
{
_tfAltSDevCReg = baseAddress + 0xe;
} else {
_tfAltSDevCReg = alternateAddress;
}
#elif defined(__i386__)
UInt32 baseIOAddress = (UInt32) baseAddress;
UInt32 alternateIOAddress = (UInt32) alternateAddress;
_tfDataReg = IOATAIOReg16::withAddress(baseIOAddress + 0x00);
_tfFeatureReg = IOATAIOReg8::withAddress(baseIOAddress + 0x01);
_tfSCountReg = IOATAIOReg8::withAddress(baseIOAddress + 0x2);
_tfSectorNReg = IOATAIOReg8::withAddress(baseIOAddress + 0x3);
_tfCylLoReg = IOATAIOReg8::withAddress(baseIOAddress + 0x4);
_tfCylHiReg = IOATAIOReg8::withAddress(baseIOAddress + 0x5);
_tfSDHReg = IOATAIOReg8::withAddress(baseIOAddress + 0x6);
_tfStatusCmdReg = IOATAIOReg8::withAddress(baseIOAddress + 0x7);
if( alternateIOAddress == 0)
{
_tfAltSDevCReg = IOATAIOReg8::withAddress(baseIOAddress + 0xe);
} else {
_tfAltSDevCReg = IOATAIOReg8::withAddress(alternateIOAddress);
}
#else
#error Unknown machine architecture
#endif
DLOG("ApplePCCardATA configTFPointers succeeded. base addr = %lx \n", baseAddress);
DLOG("ApplePCCardATA configTFPointers succeeded. phys addr = %lx \n", (UInt8*) baseAddressMap->getPhysicalAddress() );
return true;
}
IOReturn
ApplePCCardATA::provideBusInfo( IOATABusInfo* infoOut)
{
if( infoOut == 0)
{
DLOG("ApplePCCardATA: nil pointer in provideBusInfo\n");
return -1;
}
infoOut->zeroData();
infoOut->setSocketType( kPCCardSocket );
infoOut->setPIOModes( 0x01);
UInt8 units = 0;
if( _devInfo[0].type != kUnknownATADeviceType )
units++;
if( _devInfo[1].type != kUnknownATADeviceType )
units++;
infoOut->setUnits( units);
return kATANoErr;
}
IOReturn
ApplePCCardATA::selectConfig( IOATADevConfig* configRequest, UInt32 unitNumber)
{
if( configRequest == 0
|| unitNumber > 1 )
{
DLOG("ApplePCCardATA bad param in setConfig\n");
return -1;
}
if( ( configRequest->getPIOMode() & 0x01 ) == 0x00)
{
DLOG("ApplePCCardATA setConfig PIO mode not supported\n");
return kATAModeNotSupported;
}
if( configRequest->getUltraMode() != 0
|| configRequest->getDMAMode() != 0 )
{
DLOG("ApplePCCardATA setConfig DMA modes not supported\n");
return kATAModeNotSupported;
}
_devInfo[unitNumber].packetSend = configRequest->getPacketConfig();
DLOG("ApplePCCardATA setConfig packetConfig = %ld\n", _devInfo[unitNumber].packetSend );
return kATANoErr;
}
IOReturn
ApplePCCardATA::getConfig( IOATADevConfig* configOut, UInt32 unitNumber)
{
if( configOut == 0
|| unitNumber > 1 )
{
DLOG("ApplePCCardATA: bad param in getConfig\n");
return -1;
}
configOut->setPIOMode(0x01); configOut->setDMAMode(0);
configOut->setPIOCycleTime(600); configOut->setDMACycleTime(0);
configOut->setPacketConfig( _devInfo[unitNumber].packetSend );
configOut->setUltraMode(0);
return kATANoErr;
}
void
ApplePCCardATA::deviceInterruptOccurred(OSObject * owner, IOInterruptEventSource *evtSrc, int count)
{
ApplePCCardATA* self = (ApplePCCardATA*) owner;
self->handleDeviceInterrupt();
}
bool
ApplePCCardATA::createDeviceInterrupt(void)
{
_devIntSrc = IOInterruptEventSource::
interruptEventSource( (OSObject *)this,
(IOInterruptEventAction) & ApplePCCardATA::deviceInterruptOccurred, _provider->getProvider(), 0);
if( !_devIntSrc || _workLoop->addEventSource(_devIntSrc) )
{
DLOG("ApplePCCardATA failed create dev intrpt source\n");
return false;
}
_devIntSrc->enable();
return true;
}
#pragma mark -special case overrides-
IOReturn
ApplePCCardATA::handleDeviceInterrupt(void)
{
if( isBusOnline == false)
{
return kIOReturnOffline;
}
if( !_currentCommand )
{
volatile UInt8 status = *_tfStatusCmdReg;
OSSynchronizeIO();
status++; return 0;
}
return super::handleDeviceInterrupt();
}
IOATAController::transState
ApplePCCardATA::determineATAPIState(void)
{
IOATAController::transState drivePhase = super::determineATAPIState();
if( _currentCommand->state > drivePhase
|| _currentCommand->state == kATAStarted )
{
return (IOATAController::transState) _currentCommand->state;
}
return drivePhase;
}
IOReturn
ApplePCCardATA::synchronousIO(void)
{
_devIntSrc->disable();
IOReturn result = super::synchronousIO();
volatile UInt8 status = *_tfStatusCmdReg;
OSSynchronizeIO();
status++;
status = *_tfStatusCmdReg;
OSSynchronizeIO();
status++;
_devIntSrc->enable();
return result;
}
IOReturn
ApplePCCardATA::selectDevice( ataUnitID unit )
{
_devIntSrc->disable();
IOReturn result = super::selectDevice( unit);
_devIntSrc->enable();
return result;
}
#pragma mark -Removable Bus Support-
void
ApplePCCardATA::handleTimeout( void )
{
if( isBusOnline == false)
{
return;
}
super::handleTimeout();
}
IOReturn
ApplePCCardATA::message (UInt32 type, IOService* provider, void* argument)
{
switch( type )
{
case kATARemovedEvent:
DLOG( "ApplePCCardATA got remove event.\n");
if(isBusOnline == true)
{
isBusOnline = false;
_queueState = IOATAController::kQueueLocked;
_devIntSrc->disable();
stopTimer();
_workLoop->removeEventSource(_devIntSrc);
_workLoop->removeEventSource(_timer);
handleQueueFlush();
if( _currentCommand != 0)
{
DLOG( "ApplePCCardATA Calling cleanup bus.\n");
_cmdGate->runAction( (IOCommandGate::Action)
&ApplePCCardATA::cleanUpAction,
0, 0, 0, 0);
}
_workLoop->removeEventSource(_cmdGate);
DLOG( "ApplePCCardATA notify the clients.\n");
terminate( );
getProvider()->close(this);
}
break;
default:
DLOG( "ApplePCCardATA got some other event = %d\n", (int) type);
return super::message(type, provider, argument);
break;
}
return kATANoErr;
}
IOReturn
ApplePCCardATA::handleQueueFlush( void )
{
UInt32 savedQstate = _queueState;
_queueState = IOATAController::kQueueLocked;
IOReturn errPerCommand = kIOReturnError;
if( isBusOnline == false )
{
errPerCommand = kIOReturnOffline;
}
IOATABusCommand* cmdPtr = 0;
while( cmdPtr = dequeueFirstCommand() )
{
cmdPtr->setResult(errPerCommand);
cmdPtr->executeCallback();
}
_queueState = savedQstate;
return kATANoErr;
}
bool
ApplePCCardATA::checkTimeout( void )
{
if( isBusOnline == false )
{
return true;
}
return super::checkTimeout();
}
IOReturn
ApplePCCardATA::executeCommand(IOATADevice* nub, IOATABusCommand* command)
{
if( isBusOnline == false)
{
return kIOReturnOffline;
}
return super::executeCommand(nub, command);
}
void
ApplePCCardATA::cleanUpAction(OSObject * owner,
void * arg0,
void * arg1,
void * ,
void * )
{
ApplePCCardATA* self = (ApplePCCardATA*) owner;
self->cleanUpBus();
}
void
ApplePCCardATA::cleanUpBus(void)
{
if( _currentCommand != 0)
{
_currentCommand->setResult(kIOReturnOffline);
_currentCommand->executeCallback();
_currentCommand = 0;
}
}
UInt32
ApplePCCardATA::scanForDrives( void )
{
DLOG("ApplePCCardATA scanForDrives forcing reset.");
*_tfAltSDevCReg = mATADCRReset;
OSSynchronizeIO();
IODelay( 100 );
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
DLOG("ApplePCCardATA soft reset sequenced\n");
IODelay( 2000 );
UInt32 unitsFound = 0;
UInt8 status = 0x00;
UInt32 milsSpent = 0;
for( int loopMils = 0; milsSpent < 31000; loopMils++ )
{
OSSynchronizeIO();
status = *_tfStatusCmdReg;
if( (status & mATABusy) == 0x00 )
break;
IOSleep( 1 );
milsSpent++;
}
if( ! (milsSpent < 31000) )
{
DLOG("ApplePCCardATA scan failed, bus busy too long\n");
goto AllDone;
}
for( int unit = 0; unit < 1; unit++ )
{
for( int loopMils = 0; milsSpent < 31000; loopMils++ )
{
OSSynchronizeIO();
*_tfSDHReg = ( unit << 4 );
IODelay( 10 );
status = *_tfStatusCmdReg;
if( (status & mATABusy) == 0x00 )
{
break;
}
IOSleep( 1 );
milsSpent++;
}
if( ! (milsSpent < 31000) )
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) )
{
if( *_tfFeatureReg != 0x01 )
{
DLOG("ApplePCCardATA using alternate features address\n");
_tfFeatureReg += 0xc;
if( *_tfFeatureReg != 0x01 )
{
DLOG("ApplePCCardATA alternate features address not working, unknown card type.\n");
break;
}
}
_devInfo[unit].type = kATADeviceType;
_devInfo[unit].packetSend = kATAPIUnknown;
unitsFound++;
}else{
DLOG("ApplePCCardATA scan found unknown signature on unit = %d\n", unit);
DLOG("ApplePCCardATA signature = %x , %x, \n %x, %x, \n %x \n", *_tfCylLoReg, *_tfCylHiReg, *_tfSCountReg, *_tfSectorNReg, *_tfAltSDevCReg );
_devInfo[unit].type = kUnknownATADeviceType;
_devInfo[unit].packetSend = kATAPIUnknown;
}
}
AllDone:
*_tfSDHReg = 0x00;
*_tfAltSDevCReg = 0x00;
OSSynchronizeIO();
_selectedUnit = kATAInvalidDeviceID;
return unitsFound;
}