#include "UniNEnet.h"
#include "UniNEnetMII.h"
#include <libkern/OSByteOrder.h>
struct LengthOffset
{
UInt32 setLength, setOffset;
};
LengthOffset gGMACRegisterTemplate[] =
{
{ 0x20, 0x0000 },
{ 0x14, 0x1000 },
{ 0x38, 0x2000 },
{ 0x1C, 0x2100 },
{ 0x14, 0x3000 },
{ 0x2C, 0x4000 },
{ 0x24, 0x4100 },
{ 0x68, 0x6000 },
{ 0xB8, 0x6080 },
{ 0x20, 0x6200 },
{ 0x1C, 0x9000 },
{ 0x10, 0x9050 },
{ 0, 0 }
};
#define super IOEthernetController
OSDefineMetaClassAndStructors( UniNEnet, IOEthernetController ) ;
#pragma mark -
#pragma mark еее Event Logging еее
#pragma mark -
#if USE_ELG
void UniNEnet::AllocateEventLog( UInt32 size )
{
IOPhysicalAddress dartAddr; mach_timespec_t time;
fpELG = (elg*)IOMallocContiguous( size, 0x1000, &dartAddr );
if ( !fpELG )
{
kprintf( "AllocateEventLog - UniNEnet evLog allocation failed " );
return;
}
fpELGMemDesc = IOMemoryDescriptor::withAddress( (UInt32)fpELG, kEvLogSize, kIODirectionOutIn, kernel_task );
fpELGMemDesc->prepare( kIODirectionOutIn );
#ifdef SMEAGOL
fpELG->physAddr = fpELGMemDesc->getPhysicalSegment64( 0, 0x1000 ); #else
fpELG->physAddr = dartAddr;
#endif // SMEAGOL
#define OPEN_FIRMWARE true
#ifdef OPEN_FIRMWARE
IOSetProcessorCacheMode( kernel_task,
(IOVirtualAddress)fpELG,
size,
kIOMapWriteThruCache );
#endif // OPEN_FIRMWARE
bzero( fpELG, size );
fpELG->evLogBuf = (UInt8*)fpELG + sizeof( struct elg );
fpELG->evLogBufe = (UInt8*)fpELG + kEvLogSize - 0x20; fpELG->evLogBufp = fpELG->evLogBuf;
fpELG->evLogFlag = 0x03330333;
IOGetTime( &time );
fpELG->startTimeSecs = time.tv_sec;
IOLog( "\033[32mUniNEnet::AllocateEventLog - buffer=%8x phys=%8x \033[0m \n",
(unsigned int)fpELG, (unsigned int)dartAddr );
return;
}
void UniNEnet::EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str )
{
register UInt32 *lp;
register elg *pe = fpELG;
mach_timespec_t time;
UInt32 lefty;
if ( pe->evLogFlag == 0 )
{
pe->lostEvents++;
return;
}
IOGetTime( &time );
if ( pe->evLogFlag <= kEvLogSize / 0x10 )
--pe->evLogFlag;
else if ( pe->evLogFlag == 0xDebeefed ) {
for ( lp = (UInt32*)pe->evLogBuf; lp < (UInt32*)pe->evLogBufe; lp++ )
*lp = 0xDebeefed;
pe->evLogBufp = pe->evLogBuf; pe->evLogFlag = 0x03330333; }
if ( pe->evLogBufp >= pe->evLogBufe )
{
pe->evLogBufp = pe->evLogBuf;
pe->wrapCount++;
if ( pe->evLogFlag != 0xFeedBeef ) {
pe->evLogFlag = 0;
IOFlushProcessorCache( kernel_task, (IOVirtualAddress)fpELG, kEvLogSize );
return;
}
pe->startTimeSecs = time.tv_sec;
}
lp = (UInt32*)pe->evLogBufp;
pe->evLogBufp += 0x10;
lefty = time.tv_sec << 24; *lp++ = lefty | (time.tv_nsec >> 10); *lp++ = a;
*lp++ = b;
*lp = ascii;
#ifdef OPEN_FIRMWARE
IOFlushProcessorCache( kernel_task, (IOVirtualAddress)(lp - 3), 0x10 );
IOFlushProcessorCache( kernel_task, (IOVirtualAddress)pe, sizeof( elg ) );
#endif // OPEN_FIRMWARE
#ifdef STEPPABLE
if ( pe->evLogFlag == 'step' )
{ static char code[ 5 ] = {0,0,0,0,0};
*(UInt32*)&code = ascii;
IOLog( "%8x UniNEnet: %8x %8x\t%s\n",
time.tv_nsec>>10, (unsigned int)a, (unsigned int)b, code );
IOSleep( 2 );
}
#endif // STEPPABLE
return;
}
UInt32 UniNEnet::Alrt( UInt32 a, UInt32 b, UInt32 ascii, char* str )
{
char work [ 256 ];
char *bp = work;
UInt8 x;
int i;
EvLog( a, b, ascii, str );
EvLog( '****', '****', 'Alrt', "*** Alrt" );
*bp++ = '{'; for ( i = 7; i >= 0; --i )
{
x = a & 0x0F;
if ( x < 10 )
x += '0';
else x += 'A' - 10;
bp[ i ] = x;
a >>= 4;
}
bp += 8;
*bp++ = ' ';
for ( i = 7; i >= 0; --i )
{
x = b & 0x0F;
if ( x < 10 )
x += '0';
else x += 'A' - 10;
bp[ i ] = x;
b >>= 4;
}
bp += 8;
*bp++ = '}';
*bp++ = ' ';
for ( i = sizeof( work ) - (int)(bp - work) - 1; i && (*bp++ = *str++); --i ) ;
bp[ -1 ] = '\n'; *bp = 0;
fpELG->alertCount++;
#ifdef OPEN_FIRMWARE
OSSynchronizeIO();
IOFlushProcessorCache( kernel_task, (IOVirtualAddress)fpELG, kEvLogSize );
#endif // OPEN_FIRMWARE
IOLog( work );
return 0xDeadBeef;
}
#endif // USE_ELG
#pragma mark -
#pragma mark еее override methods еее
#pragma mark -
bool UniNEnet::init( OSDictionary *properties )
{
#if USE_ELG
AllocateEventLog( kEvLogSize );
ELG( this, fpELG, 'GMAC', "UniNEnet::init - event logging set up." );
#endif
if ( super::init( properties ) == false )
return false;
fCellClockEnabled = true;
fAutoNegotiate = true;
fMediumType = kIOMediumEthernetAuto; txRingIndexLast = ~0;
return true;
}
bool UniNEnet::start( IOService *provider )
{
OSString *matchEntry;
OSNumber *numObj;
IOWorkLoop *myWorkLoop = getWorkLoop();
UInt32 x, xFactor;
ELG( IOThreadSelf(), provider, 'Strt', "UniNEnet::start - this, provider." );
matchEntry = OSDynamicCast( OSString, getProperty( gIONameMatchedKey ) );
if ( matchEntry == 0 )
{
ALRT( 0, 0, 'Mat-', "UniNEnet::start: Cannot obtain matching property." );
return false;
}
fBuiltin = matchEntry->isEqualTo( "gmac" ) || matchEntry->isEqualTo( "K2-GMAC" );
fK2 = matchEntry->isEqualTo( "K2-GMAC" );
nub = OSDynamicCast( IOPCIDevice, provider );
if ( !nub || !super::start( provider ) )
return false;
if ( fBuiltin )
{
if ( matchEntry->isEqualTo( "gmac" ) )
keyLargo = waitForService( serviceMatching( "KeyLargo" ) );
else keyLargo = waitForService( serviceMatching( "AppleK2" ) );
if ( keyLargo == 0 )
return false;
keyLargo_resetUniNEthernetPhy = OSSymbol::withCString( "keyLargo_resetUniNEthernetPhy" );
ELG( IOThreadSelf(), keyLargo, 'KeyL', "UniNEnet::start - KeyLargo" );
}
else
{
keyLargo = 0;
}
transmitQueue = (IOGatedOutputQueue*)getOutputQueue();
if ( !transmitQueue )
{
IOLog( "UniNEnet::start - Output queue initialization failed\n" );
return false;
}
transmitQueue->retain();
debugQueue = IOPacketQueue::withCapacity( (UInt)-1 );
if ( !debugQueue )
return false;
fTxMbufCursor = IOMbufBigMemoryCursor::withSpecification( NETWORK_BUFSIZE, MAX_SEGS_PER_TX_MBUF );
if ( !fTxMbufCursor )
{
ALRT( 0, 0, 'tMC-', "UniNEnet::start - Tx IOMbufBigMemoryCursor allocation failure" );
return false;
}
phyId = 0xFF;
fLinkStatus = kLinkStatusUnknown;
myWorkLoop = getWorkLoop();
interruptSource = IOInterruptEventSource::interruptEventSource(
(OSObject*)this,
(IOInterruptEventAction)&UniNEnet::interruptOccurred,
(IOService*)provider,
(int)0 );
if ( interruptSource == NULL )
{ IOLog( "UniNEnet::start: Couldn't allocate Interrupt event source\n" );
return false;
}
if ( myWorkLoop->addEventSource( interruptSource ) != kIOReturnSuccess )
{ IOLog( "UniNEnet::start - Couldn't add Interrupt event source\n" );
return false;
}
timerSource = IOTimerEventSource::timerEventSource(
this,
(IOTimerEventSource::Action)&UniNEnet::timeoutOccurred );
if ( timerSource == NULL )
{
IOLog( "UniNEnet::start - Couldn't allocate timer event source\n" );
return false;
}
if ( myWorkLoop->addEventSource( timerSource ) != kIOReturnSuccess )
{
IOLog( "UniNEnet::start - Couldn't add timer event source\n" );
return false;
}
txDebuggerPkt = allocatePacket( NETWORK_BUFSIZE );
if ( !txDebuggerPkt )
{ ALRT( 0, NETWORK_BUFSIZE, 'KDP-', "UniNEnet::start - Couldn't allocate KDB buffer." );
return false;
}
txDebuggerPkt->m_next = 0;
if ( getHardwareAddress( &myAddress ) != kIOReturnSuccess )
{ ALRT( 0, 0, 'gha-', "UniNEnet::start - getHardwareAddress failed" );
return false;
}
fTxQueueSize = TRANSMIT_QUEUE_SIZE;
fTxRingElements = TX_RING_LENGTH; fTxRingLengthFactor = TX_RING_LENGTH_FACTOR;
fRxRingElements = RX_RING_LENGTH; fRxRingLengthFactor = RX_RING_LENGTH_FACTOR;
numObj = OSDynamicCast( OSNumber, getProperty( kTxQueueSize ) );
if ( numObj )
{
x = numObj->unsigned32BitValue();
if ( x >= 32 && x <= 9999 )
fTxQueueSize = x;
ELG( x, fTxQueueSize, '=txq', "UniNEnet::start - TxQueueSize" );
}
numObj = OSDynamicCast( OSNumber, getProperty( kTxRingElements ) );
if ( numObj )
{
xFactor = 0;
x = numObj->unsigned32BitValue();
switch ( x )
{
case 8192: ++xFactor;
case 4096: ++xFactor;
case 2048: ++xFactor;
case 1024: ++xFactor;
case 512: ++xFactor;
case 256: ++xFactor;
case 128: ++xFactor;
case 64: ++xFactor;
case 32:
fTxRingElements = x;
fTxRingLengthFactor = xFactor;
}
ELG( x, fTxRingElements, '=txe', "UniNEnet::start - TxRingElements" );
}
numObj = OSDynamicCast( OSNumber, getProperty( kRxRingElements ) );
if ( numObj )
{
xFactor = 0;
x = numObj->unsigned32BitValue();
switch ( x )
{
case 8192: ++xFactor;
case 4096: ++xFactor;
case 2048: ++xFactor;
case 1024: ++xFactor;
case 512: ++xFactor;
case 256: ++xFactor;
case 128: ++xFactor;
case 64: ++xFactor;
case 32:
fRxRingElements = x;
fRxRingLengthFactor = xFactor;
}
ELG( x, fRxRingElements, '=rxe', "UniNEnet::start - TxRingElements" );
}
ELG( fTxQueueSize, fTxRingElements << 16 | fRxRingElements, 'parm', "UniNEnet::start - config parms" );
if ( allocateMemory() == false )
{ ALRT( 0, 0, 'alo-', "UniNEnet::start - allocateMemory failed" );
return false;
}
ELG( IOThreadSelf(), 0, 'AttI', "UniNEnet::start - attach interface" );
if ( !attachInterface( (IONetworkInterface**)&networkInterface, false ) )
{ ALRT( 0, 0, 'Att-', "UniNEnet::start - attachInterface failed" );
return false;
}
if ( fBuiltin )
{
ELG( IOThreadSelf(), 0, 'AttD', "UniNEnet::start - attach debugger" );
attachDebuggerClient( &debugger );
}
nub->configWrite32( 0x04, 0x16 );
ioMapEnet = nub->mapDeviceMemoryWithRegister( 0x10 );
if ( ioMapEnet == NULL )
return false;
fpRegs = (GMAC_Registers*)ioMapEnet->getVirtualAddress();
ELG( ioMapEnet, fpRegs, 'Adrs', "UniNEnet::start - base eNet addr" );
fXIFConfiguration = kXIFConfiguration_Tx_MII_OE;
fMACControlConfiguration = 0;
fConfiguration = kConfiguration_TX_DMA_Limit
| kConfiguration_RX_DMA_Limit
| kConfiguration_Infinite_Burst
| kConfiguration_RonPaulBit
| kConfiguration_EnableBug2Fix;
fTxConfiguration = kTxConfiguration_TxFIFO_Threshold
| fTxRingLengthFactor << kTxConfiguration_Tx_Desc_Ring_Size_Shift;
fTxMACConfiguration = 0;
fRxConfiguration = kRxConfiguration_RX_DMA_Threshold
| fRxRingLengthFactor << kRxConfiguration_Rx_Desc_Ring_Size_Shift
| kRxConfiguration_Checksum_Start_Offset;
fRxMACConfiguration = 0;
ELG( IOThreadSelf(), 0, 'RegS', "UniNEnet::start - networkInterface->registerService" );
networkInterface->registerService();
return true;
}
bool UniNEnet::configureInterface( IONetworkInterface *netif )
{
IONetworkData *nd;
ELG( IOThreadSelf(), netif, 'cfig', "configureInterface" );
if ( super::configureInterface( netif ) == false )
return false;
nd = netif->getNetworkData( kIONetworkStatsKey );
if ( !nd || !(fpNetStats = (IONetworkStats*)nd->getBuffer()) )
{
IOLog( "EtherNet(UniN): invalid network statistics\n" );
return false;
}
nd = netif->getParameter( kIOEthernetStatsKey );
if ( !nd || !(fpEtherStats = (IOEthernetStats*)nd->getBuffer()) )
{
IOLog( "UniNEnet::configureInterface - invalid ethernet statistics\n" );
return false;
}
return true;
}
void UniNEnet::free()
{
ELG( this, 0, 'Free', "UniNEnet::free" );
flushRings( true, true );
if ( debugger ) debugger->release();
if ( getWorkLoop() ) getWorkLoop()->disableAllEventSources();
if ( timerSource ) timerSource->release();
if ( interruptSource ) interruptSource->release();
if ( txDebuggerPkt ) freePacket( txDebuggerPkt );
if ( transmitQueue ) transmitQueue->release();
if ( debugQueue ) debugQueue->release();
if ( networkInterface ) networkInterface->release();
if ( fTxMbufCursor ) fTxMbufCursor->release();
if ( fMediumDict ) fMediumDict->release();
if ( ioMapEnet ) ioMapEnet->release();
if ( fTxRingMemDesc )
{ fTxRingMemDesc->complete( kIODirectionOutIn );
fTxRingMemDesc->release();
}
if ( fRxRingMemDesc )
{ fRxRingMemDesc->complete( kIODirectionOutIn );
fRxRingMemDesc->release();
}
if ( fTxDescriptorRing )IOFreeContiguous( (void*)fTxDescriptorRing, fTxRingElements * sizeof( TxDescriptor ) );
if ( fRxDescriptorRing )IOFreeContiguous( (void*)fRxDescriptorRing, fRxRingElements * sizeof( RxDescriptor ) );
if ( fTxMbuf ) IOFree( fTxMbuf, sizeof( mbuf* ) * fTxRingElements );
if ( fRxMbuf ) IOFree( fRxMbuf, sizeof( mbuf* ) * fRxRingElements );
if ( workLoop ) workLoop->release();
if ( keyLargo_resetUniNEthernetPhy )
{
keyLargo_resetUniNEthernetPhy->release();
keyLargo_resetUniNEthernetPhy = 0;
}
super::free();
return;
}
bool UniNEnet::createWorkLoop()
{
workLoop = IOWorkLoop::workLoop();
return ( workLoop != 0 );
}
IOWorkLoop* UniNEnet::getWorkLoop() const
{
return workLoop;
}
void UniNEnet::interruptOccurred( IOInterruptEventSource *src, int )
{
IODebuggerLockState lockState = kIODebuggerLockTaken;
UInt32 interruptStatus;
UInt32 rxMACStatus;
bool doFlushQueue;
bool doService;
if ( fReady == false )
{
if ( fCellClockEnabled == false )
interruptStatus = 0x8BadF00d;
else interruptStatus = READ_REGISTER( Status );
ALRT( this, interruptStatus, 'int-', "interruptOccurred - not ready" );
return;
}
if ( fBuiltin ) lockState = IODebuggerLock( this );
interruptStatus = READ_REGISTER( Status );
ELG( READ_REGISTER( RxCompletion ), interruptStatus, 'Int+', "interruptOccurred - got status" );
rxMACStatus = READ_REGISTER( RxMACStatus );
fRxMACStatus |= rxMACStatus;
if ( interruptStatus & kStatus_Rx_Buffer_Not_Available
|| rxMACStatus & kRX_MAC_Status_Rx_Overflow )
{
ELG( 0, rxMACStatus, 'Rx--', "interruptOccurred - Rx overflow" );
if ( interruptStatus & kStatus_Rx_Buffer_Not_Available )
ETHERNET_STAT_ADD( dot3RxExtraEntry.watchdogTimeouts );
else ETHERNET_STAT_ADD( dot3RxExtraEntry.overruns );
NETWORK_STAT_ADD( inputErrors );
}
fIntStatusForTO |= interruptStatus;
doService = false;
if ( interruptStatus & kStatus_TX_INT_ME )
{
KERNEL_DEBUG( DBG_GEM_TXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );
doService = transmitInterruptOccurred();
KERNEL_DEBUG( DBG_GEM_TXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 );
ETHERNET_STAT_ADD( dot3TxExtraEntry.interrupts );
}
doFlushQueue = false;
{
KERNEL_DEBUG( DBG_GEM_RXIRQ | DBG_FUNC_START, 0, 0, 0, 0, 0 );
doFlushQueue = receivePackets( false );
KERNEL_DEBUG( DBG_GEM_RXIRQ | DBG_FUNC_END, 0, 0, 0, 0, 0 );
ETHERNET_STAT_ADD( dot3RxExtraEntry.interrupts );
}
#ifdef LATER
UInt32 mifStatus;
if ( interruptStatus & kStatus_MIF_Interrupt )
{
mifStatus = READ_REGISTER( MIFStatus ); ELG( 0, mifStatus, '*MIF', "interruptOccurred - MIF interrupt" );
}
#endif // LATER
if ( fBuiltin ) IODebuggerUnlock( lockState );
if ( doFlushQueue )
networkInterface->flushInputQueue();
if ( doService && netifEnabled )
transmitQueue->service( IOBasicOutputQueue::kServiceAsync );
return;
}
UInt32 UniNEnet::outputPacket( struct mbuf *pkt, void *param )
{
IODebuggerLockState lockState = kIODebuggerLockTaken;
UInt32 ret = kIOReturnOutputSuccess;
KERNEL_DEBUG( DBG_GEM_TXQUEUE | DBG_FUNC_NONE, (int)pkt, (int)pkt->m_pkthdr.len, 0, 0, 0 );
ELG( READ_REGISTER( TxFIFOPacketCounter ) << 16 | READ_REGISTER( TxCompletion ),
pkt->m_pkthdr.len,
'OutP', "outputPacket" );
if ( fBuiltin ) lockState = IODebuggerLock( this );
if ( fLinkStatus != kLinkStatusUp )
{
ELG( pkt, fLinkStatus, 'Out-', "UniNEnet::outputPacket - link is down" );
freePacket( pkt );
}
else if ( transmitPacket( pkt ) == false )
{ ret = kIOReturnOutputStall;
}
if ( fBuiltin ) IODebuggerUnlock( lockState );
return ret;
}
void UniNEnet::putToSleep( bool sleepCellClockOnly )
{
IOMediumType mediumType = kIOMediumEthernetNone;
IONetworkMedium *medium;
IODebuggerLockState lockState = kIODebuggerLockTaken;
ELG( fCellClockEnabled, sleepCellClockOnly, 'Slep', "UniNEnet::putToSleep" );
if ( fCellClockEnabled == false )
{
if ( sleepCellClockOnly )
return;
enableCellClock();
}
if ( fBuiltin ) lockState = IODebuggerLock( this );
fReady = false;
if ( timerSource )
timerSource->cancelTimeout();
WRITE_REGISTER( InterruptMask, kInterruptMask_None );
if ( getWorkLoop() )
getWorkLoop()->disableAllInterrupts();
medium = IONetworkMedium::getMediumWithType( fMediumDict, mediumType );
setLinkStatus( kIONetworkLinkValid, medium, 0 );
if ( sleepCellClockOnly == false )
{
stopChip(); stopPHY(); }
flushRings( true, false );
if ( fBuiltin && (!fWOL || sleepCellClockOnly) )
disableCellClock();
if ( sleepCellClockOnly )
timerSource->setTimeoutMS( WATCHDOG_TIMER_MS );
if ( fBuiltin ) IODebuggerUnlock( lockState );
return;
}
bool UniNEnet::wakeUp( bool wakeCellClockOnly )
{
IOMediumType mediumType = kIOMediumEthernetNone;
IONetworkMedium *medium;
IODebuggerLockState lockState = kIODebuggerLockTaken;
UInt16 status, control;
UInt32 gemReg;
ELG( this, wakeCellClockOnly, 'Wake', "UniNEnet::wakeUp" );
if ( fBuiltin ) lockState = IODebuggerLock( this );
fReady = false;
medium = IONetworkMedium::getMediumWithType( fMediumDict, mediumType );
setLinkStatus( kIONetworkLinkValid, medium, 0 );
if ( fBuiltin )
enableCellClock();
if ( !wakeCellClockOnly )
{
nub->configWrite32( 0x04, 0x16 ); }
if ( !initRxRing() || !initTxRing() )
goto wakeUp_exit;
WRITE_REGISTER( SoftwareReset, kSoftwareReset_TX | kSoftwareReset_RX );
do
{ gemReg = READ_REGISTER( SoftwareReset );
}
while( gemReg & (kSoftwareReset_TX | kSoftwareReset_RX) );
initChip();
if ( !wakeCellClockOnly )
{
if ( fBuiltin )
{
keyLargo->callPlatformFunction( keyLargo_resetUniNEthernetPhy, false, 0, 0, 0, 0 );
ELG( keyLargo, phyId, 'hwRP', "UniNEnet::wakeUp - hardware reset the PHY." );
phyId = 0;
if ( fK2 ) phyId = 1;
miiResetPHY();
miiReadWord( &status, MII_STATUS );
miiReadWord( &control, MII_CONTROL );
if ( status == 0xFFFF && control == 0xFFFF )
{ phyId = 0xFF;
goto wakeUp_exit;
}
getPhyType(); }
if ( !fMediumDict && createMediumTables() == false )
{
ALRT( 0, 0, 'cmt-', "UniNEnet::start - createMediumTables failed" );
goto wakeUp_exit;
}
startPHY();
if ( fBuiltin )
miiInitializePHY();
}
if ( fMediumType != kIOMediumEthernetAuto )
{
medium = IONetworkMedium::getMediumWithType( fMediumDict, fMediumType );
selectMedium( medium ); }
timerSource->setTimeoutMS( WATCHDOG_TIMER_MS );
if ( getWorkLoop() )
getWorkLoop()->enableAllInterrupts();
fRxMACStatus = 0;
fReady = true;
if ( !wakeCellClockOnly )
monitorLinkStatus( true );
wakeUp_exit:
if ( fBuiltin ) IODebuggerUnlock( lockState );
return true;
}
IOReturn UniNEnet::enable( IONetworkInterface* netif )
{
ELG( this, netif, 'NetE', "UniNEnet::enable( netInterface* )" );
if ( netifEnabled )
{
IOLog( "EtherNet(UniN): already enabled\n" );
return kIOReturnSuccess;
}
if ( (fReady == false) && !wakeUp( false ) )
return kIOReturnIOError;
netifEnabled = true;
transmitQueue->setCapacity( TRANSMIT_QUEUE_SIZE );
transmitQueue->start();
return kIOReturnSuccess;
}
IOReturn UniNEnet::disable( IONetworkInterface* )
{
#if USE_ELG
#endif // USE_ELG
ELG( this, debugEnabled, 'NetD', "disable( IONetworkInterface* )" );
transmitQueue->stop();
transmitQueue->setCapacity( 0 );
transmitQueue->flush();
if ( debugEnabled == false )
putToSleep( false );
netifEnabled = false;
return kIOReturnSuccess;
}
IOReturn UniNEnet::enable( IOKernelDebugger* )
{
ELG( this, fReady, 'DbgE', "UniNEnet::enable( IOKernelDebugger* )" );
if ( (fReady == false) && !wakeUp( false ) )
return kIOReturnIOError;
debugEnabled = true;
return kIOReturnSuccess;
}
IOReturn UniNEnet::disable( IOKernelDebugger* )
{
ELG( this, netifEnabled, 'DbgD', "UniNEnet::disable( IOKernelDebugger* )" );
debugEnabled = false;
if ( netifEnabled == false )
putToSleep( false );
return kIOReturnSuccess;
}
IOReturn UniNEnet::getPacketFilters( const OSSymbol *group, UInt32 *filters ) const
{
if ( group == gIOEthernetWakeOnLANFilterGroup )
{
if ( fBuiltin )
*filters = kIOEthernetWakeOnMagicPacket;
else *filters = 0;
return kIOReturnSuccess;
}
return super::getPacketFilters( group, filters );
}
IOReturn UniNEnet::setWakeOnMagicPacket( bool active )
{
ELG( this, active, 'WoMP', "UniNEnet::setWakeOnMagicPacket" );
fWOL = active;
return kIOReturnSuccess;
}
void UniNEnet::timeoutOccurred( IOTimerEventSource* )
{
IODebuggerLockState lockState;
bool doService = false;
UInt32 txRingIndex;
UInt32 x;
ELG( txCommandHead << 16 | txCommandTail, fCellClockEnabled, 'Time', "UniNEnet::timeoutOccurred" );
if ( fLoopback )
return;
if ( fCellClockEnabled == false )
{
monitorLinkStatus( false );
timerSource->setTimeoutMS( WATCHDOG_TIMER_MS );
return;
}
x = READ_REGISTER( TxMACStatus );
fRxMACStatus |= READ_REGISTER( RxMACStatus );
ELG( x, fRxMACStatus, 'MACS', "timeoutOccurred - Tx and Rx MAC Status regs" );
x = READ_REGISTER( LengthErrorCounter );
if ( x )
{ WRITE_REGISTER( LengthErrorCounter, 0 );
fpEtherStats->dot3StatsEntry.frameTooLongs += x;
}
x = READ_REGISTER( AlignmentErrorCounter );
if ( x )
{ WRITE_REGISTER( AlignmentErrorCounter, 0 );
fpEtherStats->dot3StatsEntry.alignmentErrors += x;
}
x = READ_REGISTER( FCSErrorCounter );
if ( x )
{ WRITE_REGISTER( FCSErrorCounter, 0 );
fpEtherStats->dot3StatsEntry.fcsErrors += x;
}
x = READ_REGISTER( RxCodeViolationErrorCounter );
if ( x )
{ WRITE_REGISTER( RxCodeViolationErrorCounter, 0 );
fpEtherStats->dot3StatsEntry.internalMacTransmitErrors += x;
}
x = READ_REGISTER( FirstAttemptSuccessfulCollisionCounter );
if ( x )
{ WRITE_REGISTER( FirstAttemptSuccessfulCollisionCounter, 0 );
fpEtherStats->dot3StatsEntry.singleCollisionFrames += x;
}
x = READ_REGISTER( ExcessiveCollisionCounter );
if ( x )
{ WRITE_REGISTER( ExcessiveCollisionCounter, 0 );
fpEtherStats->dot3StatsEntry.excessiveCollisions += x;
}
x = READ_REGISTER( LateCollisionCounter );
if ( x )
{ WRITE_REGISTER( LateCollisionCounter, 0 );
fpEtherStats->dot3StatsEntry.lateCollisions += x;
}
lockState = IODebuggerLock( this );
if ( (fIntStatusForTO & (kStatus_TX_DONE | kStatus_RX_DONE)) == 0 )
monitorLinkStatus( false );
if ( fLinkStatus == kLinkStatusDown )
{
putToSleep( true );
timerSource->setTimeoutMS( WATCHDOG_TIMER_MS );
IODebuggerUnlock( lockState );
return;
}
txRingIndex = READ_REGISTER( TxCompletion );
if ( (fIntStatusForTO & kStatus_TX_DONE) || (txCommandHead == txCommandTail) )
{
txWDCount = 0;
}
else
{
if ( txRingIndex == txRingIndexLast )
{
txWDCount++;
}
else
{
txWDCount = 0;
}
if ( txWDCount >= 8 )
{
transmitInterruptOccurred();
doService = true;
if ( txCommandHead == txCommandTail )
txWDCount = 0;
else
{
UInt32 intStatus, compReg, kickReg;
intStatus = READ_REGISTER( StatusAlias ); compReg = READ_REGISTER( TxCompletion );
kickReg = READ_REGISTER( TxKick );
if ( compReg != kickReg )
{
ELG( txWDCount, kickReg << 16 | compReg, 'Tx--', "UniNEnet::timeoutOccurred - Tx Int Timeout" );
restartTransmitter();
txWDCount = 0;
}
}
}
}
txRingIndexLast = txRingIndex;
if ( fIntStatusForTO & kStatus_RX_DONE )
{
rxWDCount = 0; }
else if ((rxWDCount++ > (5 * 1000 / WATCHDOG_TIMER_MS)) && (fRxMACStatus & kRX_MAC_Status_Rx_Overflow) ) {
restartReceiver();
NETWORK_STAT_ADD( inputErrors );
ETHERNET_STAT_ADD( dot3RxExtraEntry.watchdogTimeouts );
fRxMACStatus = 0; rxWDCount = 0; }
fIntStatusForTO = 0;
if ( debugTxPoll )
{
debugQueue->flush(); debugTxPoll = false;
doService = true;
}
IODebuggerUnlock( lockState );
if ( doService && netifEnabled )
transmitQueue->service( IOBasicOutputQueue::kServiceAsync );
timerSource->setTimeoutMS( WATCHDOG_TIMER_MS );
return;
}
const OSString* UniNEnet::newVendorString() const
{
return OSString::withCString( "Apple" );
}
const OSString* UniNEnet::newModelString() const
{
return OSString::withCString( "gmac+" );
}
const OSString* UniNEnet::newRevisionString() const
{
return OSString::withCString( "" );
}
IOReturn UniNEnet::setPromiscuousMode( bool active )
{
IODebuggerLockState lockState = kIODebuggerLockTaken;
ELG( 0, active, 'SetP', "setPromiscuousMode" );
if ( fBuiltin ) lockState = IODebuggerLock( this );
fIsPromiscuous = active;
if ( fCellClockEnabled )
{
if ( active )
{
fRxMACConfiguration |= kRxMACConfiguration_Promiscuous;
fRxMACConfiguration &= ~kRxMACConfiguration_Strip_FCS;
}
else
{
fRxMACConfiguration &= ~kRxMACConfiguration_Promiscuous;
fRxMACConfiguration |= kRxMACConfiguration_Strip_FCS;
}
WRITE_REGISTER( RxMACConfiguration, fRxMACConfiguration );
}
if ( fBuiltin ) IODebuggerUnlock( lockState );
return kIOReturnSuccess;
}
IOReturn UniNEnet::setMulticastMode( bool active )
{
ELG( this, active, 'SetM', "setMulticastMode" );
multicastEnabled = active;
return kIOReturnSuccess;
}
IOReturn UniNEnet::setMulticastList(IOEthernetAddress *addrs, UInt32 count)
{
IODebuggerLockState lockState = kIODebuggerLockTaken;
ELG( addrs, count, 'SetL', "setMulticastList" );
if ( fCellClockEnabled == false )
return kIOReturnSuccess;
if ( fBuiltin ) lockState = IODebuggerLock( this );
resetHashTableMask();
for (UInt32 i = 0; i < count; i++)
{
addToHashTableMask(addrs->bytes);
addrs++;
}
updateHashTableMask();
if ( fBuiltin ) IODebuggerUnlock( lockState );
return kIOReturnSuccess;
}
IOOutputQueue* UniNEnet::createOutputQueue()
{
return IOBasicOutputQueue::withTarget( this, TRANSMIT_QUEUE_SIZE );
}
bool UniNEnet::createMediumTables()
{
IONetworkMedium *medium;
UInt32 i;
fMediumDict = OSDictionary::withCapacity( fMediumTableCount );
ELG( 0, fMediumDict, 'MTbl', "createMediumTables" );
if ( fMediumDict == 0 )
return false;
for ( i = 0; i < fMediumTableCount; i++ )
{
medium = IONetworkMedium::medium( fpgMediumTable[i].type, fpgMediumTable[i].speed );
IONetworkMedium::addMedium( fMediumDict, medium );
medium->release();
}
if ( publishMediumDictionary( fMediumDict ) != true )
return false;
medium = IONetworkMedium::getMediumWithType( fMediumDict, kIOMediumEthernetAuto );
setCurrentMedium( medium );
return true;
}
IOReturn UniNEnet::selectMedium( const IONetworkMedium *medium )
{
IOMediumType mType = medium->getType();
UInt16 controlReg; IOReturn ior;
bool gotReg;
IOMediumType oldMediumType = fMediumType;
if ( fCellClockEnabled == false )
wakeUp( true );
gotReg = miiReadWord( &controlReg, MII_CONTROL );
ALRT( controlReg, mType, 'sMed', "selectMedium" );
if ( !gotReg || controlReg == 0xFFFF )
{
ALRT( fPHYType, controlReg, 'Pnr-', "UniNEnet::selectMedium - PHY not responding" );
return kIOReturnIOError;
}
if ( (mType & kIOMediumNetworkTypeMask) != kIOMediumEthernet )
{
ALRT( fPHYType, controlReg, 'sMe-', "UniNEnet::selectMedium - not ethernet medium" );
return kIOReturnBadArgument;
}
fMediumType = mType;
if ( (mType & kIOMediumOptionLoopback) ^ (controlReg & MII_CONTROL_LOOPBACK) )
{
setLinkStatus( kIONetworkLinkValid );
fPHYStatus = 0;
fLinkStatus = kLinkStatusUnknown;
ELG( 0, mType & kIOMediumOptionLoopback, 'LpBk', "UniNEnet::selectMedium - changing loopback mode." );
}
if ( mType & kIOMediumOptionLoopback )
{ fLoopback = true;
ior = kIOReturnIOError;
}
else
{ fLoopback = false;
ior = negotiateSpeedDuplex();
}
if ( ior != kIOReturnSuccess )
{
ior = forceSpeedDuplex();
if ( ior != kIOReturnSuccess )
{ fMediumType = oldMediumType; return ior;
}
fAutoNegotiate = false;
}
setSelectedMedium( medium );
ELG( fMediumType, fXIFConfiguration, 'sMe+', "UniNEnet::selectMedium - returning kIOReturnSuccess" );
monitorLinkStatus( true );
return kIOReturnSuccess;
}
IOReturn UniNEnet::negotiateSpeedDuplex()
{
UInt16 controlReg; UInt16 statusReg; UInt16 anar; UInt16 gigReg; IOMediumType mType, mtyp; bool br;
mType = fMediumType & (kIOMediumNetworkTypeMask | kIOMediumSubTypeMask | kIOMediumCommonOptionsMask);
mtyp = mType & ~(kIOMediumOptionLoopback | kIOMediumOptionFlowControl);
controlReg = MII_CONTROL_AUTONEGOTIATION | MII_CONTROL_RESTART_NEGOTIATION;
br = miiReadWord( &anar, MII_ADVERTISEMENT );
ELG( anar, mType, 'n SD', "UniNEnet::negotiateSpeedDuplex" );
anar &= ~( MII_ANAR_100BASET4
| MII_ANAR_100BASETX_FD
| MII_ANAR_100BASETX
| MII_ANAR_10BASET_FD
| MII_ANAR_10BASET );
switch ( mtyp )
{
case kIOMediumEthernetAuto:
anar |= ( MII_ANAR_100BASETX_FD
| MII_ANAR_100BASETX
| MII_ANAR_10BASET_FD
| MII_ANAR_10BASET );
break;
case kIOMediumEthernet10BaseT | kIOMediumOptionFullDuplex: anar |= MII_ANAR_10BASET_FD;
break;
case kIOMediumEthernet10BaseT | kIOMediumOptionHalfDuplex: anar |= MII_ANAR_10BASET;
break;
case kIOMediumEthernet100BaseTX | kIOMediumOptionFullDuplex: anar |= MII_ANAR_100BASETX_FD;
break;
case kIOMediumEthernet100BaseTX | kIOMediumOptionHalfDuplex: anar |= MII_ANAR_100BASETX;
break;
case kIOMediumEthernet1000BaseTX | kIOMediumOptionFullDuplex: case kIOMediumEthernet1000BaseTX | kIOMediumOptionHalfDuplex: break;
default:
ELG( 0, 0, ' ?sd', "UniNEnet::negotiateSpeedDuplex - not 10 nor 100 combo." );
break;
}
miiWriteWord( anar, MII_ADVERTISEMENT );
switch ( fPHYType )
{
case 0x0971: case 0x5201: case 0x5221:
break;
case 0x1011:
br = miiReadWord( &gigReg, MII_MARVELL_PHY_SPECIFIC_CONTROL );
gigReg |= MII_MARVELL_PHY_SPECIFIC_CONTROL_AUTOL_MDIX;
miiWriteWord( gigReg, MII_MARVELL_PHY_SPECIFIC_CONTROL );
controlReg |= MII_CONTROL_RESET;
case 0x5400: case 0x5401:
case 0x5411:
case 0x5421:
br = miiReadWord( &gigReg, MII_1000BASETCONTROL );
gigReg &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP | MII_1000BASETCONTROL_HALFDUPLEXCAP);
switch ( mtyp )
{ case kIOMediumEthernetAuto:
case kIOMediumEthernet1000BaseTX | kIOMediumOptionFullDuplex:
gigReg |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
break;
case kIOMediumEthernet1000BaseTX | kIOMediumOptionHalfDuplex:
gigReg |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
break;
}
miiWriteWord( gigReg, MII_1000BASETCONTROL );
break;
case ' GEM': default:
break;
}
miiWriteWord( controlReg, MII_CONTROL );
br = miiWaitForAutoNegotiation();
if ( br == false )
return kIOReturnIOError;
miiReadWord( &statusReg, MII_STATUS );
if ( !(statusReg & MII_STATUS_LINK_STATUS) )
return kIOReturnIOError;
return kIOReturnSuccess;
}
IOReturn UniNEnet::forceSpeedDuplex()
{
IOMediumType mType;
UInt16 controlReg = 0; UInt16 statusReg; UInt16 gigReg; bool br;
mType = fMediumType & (kIOMediumNetworkTypeMask | kIOMediumSubTypeMask | kIOMediumCommonOptionsMask);
ELG( 0, mType, 'f SD', "UniNEnet::forceSpeedDuplex" );
switch ( mType & (kIOMediumNetworkTypeMask | kIOMediumSubTypeMask) )
{
case kIOMediumEthernetAuto:
return kIOReturnIOError;
case kIOMediumEthernet10BaseT:
break;
case kIOMediumEthernet100BaseTX:
controlReg = MII_CONTROL_SPEED_SELECTION;
break;
case kIOMediumEthernet1000BaseTX:
controlReg = MII_CONTROL_SPEED_SELECTION_2;
break;
}
if ( mType & kIOMediumOptionFullDuplex ) controlReg |= MII_CONTROL_FULLDUPLEX;
if ( mType & kIOMediumOptionLoopback ) controlReg |= MII_CONTROL_LOOPBACK;
switch ( fPHYType )
{
case 0x1011:
br = miiReadWord( &gigReg, MII_MARVELL_PHY_SPECIFIC_CONTROL );
gigReg &= ~(MII_MARVELL_PHY_SPECIFIC_CONTROL_AUTOL_MDIX
| MII_MARVELL_PHY_SPECIFIC_CONTROL_MANUAL_MDIX);
miiWriteWord( gigReg, MII_MARVELL_PHY_SPECIFIC_CONTROL );
controlReg |= MII_CONTROL_RESET;
break;
case 0x0971: case 0x5201: case 0x5221:
case 0x5400:
case 0x5401:
case 0x5411:
case 0x5421:
case ' GEM':
default: miiWriteWord( MII_CONTROL_RESET, MII_CONTROL );
IOSleep( 3 );
break;
}
miiWriteWord( controlReg, MII_CONTROL );
if ( controlReg & MII_CONTROL_SPEED_SELECTION_2 )
fXIFConfiguration |= kXIFConfiguration_GMIIMODE; else fXIFConfiguration &= kXIFConfiguration_GMIIMODE; WRITE_REGISTER( XIFConfiguration, fXIFConfiguration );
if ( fLoopback )
{
IOSleep( 5 );
return kIOReturnSuccess;
}
for ( int i = 0; i < 5000; i += 10 )
{
miiReadWord( &statusReg, MII_STATUS );
if ( statusReg & MII_STATUS_LINK_STATUS )
return kIOReturnSuccess; IOSleep( 10 );
}
return kIOReturnIOError;
}
IOReturn UniNEnet::getChecksumSupport( UInt32 *checksumMask,
UInt32 checksumFamily,
bool )
{
if ( checksumFamily != kChecksumFamilyTCPIP )
return kIOReturnUnsupported;
*checksumMask = kChecksumTCPSum16;
return kIOReturnSuccess;
}
void UniNEnet::writeRegister( volatile UInt32 *pReg, UInt32 data )
{
if ( fCellClockEnabled == false )
{
ALRT( data, pReg, 'Wrg-', "writeRegister: enet clock is disabled" );
return;
}
if ( pReg != &fpRegs->MIFBitBangFrame_Output )
ELG( data, (UInt32)pReg - (UInt32)fpRegs, 'wReg', "writeRegister" );
OSWriteLittleInt32( pReg, 0, data );
return;
}
void UniNEnet::enableCellClock()
{
ELG( 0, 0, '+Clk', "UniNEnet::enableCellClock" );
callPlatformFunction( "EnableUniNEthernetClock", true, (void*)true, (void*)nub, 0, 0 );
OSSynchronizeIO();
IODelay( 3 ); fCellClockEnabled = true;
return;
}
void UniNEnet::disableCellClock()
{
fCellClockEnabled = false;
ELG( 0, 0, '-Clk', "UniNEnet::disableCellClock - disabling cell clock!!!" );
OSSynchronizeIO();
callPlatformFunction( "EnableUniNEthernetClock", true, (void*)false, (void*)nub, 0, 0 );
OSSynchronizeIO();
return;
}
IOReturn UniNEnet::newUserClient( task_t owningTask,
void*, UInt32 type, IOUserClient **handler ) {
IOReturn ior = kIOReturnSuccess;
UniNEnetUserClient *client = NULL;
ELG( type, type, 'Usr+', "UniNEnet::newUserClient" );
if ( type != 'GMAC' )
{ ELG( 0, type, 'Usr-', "UniNEnet::newUserClient - unlucky." );
return kIOReturnError;
}
client = UniNEnetUserClient::withTask( owningTask );
if ( !client )
{
ELG( 0, 0, 'usr-', "UniNEnet::newUserClient: Can't create user client" );
return kIOReturnError;
}
if ( ior == kIOReturnSuccess )
{ if ( client->attach( this ) == false )
{
ior = kIOReturnError;
ELG( 0, 0, 'USR-', "UniNEnet::newUserClient: Can't attach user client" );
}
}
if ( ior == kIOReturnSuccess )
{ if ( client->start( this ) == false )
{
ior = kIOReturnError;
ELG( 0, 0, 'USR-', "UniNEnet::newUserClient: Can't start user client" );
}
}
if ( client && (ior != kIOReturnSuccess) )
{
client->detach( this );
client->release();
client = 0;
}
*handler = client;
return ior;
}
#pragma mark -
#pragma mark еее User Client еее
#pragma mark -
#undef super
#define super IOUserClient
OSDefineMetaClassAndStructors( UniNEnetUserClient, IOUserClient ) ;
UniNEnetUserClient* UniNEnetUserClient::withTask( task_t owningTask )
{
UniNEnetUserClient* me = new UniNEnetUserClient;
if ( me && me->init() == false )
{
me->release();
return 0;
}
me->fTask = owningTask;
return me;
}
bool UniNEnetUserClient::start( IOService *provider )
{
if ( super::start( provider ) == false ) return false;
if ( provider->open( this ) == false ) return false;
fProvider = (UniNEnet*)provider;
UC_ELG( 0, provider, 'UC S', "UniNEnetUserClient::start" );
fMethods[0].object = this;
fMethods[0].func = (IOMethod)&UniNEnetUserClient::doRequest;
fMethods[0].count0 = 0xFFFFFFFF;
fMethods[0].count1 = 0xFFFFFFFF;
fMethods[0].flags = kIOUCStructIStructO;
return true;
}
IOReturn UniNEnetUserClient::clientClose()
{
if ( fProvider )
{
UC_ELG( 0, 0, 'UC C', "UniNEnetUserClient::clientClose" );
if ( fmap ) { fmap-> release(); fmap = 0; }
if ( fProvider->isOpen( this ) )
fProvider->close( this );
detach( fProvider );
fProvider = 0;
}
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::clientDied()
{
if ( fProvider )
{
UC_ELG( 0, fProvider, 'UC D', "UniNEnetUserClient::clientDied" );
if ( fmap ) { fmap-> release(); fmap = 0; }
}
return clientClose();
}
IOReturn UniNEnetUserClient::connectClient( IOUserClient *client )
{
UC_ELG( 0, 0, 'uCon', "UniNEnetUserClient::connectClient - connect client" );
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::registerNotificationPort( mach_port_t port, UInt32 type )
{
UC_ELG( 0, 0, 'uRNP', "UniNEnetUserClient - register notification ignored" );
return kIOReturnUnsupported;
}
IOExternalMethod* UniNEnetUserClient::getExternalMethodForIndex( UInt32 index )
{
IOExternalMethod *result = NULL;
if ( index == 0 )
result = &fMethods[0];
UC_ELG( result, index, 'uXMi', "UniNEnetUserClient::getExternalMethodForIndex - get external method" );
return result;
}
IOReturn UniNEnetUserClient::doRequest( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *pOutPutSize )
{
UInt32 reqID;
IOReturn ior;
bool cellClockEnabled;
UC_ELG( inputSize, (UInt32)pIn, 'uRqI', "UniNEnetUserClient::doRequest - input parameters" );
UC_ELG( pOutPutSize ? *pOutPutSize : 0xDeadBeef, (UInt32)pOut, 'uRqO', "UniNEnetUserClient::doRequest - output parameters" );
if ( !pIn || (inputSize < sizeof( UCRequest )) )
{
IOLog( "UniNEnetUserClient::doRequest - bad argument(s) - pIn = %lx, inputSize = %ld.\n",
(UInt32)pIn, inputSize );
return kIOReturnBadArgument;
}
cellClockEnabled = fProvider->fCellClockEnabled;
if ( cellClockEnabled == false )
fProvider->enableCellClock();
reqID = *(UInt32*)pIn;
switch ( reqID )
{
case kGMACUserCmd_GetLog: ior = getGMACLog( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_GetRegs: ior = getGMACRegs( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_GetOneReg: ior = getOneGMACReg( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_GetTxRing: ior = getGMACTxRing( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_GetRxRing: ior = getGMACRxRing( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_WriteOneReg: ior = writeOneGMACReg( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_ReadAllMII: ior = readAllMII( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_ReadMII: ior = readMII( pIn, pOut, inputSize, pOutPutSize ); break;
case kGMACUserCmd_WriteMII: ior = writeMII( pIn, pOut, inputSize, pOutPutSize ); break;
default:
IOLog( "UniNEnetUserClient::doRequest - Bad command, %lx\n", reqID );
UC_ELG( 0, reqID, 'uRq?', "UniNEnetUserClient::doRequest - unknown" );
ior = kIOReturnBadArgument;
break;
}
if ( cellClockEnabled == false )
fProvider->disableCellClock();
return ior;
}
#if USE_ELG
IOReturn UniNEnetUserClient::getGMACLog( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *pOutPutSize )
{
UCRequest *req = (UCRequest*)pIn;
IOVirtualAddress clientAddr;
UC_ELG( (UInt32)pIn, inputSize, 'UgLg', "UniNEnetUserClient::getGMACLog" );
fmap = fProvider->fpELGMemDesc->map( fTask, 0, kIOMapAnywhere );
clientAddr = fmap->getVirtualAddress();
req->pLogBuffer = (UInt8*)clientAddr;
req->bufSize = (fProvider->fpELG->evLogBufe - fProvider->fpELG->evLogBuf + PAGE_SIZE) & ~(PAGE_SIZE - 1);
UC_ELG( req->bufSize, req->pLogBuffer, '=uBf', "UniNEnetUserClient::getGMACLog - user buffer" );
bcopy( req, pOut, sizeof( UCRequest ) );
*pOutPutSize = sizeof( UCRequest );
return kIOReturnSuccess;
}
#else // Production driver - no event logging buffer:
IOReturn UniNEnetUserClient::getGMACLog( void*, void*, IOByteCount, IOByteCount* )
{
return kIOReturnBadArgument;
}
#endif // USE_ELG
IOReturn UniNEnetUserClient::getGMACRegs( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *pOutPutSize )
{
LengthOffset *pTemplate;
UInt8 *src;
UInt8 *dest = (UInt8*)pOut;
UInt32 len;
UInt32 lowRegs[ 8 ];
UInt32 *pl;
if ( !pOut || (*pOutPutSize < PAGE_SIZE) ) {
IOLog( "UniNEnetUserClient::getGMACRegs - bad argument(s) - pOut = %lx, *pOutPutSize = %ld.\n",
(UInt32)pOut, (UInt32)*pOutPutSize );
return kIOReturnBadArgument;
}
if ( !fProvider->fCellClockEnabled )
return kIOReturnNoPower;
for ( pTemplate = gGMACRegisterTemplate; true; pTemplate++ )
{
bcopy( pTemplate, dest, sizeof( LengthOffset ) );
dest += sizeof( LengthOffset );
len = pTemplate->setLength;
if ( len == 0 )
break;
if ( pTemplate->setOffset != 0 )
{
src = (UInt8*)fProvider->fpRegs + pTemplate->setOffset;
}
else
{ pl = (UInt32*)fProvider->fpRegs;
lowRegs[ 0 ] = pl[ 0 ];
lowRegs[ 1 ] = pl[ 1 ];
lowRegs[ 2 ] = pl[ 2 ];
lowRegs[ 3 ] = pl[ 7 ];
lowRegs[ 4 ] = pl[ 4 ];
lowRegs[ 5 ] = pl[ 5 ];
lowRegs[ 6 ] = pl[ 6 ];
lowRegs[ 7 ] = pl[ 7 ];
src = (UInt8*)lowRegs;
}
UInt32 i;
UInt32* d32 = (UInt32*)dest;
UInt32* s32 = (UInt32*)src;
for ( i = 0; i < len / 4; i++ )
{
*d32++ = OSReadLittleInt32( s32, 0 );
s32++;
}
dest += len;
}
*pOutPutSize = dest - (UInt8*)pOut;
UC_ELG( 0, *pOutPutSize, 'gReg', "UniNEnetUserClient::getGMACRegs - done" );
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::getOneGMACReg( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *outPutSize )
{
UCRequest *req = (UCRequest*)pIn;
UInt32 *reg_value = (UInt32*)pOut;
UInt32 reg;
*outPutSize = 0; reg = (UInt32)req->pLogBuffer; if ( (reg > 0x905C) || (reg & 3) )
return kIOReturnError;
if ( !fProvider->fCellClockEnabled )
return kIOReturnNoPower;
*reg_value = OSReadLittleInt32( (UInt32*)fProvider->fpRegs, reg );
*outPutSize = sizeof( UInt32 );
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::writeOneGMACReg( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *outPutSize )
{
UCRequest *req = (UCRequest*)pIn;
UInt32 regValue;
UInt32 reg;
*outPutSize = 0; reg = (UInt32)req->pLogBuffer; if ( (reg > 0x905C) || (reg & 3) )
{
IOLog( "UniNEnetUserClient::writeOneGMACReg - invalid register number: 0x%lx \n",
reg );
return kIOReturnError;
}
if ( !fProvider->fCellClockEnabled )
return kIOReturnNoPower;
regValue = req->bufSize;
OSWriteLittleInt32( (UInt32*)fProvider->fpRegs, reg, regValue );
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::getGMACTxRing( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *pOutPutSize )
{
UInt64 *src;
UInt64 *dst;
UInt32 len;
src = (UInt64*)fProvider->fTxDescriptorRing;
dst = (UInt64*)pOut;
len = fProvider->fTxRingElements * sizeof( TxDescriptor );
*pOutPutSize = len;
while ( len )
{
*dst++ = OSReadLittleInt64( src, 0 );
src += 1;
len -= sizeof( UInt64 );
}
UC_ELG( fProvider->fTxRingElements, fProvider->fTxDescriptorRing, 'gTxR', "UniNEnetUserClient::getGMACTxRing - done" );
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::getGMACRxRing( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *pOutPutSize )
{
UInt64 *src = (UInt64*)fProvider->fRxDescriptorRing;
UInt64 *dst = (UInt64*)pOut;
UInt32 len = fProvider->fRxRingElements * sizeof( RxDescriptor );
*pOutPutSize = len;
while ( len )
{
*dst++ = OSReadLittleInt64( src, 0 );
src++;
len -= sizeof( UInt64 );
}
UC_ELG( fProvider->fRxRingElements, fProvider->fRxDescriptorRing, 'gRxR', "UniNEnetUserClient::getGMACRxRing - done" );
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::readAllMII( void *pIn,
void *pOut,
IOByteCount inputSize,
IOByteCount *outPutSize )
{
UCRequest *req = (UCRequest*)pIn;
UInt32 phyAddr, origPHYaddr = 0;
UInt16 *reg_value; UInt16 i;
bool result;
bool cellClockEnabled;
IOReturn ior = kIOReturnSuccess;
if ( !(pOut && outPutSize && *outPutSize >= (32 * sizeof( UInt16 ))) )
{
UC_ELG( pOut, outPutSize, 'rAll', "UniNEnetUserClient::readAllMII - bad argument" );
IOLog( "UniNEnetUserClient::readAllMII - bad argument(s) pOut = %08lx, outPutSize = %08lx, ", (UInt32)pOut, (UInt32)outPutSize );
if ( outPutSize )
IOLog( "*outPutSize = %ld.", (UInt32)*outPutSize );
IOLog( "\n" );
return kIOReturnBadArgument;
}
phyAddr = (UInt32)req->pLogBuffer >> 16;
if ( phyAddr > 31 && phyAddr != 0xFF )
{
IOLog( "UniNEnetUserClient::readAllMII - bad argument PHY address = %lx\n", phyAddr );
return kIOReturnBadArgument;
}
cellClockEnabled = fProvider->fCellClockEnabled;
if ( cellClockEnabled == false )
fProvider->enableCellClock();
if ( phyAddr != 0xFF )
{
origPHYaddr = fProvider->phyId;
fProvider->phyId = phyAddr;
}
reg_value = (UInt16*)pOut;
*outPutSize = 0;
for ( i = 0 ; i < 32; i++ )
{
result = fProvider->miiReadWord( reg_value, i );
if ( result )
{
reg_value++; *outPutSize += sizeof( UInt16 ); }
else
{
IOLog( "Read of PHY register %d failed.\n", i );
ior = kIOReturnError;
break;
}
}
if ( phyAddr != 0xFF )
fProvider->phyId = origPHYaddr;
if ( cellClockEnabled == false )
fProvider->disableCellClock();
return ior;
}
IOReturn UniNEnetUserClient::readMII( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *outPutSize )
{
UCRequest *req = (UCRequest*)pIn;
UInt16 *reg_value = (UInt16*)pOut;
UInt32 reg_num, phyAddr, origPHYaddr = 0;
bool result;
bool cellClockEnabled;
*outPutSize = 0; phyAddr = (UInt32)req->pLogBuffer >> 16;
reg_num = (UInt32)req->pLogBuffer & 0xFFFF; if ( reg_num > 31 )
return kIOReturnError;
cellClockEnabled = fProvider->fCellClockEnabled;
if ( cellClockEnabled == false )
fProvider->enableCellClock();
if ( phyAddr != 0xFF )
{
origPHYaddr = fProvider->phyId;
fProvider->phyId = phyAddr;
}
result = fProvider->miiReadWord( reg_value, reg_num );
if ( phyAddr != 0xFF )
fProvider->phyId = origPHYaddr;
if ( cellClockEnabled == false )
fProvider->disableCellClock();
if ( !result )
{
IOLog( "Read of PHY register %ld failed.\n", reg_num );
return kIOReturnError; }
*outPutSize = sizeof( UInt16 );
return kIOReturnSuccess;
}
IOReturn UniNEnetUserClient::writeMII( void *pIn, void *pOut,
IOByteCount inputSize, IOByteCount *outPutSize )
{
UCRequest *req = (UCRequest*)pIn;
UInt32 reg_num, reg_val, phyAddr, origPHYaddr = 0;
bool result;
bool cellClockEnabled;
phyAddr = (UInt32)req->pLogBuffer >> 16;
reg_num = (UInt32)req->pLogBuffer & 0xFFFF;
reg_val = req->bufSize;
if ( reg_num > 31 || reg_val > 0xFFFF || (phyAddr > 31 && phyAddr != 0xFF) )
return kIOReturnBadArgument;
cellClockEnabled = fProvider->fCellClockEnabled;
if ( cellClockEnabled == false )
fProvider->enableCellClock();
if ( phyAddr != 0xFF )
{
origPHYaddr = fProvider->phyId;
fProvider->phyId = phyAddr;
}
result = fProvider->miiWriteWord( reg_val, reg_num );
if ( phyAddr != 0xFF )
fProvider->phyId = origPHYaddr;
if ( cellClockEnabled == false )
fProvider->disableCellClock();
if ( !result )
{
IOLog( "Write of PHY register %ld failed.\n", reg_num );
return kIOReturnError; }
return kIOReturnSuccess;
}