#include "RTL8139.h"
#define super IOEthernetController
OSDefineMetaClassAndStructors( com_apple_driver_RTL8139,
IOEthernetController )
bool RTL8139::init( OSDictionary * properties )
{
if ( false == super::init(properties) ) return false;
currentLevel = kActivationLevel0;
currentMediumIndex = MEDIUM_INDEX_NONE;
return true;
}
bool RTL8139::start( IOService * provider )
{
bool success = false;
DEBUG_LOG("start() ===>\n");
do {
if ( false == super::start(provider) )
break;
pciNub = OSDynamicCast( IOPCIDevice, provider );
if ( 0 == pciNub )
break;
pciNub->retain();
if ( false == pciNub->open(this) )
break;
if ( false == initEventSources(provider) )
break;
if ( false == allocateDescriptorMemory() )
break;
csrMap = pciNub->mapDeviceMemoryWithRegister( kIOPCIConfigBaseAddress0 );
if ( 0 == csrMap )
break;
csrBase = (volatile void *) csrMap->getVirtualAddress();
if ( false == initPCIConfigSpace( pciNub ) )
break;
if ( initAdapter( kResetChip ) == false )
{
IOLog("%s: initAdapter() failed\n", getName());
break;
}
phyProbeMediaCapability();
if (false == publishMediumDictionary(mediumDict))
break;
success = true;
}
while ( false );
if (pciNub) pciNub->close(this);
do {
if (false == success)
break;
success = false;
if (false == attachInterface((IONetworkInterface **) &netif, false))
break;
attachDebuggerClient( &debugger );
netif->registerService();
success = true;
}
while ( false );
DEBUG_LOG("start() <===\n");
return success;
}
void RTL8139::stop( IOService * provider )
{
DEBUG_LOG("stop() ===>\n");
super::stop( provider );
DEBUG_LOG("stop() <===\n");
}
bool RTL8139::initEventSources( IOService * provider )
{
DEBUG_LOG("initEventSources() ===>\n");
IOWorkLoop * wl = getWorkLoop();
if ( 0 == wl )
return false;
transmitQueue = getOutputQueue();
if ( 0 == transmitQueue )
return false;
interruptSrc = IOInterruptEventSource::interruptEventSource(this,
OSMemberFunctionCast(IOInterruptEventAction, this,
&RTL8139::interruptOccurred),
provider);
if ( !interruptSrc ||
(wl->addEventSource(interruptSrc) != kIOReturnSuccess) )
return false;
interruptSrc->enable();
timerSrc = IOTimerEventSource::timerEventSource( this,
OSMemberFunctionCast(IOTimerEventSource::Action, this,
&RTL8139::timeoutOccurred ));
if ( !timerSrc || (wl->addEventSource(timerSrc) != kIOReturnSuccess) )
return false;
mediumDict = OSDictionary::withCapacity(5);
if ( 0 == mediumDict )
return false;
DEBUG_LOG("initEventSources() <===\n");
return true;
}
bool RTL8139::initPCIConfigSpace( IOPCIDevice * provider )
{
UInt16 reg;
DEBUG_LOG("pciConfigInit() ===>\n");
reg = provider->configRead16( kIOPCIConfigCommand );
reg |= ( kIOPCICommandBusMaster |
kIOPCICommandIOSpace |
kIOPCICommandMemWrInvalidate );
reg &= ~kIOPCICommandMemorySpace;
provider->configWrite16( kIOPCIConfigCommand, reg );
DEBUG_LOG("pciConfigInit() <===\n");
return true;
}
bool RTL8139::createWorkLoop( void )
{
DEBUG_LOG("createWorkLoop() ===>\n");
workLoop = IOWorkLoop::workLoop();
DEBUG_LOG("createWorkLoop() <===\n");
return ( workLoop != 0 );
}
IOWorkLoop * RTL8139::getWorkLoop( void ) const
{
DEBUG_LOG("getWorkLoop() ===>\n");
DEBUG_LOG("getWorkLoop() <===\n");
return workLoop;
}
bool RTL8139::configureInterface( IONetworkInterface * netif )
{
IONetworkData * data;
DEBUG_LOG("configureInterface() ===>\n");
if ( false == super::configureInterface(netif) )
return false;
data = netif->getParameter( kIONetworkStatsKey );
if ( !data || !(netStats = (IONetworkStats *) data->getBuffer()) )
{
return false;
}
data = netif->getParameter( kIOEthernetStatsKey );
if ( !data || !(etherStats = (IOEthernetStats *) data->getBuffer()) )
{
return false;
}
DEBUG_LOG("configureInterface() <===\n");
return true;
}
void RTL8139::free( void )
{
#define RELEASE(x) do { if(x) { (x)->release(); (x) = 0; } } while(0)
DEBUG_LOG("free() ===>\n");
if ( interruptSrc && workLoop )
{
workLoop->removeEventSource( interruptSrc );
}
RELEASE( netif );
RELEASE( debugger );
RELEASE( interruptSrc );
RELEASE( timerSrc );
RELEASE( csrMap );
RELEASE( mediumDict );
RELEASE( pciNub );
RELEASE( workLoop );
if ( rx_md )
{
rx_md->complete();
rx_md->release();
rx_md = 0;
}
for ( int i = 0; i < kTxBufferCount; i++ )
{
if ( tx_md[i] )
{
tx_md[i]->complete();
tx_md[i]->release();
tx_md[i] = 0;
}
}
super::free();
DEBUG_LOG("free() <===\n");
}
bool RTL8139::enableAdapter( UInt32 level )
{
bool success = false;
DEBUG_LOG("enableAdapter() ===>\n");
DEBUG_LOG("enable level %ld\n", level);
switch ( level )
{
case kActivationLevel1:
if ( (0 == pciNub) || (false == pciNub->open(this)) )
break;
if ( initAdapter( kFullInitialization ) != true )
break;
if ( selectMedium( getSelectedMedium() ) != kIOReturnSuccess )
break;
timerSrc->setTimeoutMS( kWatchdogTimerPeriod );
for ( int i = 0; i < 100; i++ )
{
UInt16 isr = csrRead16( RTL_ISR );
if ( isr & R_ISR_PUN )
{
csrWrite16( RTL_ISR, R_ISR_PUN );
DEBUG_LOG("cleared PUN interrupt %x in %d\n", isr, i);
break;
}
IOSleep(10);
}
success = true;
break;
case kActivationLevel2:
transmitQueue->setCapacity( kTransmitQueueCapacity );
transmitQueue->start();
enableHardwareInterrupts();
success = true;
break;
}
if ( false == success )
IOLog("enable level %ld failed\n", level);
DEBUG_LOG("enableAdapter() <===\n");
return success;
}
bool RTL8139::disableAdapter( UInt32 level )
{
bool success = false;
DEBUG_LOG("disableAdapter() ===>\n");
DEBUG_LOG("disable level %ld\n", level);
switch ( level )
{
case kActivationLevel1:
timerSrc->cancelTimeout();
initAdapter( kResetChip );
phySetMedium( MEDIUM_INDEX_NONE );
setLinkStatus( 0 );
transmitQueue->setCapacity( 0 );
transmitQueue->flush();
if ( pciNub ) pciNub->close(this);
success = true;
break;
case kActivationLevel2:
disableHardwareInterrupts();
transmitQueue->stop();
success = true;
break;
}
if ( false == success )
IOLog("disable level %ld failed\n", level);
DEBUG_LOG("disableAdapter() <===\n");
return success;
}
bool RTL8139::setActivationLevel( UInt32 level )
{
bool success = false;
UInt32 nextLevel;
DEBUG_LOG("setActivationLevel() ===>\n");
DEBUG_LOG("---> CURRENT LEVEL: %ld DESIRED LEVEL: %ld\n",
currentLevel, level);
if (currentLevel == level)
return true;
for ( ; currentLevel > level; currentLevel--)
{
if ( (success = disableAdapter(currentLevel)) == false )
break;
}
for ( nextLevel = currentLevel + 1; currentLevel < level;
currentLevel++, nextLevel++ )
{
if ( (success = enableAdapter(nextLevel)) == false )
break;
}
DEBUG_LOG("---> PRESENT LEVEL: %ld\n\n", currentLevel);
DEBUG_LOG("setActivationLevel() <===\n");
return success;
}
IOReturn RTL8139::enable( IONetworkInterface * netif )
{
DEBUG_LOG("enable(netif) ===>\n");
if ( true == enabledByBSD )
{
DEBUG_LOG("enable() <===\n");
return kIOReturnSuccess;
}
enabledByBSD = setActivationLevel( kActivationLevel2 );
DEBUG_LOG("enable(netif) <===\n");
return enabledByBSD ? kIOReturnSuccess : kIOReturnIOError;
}
IOReturn RTL8139::disable( IONetworkInterface * )
{
DEBUG_LOG("disable(netif) ===>\n");
enabledByBSD = false;
setActivationLevel( enabledByKDP ?
kActivationLevel1 : kActivationLevel0 );
DEBUG_LOG("disable(netif) <===\n");
return kIOReturnSuccess;
}
IOReturn RTL8139::enable( IOKernelDebugger * )
{
if ( enabledByKDP || enabledByBSD )
{
enabledByKDP = true;
return kIOReturnSuccess;
}
enabledByKDP = setActivationLevel( kActivationLevel1 );
return enabledByKDP ? kIOReturnSuccess : kIOReturnIOError;
}
IOReturn RTL8139::disable( IOKernelDebugger * )
{
enabledByKDP = false;
if ( enabledByBSD == false )
setActivationLevel( kActivationLevel0 );
return kIOReturnSuccess;
}
void RTL8139::timeoutOccurred( IOTimerEventSource * timer )
{
phyReportLinkStatus();
timerSrc->setTimeoutMS( kWatchdogTimerPeriod );
}
IOReturn RTL8139::setPromiscuousMode( bool enabled )
{
DEBUG_LOG("setPromiscuousMode() ===>\n");
if ( enabled )
{
reg_rcr |= R_RCR_AAP;
csrWrite32( RTL_MAR0, 0xffffffff );
csrWrite32( RTL_MAR4, 0xffffffff );
}
else
{
reg_rcr &= ~R_RCR_AAP;
csrWrite32( RTL_MAR0, reg_mar0 );
csrWrite32( RTL_MAR4, reg_mar4 );
}
csrWrite32( RTL_RCR, reg_rcr );
DEBUG_LOG("setPromiscuousMode RTL_RCR = 0x%lx\n", reg_rcr );
DEBUG_LOG("setPromiscuousMode() <===\n");
return kIOReturnSuccess;
}
IOReturn RTL8139::setMulticastMode( bool enabled )
{
DEBUG_LOG("setMulticastMode() ===>\n");
DEBUG_LOG("setMulticastMode RTL_RCR = 0x%lx\n", reg_rcr );
DEBUG_LOG("setMulticastMode() <===\n");
return kIOReturnSuccess;
}
static inline UInt32 rtl_ether_crc( int length, const unsigned char * data )
{
static unsigned const ethernet_polynomial = 0x04c11db7U;
int crc = -1;
while (--length >= 0) {
unsigned char current_octet = *data++;
for (int bit = 0; bit < 8; bit++, current_octet >>= 1)
crc = (crc << 1) ^
((crc < 0) ^ (current_octet & 1) ? ethernet_polynomial : 0);
}
return crc;
}
IOReturn RTL8139::setMulticastList( IOEthernetAddress * addrs, UInt32 count )
{
DEBUG_LOG("setMulticastList() ===>\n");
for ( UInt32 i = 0; i < count; i++, addrs++ )
{
int bit = rtl_ether_crc(6, (const UInt8 *) addrs) >> 26;
if (bit < 32)
reg_mar0 |= (1 << bit);
else
reg_mar4 |= (1 << (bit - 32));
}
csrWrite32( RTL_MAR0, reg_mar0 );
csrWrite32( RTL_MAR4, reg_mar4 );
DEBUG_LOG("setMulticastList() <===\n");
return kIOReturnSuccess;
}
void RTL8139::getPacketBufferConstraints(
IOPacketBufferConstraints * constraints ) const
{
DEBUG_LOG("getPacketBufferConstraints() ===>\n");
constraints->alignStart = kIOPacketBufferAlign1; constraints->alignLength = kIOPacketBufferAlign1;
DEBUG_LOG("getPacketBufferConstraints() <===\n");
}
IOReturn RTL8139::getHardwareAddress( IOEthernetAddress * address )
{
union {
UInt8 bytes[4];
UInt32 int32;
} idr;
DEBUG_LOG("getHardwareAddress() ===>\n");
idr.int32 = OSSwapLittleToHostInt32(csrRead32( RTL_IDR0 ));
address->bytes[0] = idr.bytes[0];
address->bytes[1] = idr.bytes[1];
address->bytes[2] = idr.bytes[2];
address->bytes[3] = idr.bytes[3];
idr.int32 = OSSwapLittleToHostInt32(csrRead32( RTL_IDR4 ));
address->bytes[4] = idr.bytes[0];
address->bytes[5] = idr.bytes[1];
DEBUG_LOG("getHardwareAddress() <===\n");
return kIOReturnSuccess;
}
IOOutputQueue * RTL8139::createOutputQueue( void )
{
DEBUG_LOG("createOutputQueue() ===>\n");
DEBUG_LOG("createOutputQueue() <===\n");
return IOGatedOutputQueue::withTarget( this, getWorkLoop() );
}
IOReturn RTL8139::selectMedium( const IONetworkMedium * medium )
{
bool success;
if ( medium == 0 )
medium = phyGetMediumWithIndex( MEDIUM_INDEX_AUTO );
if ( medium == 0 )
return kIOReturnUnsupported;
success = phySetMedium( medium );
if (success)
{
setCurrentMedium( medium );
setLinkStatus( kIONetworkLinkValid );
phyReportLinkStatus( true );
}
return success ? kIOReturnSuccess : kIOReturnIOError;
}
const OSString * RTL8139::newVendorString( void ) const
{
DEBUG_LOG("newVendorString() ===>\n");
DEBUG_LOG("newVendorString() <===\n");
return OSString::withCString("Realtek");
}
const OSString * RTL8139::newModelString( void ) const
{
const char * model = "8139";
DEBUG_LOG("newModelString() ===>\n");
DEBUG_LOG("newModelString() <===\n");
return OSString::withCString(model);
}
IOReturn RTL8139::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 RTL8139::setPowerState( unsigned long powerStateOrdinal,
IOService * policyMaker )
{
DEBUG_LOG("setPowerState state %d\n", powerStateOrdinal);
return IOPMAckImplied;
}