#include "3C90x.h"
#define super IOEthernetController
OSDefineMetaClassAndStructors( Apple3Com3C90x, IOEthernetController )
static const AdapterInfo *
getAdapterInfo( UInt16 pciDeviceID )
{
UInt32 index = adapterInfoTableCount - 1;
for ( UInt32 i = 0; i < adapterInfoTableCount; i++ )
{
if ( adapterInfoTable[i].deviceID == pciDeviceID )
{
index = i;
break;
}
}
return &adapterInfoTable[index];
}
static void
interruptOccurred( OSObject * target,
IOInterruptEventSource * sender,
int count )
{
((Apple3Com3C90x *) target)->interruptHandler( sender );
}
static void
timeoutOccurred( OSObject * target,
IOTimerEventSource * sender )
{
((Apple3Com3C90x *) target)->timeoutHandler( sender );
}
void Apple3Com3C90x::initPCIConfigSpace()
{
UInt32 reg;
reg = _pciDevice->configRead16( kIOPCIConfigCommand );
reg |= ( kIOPCICommandIOSpace |
kIOPCICommandBusMaster |
kIOPCICommandMemWrInvalidate |
kIOPCICommandParityError |
kIOPCICommandSERR );
reg &= ~kIOPCICommandMemorySpace;
_pciDevice->configWrite16( kIOPCIConfigCommand, reg );
#if 0
reg = _pciDevice->configRead32( kIOPCIConfigCacheLineSize );
reg |= 0xFF00; _pciDevice->configWrite32( kIOPCIConfigCacheLineSize, reg );
#endif
}
bool Apple3Com3C90x::createSupportObjects( IOService * provider )
{
_transmitQueue = getOutputQueue();
if ( _transmitQueue == 0 ) return false;
_mbufCursor = IOMbufLittleMemoryCursor::withSpecification( 1522, 1 );
if ( _mbufCursor == 0 )
return false;
IOWorkLoop * myWorkLoop = (IOWorkLoop *) getWorkLoop();
if ( myWorkLoop == 0 )
return false;
_interruptSrc = IOInterruptEventSource::interruptEventSource(
this,
(IOInterruptEventAction) interruptOccurred,
provider );
if ( (_interruptSrc == 0 ) ||
(myWorkLoop->addEventSource(_interruptSrc) != kIOReturnSuccess) )
{
return false;
}
_interruptSrc->enable();
_timerSrc = IOTimerEventSource::timerEventSource(
this,
(IOTimerEventSource::Action) timeoutOccurred );
if ( (_timerSrc == 0) ||
(myWorkLoop->addEventSource(_timerSrc) != kIOReturnSuccess) )
{
return false;
}
return true;
}
bool Apple3Com3C90x::start( IOService * provider )
{
UInt32 pciReg;
UInt8 irqNumber;
LOG_DEBUG("%s::%s rev:16\n", getName(), __FUNCTION__);
if ( super::start(provider) == false )
return false;
_pciDevice = OSDynamicCast( IOPCIDevice, provider );
if ( _pciDevice == 0 )
goto fail;
_pciDevice->retain();
if ( _pciDevice->open(this) == false )
goto fail;
initPCIConfigSpace();
pciReg = _pciDevice->configRead32( kIOPCIConfigVendorID );
_adapterInfo = getAdapterInfo( (pciReg >> 16) & 0xffff );
_asicType = kASIC_Unknown;
if ( _adapterInfo->deviceID == 0x9055 )
{
UInt8 asicRev;
pciReg = _pciDevice->configRead32( kIOPCIConfigRevisionID );
_asicType = ( (pciReg >> 5) & 0x07 );
switch ( _asicType )
{
case kASIC_40_0502_00x:
asicRev = pciReg & 0x1f;
IOLog("(40-0502-00x, Rev:%x)", asicRev);
break;
case kASIC_40_0483_00x:
asicRev = (pciReg >> 2) & 0x7;
IOLog("(40-0483-00x, Rev:%x)", asicRev);
break;
case kASIC_40_0476_001:
asicRev = (pciReg >> 2) & 0x7;
IOLog("(40-0476-001, Rev:%x)", asicRev);
break;
default:
IOLog("(Unknown ASIC:%02lx)", pciReg);
_asicType = kASIC_40_0502_00x; }
}
pciReg = _pciDevice->configRead32( kIOPCIConfigBaseAddress0 );
_ioBase = pciReg & ~0x01;
LOG_DEBUG("%s: PCI BAR0 = %x\n", getName(), _ioBase);
irqNumber = _pciDevice->configRead32( kIOPCIConfigInterruptLine );
_window = ((UInt8) -1); _rxFilterMask = kFilterIndividual | kFilterBroadcast;
if ( createSupportObjects( provider ) == false )
{
IOLog("%s: createSupportObjects failed\n", getName());
goto fail;
}
if ( resetAndEnable( false ) == false )
{
IOLog("%s: resetAndEnable failed\n", getName());
goto fail;
}
if ( parseEEPROM() == false )
{
IOLog("%s: EEPROM parse error\n", getName());
goto fail;
}
_mediumDict = OSDictionary::withCapacity( 5 );
if ( _mediumDict == 0 )
{
goto fail;
}
probeMediaSupport();
publishMediaCapability( _mediumDict );
getDriverSettings();
if ( allocateMemory() == false )
{
goto fail;
}
_kdpMbuf = allocatePacket( 1 );
if ( _kdpMbuf == 0 )
{
IOLog("%s: KDB mbuf allocation failed\n", getName());
goto fail;
}
IOLog("%s: 3Com EtherLink %s Port 0x%0x IRQ %d\n", getName(),
_adapterInfo->name, _ioBase, irqNumber);
if ( _pciDevice ) _pciDevice->close(this);
if ( attachInterface((IONetworkInterface **) &_netif) == false )
goto fail;
attachDebuggerClient( &_debugger );
return true;
fail:
if ( _pciDevice ) _pciDevice->close(this);
return false;
}
bool Apple3Com3C90x::createWorkLoop()
{
_workLoop = IOWorkLoop::workLoop();
return ( _workLoop != 0 );
}
IOWorkLoop * Apple3Com3C90x::getWorkLoop() const
{
return _workLoop;
}
void Apple3Com3C90x::getDriverSettings()
{
_flowControlEnabled = true;
_extendAfterCollision = false;
_dnBurstThresh = 256;
_dnPriorityThresh = 128;
_txReclaimThresh = 128;
_txStartThresh_10 = 128;
_txStartThresh_100 = 512;
_upBurstThresh = 256;
_upPriorityThresh = 128;
_storeAndForward = false;
_txRingSize = kTxRingSize;
_rxRingSize = kRxRingSize;
_txIntThreshold = _txRingSize / 4;
LOG_DEBUG("%s: Ring sizes: transmit=%ld(%ld), receive=%ld\n",
getName(), _txRingSize, _txIntThreshold, _rxRingSize);
}
bool Apple3Com3C90x::checkEEPROMChecksum()
{
UInt16 data;
int csum = 0;
const UInt32 wordCount = (_adapterInfo->type == kAdapterType3C90x) ?
kEEPROMSizeBoomerang : kEEPROMSizeCyclone;
for ( UInt32 i = 0; i < wordCount; i++ )
{
data = readEEPROM(i);
if (i == (wordCount - 1)) data &= 0xff; csum ^= data;
#ifdef DUMP_EEPROM
IOLog("%02x: 0x%04x\n", i, data);
#endif DUMP_EEPROM
}
csum = (csum ^ (csum >> 8)) & 0xff;
if ( csum != 0 && _adapterInfo->type != kAdapterType3C90xC )
IOLog("%s: WARNING: EEPROM checksum mismatch\n", getName());
return true;
}
bool Apple3Com3C90x::parseEEPROM()
{
if ( checkEEPROMChecksum() == false )
{
IOLog("%s: Invalid EEPROM checksum\n", getName());
return false;
}
getStationAddress( &_etherAddress );
LOG_DEBUG("%s: Station Address = %02x:%02x:%02x:%02x:%02x:%02x\n",
getName(),
_etherAddress.bytes[0],
_etherAddress.bytes[1],
_etherAddress.bytes[2],
_etherAddress.bytes[3],
_etherAddress.bytes[4],
_etherAddress.bytes[5]);
_mediaOptions = getMediaOptions();
LOG_DEBUG("%s: Media options: 0x%04x\n", getName(), _mediaOptions);
_defaultPort = (MediaPort) ReadBitField( InternalConfig, XcvrSelect );
LOG_DEBUG("%s: Default media port: %s\n", getName(),
mediaPortTable[_defaultPort].name);
return true;
}
bool Apple3Com3C90x::resetAndEnable( bool enable )
{
disableAdapterInterrupts();
stopPeriodicTimer();
sendCommandWait( GlobalReset );
resetAdapter();
setLinkStatus( kIONetworkLinkValid );
if ( enable )
{
LOG_DEBUG("%s: enabling adapter\n", getName());
resetAndEnableAdapter( false );
resetMedia( getSelectedMedium() );
bzero( _oldWDCounters, sizeof(_oldWDCounters) );
bzero( _newWDCounters, sizeof(_newWDCounters) );
_oldWDCounters[ kWDInterruptsRetired ] = 1;
_watchdogTimerEnabled = true;
_linkMonitorTimerEnabled = true;
for ( int loops = 10;
loops && ( (_media.linkStatus & kIONetworkLinkActive) == 0 );
loops-- )
{
if ( _media.mediaPort == kMediaPortAuto ) break;
monitorLinkStatus();
IOSleep( 100 );
}
startPeriodicTimer();
enableAdapterInterrupts();
}
return true;
}
void Apple3Com3C90x::free()
{
#define RELEASE(x) do { if(x) { (x)->release(); (x) = 0; } } while(0)
RELEASE( _debugger );
RELEASE( _netif );
RELEASE( _interruptSrc );
RELEASE( _timerSrc );
RELEASE( _mbufCursor );
RELEASE( _pciDevice );
RELEASE( _workLoop );
RELEASE( _mediumDict );
if ( _txRing )
{
for ( UInt32 i = 0; i < _txRingSize; i++ )
{
if ( _txRing[i].drvMbuf )
{
freePacket( _txRing[i].drvMbuf );
_txRing[i].drvMbuf = 0;
}
}
}
if ( _rxRing )
{
for ( UInt32 i = 0; i < _rxRingSize; i++ )
{
if ( _rxRing[i].drvMbuf )
{
freePacket( _rxRing[i].drvMbuf );
_rxRing[i].drvMbuf = 0;
}
}
}
if ( _kdpMbuf )
{
freePacket( _kdpMbuf );
_kdpMbuf = 0;
}
freeDescMemory( &_txRingMem );
freeDescMemory( &_rxRingMem );
super::free();
}
void Apple3Com3C90x::timeoutHandler( IOTimerEventSource * src )
{
if ( _driverEnableCount == 0 ) return;
reserveDebuggerLock();
if ( _linkMonitorTimerEnabled )
{
monitorLinkStatus();
}
if ( _watchdogTimerEnabled )
{
if ( _oldWDCounters[ kWDInterruptsPending ] &&
(( _oldWDCounters[ kWDInterruptsRetired ] +
_newWDCounters[ kWDInterruptsRetired ] ) == 0 ) )
{
IOLog("%s: Watchdog timer expired\n", getName());
if ( resetAndEnable(true) == false )
{
IOLog("%s: WatchDog: resetAndEnable failed\n", getName());
}
_netStats->outputErrors++;
_etherStats->dot3TxExtraEntry.timeouts++;
_etherStats->dot3TxExtraEntry.resets++;
}
else
{
bcopy( _newWDCounters, _oldWDCounters, sizeof(_newWDCounters) );
bzero( _newWDCounters, sizeof(_newWDCounters) );
}
}
releaseDebuggerLock();
src->setTimeoutMS( kPeriodicTimerMSInterval );
}
IOReturn Apple3Com3C90x::setMulticastMode( bool enable )
{
reserveDebuggerLock();
if ( enable )
{
if ( _adapterInfo->type >= kAdapterType3C90xB )
_rxFilterMask |= kFilterMulticastHash;
else
_rxFilterMask |= kFilterMulticast;
}
else
{
_rxFilterMask &= ~( kFilterMulticast | kFilterMulticastHash );
}
sendCommand( SetRxFilter, _rxFilterMask );
releaseDebuggerLock();
return kIOReturnSuccess;
}
IOReturn
Apple3Com3C90x::setMulticastList( IOEthernetAddress * addrs, UInt32 count )
{
reserveDebuggerLock();
setupMulticastHashFilter( addrs, count );
releaseDebuggerLock();
return kIOReturnSuccess;
}
IOReturn Apple3Com3C90x::setPromiscuousMode( bool enable )
{
reserveDebuggerLock();
if ( enable )
_rxFilterMask |= kFilterPromiscuous;
else
_rxFilterMask &= ~kFilterPromiscuous;
sendCommand( SetRxFilter, _rxFilterMask );
releaseDebuggerLock();
return kIOReturnSuccess;
}
IOReturn Apple3Com3C90x::getHardwareAddress( IOEthernetAddress * addr )
{
bcopy( &_etherAddress, addr, sizeof(*addr) );
return kIOReturnSuccess;
}
IOOutputQueue * Apple3Com3C90x::createOutputQueue()
{
return IOGatedOutputQueue::withTarget( this, getWorkLoop() );
}
IOReturn Apple3Com3C90x::enable( IONetworkInterface * netif )
{
LOG_DEBUG("%s::%s(netif)\n", getName(), __FUNCTION__);
if ( _driverEnableCount == 0 )
{
if ( _pciDevice->open(this) != true )
{
return kIOReturnError;
}
if ( resetAndEnable( true ) == false )
{
_pciDevice->close(this);
return kIOReturnIOError;
}
}
_driverEnableCount++;
enableAdapterInterrupts();
_transmitQueue->setCapacity( 512 );
_transmitQueue->start();
_netifEnabled = true;
return kIOReturnSuccess;
}
IOReturn Apple3Com3C90x::disable( IONetworkInterface * netif )
{
LOG_DEBUG("%s::%s(netif)\n", getName(), __FUNCTION__);
_netifEnabled = false;
_transmitQueue->stop();
_transmitQueue->setCapacity( 0 );
_transmitQueue->flush();
disableAdapterInterrupts();
if ( _driverEnableCount && ( --_driverEnableCount == 0 ) )
{
resetAndEnable( false );
if ( _pciDevice )
{
_pciDevice->close( this );
}
}
return kIOReturnSuccess;
}
IOReturn Apple3Com3C90x::enable( IOKernelDebugger * )
{
LOG_DEBUG("%s::%s(debugger)\n", getName(), __FUNCTION__);
if ( _driverEnableCount == 0 )
{
if ( _pciDevice->open( this ) != true )
{
return kIOReturnError;
}
if ( resetAndEnable( true ) == false )
{
_pciDevice->close(this);
return kIOReturnIOError;
}
}
_driverEnableCount++;
return kIOReturnSuccess;
}
IOReturn Apple3Com3C90x::disable( IOKernelDebugger * )
{
LOG_DEBUG("%s::%s(debugger)\n", getName(), __FUNCTION__);
if ( _driverEnableCount && ( --_driverEnableCount == 0 ) )
{
resetAndEnable( false );
if ( _pciDevice )
{
_pciDevice->close( this );
}
}
return kIOReturnSuccess;
}
void Apple3Com3C90x::startPeriodicTimer()
{
if ( _linkMonitorTimerEnabled || _watchdogTimerEnabled )
{
if ( _timerSrc ) _timerSrc->setTimeoutMS( kPeriodicTimerMSInterval );
}
}
void Apple3Com3C90x::stopPeriodicTimer()
{
if ( _timerSrc ) _timerSrc->cancelTimeout();
}
bool Apple3Com3C90x::configureInterface( IONetworkInterface * netif )
{
IONetworkData * data;
if ( super::configureInterface(netif) == false )
return false;
data = netif->getNetworkData( kIONetworkStatsKey );
if (!data || !(_netStats = (IONetworkStats *) data->getBuffer()))
{
return false;
}
data = netif->getNetworkData( kIOEthernetStatsKey );
if (!data || !(_etherStats = (IOEthernetStats *) data->getBuffer()))
{
return false;
}
return true;
}
IOReturn
Apple3Com3C90x::selectMedium(const IONetworkMedium * medium)
{
resetMedia( medium );
return kIOReturnSuccess;
}
IOReturn Apple3Com3C90x::registerWithPolicyMaker( IOService * policyMaker )
{
enum {
kPowerStateOff = 0,
kPowerStateOn,
kPowerStateCount
};
static IOPMPowerState powerStateArray[ kPowerStateCount ] =
{
{ 1,0,0,0,0,0,0,0,0,0,0,0 },
{ 1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0 }
};
IOReturn ret;
ret = policyMaker->registerPowerDriver( this,
powerStateArray,
kPowerStateCount );
return ret;
}
IOReturn Apple3Com3C90x::setPowerState( unsigned long powerStateOrdinal,
IOService * policyMaker )
{
return IOPMAckImplied;
}